Begin to add a test parachain

This commit is contained in:
Bastian Köcher
2019-10-16 13:39:38 +02:00
parent 28ec1f3180
commit d9c6bc408c
15 changed files with 2949 additions and 181 deletions
+79
View File
@@ -0,0 +1,79 @@
use primitives::{Pair, Public};
use parachain_runtime::{
AccountId, BalancesConfig, GenesisConfig, SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY,
};
use substrate_service;
/// Specialized `ChainSpec`. This is a specialization of the general Substrate ChainSpec type.
pub type ChainSpec = substrate_service::ChainSpec<GenesisConfig>;
/// 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()
}
/// Helper function to generate stash, controller and session key from seed
pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId) {
(get_from_seed::<AccountId>(&format!("{}//stash", seed)), get_from_seed::<AccountId>(seed))
}
/// Returns the chain spec.
pub fn get_chain_spec() -> ChainSpec {
ChainSpec::from_genesis(
"Local Testnet",
"local_testnet",
|| testnet_genesis(
vec![
get_authority_keys_from_seed("Alice"),
get_authority_keys_from_seed("Bob"),
],
get_from_seed::<AccountId>("Alice"),
vec![
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"),
],
true,
),
vec![],
None,
None,
None,
None,
)
}
fn testnet_genesis(
initial_authorities: Vec<(AccountId, AccountId)>,
root_key: AccountId,
endowed_accounts: Vec<AccountId>,
_enable_println: bool,
) -> GenesisConfig {
GenesisConfig {
system: Some(SystemConfig {
code: WASM_BINARY.to_vec(),
changes_trie_config: Default::default(),
}),
indices: Some(IndicesConfig {
ids: endowed_accounts.clone(),
}),
balances: Some(BalancesConfig {
balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(),
vesting: vec![],
}),
sudo: Some(SudoConfig {
key: root_key,
}),
}
}
+133
View File
@@ -0,0 +1,133 @@
use crate::{chain_spec, service};
use parachain_runtime::Block;
pub use substrate_cli::{VersionInfo, IntoExit, error};
use substrate_cli::{informant, parse_and_prepare, ParseAndPrepare, NoCustom};
use substrate_service::{AbstractService, Roles as ServiceRoles, Configuration};
use sr_primitives::{traits::{Block as BlockT, Header as HeaderT, Hash as HashT}, BuildStorage};
use substrate_client::genesis;
use primitives::hexdisplay::HexDisplay;
use codec::Encode;
use log::info;
use std::{path::PathBuf, cell::RefCell};
use structopt::StructOpt;
use futures::{sync::oneshot, future, Future};
/// Sub-commands supported by the collator.
#[derive(Debug, StructOpt, Clone)]
enum SubCommands {
/// Export the genesis state of the parachain.
#[structopt(name = "export-genesis-state")]
ExportGenesisState(ExportGenesisStateCommand),
}
impl substrate_cli::GetLogFilter for SubCommands {
fn get_log_filter(&self) -> Option<String> { None }
}
/// Command for exporting the genesis state of the parachain
#[derive(Debug, StructOpt, Clone)]
struct ExportGenesisStateCommand {
/// Output file name or stdout if unspecified.
#[structopt(parse(from_os_str))]
pub output: Option<PathBuf>,
}
/// Parse command line arguments into service configuration.
pub fn run<I, T, E>(args: I, exit: E, version: VersionInfo) -> error::Result<()>
where
I: IntoIterator<Item = T>,
T: Into<std::ffi::OsString> + Clone,
E: IntoExit,
{
type Config<T> = Configuration<(), T>;
match parse_and_prepare::<SubCommands, NoCustom, _>(
&version,
"cumulus-test-parachain-collator",
args,
) {
ParseAndPrepare::Run(cmd) => cmd.run(load_spec, exit,
|exit, _cli_args, _custom_args, config: 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);
panic!();
// match config.roles {
// ServiceRoles::LIGHT => unimplemented!("Light client not supported!"),
// _ => unimplemented!(),
// }.map_err(|e| format!("{:?}", e))
}),
ParseAndPrepare::BuildSpec(cmd) => cmd.run(load_spec),
ParseAndPrepare::ExportBlocks(cmd) => cmd.run_with_builder(|config: Config<_>|
Ok(new_full_start!(config).0), load_spec, exit),
ParseAndPrepare::ImportBlocks(cmd) => cmd.run_with_builder(|config: 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: Config<_>|
Ok(new_full_start!(config).0), load_spec),
ParseAndPrepare::CustomCommand(SubCommands::ExportGenesisState(cmd)) => {
export_genesis_state(cmd.output)
}
}?;
Ok(())
}
fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
Ok(Some(chain_spec::get_chain_spec()))
}
/// Export the genesis state of the parachain.
fn export_genesis_state(output: Option<PathBuf>) -> error::Result<()> {
let storage = chain_spec::get_chain_spec().build_storage()?;
let child_roots = storage.1.iter().map(|(sk, child_map)| {
let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
child_map.clone().into_iter().collect()
);
(sk.clone(), state_root.encode())
});
let state_root = <<<Block as BlockT>::Header as HeaderT>::Hashing as HashT>::trie_root(
storage.0.clone().into_iter().chain(child_roots).collect()
);
let block: Block = genesis::construct_genesis_block(state_root);
let header_hex = format!("0x{:?}", HexDisplay::from(&block.header().encode()));
if let Some(output) = output {
std::fs::write(output, header_hex)?;
} else {
println!("{}", header_hex);
}
Ok(())
}
// handles ctrl-c
pub struct Exit;
impl IntoExit for Exit {
type Exit = future::MapErr<oneshot::Receiver<()>, fn(oneshot::Canceled) -> ()>;
fn into_exit(self) -> Self::Exit {
// can't use signal directly here because CtrlC takes only `Fn`.
let (exit_send, exit) = oneshot::channel();
let exit_send_cell = RefCell::new(Some(exit_send));
ctrlc::set_handler(move || {
let exit_send = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take();
if let Some(exit_send) = exit_send {
exit_send.send(()).expect("Error sending exit notification");
}
}).expect("Error setting Ctrl-C handler");
exit.map_err(drop)
}
}
+25
View File
@@ -0,0 +1,25 @@
//! Substrate Node Template CLI library.
#![warn(missing_docs)]
#![warn(unused_extern_crates)]
mod chain_spec;
#[macro_use]
mod service;
mod cli;
pub use substrate_cli::{VersionInfo, IntoExit, error};
fn main() -> Result<(), cli::error::Error> {
let version = VersionInfo {
name: "Cumulus Test Parachain Collator",
commit: env!("VERGEN_SHA_SHORT"),
version: env!("CARGO_PKG_VERSION"),
executable_name: "cumulus-test-parachain-collator",
author: "Parity Technologies <admin@parity.io>",
description: "Cumulus test parachain collator",
support_url: "https://github.com/paritytech/cumulus/issues/new",
};
cli::run(std::env::args(), cli::Exit, version)
}
+73
View File
@@ -0,0 +1,73 @@
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.
use std::sync::Arc;
use std::time::Duration;
use substrate_client::LongestChain;
use futures::prelude::*;
use parachain_runtime::{self, GenesisConfig, opaque::Block, RuntimeApi};
use substrate_service::{error::{Error as ServiceError}, AbstractService, Configuration, ServiceBuilder};
use transaction_pool::{self, txpool::{Pool as TransactionPool}};
use inherents::InherentDataProviders;
use network::construct_simple_protocol;
use substrate_executor::native_executor_instance;
pub use substrate_executor::NativeExecutor;
// Our native executor instance.
native_executor_instance!(
pub Executor,
parachain_runtime::api::dispatch,
parachain_runtime::native_version,
);
construct_simple_protocol! {
/// Demo protocol attachment for substrate.
pub struct NodeProtocol where Block = Block { }
}
/// 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_rules! new_full_start {
($config:expr) => {{
let inherent_data_providers = inherents::InherentDataProviders::new();
let builder = substrate_service::ServiceBuilder::new_full::<
parachain_runtime::opaque::Block, parachain_runtime::RuntimeApi, crate::service::Executor,
>($config)?
.with_select_chain(|_config, backend| {
Ok(substrate_client::LongestChain::new(backend.clone()))
})?
.with_transaction_pool(|config, client|
Ok(transaction_pool::txpool::Pool::new(config, transaction_pool::FullChainApi::new(client)))
)?
.with_import_queue(|_config, client, _, _| {
let import_queue = cumulus_consensus::import_queue::import_queue(
client.clone(),
client,
inherent_data_providers.clone(),
)?;
Ok(import_queue)
})?;
(builder, inherent_data_providers)
}}
}
/// 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 is_authority = config.roles.is_authority();
let name = config.name.clone();
let disable_grandpa = config.disable_grandpa;
let force_authoring = config.force_authoring;
let (builder, inherent_data_providers) = new_full_start!(config);
let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))?.build()?;
Ok(service)
}