Browse Source

substrate v2: updated node template and dependencies

Mokhtar Naamani 5 years ago
parent
commit
246860547a
8 changed files with 1036 additions and 776 deletions
  1. 373 225
      Cargo.lock
  2. 42 22
      Cargo.toml
  3. 23 4
      build.rs
  4. 288 278
      src/chain_spec.rs
  5. 69 65
      src/cli.rs
  6. 0 29
      src/error.rs
  7. 6 4
      src/main.rs
  8. 235 149
      src/service.rs

File diff suppressed because it is too large
+ 373 - 225
Cargo.lock


+ 42 - 22
Cargo.toml

@@ -7,28 +7,36 @@ authors = ['Joystream']
 build = 'build.rs'
 edition = '2018'
 name = 'joystream-node'
-version = '1.1.0'
+version = '2.0.0'
 
 [dependencies]
-error-chain = '0.12'
+hex-literal = '0.1'
+derive_more = '0.14.0'
 exit-future = '0.1'
 futures = '0.1'
-hex-literal = '0.1'
 log = '0.4'
-parity-codec = '3.2'
-parking_lot = '0.7.1'
+parking_lot = '0.9.0'
 tokio = '0.1'
-trie-root = '0.12.0'
+trie-root = '0.15.2'
 
 [dependencies.basic-authorship]
 git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-basic-authorship'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
-[dependencies.consensus]
+[dependencies.babe]
 git = 'https://github.com/paritytech/substrate.git'
-package = 'substrate-consensus-aura'
-branch = 'v1.0'
+package = 'substrate-consensus-babe'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
+
+[dependencies.babe-primitives]
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-consensus-babe-primitives'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
+
+[dependencies.codec]
+package = 'parity-scale-codec'
+version = '1.0.0'
 
 [dependencies.ctrlc]
 features = ['termination']
@@ -37,56 +45,68 @@ version = '3.0'
 [dependencies.inherents]
 git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-inherents'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.network]
 git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-network'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
-[dependencies.joystream-node-runtime]
+[dependencies.node-template-runtime]
+package = "joystream-node-runtime"
 # clone https://github.com/joystream/substrate-runtime-joystream to this path:
 path = 'substrate-runtime-joystream'
 
 [dependencies.primitives]
 git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-primitives'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.sr-io]
 git = 'https://github.com/paritytech/substrate.git'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.substrate-cli]
 git = 'https://github.com/paritytech/substrate.git'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.substrate-client]
 git = 'https://github.com/paritytech/substrate.git'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.substrate-executor]
 git = 'https://github.com/paritytech/substrate.git'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.substrate-service]
 git = 'https://github.com/paritytech/substrate.git'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.transaction-pool]
 git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-transaction-pool'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.substrate-telemetry]
 git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-telemetry'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [dependencies.grandpa]
 git = 'https://github.com/paritytech/substrate.git'
 package = 'substrate-finality-grandpa'
-branch = 'v1.0'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
+
+[dependencies.grandpa-primitives]
+git = 'https://github.com/paritytech/substrate.git'
+package = 'substrate-finality-grandpa-primitives'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
+
+[dependencies.im-online]
+default_features = false
+git = 'https://github.com/paritytech/substrate.git'
+package = 'srml-im-online'
+rev = 'a2a0eb5398d6223e531455b4c155ef053a4a3a2b'
 
 [profile.release]
 panic = 'unwind'

+ 23 - 4
build.rs

@@ -1,8 +1,27 @@
-use vergen::{ConstantsFlags, generate_cargo_keys};
+use std::{env, path::PathBuf};
 
-const ERROR_MSG: &'static str = "Failed to generate metadata files";
+use vergen::{generate_cargo_keys, ConstantsFlags};
+
+const ERROR_MSG: &str = "Failed to generate metadata files";
 
 fn main() {
-	generate_cargo_keys(ConstantsFlags::all()).expect(ERROR_MSG);
-	println!("cargo:rerun-if-changed=.git/HEAD");
+    generate_cargo_keys(ConstantsFlags::SHA_SHORT).expect(ERROR_MSG);
+
+    let mut manifest_dir = PathBuf::from(
+        env::var("CARGO_MANIFEST_DIR").expect("`CARGO_MANIFEST_DIR` is always set by cargo."),
+    );
+
+    while manifest_dir.parent().is_some() {
+        if manifest_dir.join(".git/HEAD").exists() {
+            println!(
+                "cargo:rerun-if-changed={}",
+                manifest_dir.join(".git/HEAD").display()
+            );
+            return;
+        }
+
+        manifest_dir.pop();
+    }
+
+    println!("cargo:warning=Could not find `.git/HEAD` from manifest dir!");
 }

+ 288 - 278
src/chain_spec.rs

@@ -15,23 +15,26 @@
 // along with Joystream node.  If not, see <http://www.gnu.org/licenses/>.
 
 use hex_literal::{hex, hex_impl};
-use joystream_node_runtime::{
-    forum::InputValidationLengthConstraint, AccountId, ActorsConfig, BalancesConfig,
-    ConsensusConfig, CouncilConfig, CouncilElectionConfig, DataObjectStorageRegistryConfig,
-    DataObjectTypeRegistryConfig, DownloadSessionsConfig, ForumConfig, GenesisConfig,
-    GrandpaConfig, IndicesConfig, MembersConfig, Perbill, ProposalsConfig, SessionConfig,
-    StakerStatus, StakingConfig, SudoConfig, TimestampConfig,
+use node_template_runtime::{
+    forum::InputValidationLengthConstraint, AccountId, ActorsConfig, AuthorityDiscoveryConfig,
+    BabeConfig, Balance, BalancesConfig, CouncilConfig, CouncilElectionConfig,
+    DataObjectStorageRegistryConfig, DataObjectTypeRegistryConfig, DownloadSessionsConfig,
+    ForumConfig, GenesisConfig, GrandpaConfig, ImOnlineConfig, IndicesConfig, MembersConfig,
+    Perbill, ProposalsConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, SudoConfig,
+    SystemConfig, DAYS, WASM_BINARY,
 };
-use primitives::{crypto::UncheckedInto, ed25519, sr25519, Pair};
+use primitives::{crypto::UncheckedInto, Pair, Public};
+
+use babe_primitives::AuthorityId as BabeId;
+use grandpa_primitives::AuthorityId as GrandpaId;
+use im_online::sr25519::AuthorityId as ImOnlineId;
 use substrate_service;
 use substrate_telemetry::TelemetryEndpoints;
 
-use ed25519::Public as AuthorityId;
-
 // Note this is the URL for the telemetry server
 const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
 
-/// Specialised `ChainSpec`. This is a specialisation of the general Substrate ChainSpec type.
+/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
 pub type ChainSpec = substrate_service::ChainSpec<GenesisConfig>;
 
 /// The chain specification option. This is expected to come in from the CLI and
@@ -49,16 +52,32 @@ pub enum Alternative {
     LiveTestnet,
 }
 
-fn authority_key(s: &str) -> AuthorityId {
-    ed25519::Pair::from_string(&format!("//{}", s), None)
+/// Helper function to generate a crypto pair from seed
+pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Public {
+    TPublic::Pair::from_string(&format!("//{}", seed), None)
         .expect("static values are valid; qed")
         .public()
 }
 
-fn account_key(s: &str) -> AccountId {
-    sr25519::Pair::from_string(&format!("//{}", s), None)
-        .expect("static values are valid; qed")
-        .public()
+/// Helper function to generate stash, controller and session key from seed
+pub fn get_authority_keys_from_seed(
+    seed: &str,
+) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId) {
+    (
+        get_from_seed::<AccountId>(&format!("{}//stash", seed)),
+        get_from_seed::<AccountId>(seed),
+        get_from_seed::<GrandpaId>(seed),
+        get_from_seed::<BabeId>(seed),
+        get_from_seed::<ImOnlineId>(seed),
+    )
+}
+
+fn session_keys(grandpa: GrandpaId, babe: BabeId, im_online: ImOnlineId) -> SessionKeys {
+    SessionKeys {
+        grandpa,
+        babe,
+        im_online,
+    }
 }
 
 impl Alternative {
@@ -70,20 +89,14 @@ impl Alternative {
                 "dev",
                 || {
                     testnet_genesis(
+                        vec![get_authority_keys_from_seed("Alice")],
+                        get_from_seed::<AccountId>("Alice"),
                         vec![
-                            // stash, controller, authority
-                            (
-                                account_key("Alice//stash"),
-                                account_key("Alice"),
-                                authority_key("Alice"),
-                            ),
+                            get_from_seed::<AccountId>("Alice"),
+                            get_from_seed::<AccountId>("Bob"),
+                            get_from_seed::<AccountId>("Alice//stash"),
+                            get_from_seed::<AccountId>("Bob//stash"),
                         ],
-                        vec![
-                            // endowed account
-                            account_key("Alice"),
-                        ],
-                        // sudo key
-                        account_key("Alice"),
                     )
                 },
                 vec![],
@@ -98,26 +111,24 @@ impl Alternative {
                 || {
                     testnet_genesis(
                         vec![
-                            (
-                                account_key("Alice//stash"),
-                                account_key("Alice"),
-                                authority_key("Alice"),
-                            ),
-                            (
-                                account_key("Bob//stash"),
-                                account_key("Bob"),
-                                authority_key("Bob"),
-                            ),
+                            get_authority_keys_from_seed("Alice"),
+                            get_authority_keys_from_seed("Bob"),
                         ],
+                        get_from_seed::<AccountId>("Alice"),
                         vec![
-                            account_key("Alice"),
-                            account_key("Bob"),
-                            account_key("Charlie"),
-                            account_key("Dave"),
-                            account_key("Eve"),
-                            account_key("Ferdie"),
+                            get_from_seed::<AccountId>("Alice"),
+                            get_from_seed::<AccountId>("Bob"),
+                            get_from_seed::<AccountId>("Charlie"),
+                            get_from_seed::<AccountId>("Dave"),
+                            get_from_seed::<AccountId>("Eve"),
+                            get_from_seed::<AccountId>("Ferdie"),
+                            get_from_seed::<AccountId>("Alice//stash"),
+                            get_from_seed::<AccountId>("Bob//stash"),
+                            get_from_seed::<AccountId>("Charlie//stash"),
+                            get_from_seed::<AccountId>("Dave//stash"),
+                            get_from_seed::<AccountId>("Eve//stash"),
+                            get_from_seed::<AccountId>("Ferdie//stash"),
                         ],
-                        account_key("Alice"),
                     )
                 },
                 vec![],
@@ -142,9 +153,10 @@ impl Alternative {
     }
 }
 
-/// LiveTestnet generator
+/// Joystream LiveTestnet generator
 pub fn live_testnet_config() -> Result<ChainSpec, String> {
-    ChainSpec::from_embedded(include_bytes!("../res/joy_testnet_2.json"))
+    //ChainSpec::from_embedded(include_bytes!("../res/joy_testnet_2.json"))
+    ChainSpec::from_json_bytes(&include_bytes!("../res/joy_testnet_2.json")[..])
 }
 
 /// Staging testnet config
@@ -155,16 +167,16 @@ pub fn staging_testnet_config() -> ChainSpec {
 	];
     ChainSpec::from_genesis(
         "Joystream Staging Testnet",
-        "joy_staging_5",
+        "joy_staging_6",
         staging_testnet_config_genesis,
         boot_nodes,
         Some(TelemetryEndpoints::new(vec![(
             STAGING_TELEMETRY_URL.to_string(),
             0,
         )])),
-        None,
-        None,
-        None,
+        Some(&*"joy"), // protocol_id
+        None, // consensus engine
+        None, // properties
     )
 }
 
@@ -173,248 +185,246 @@ fn new_validation(min: u16, max_min_diff: u16) -> InputValidationLengthConstrain
 }
 
 fn staging_testnet_config_genesis() -> GenesisConfig {
-    let initial_authorities: Vec<(AccountId, AccountId, AuthorityId)> = vec![(
+    let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)> = vec![(
         hex!["0610d1a2b1d704723e588c842a934737491688b18b052baae1286f12e96adb65"].unchecked_into(), // stash
         hex!["609cee3edd9900e69be44bcbf7a1892cad10408840a2d72d563811d72d9bb339"].unchecked_into(), // controller
         hex!["65179fd9c39ec301457d1ee47a13f3bb0fef65812a57b6c93212e609b10d35d2"].unchecked_into(), // session key
+        hex!["8acbf7448d96592e61881c5ef0f0ab18da6955cf4824534eb19b26f03df56b5a"].unchecked_into(),
+        hex!["8acbf7448d96592e61881c5ef0f0ab18da6955cf4824534eb19b26f03df56b5a"].unchecked_into(),
     )];
     let endowed_accounts = vec![hex![
         "0ae55282e669fc55cb9529c0b12b989f2c5bf636d0de7630b5a4850055ed9c30"
     ]
     .unchecked_into()];
 
-    const CENTS: u128 = 1;
-    const DOLLARS: u128 = 100 * CENTS;
-
-    const SECS_PER_BLOCK: u64 = 6;
-    const MINUTES: u64 = 60 / SECS_PER_BLOCK;
-    const HOURS: u64 = MINUTES * 60;
-    const DAYS: u64 = HOURS * 24;
-    const STASH: u128 = 50 * DOLLARS;
-    const ENDOWMENT: u128 = 100_000_000 * DOLLARS;
+    const CENTS: Balance = 1;
+    const DOLLARS: Balance = 100 * CENTS;
+    const STASH: Balance = 50 * DOLLARS;
+    const ENDOWMENT: Balance = 100_000_000 * DOLLARS;
 
     GenesisConfig {
-		consensus: Some(ConsensusConfig {
-			code: include_bytes!("../substrate-runtime-joystream/wasm/target/wasm32-unknown-unknown/release/joystream_node_runtime_wasm.compact.wasm").to_vec(),
-			authorities: initial_authorities.iter().map(|x| x.2.clone()).collect(),
-		}),
-		system: None,
-		timestamp: Some(TimestampConfig {
-			minimum_period: SECS_PER_BLOCK / 2, // due to the nature of aura the slots are 2*period
-		}),
-		indices: Some(IndicesConfig {
-			ids: vec![],
-		}),
-		balances: Some(BalancesConfig {
-			balances: endowed_accounts.iter().cloned()
-				.map(|k| (k, ENDOWMENT))
-				.chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
-				.collect(),
-			existential_deposit: 0,
-			transfer_fee: 0,
-			creation_fee: 0,
-			vesting: vec![],
-			transaction_base_fee: 1,
-			transaction_byte_fee: 0,
-		}),
-		sudo: Some(SudoConfig {
-			key: endowed_accounts[0].clone(),
-		}),
-		session: Some(SessionConfig {
-			validators: initial_authorities.iter().map(|x| x.1.clone()).collect(),
-			session_length: 10 * MINUTES,
-			keys: initial_authorities.iter().map(|x| (x.1.clone(), x.2.clone())).collect::<Vec<_>>(),
-		}),
-		staking: Some(StakingConfig {
-			current_era: 0,
-			offline_slash: Perbill::from_millionths(10_000),  // 1/ 100 => 1%
-			session_reward: Perbill::from_millionths(1_000),  // 1/1000 => 0.1% (min stake -> 1000 units for reward to be GT 0)
-			current_session_reward: 0,
-			validator_count: 20,
-			sessions_per_era: 6,
-			bonding_duration: 1, // Number of ERAs
-			offline_slash_grace: 4,
-			minimum_validator_count: 1,
-			stakers: initial_authorities.iter().map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)).collect(),
-			invulnerables: initial_authorities.iter().map(|x| x.1.clone()).collect(),
-		}),
-		grandpa: Some(GrandpaConfig {
-			authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(),
-		}),
-		council: Some(CouncilConfig {
-			active_council: vec![],
-			term_ends_at: 1,
-		}),
-		election: Some(CouncilElectionConfig {
-			auto_start: true,
-			announcing_period: 3 * DAYS,
-			voting_period: 1 * DAYS,
-			revealing_period: 1 * DAYS,
-			council_size: 12,
-			candidacy_limit: 25,
-			min_council_stake: 10 * DOLLARS,
-			new_term_duration: 14 * DAYS,
-			min_voting_stake: 1 * DOLLARS,
-		}),
-		proposals: Some(ProposalsConfig {
-			approval_quorum: 66,
-			min_stake: 2 * DOLLARS,
-			cancellation_fee: 10 * CENTS,
-			rejection_fee: 1 * DOLLARS,
-			voting_period: 2 * DAYS,
-			name_max_len: 512,
-			description_max_len: 10_000,
-			wasm_code_max_len: 2_000_000,
-		}),
-		members: Some(MembersConfig {
-			default_paid_membership_fee: 100u128,
-			first_member_id: 1,
-		}),
-		forum: Some(ForumConfig {
-			category_by_id: vec![],
-			thread_by_id: vec![],
-			post_by_id: vec![],
-			next_category_id: 1,
-			next_thread_id: 1,
-			next_post_id: 1,
-			forum_sudo: endowed_accounts[0].clone(),
-			category_title_constraint: new_validation(10, 90),
-			category_description_constraint: new_validation(10, 490),
-			thread_title_constraint: new_validation(10, 90),
-			post_text_constraint: new_validation(10, 990),
-			thread_moderation_rationale_constraint: new_validation(10, 290),
-			post_moderation_rationale_constraint: new_validation(10, 290)
-		}),
-		data_object_type_registry: Some(DataObjectTypeRegistryConfig {
-			first_data_object_type_id: 1,
-		}),
-		data_object_storage_registry: Some(DataObjectStorageRegistryConfig{
-			first_relationship_id: 1,
-		}),
-		downloads: Some(DownloadSessionsConfig{
-			first_download_session_id: 1,
-		}),
-		actors: Some(ActorsConfig{
-			enable_storage_role: true,
-			request_life_time: 300,
-			_genesis_phantom_data: Default::default(),
-		})
-	}
+        system: Some(SystemConfig {
+            code: WASM_BINARY.to_vec(),
+            changes_trie_config: Default::default(),
+        }),
+        balances: Some(BalancesConfig {
+            balances: endowed_accounts
+                .iter()
+                .cloned()
+                .map(|k| (k, ENDOWMENT))
+                .chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
+                .collect(),
+            vesting: vec![],
+        }),
+        indices: Some(IndicesConfig { ids: vec![] }),
+        session: Some(SessionConfig {
+            keys: initial_authorities
+                .iter()
+                .map(|x| {
+                    (
+                        x.0.clone(),
+                        session_keys(x.2.clone(), x.3.clone(), x.4.clone()),
+                    )
+                })
+                .collect::<Vec<_>>(),
+        }),
+        staking: Some(StakingConfig {
+            current_era: 0,
+            validator_count: 20,
+            minimum_validator_count: 1,
+            stakers: initial_authorities
+                .iter()
+                .map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator))
+                .collect(),
+            invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+            slash_reward_fraction: Perbill::from_percent(10),
+            ..Default::default()
+        }),
+        sudo: Some(SudoConfig {
+            key: endowed_accounts[0].clone(),
+        }),
+        babe: Some(BabeConfig {
+            authorities: vec![],
+        }),
+        im_online: Some(ImOnlineConfig { keys: vec![] }),
+        authority_discovery: Some(AuthorityDiscoveryConfig { keys: vec![] }),
+        grandpa: Some(GrandpaConfig {
+            authorities: vec![],
+        }),
+        council: Some(CouncilConfig {
+            active_council: vec![],
+            term_ends_at: 1,
+        }),
+        election: Some(CouncilElectionConfig {
+            auto_start: true,
+            announcing_period: 3 * DAYS,
+            voting_period: 1 * DAYS,
+            revealing_period: 1 * DAYS,
+            council_size: 12,
+            candidacy_limit: 25,
+            min_council_stake: 10 * DOLLARS,
+            new_term_duration: 14 * DAYS,
+            min_voting_stake: 1 * DOLLARS,
+        }),
+        proposals: Some(ProposalsConfig {
+            approval_quorum: 66,
+            min_stake: 2 * DOLLARS,
+            cancellation_fee: 10 * CENTS,
+            rejection_fee: 1 * DOLLARS,
+            voting_period: 2 * DAYS,
+            name_max_len: 512,
+            description_max_len: 10_000,
+            wasm_code_max_len: 2_000_000,
+        }),
+        members: Some(MembersConfig {
+            default_paid_membership_fee: 100u128,
+            first_member_id: 1,
+        }),
+        forum: Some(ForumConfig {
+            category_by_id: vec![],
+            thread_by_id: vec![],
+            post_by_id: vec![],
+            next_category_id: 1,
+            next_thread_id: 1,
+            next_post_id: 1,
+            forum_sudo: endowed_accounts[0].clone(),
+            category_title_constraint: new_validation(10, 90),
+            category_description_constraint: new_validation(10, 490),
+            thread_title_constraint: new_validation(10, 90),
+            post_text_constraint: new_validation(10, 990),
+            thread_moderation_rationale_constraint: new_validation(10, 290),
+            post_moderation_rationale_constraint: new_validation(10, 290),
+        }),
+        data_object_type_registry: Some(DataObjectTypeRegistryConfig {
+            first_data_object_type_id: 1,
+        }),
+        data_object_storage_registry: Some(DataObjectStorageRegistryConfig {
+            first_relationship_id: 1,
+        }),
+        downloads: Some(DownloadSessionsConfig {
+            first_download_session_id: 1,
+        }),
+        actors: Some(ActorsConfig {
+            enable_storage_role: true,
+            request_life_time: 300,
+        }),
+    }
 }
 
-fn testnet_genesis(
-    initial_authorities: Vec<(AccountId, AccountId, AuthorityId)>,
-    endowed_accounts: Vec<AccountId>,
+/// Helper function to create GenesisConfig for testing
+pub fn testnet_genesis(
+    initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)>,
     root_key: AccountId,
+    endowed_accounts: Vec<AccountId>,
 ) -> GenesisConfig {
-    const STASH: u128 = 100;
-    const ENDOWMENT: u128 = 100_000_000;
+    const STASH: Balance = 10000;
+    const ENDOWMENT: Balance = 100_000_000;
 
     GenesisConfig {
-		consensus: Some(ConsensusConfig {
-			code: include_bytes!("../substrate-runtime-joystream/wasm/target/wasm32-unknown-unknown/release/joystream_node_runtime_wasm.compact.wasm").to_vec(),
-			authorities: initial_authorities.iter().map(|x| x.2.clone()).collect(),
-		}),
-		system: None,
-		timestamp: Some(TimestampConfig {
-			minimum_period: 3,                    // 3*2=6 second block time.
-		}),
-		indices: Some(IndicesConfig {
-			ids: vec![]
-		}),
-		balances: Some(BalancesConfig {
-			existential_deposit: 0,
-			transfer_fee: 0,
-			creation_fee: 0,
-			balances: endowed_accounts.iter().cloned()
-				.map(|k| (k, ENDOWMENT))
-				.chain(initial_authorities.iter().map(|x| (x.0.clone(), STASH)))
-				.collect(),
-			vesting: vec![],
-			transaction_base_fee: 1,
-			transaction_byte_fee: 0,
-		}),
-		sudo: Some(SudoConfig {
-			key: root_key.clone(),
-		}),
-		session: Some(SessionConfig {
-			validators: initial_authorities.iter().map(|x| x.1.clone()).collect(),
-			session_length: 10,
-			keys: initial_authorities.iter().map(|x| (x.1.clone(), x.2.clone())).collect::<Vec<_>>(),
-		}),
-		staking: Some(StakingConfig {
-			current_era: 0,
-			minimum_validator_count: 1,
-			validator_count: 2,
-			sessions_per_era: 5,
-			bonding_duration: 1, // Number of Eras
-			offline_slash: Perbill::zero(),
-			session_reward: Perbill::zero(),
-			current_session_reward: 0,
-			offline_slash_grace: 0,
-			stakers: initial_authorities.iter().map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator)).collect(),
-			invulnerables: initial_authorities.iter().map(|x| x.1.clone()).collect(),
-		}),
-		grandpa: Some(GrandpaConfig {
-			authorities: initial_authorities.iter().map(|x| (x.2.clone(), 1)).collect(),
-		}),
-		council: Some(CouncilConfig {
-			active_council: vec![],
-			term_ends_at: 1,
-		}),
-		election: Some(CouncilElectionConfig {
-			auto_start: true,
-			announcing_period: 50,
-			voting_period: 50,
-			revealing_period: 50,
-			council_size: 2,
-			candidacy_limit: 25,
-			min_council_stake: 100,
-			new_term_duration: 1000,
-			min_voting_stake: 10,
-		}),
-		proposals: Some(ProposalsConfig {
-			approval_quorum: 66,
-			min_stake: 100,
-			cancellation_fee: 5,
-			rejection_fee: 10,
-			voting_period: 100,
-			name_max_len: 100,
-			description_max_len: 10_000,
-			wasm_code_max_len: 2_000_000,
-		}),
-		members: Some(MembersConfig {
-			default_paid_membership_fee: 100u128,
-			first_member_id: 1,
-		}),
-		forum: Some(ForumConfig {
-			category_by_id: vec![],
-			thread_by_id: vec![],
-			post_by_id: vec![],
-			next_category_id: 1,
-			next_thread_id: 1,
-			next_post_id: 1,
-			forum_sudo: root_key,
-			category_title_constraint: new_validation(10, 90),
-			category_description_constraint: new_validation(10, 490),
-			thread_title_constraint: new_validation(10, 90),
-			post_text_constraint: new_validation(10, 990),
-			thread_moderation_rationale_constraint: new_validation(10, 290),
-			post_moderation_rationale_constraint: new_validation(10, 290)
-		}),
-		data_object_type_registry: Some(DataObjectTypeRegistryConfig {
-			first_data_object_type_id: 1,
-		}),
-		data_object_storage_registry: Some(DataObjectStorageRegistryConfig{
-			first_relationship_id: 1,
-		}),
-		downloads: Some(DownloadSessionsConfig{
-			first_download_session_id: 1,
-		}),
-		actors: Some(ActorsConfig{
-			enable_storage_role: true,
-			request_life_time: 300,
-			_genesis_phantom_data: Default::default(),
-		})
-	}
+        //substrate modules
+        system: Some(SystemConfig {
+            code: WASM_BINARY.to_vec(),
+            changes_trie_config: Default::default(),
+        }),
+        balances: Some(BalancesConfig {
+            balances: endowed_accounts
+                .iter()
+                .map(|k| (k.clone(), ENDOWMENT))
+                .collect(),
+            vesting: vec![],
+        }),
+        indices: Some(IndicesConfig { ids: vec![] }),
+        staking: Some(StakingConfig {
+            current_era: 0,
+            minimum_validator_count: 1,
+            validator_count: 2,
+            stakers: initial_authorities
+                .iter()
+                .map(|x| (x.0.clone(), x.1.clone(), STASH, StakerStatus::Validator))
+                .collect(),
+            invulnerables: initial_authorities.iter().map(|x| x.0.clone()).collect(),
+            slash_reward_fraction: Perbill::from_percent(10),
+            ..Default::default()
+        }),
+        session: Some(SessionConfig {
+            keys: initial_authorities
+                .iter()
+                .map(|x| {
+                    (
+                        x.0.clone(),
+                        session_keys(x.2.clone(), x.3.clone(), x.4.clone()),
+                    )
+                })
+                .collect::<Vec<_>>(),
+        }),
+        sudo: Some(SudoConfig {
+            key: root_key.clone(),
+        }),
+        babe: Some(BabeConfig {
+            authorities: vec![],
+        }),
+        im_online: Some(ImOnlineConfig { keys: vec![] }),
+        authority_discovery: Some(AuthorityDiscoveryConfig { keys: vec![] }),
+        grandpa: Some(GrandpaConfig {
+            authorities: vec![],
+        }),
+        // joystream modules
+        council: Some(CouncilConfig {
+            active_council: vec![],
+            term_ends_at: 1,
+        }),
+        election: Some(CouncilElectionConfig {
+            auto_start: true,
+            announcing_period: 50,
+            voting_period: 50,
+            revealing_period: 50,
+            council_size: 2,
+            candidacy_limit: 25,
+            min_council_stake: 100,
+            new_term_duration: 1000,
+            min_voting_stake: 10,
+        }),
+        proposals: Some(ProposalsConfig {
+            approval_quorum: 66,
+            min_stake: 100,
+            cancellation_fee: 5,
+            rejection_fee: 10,
+            voting_period: 100,
+            name_max_len: 100,
+            description_max_len: 10_000,
+            wasm_code_max_len: 2_000_000,
+        }),
+        members: Some(MembersConfig {
+            default_paid_membership_fee: 100u128,
+            first_member_id: 1,
+        }),
+        forum: Some(ForumConfig {
+            category_by_id: vec![],
+            thread_by_id: vec![],
+            post_by_id: vec![],
+            next_category_id: 1,
+            next_thread_id: 1,
+            next_post_id: 1,
+            forum_sudo: root_key,
+            category_title_constraint: new_validation(10, 90),
+            category_description_constraint: new_validation(10, 490),
+            thread_title_constraint: new_validation(10, 90),
+            post_text_constraint: new_validation(10, 990),
+            thread_moderation_rationale_constraint: new_validation(10, 290),
+            post_moderation_rationale_constraint: new_validation(10, 290),
+        }),
+        data_object_type_registry: Some(DataObjectTypeRegistryConfig {
+            first_data_object_type_id: 1,
+        }),
+        data_object_storage_registry: Some(DataObjectStorageRegistryConfig {
+            first_relationship_id: 1,
+        }),
+        downloads: Some(DownloadSessionsConfig {
+            first_download_session_id: 1,
+        }),
+        actors: Some(ActorsConfig {
+            enable_storage_role: true,
+            request_life_time: 300,
+        }),
+    }
 }

+ 69 - 65
src/cli.rs

@@ -1,28 +1,12 @@
-// Copyright 2019 Joystream Contributors
-// This file is part of Joystream node.
-
-// Joystream node is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Joystream node is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Joystream node.  If not, see <http://www.gnu.org/licenses/>.
-
 use crate::chain_spec;
+use crate::new_full_start;
 use crate::service;
 use futures::{future, sync::oneshot, Future};
 use log::info;
 use std::cell::RefCell;
-use std::ops::Deref;
 pub use substrate_cli::{error, IntoExit, VersionInfo};
-use substrate_cli::{informant, parse_and_execute, NoCustom};
-use substrate_service::{Roles as ServiceRoles, ServiceFactory};
+use substrate_cli::{informant, parse_and_prepare, NoCustom, ParseAndPrepare};
+use substrate_service::{AbstractService, Roles as ServiceRoles};
 use tokio::runtime::Runtime;
 
 /// Parse command line arguments into service configuration.
@@ -32,39 +16,49 @@ where
     T: Into<std::ffi::OsString> + Clone,
     E: IntoExit,
 {
-    parse_and_execute::<service::Factory, NoCustom, NoCustom, _, _, _, _, _>(
-        load_spec,
-        &version,
-        "joystream-node",
-        args,
-        exit,
-        |exit, _custom_args, config| {
-            info!("{}", version.name);
-            info!("  version {}", config.full_version());
-            info!("  by {}, 2019", version.author);
-            info!("Chain specification: {}", config.chain_spec.name());
-            info!("Node name: {}", config.name);
-            info!("Roles: {:?}", config.roles);
-            let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
-            let executor = runtime.executor();
-            match config.roles {
-                ServiceRoles::LIGHT => run_until_exit(
-                    runtime,
-                    service::Factory::new_light(config, executor)
-                        .map_err(|e| format!("{:?}", e))?,
-                    exit,
-                ),
-                _ => run_until_exit(
-                    runtime,
-                    service::Factory::new_full(config, executor).map_err(|e| format!("{:?}", e))?,
-                    exit,
-                ),
-            }
-            .map_err(|e| format!("{:?}", e))
-        },
-    )
-    .map_err(Into::into)
-    .map(|_| ())
+    match parse_and_prepare::<NoCustom, NoCustom, _>(&version, "joystream-node", args) {
+        ParseAndPrepare::Run(cmd) => {
+            cmd.run::<(), _, _, _, _>(load_spec, exit, |exit, _cli_args, _custom_args, config| {
+                info!("{}", version.name);
+                info!("  version {}", config.full_version());
+                info!("  by {}, 2019", version.author);
+                info!("Chain specification: {}", config.chain_spec.name());
+                info!("Node name: {}", config.name);
+                info!("Roles: {:?}", config.roles);
+                let runtime = Runtime::new().map_err(|e| format!("{:?}", e))?;
+                match config.roles {
+                    ServiceRoles::LIGHT => run_until_exit(
+                        runtime,
+                        service::new_light(config).map_err(|e| format!("{:?}", e))?,
+                        exit,
+                    ),
+                    _ => run_until_exit(
+                        runtime,
+                        service::new_full(config).map_err(|e| format!("{:?}", e))?,
+                        exit,
+                    ),
+                }
+                .map_err(|e| format!("{:?}", e))
+            })
+        }
+        ParseAndPrepare::BuildSpec(cmd) => cmd.run(load_spec),
+        ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder::<(), _, _, _, _, _>(
+            |config| Ok(new_full_start!(config).0),
+            load_spec,
+            exit,
+        ),
+        ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder::<(), _, _, _, _, _>(
+            |config| Ok(new_full_start!(config).0),
+            load_spec,
+            exit,
+        ),
+        ParseAndPrepare::PurgeChain(cmd) => cmd.run(load_spec),
+        ParseAndPrepare::RevertChain(cmd) => cmd
+            .run_with_builder::<(), _, _, _, _>(|config| Ok(new_full_start!(config).0), load_spec),
+        ParseAndPrepare::CustomCommand(_) => Ok(()),
+    }?;
+
+    Ok(())
 }
 
 fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
@@ -74,25 +68,35 @@ fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
     })
 }
 
-fn run_until_exit<T, C, E>(mut runtime: Runtime, service: T, e: E) -> error::Result<()>
+fn run_until_exit<T, E>(mut runtime: Runtime, service: T, e: E) -> error::Result<()>
 where
-    T: Deref<Target = substrate_service::Service<C>>,
-    C: substrate_service::Components,
+    T: AbstractService,
     E: IntoExit,
 {
     let (exit_send, exit) = exit_future::signal();
 
-    let executor = runtime.executor();
-    informant::start(&service, exit.clone(), executor.clone());
-
-    let _ = runtime.block_on(e.into_exit());
-    exit_send.fire();
+    let informant = informant::build(&service);
+    runtime.executor().spawn(exit.until(informant).map(|_| ()));
 
     // we eagerly drop the service so that the internal exit future is fired,
     // but we need to keep holding a reference to the global telemetry guard
     let _telemetry = service.telemetry();
-    drop(service);
-    Ok(())
+
+    let service_res = {
+        let exit = e
+            .into_exit()
+            .map_err(|_| error::Error::Other("Exit future failed.".into()));
+        let service = service.map_err(|err| error::Error::Service(err));
+        let select = service.select(exit).map(|_| ()).map_err(|(err, _)| err);
+        runtime.block_on(select)
+    };
+
+    exit_send.fire();
+
+    // TODO [andre]: timeout this future #1318
+    let _ = runtime.shutdown_on_idle().wait();
+
+    service_res
 }
 
 // handles ctrl-c
@@ -105,11 +109,11 @@ impl IntoExit for Exit {
 
         let exit_send_cell = RefCell::new(Some(exit_send));
         ctrlc::set_handler(move || {
-            if let Some(exit_send) = exit_send_cell
+            let exit_send = exit_send_cell
                 .try_borrow_mut()
                 .expect("signal handler not reentrant; qed")
-                .take()
-            {
+                .take();
+            if let Some(exit_send) = exit_send {
                 exit_send.send(()).expect("Error sending exit notification");
             }
         })

+ 0 - 29
src/error.rs

@@ -1,29 +0,0 @@
-// Copyright 2019 Joystream Contributors
-// This file is part of Joystream node.
-
-// Joystream node is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-
-// Joystream node is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-
-// You should have received a copy of the GNU General Public License
-// along with Joystream node.  If not, see <http://www.gnu.org/licenses/>.
-
-//! Initialization errors.
-
-use client;
-
-error_chain! {
-    foreign_links {
-        Io(::std::io::Error) #[doc="IO error"];
-        Cli(::clap::Error) #[doc="CLI error"];
-    }
-    links {
-        Client(client::error::Error, client::error::ErrorKind) #[doc="Client error"];
-    }
-}

+ 6 - 4
src/main.rs

@@ -25,7 +25,7 @@ mod service;
 
 pub use substrate_cli::{error, IntoExit, VersionInfo};
 
-fn run() -> cli::error::Result<()> {
+fn main() {
     let version = VersionInfo {
         name: "Joystream Node",
         commit: env!("VERGEN_SHA_SHORT"),
@@ -35,7 +35,9 @@ fn run() -> cli::error::Result<()> {
         description: "Joystream substrate node",
         support_url: "https://www.joystream.org/",
     };
-    cli::run(::std::env::args(), cli::Exit, version)
-}
 
-error_chain::quick_main!(run);
+    if let Err(e) = cli::run(::std::env::args(), cli::Exit, version) {
+        eprintln!("Fatal error: {}\n\n{:?}", e, e);
+        std::process::exit(1)
+    }
+}

+ 235 - 149
src/service.rs

@@ -14,176 +14,262 @@
 // You should have received a copy of the GNU General Public License
 // along with Joystream node.  If not, see <http://www.gnu.org/licenses/>.
 
-//! Service and ServiceFactory implementation. Specialized wrapper over Substrate service.
-
 #![warn(unused_extern_crates)]
 
-use basic_authorship::ProposerFactory;
-use consensus::{import_queue, start_aura, AuraImportQueue, NothingExtra, SlotDuration};
-use grandpa;
+//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
+
+use babe::{import_queue, start_babe, Config};
+use futures::prelude::*;
+use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider};
 use inherents::InherentDataProviders;
-use joystream_node_runtime::{self, opaque::Block, GenesisConfig, RuntimeApi};
-use log::info;
 use network::construct_simple_protocol;
-use primitives::{crypto::Ss58Codec, ed25519::Pair, sr25519::Public as SrPublic, Pair as PairT};
+use node_template_runtime::{self, opaque::Block, GenesisConfig, RuntimeApi};
 use std::sync::Arc;
 use std::time::Duration;
-use substrate_client as client;
+use substrate_client::LongestChain;
 use substrate_executor::native_executor_instance;
-use substrate_service::construct_service_factory;
+pub use substrate_executor::NativeExecutor;
 use substrate_service::{
-    FactoryFullConfiguration, FullBackend, FullClient, FullComponents, FullExecutor, LightBackend,
-    LightClient, LightComponents, LightExecutor, TaskExecutor,
+    error::Error as ServiceError, AbstractService, Configuration, ServiceBuilder,
 };
 use transaction_pool::{self, txpool::Pool as TransactionPool};
 
-// Get new prefixed ss58 address encoding for ed25519 public keys
-fn ed_ss58check(public_key: &primitives::ed25519::Public) -> String {
-    let raw_bytes: &[u8; 32] = public_key.as_ref();
-    // Interpret bytes as sr25519 public key
-    let v: SrPublic = SrPublic::from_raw(raw_bytes.clone());
-    v.to_ss58check()
-}
-
-pub use substrate_executor::NativeExecutor;
 // Our native executor instance.
 native_executor_instance!(
 	pub Executor,
-	joystream_node_runtime::api::dispatch,
-	joystream_node_runtime::native_version,
-	include_bytes!("../substrate-runtime-joystream/wasm/target/wasm32-unknown-unknown/release/joystream_node_runtime_wasm.compact.wasm")
+	node_template_runtime::api::dispatch,
+	node_template_runtime::native_version
 );
 
-pub struct NodeConfig<F: substrate_service::ServiceFactory> {
-    pub grandpa_import_setup: Option<(
-        Arc<grandpa::BlockImportForService<F>>,
-        grandpa::LinkHalfForService<F>,
-    )>,
-    inherent_data_providers: InherentDataProviders,
-}
-
-impl<F> Default for NodeConfig<F>
-where
-    F: substrate_service::ServiceFactory,
-{
-    fn default() -> NodeConfig<F> {
-        NodeConfig {
-            grandpa_import_setup: None,
-            inherent_data_providers: InherentDataProviders::new(),
-        }
-    }
-}
-
 construct_simple_protocol! {
     /// Demo protocol attachment for substrate.
     pub struct NodeProtocol where Block = Block { }
 }
 
-construct_service_factory! {
-    struct Factory {
-        Block = Block,
-        RuntimeApi = RuntimeApi,
-        NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) },
-        RuntimeDispatch = Executor,
-        FullTransactionPoolApi = transaction_pool::ChainApi<client::Client<FullBackend<Self>, FullExecutor<Self>, Block, RuntimeApi>, Block>
-            { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
-        LightTransactionPoolApi = transaction_pool::ChainApi<client::Client<LightBackend<Self>, LightExecutor<Self>, Block, RuntimeApi>, Block>
-            { |config, client| Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(client))) },
-        Genesis = GenesisConfig,
-        Configuration = NodeConfig<Self>,
-        FullService = FullComponents<Self>
-            { |config: FactoryFullConfiguration<Self>, executor: TaskExecutor|
-                FullComponents::<Factory>::new(config, executor)
-            },
-        AuthoritySetup = {
-            |mut service: Self::FullService, executor: TaskExecutor, key: Option<Arc<Pair>>| {
-                let (block_import, link_half) = service.config.custom.grandpa_import_setup.take()
-                    .expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
-
-                if let Some(ref key) = key {
-                    info!("Using authority key {}", ed_ss58check(&key.public()));
-                    let proposer = Arc::new(ProposerFactory {
-                        client: service.client(),
-                        transaction_pool: service.transaction_pool(),
-                        inherents_pool: service.inherents_pool(),
-                    });
-
-                    let client = service.client();
-                    executor.spawn(start_aura(
-                        SlotDuration::get_or_compute(&*client)?,
-                        key.clone(),
-                        client,
-                        block_import.clone(),
-                        proposer,
-                        service.network(),
-                        service.on_exit(),
-                        service.config.custom.inherent_data_providers.clone(),
-                        service.config.force_authoring,
-                    )?);
-
-                    info!("Running Grandpa session as Authority {}", ed_ss58check(&key.public()));
-                }
-
-                let key = if service.config.disable_grandpa {
-                    None
-                } else {
-                    key
-                };
-
-                executor.spawn(grandpa::run_grandpa(
-                    grandpa::Config {
-                        local_key: key,
-                        // FIXME #1578 make this available through chainspec
-                        gossip_duration: Duration::from_millis(333),
-                        justification_period: 4096,
-                        name: Some(service.config.name.clone())
-                    },
-                    link_half,
-                    grandpa::NetworkBridge::new(service.network()),
-                    service.config.custom.inherent_data_providers.clone(),
-                    service.on_exit(),
-                )?);
-
-                Ok(service)
-            }
-        },
-        LightService = LightComponents<Self>
-            { |config, executor| <LightComponents<Factory>>::new(config, executor) },
-        FullImportQueue = AuraImportQueue<
-            Self::Block,
-        >
-            { |config: &mut FactoryFullConfiguration<Self> , client: Arc<FullClient<Self>>| {
-                let slot_duration = SlotDuration::get_or_compute(&*client)?;
-                let (block_import, link_half) =
-                    grandpa::block_import::<_, _, _, RuntimeApi, FullClient<Self>>(
-                        client.clone(), client.clone()
-                    )?;
-                let block_import = Arc::new(block_import);
-                let justification_import = block_import.clone();
-
-                config.custom.grandpa_import_setup = Some((block_import.clone(), link_half));
-
-                import_queue::<_, _, _, Pair>(
-                    slot_duration,
-                    block_import,
-                    Some(justification_import),
-                    client,
-                    NothingExtra,
-                    config.custom.inherent_data_providers.clone(),
-                ).map_err(Into::into)
-            }},
-        LightImportQueue = AuraImportQueue<
-            Self::Block,
-        >
-            { |config: &mut FactoryFullConfiguration<Self>, client: Arc<LightClient<Self>>|
-                import_queue::<_, _, _, Pair>(
-                    SlotDuration::get_or_compute(&*client)?,
+/// Starts a `ServiceBuilder` for a full service.
+///
+/// Use this macro if you don't actually need the full service, but just the builder in order to
+/// be able to perform chain operations.
+#[macro_export]
+macro_rules! new_full_start {
+    ($config:expr) => {{
+        let mut import_setup = None;
+        let inherent_data_providers = inherents::InherentDataProviders::new();
+        let mut tasks_to_spawn = None;
+
+        let builder = substrate_service::ServiceBuilder::new_full::<
+            node_template_runtime::opaque::Block,
+            node_template_runtime::RuntimeApi,
+            crate::service::Executor,
+        >($config)?
+        .with_select_chain(|_config, client| {
+            #[allow(deprecated)]
+            Ok(substrate_client::LongestChain::new(
+                client.backend().clone(),
+            ))
+        })?
+        .with_transaction_pool(|config, client| {
+            Ok(transaction_pool::txpool::Pool::new(
+                config,
+                transaction_pool::ChainApi::new(client),
+            ))
+        })?
+        .with_import_queue(|_config, client, mut select_chain, transaction_pool| {
+            let select_chain = select_chain
+                .take()
+                .ok_or_else(|| substrate_service::Error::SelectChainRequired)?;
+            let (block_import, link_half) =
+                grandpa::block_import::<_, _, _, node_template_runtime::RuntimeApi, _, _>(
+                    client.clone(),
                     client.clone(),
-                    None,
-                    client,
-                    NothingExtra,
-                    config.custom.inherent_data_providers.clone(),
-                ).map_err(Into::into)
-            },
+                    select_chain,
+                )?;
+            let justification_import = block_import.clone();
+
+            let (import_queue, babe_link, babe_block_import, pruning_task) = babe::import_queue(
+                babe::Config::get_or_compute(&*client)?,
+                block_import,
+                Some(Box::new(justification_import)),
+                None,
+                client.clone(),
+                client,
+                inherent_data_providers.clone(),
+                Some(transaction_pool),
+            )?;
+
+            import_setup = Some((babe_block_import.clone(), link_half, babe_link));
+            tasks_to_spawn = Some(vec![Box::new(pruning_task)]);
+
+            Ok(import_queue)
+        })?;
+
+        (
+            builder,
+            import_setup,
+            inherent_data_providers,
+            tasks_to_spawn,
+        )
+    }};
+}
+
+/// Builds a new service for a full client.
+pub fn new_full<C: Send + Default + 'static>(
+    config: Configuration<C, GenesisConfig>,
+) -> Result<impl AbstractService, ServiceError> {
+    let (builder, mut import_setup, inherent_data_providers, mut tasks_to_spawn) =
+        new_full_start!(config);
+
+    let service = builder
+        .with_network_protocol(|_| Ok(NodeProtocol::new()))?
+        .with_finality_proof_provider(|client| {
+            Ok(Arc::new(GrandpaFinalityProofProvider::new(client.clone(), client)) as _)
+        })?
+        .build()?;
+
+    let (block_import, link_half, babe_link) = import_setup.take().expect(
+        "Link Half and Block Import are present for Full Services or setup failed before. qed",
+    );
+
+    // spawn any futures that were created in the previous setup steps
+    if let Some(tasks) = tasks_to_spawn.take() {
+        for task in tasks {
+            service.spawn_task(task.select(service.on_exit()).map(|_| ()).map_err(|_| ()));
+        }
+    }
+
+    if service.config().roles.is_authority() {
+        let proposer = basic_authorship::ProposerFactory {
+            client: service.client(),
+            transaction_pool: service.transaction_pool(),
+        };
+
+        let client = service.client();
+        let select_chain = service
+            .select_chain()
+            .ok_or(ServiceError::SelectChainRequired)?;
+
+        let babe_config = babe::BabeParams {
+            config: Config::get_or_compute(&*client)?,
+            keystore: service.keystore(),
+            client,
+            select_chain,
+            block_import,
+            env: proposer,
+            sync_oracle: service.network(),
+            inherent_data_providers: inherent_data_providers.clone(),
+            force_authoring: service.config().force_authoring,
+            time_source: babe_link,
+        };
+
+        let babe = start_babe(babe_config)?;
+        let select = babe.select(service.on_exit()).then(|_| Ok(()));
+
+        // the BABE authoring task is considered infallible, i.e. if it
+        // fails we take down the service with it.
+        service.spawn_essential_task(select);
+    }
+
+    let config = grandpa::Config {
+        // FIXME #1578 make this available through chainspec
+        gossip_duration: Duration::from_millis(333),
+        justification_period: 4096,
+        name: Some(service.config().name.clone()),
+        keystore: Some(service.keystore()),
+    };
+
+    match (
+        service.config().roles.is_authority(),
+        service.config().disable_grandpa,
+    ) {
+        (false, false) => {
+            // start the lightweight GRANDPA observer
+            service.spawn_task(Box::new(grandpa::run_grandpa_observer(
+                config,
+                link_half,
+                service.network(),
+                service.on_exit(),
+            )?));
+        }
+        (true, false) => {
+            // start the full GRANDPA voter
+            let grandpa_config = grandpa::GrandpaParams {
+                config: config,
+                link: link_half,
+                network: service.network(),
+                inherent_data_providers: inherent_data_providers.clone(),
+                on_exit: service.on_exit(),
+                telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
+            };
+
+            // the GRANDPA voter task is considered infallible, i.e.
+            // if it fails we take down the service with it.
+            service.spawn_essential_task(grandpa::run_grandpa_voter(grandpa_config)?);
+        }
+        (_, true) => {
+            grandpa::setup_disabled_grandpa(
+                service.client(),
+                &inherent_data_providers,
+                service.network(),
+            )?;
+        }
     }
+
+    Ok(service)
+}
+
+/// Builds a new service for a light client.
+pub fn new_light<C: Send + Default + 'static>(
+    config: Configuration<C, GenesisConfig>,
+) -> Result<impl AbstractService, ServiceError> {
+    let inherent_data_providers = InherentDataProviders::new();
+
+    ServiceBuilder::new_light::<Block, RuntimeApi, Executor>(config)?
+        .with_select_chain(|_config, client| {
+            #[allow(deprecated)]
+            Ok(LongestChain::new(client.backend().clone()))
+        })?
+        .with_transaction_pool(|config, client| {
+            Ok(TransactionPool::new(
+                config,
+                transaction_pool::ChainApi::new(client),
+            ))
+        })?
+        .with_import_queue_and_fprb(|_config, client, _select_chain, transaction_pool| {
+            #[allow(deprecated)]
+            let fetch_checker = client
+                .backend()
+                .blockchain()
+                .fetcher()
+                .upgrade()
+                .map(|fetcher| fetcher.checker().clone())
+                .ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
+            let block_import = grandpa::light_block_import::<_, _, _, RuntimeApi, _>(
+                client.clone(),
+                Arc::new(fetch_checker),
+                client.clone(),
+            )?;
+
+            let finality_proof_import = block_import.clone();
+            let finality_proof_request_builder =
+                finality_proof_import.create_finality_proof_request_builder();
+
+            // FIXME: pruning task isn't started since light client doesn't do `AuthoritySetup`.
+            let (import_queue, ..) = import_queue(
+                Config::get_or_compute(&*client)?,
+                block_import,
+                None,
+                Some(Box::new(finality_proof_import)),
+                client.clone(),
+                client,
+                inherent_data_providers.clone(),
+                Some(transaction_pool),
+            )?;
+
+            Ok((import_queue, finality_proof_request_builder))
+        })?
+        .with_network_protocol(|_| Ok(NodeProtocol::new()))?
+        .with_finality_proof_provider(|client| {
+            Ok(Arc::new(GrandpaFinalityProofProvider::new(client.clone(), client)) as _)
+        })?
+        .build()
 }

Some files were not shown because too many files changed in this diff