mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 18:07:58 +00:00
Reorganising the repository - external renames and moves (#4074)
* Adding first rough ouline of the repository structure * Remove old CI stuff * add title * formatting fixes * move node-exits job's script to scripts dir * Move docs into subdir * move to bin * move maintainence scripts, configs and helpers into its own dir * add .local to ignore * move core->client * start up 'test' area * move test client * move test runtime * make test move compile * Add dependencies rule enforcement. * Fix indexing. * Update docs to reflect latest changes * Moving /srml->/paint * update docs * move client/sr-* -> primitives/ * clean old readme * remove old broken code in rhd * update lock * Step 1. * starting to untangle client * Fix after merge. * start splitting out client interfaces * move children and blockchain interfaces * Move trie and state-machine to primitives. * Fix WASM builds. * fixing broken imports * more interface moves * move backend and light to interfaces * move CallExecutor * move cli off client * moving around more interfaces * re-add consensus crates into the mix * fix subkey path * relieve client from executor * starting to pull out client from grandpa * move is_decendent_of out of client * grandpa still depends on client directly * lemme tests pass * rename srml->paint * Make it compile. * rename interfaces->client-api * Move keyring to primitives. * fixup libp2p dep * fix broken use * allow dependency enforcement to fail * move fork-tree * Moving wasm-builder * make env * move build-script-utils * fixup broken crate depdencies and names * fix imports for authority discovery * fix typo * update cargo.lock * fixing imports * Fix paths and add missing crates * re-add missing crates
This commit is contained in:
committed by
Bastian Köcher
parent
becc3b0a4f
commit
60e5011c72
@@ -0,0 +1,160 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::ChainSpec;
|
||||
use futures::{prelude::*, sync::oneshot, sync::mpsc};
|
||||
use libp2p::wasm_ext;
|
||||
use log::{debug, info};
|
||||
use std::sync::Arc;
|
||||
use substrate_service::{AbstractService, RpcSession, Roles as ServiceRoles, Configuration, config::DatabaseConfig};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
/// Starts the client.
|
||||
///
|
||||
/// You must pass a libp2p transport that supports .
|
||||
#[wasm_bindgen]
|
||||
pub fn start_client(wasm_ext: wasm_ext::ffi::Transport) -> Result<Client, JsValue> {
|
||||
start_inner(wasm_ext)
|
||||
.map_err(|err| JsValue::from_str(&err.to_string()))
|
||||
}
|
||||
|
||||
fn start_inner(wasm_ext: wasm_ext::ffi::Transport) -> Result<Client, Box<dyn std::error::Error>> {
|
||||
console_error_panic_hook::set_once();
|
||||
console_log::init_with_level(log::Level::Info);
|
||||
|
||||
// Build the configuration to pass to the service.
|
||||
let config = {
|
||||
let wasm_ext = wasm_ext::ExtTransport::new(wasm_ext);
|
||||
let chain_spec = ChainSpec::FlamingFir.load().map_err(|e| format!("{:?}", e))?;
|
||||
let mut config = Configuration::<(), _, _>::default_with_spec_and_base_path(chain_spec, None);
|
||||
config.network.transport = network::config::TransportConfig::Normal {
|
||||
wasm_external_transport: Some(wasm_ext.clone()),
|
||||
allow_private_ipv4: true,
|
||||
enable_mdns: false,
|
||||
};
|
||||
config.telemetry_external_transport = Some(wasm_ext);
|
||||
config.roles = ServiceRoles::LIGHT;
|
||||
config.name = "Browser node".to_string();
|
||||
config.database = {
|
||||
let db = Arc::new(kvdb_memorydb::create(10));
|
||||
DatabaseConfig::Custom(db)
|
||||
};
|
||||
config
|
||||
};
|
||||
|
||||
info!("Substrate browser node");
|
||||
info!(" version {}", config.full_version());
|
||||
info!(" by Parity Technologies, 2017-2019");
|
||||
info!("Chain specification: {}", config.chain_spec.name());
|
||||
info!("Node name: {}", config.name);
|
||||
info!("Roles: {:?}", config.roles);
|
||||
|
||||
// Create the service. This is the most heavy initialization step.
|
||||
let mut service = crate::service::new_light(config).map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
// We now dispatch a background task responsible for processing the service.
|
||||
//
|
||||
// The main action performed by the code below consists in polling the service with
|
||||
// `service.poll()`.
|
||||
// The rest consists in handling RPC requests.
|
||||
let (rpc_send_tx, mut rpc_send_rx) = mpsc::unbounded::<RpcMessage>();
|
||||
wasm_bindgen_futures::spawn_local(futures::future::poll_fn(move || {
|
||||
loop {
|
||||
match rpc_send_rx.poll() {
|
||||
Ok(Async::Ready(Some(message))) => {
|
||||
let fut = service.rpc_query(&message.session, &message.rpc_json);
|
||||
let _ = message.send_back.send(Box::new(fut));
|
||||
},
|
||||
Ok(Async::NotReady) => break,
|
||||
Err(_) | Ok(Async::Ready(None)) => return Ok(Async::Ready(())),
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match service.poll().map_err(|_| ())? {
|
||||
Async::Ready(()) => return Ok(Async::Ready(())),
|
||||
Async::NotReady => break
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Async::NotReady)
|
||||
}));
|
||||
|
||||
Ok(Client {
|
||||
rpc_send_tx,
|
||||
})
|
||||
}
|
||||
|
||||
/// A running client.
|
||||
#[wasm_bindgen]
|
||||
pub struct Client {
|
||||
rpc_send_tx: mpsc::UnboundedSender<RpcMessage>,
|
||||
}
|
||||
|
||||
struct RpcMessage {
|
||||
rpc_json: String,
|
||||
session: RpcSession,
|
||||
send_back: oneshot::Sender<Box<dyn Future<Item = Option<String>, Error = ()>>>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl Client {
|
||||
/// Allows starting an RPC request. Returns a `Promise` containing the result of that request.
|
||||
#[wasm_bindgen(js_name = "rpcSend")]
|
||||
pub fn rpc_send(&mut self, rpc: &str) -> js_sys::Promise {
|
||||
let rpc_session = RpcSession::new(mpsc::channel(1).0);
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let _ = self.rpc_send_tx.unbounded_send(RpcMessage {
|
||||
rpc_json: rpc.to_owned(),
|
||||
session: rpc_session,
|
||||
send_back: tx,
|
||||
});
|
||||
let fut = rx
|
||||
.map_err(|_| ())
|
||||
.and_then(|fut| fut)
|
||||
.map(|s| JsValue::from_str(&s.unwrap_or(String::new())))
|
||||
.map_err(|_| JsValue::NULL);
|
||||
wasm_bindgen_futures::future_to_promise(fut)
|
||||
}
|
||||
|
||||
/// Subscribes to an RPC pubsub endpoint.
|
||||
#[wasm_bindgen(js_name = "rpcSubscribe")]
|
||||
pub fn rpc_subscribe(&mut self, rpc: &str, callback: js_sys::Function) {
|
||||
let (tx, rx) = mpsc::channel(4);
|
||||
let rpc_session = RpcSession::new(tx);
|
||||
let (fut_tx, fut_rx) = oneshot::channel();
|
||||
let _ = self.rpc_send_tx.unbounded_send(RpcMessage {
|
||||
rpc_json: rpc.to_owned(),
|
||||
session: rpc_session.clone(),
|
||||
send_back: fut_tx,
|
||||
});
|
||||
let fut_rx = fut_rx
|
||||
.map_err(|_| ())
|
||||
.and_then(|fut| fut);
|
||||
wasm_bindgen_futures::spawn_local(fut_rx.then(|_| Ok(())));
|
||||
wasm_bindgen_futures::spawn_local(rx.for_each(move |s| {
|
||||
match callback.call1(&callback, &JsValue::from_str(&s)) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}).then(move |v| {
|
||||
// We need to keep `rpc_session` alive.
|
||||
debug!("RPC subscription has ended");
|
||||
drop(rpc_session);
|
||||
v
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate chain configurations.
|
||||
|
||||
use chain_spec::ChainSpecExtension;
|
||||
use primitives::{Pair, Public, crypto::UncheckedInto, sr25519};
|
||||
use serde::{Serialize, Deserialize};
|
||||
use node_runtime::{
|
||||
AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig,
|
||||
GrandpaConfig, ImOnlineConfig, IndicesConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, SudoConfig,
|
||||
SystemConfig, TechnicalCommitteeConfig, WASM_BINARY,
|
||||
};
|
||||
use node_runtime::Block;
|
||||
use node_runtime::constants::currency::*;
|
||||
use substrate_service;
|
||||
use hex_literal::hex;
|
||||
use substrate_telemetry::TelemetryEndpoints;
|
||||
use grandpa_primitives::{AuthorityId as GrandpaId};
|
||||
use babe_primitives::{AuthorityId as BabeId};
|
||||
use im_online::sr25519::{AuthorityId as ImOnlineId};
|
||||
use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId;
|
||||
use sr_primitives::{Perbill, traits::{Verify, IdentifyAccount}};
|
||||
|
||||
pub use node_primitives::{AccountId, Balance, Signature};
|
||||
pub use node_runtime::GenesisConfig;
|
||||
|
||||
type AccountPublic = <Signature as Verify>::Signer;
|
||||
|
||||
const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
|
||||
|
||||
/// Node `ChainSpec` extensions.
|
||||
///
|
||||
/// Additional parameters for some Substrate core modules,
|
||||
/// customizable from the chain spec.
|
||||
#[derive(Default, Clone, Serialize, Deserialize, ChainSpecExtension)]
|
||||
pub struct Extensions {
|
||||
/// Block numbers with known hashes.
|
||||
pub fork_blocks: client::ForkBlocks<Block>,
|
||||
}
|
||||
|
||||
/// Specialized `ChainSpec`.
|
||||
pub type ChainSpec = substrate_service::ChainSpec<
|
||||
GenesisConfig,
|
||||
Extensions,
|
||||
>;
|
||||
/// Flaming Fir testnet generator
|
||||
pub fn flaming_fir_config() -> Result<ChainSpec, String> {
|
||||
ChainSpec::from_json_bytes(&include_bytes!("../res/flaming-fir.json")[..])
|
||||
}
|
||||
|
||||
fn session_keys(
|
||||
grandpa: GrandpaId,
|
||||
babe: BabeId,
|
||||
im_online: ImOnlineId,
|
||||
authority_discovery: AuthorityDiscoveryId,
|
||||
) -> SessionKeys {
|
||||
SessionKeys { grandpa, babe, im_online, authority_discovery }
|
||||
}
|
||||
|
||||
fn staging_testnet_config_genesis() -> GenesisConfig {
|
||||
// stash, controller, session-key
|
||||
// generated with secret:
|
||||
// for i in 1 2 3 4 ; do for j in stash controller; do subkey inspect "$secret"/fir/$j/$i; done; done
|
||||
// and
|
||||
// for i in 1 2 3 4 ; do for j in session; do subkey --ed25519 inspect "$secret"//fir//$j//$i; done; done
|
||||
|
||||
let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId)> = vec![(
|
||||
// 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy
|
||||
hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].into(),
|
||||
// 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq
|
||||
hex!["781ead1e2fa9ccb74b44c19d29cb2a7a4b5be3972927ae98cd3877523976a276"].into(),
|
||||
// 5Fb9ayurnxnaXj56CjmyQLBiadfRCqUbL2VWNbbe1nZU6wiC
|
||||
hex!["9becad03e6dcac03cee07edebca5475314861492cdfc96a2144a67bbe9699332"].unchecked_into(),
|
||||
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
|
||||
hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(),
|
||||
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
|
||||
hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(),
|
||||
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
|
||||
hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(),
|
||||
),(
|
||||
// 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2
|
||||
hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].into(),
|
||||
// 5Gc4vr42hH1uDZc93Nayk5G7i687bAQdHHc9unLuyeawHipF
|
||||
hex!["c8dc79e36b29395413399edaec3e20fcca7205fb19776ed8ddb25d6f427ec40e"].into(),
|
||||
// 5EockCXN6YkiNCDjpqqnbcqd4ad35nU4RmA1ikM4YeRN4WcE
|
||||
hex!["7932cff431e748892fa48e10c63c17d30f80ca42e4de3921e641249cd7fa3c2f"].unchecked_into(),
|
||||
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
|
||||
hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(),
|
||||
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
|
||||
hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(),
|
||||
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
|
||||
hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(),
|
||||
),(
|
||||
// 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp
|
||||
hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].into(),
|
||||
// 5FeD54vGVNpFX3PndHPXJ2MDakc462vBCD5mgtWRnWYCpZU9
|
||||
hex!["9e42241d7cd91d001773b0b616d523dd80e13c6c2cab860b1234ef1b9ffc1526"].into(),
|
||||
// 5E1jLYfLdUQKrFrtqoKgFrRvxM3oQPMbf6DfcsrugZZ5Bn8d
|
||||
hex!["5633b70b80a6c8bb16270f82cca6d56b27ed7b76c8fd5af2986a25a4788ce440"].unchecked_into(),
|
||||
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
|
||||
hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(),
|
||||
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
|
||||
hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(),
|
||||
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
|
||||
hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(),
|
||||
),(
|
||||
// 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9
|
||||
hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].into(),
|
||||
// 5EPQdAQ39WQNLCRjWsCk5jErsCitHiY5ZmjfWzzbXDoAoYbn
|
||||
hex!["66bc1e5d275da50b72b15de072a2468a5ad414919ca9054d2695767cf650012f"].into(),
|
||||
// 5DMa31Hd5u1dwoRKgC4uvqyrdK45RHv3CpwvpUC1EzuwDit4
|
||||
hex!["3919132b851ef0fd2dae42a7e734fe547af5a6b809006100f48944d7fae8e8ef"].unchecked_into(),
|
||||
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
|
||||
hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(),
|
||||
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
|
||||
hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(),
|
||||
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
|
||||
hex!["00299981a2b92f878baaf5dbeba5c18d4e70f2a1fcd9c61b32ea18daf38f4378"].unchecked_into(),
|
||||
)];
|
||||
|
||||
// generated with secret: subkey inspect "$secret"/fir
|
||||
let root_key: AccountId = hex![
|
||||
// 5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo
|
||||
"9ee5e5bdc0ec239eb164f865ecc345ce4c88e76ee002e0f7e318097347471809"
|
||||
].into();
|
||||
|
||||
let endowed_accounts: Vec<AccountId> = vec![root_key.clone()];
|
||||
|
||||
testnet_genesis(
|
||||
initial_authorities,
|
||||
root_key,
|
||||
Some(endowed_accounts),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Staging testnet config.
|
||||
pub fn staging_testnet_config() -> ChainSpec {
|
||||
let boot_nodes = vec![];
|
||||
ChainSpec::from_genesis(
|
||||
"Staging Testnet",
|
||||
"staging_testnet",
|
||||
staging_testnet_config_genesis,
|
||||
boot_nodes,
|
||||
Some(TelemetryEndpoints::new(vec![(STAGING_TELEMETRY_URL.to_string(), 0)])),
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
/// 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 an account ID from seed
|
||||
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where
|
||||
AccountPublic: From<<TPublic::Pair as Pair>::Public>
|
||||
{
|
||||
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
|
||||
}
|
||||
|
||||
/// 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,
|
||||
AuthorityDiscoveryId,
|
||||
) {
|
||||
(
|
||||
get_account_id_from_seed::<sr25519::Public>(&format!("{}//stash", seed)),
|
||||
get_account_id_from_seed::<sr25519::Public>(seed),
|
||||
get_from_seed::<GrandpaId>(seed),
|
||||
get_from_seed::<BabeId>(seed),
|
||||
get_from_seed::<ImOnlineId>(seed),
|
||||
get_from_seed::<AuthorityDiscoveryId>(seed),
|
||||
)
|
||||
}
|
||||
|
||||
/// Helper function to create GenesisConfig for testing
|
||||
pub fn testnet_genesis(
|
||||
initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId, AuthorityDiscoveryId)>,
|
||||
root_key: AccountId,
|
||||
endowed_accounts: Option<Vec<AccountId>>,
|
||||
enable_println: bool,
|
||||
) -> GenesisConfig {
|
||||
let endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(|| {
|
||||
vec![
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Charlie"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Dave"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Eve"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
|
||||
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
|
||||
]
|
||||
});
|
||||
|
||||
const ENDOWMENT: Balance = 10_000_000 * DOLLARS;
|
||||
const STASH: Balance = 100 * DOLLARS;
|
||||
|
||||
GenesisConfig {
|
||||
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: endowed_accounts.iter().cloned()
|
||||
.chain(initial_authorities.iter().map(|x| x.0.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
}),
|
||||
session: Some(SessionConfig {
|
||||
keys: initial_authorities.iter().map(|x| {
|
||||
(x.0.clone(), session_keys(x.2.clone(), x.3.clone(), x.4.clone(), x.5.clone()))
|
||||
}).collect::<Vec<_>>(),
|
||||
}),
|
||||
staking: Some(StakingConfig {
|
||||
current_era: 0,
|
||||
validator_count: initial_authorities.len() as u32 * 2,
|
||||
minimum_validator_count: initial_authorities.len() as u32,
|
||||
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()
|
||||
}),
|
||||
democracy: Some(DemocracyConfig::default()),
|
||||
collective_Instance1: Some(CouncilConfig {
|
||||
members: vec![],
|
||||
phantom: Default::default(),
|
||||
}),
|
||||
collective_Instance2: Some(TechnicalCommitteeConfig {
|
||||
members: vec![],
|
||||
phantom: Default::default(),
|
||||
}),
|
||||
contracts: Some(ContractsConfig {
|
||||
current_schedule: contracts::Schedule {
|
||||
enable_println, // this should only be enabled on development chains
|
||||
..Default::default()
|
||||
},
|
||||
gas_price: 1 * MILLICENTS,
|
||||
}),
|
||||
sudo: Some(SudoConfig {
|
||||
key: root_key,
|
||||
}),
|
||||
babe: Some(BabeConfig {
|
||||
authorities: vec![],
|
||||
}),
|
||||
im_online: Some(ImOnlineConfig {
|
||||
keys: vec![],
|
||||
}),
|
||||
authority_discovery: Some(AuthorityDiscoveryConfig {
|
||||
keys: vec![],
|
||||
}),
|
||||
grandpa: Some(GrandpaConfig {
|
||||
authorities: vec![],
|
||||
}),
|
||||
membership_Instance1: Some(Default::default()),
|
||||
treasury: Some(Default::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn development_config_genesis() -> GenesisConfig {
|
||||
testnet_genesis(
|
||||
vec![
|
||||
get_authority_keys_from_seed("Alice"),
|
||||
],
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
None,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
/// Development config (single validator Alice)
|
||||
pub fn development_config() -> ChainSpec {
|
||||
ChainSpec::from_genesis(
|
||||
"Development",
|
||||
"dev",
|
||||
development_config_genesis,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
fn local_testnet_genesis() -> GenesisConfig {
|
||||
testnet_genesis(
|
||||
vec![
|
||||
get_authority_keys_from_seed("Alice"),
|
||||
get_authority_keys_from_seed("Bob"),
|
||||
],
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Local testnet config (multivalidator Alice + Bob)
|
||||
pub fn local_testnet_config() -> ChainSpec {
|
||||
ChainSpec::from_genesis(
|
||||
"Local Testnet",
|
||||
"local_testnet",
|
||||
local_testnet_genesis,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::service::new_full;
|
||||
use substrate_service::Roles;
|
||||
use service_test;
|
||||
|
||||
fn local_testnet_genesis_instant_single() -> GenesisConfig {
|
||||
testnet_genesis(
|
||||
vec![
|
||||
get_authority_keys_from_seed("Alice"),
|
||||
],
|
||||
get_account_id_from_seed::<sr25519::Public>("Alice"),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
/// Local testnet config (single validator - Alice)
|
||||
pub fn integration_test_config_with_single_authority() -> ChainSpec {
|
||||
ChainSpec::from_genesis(
|
||||
"Integration Test",
|
||||
"test",
|
||||
local_testnet_genesis_instant_single,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Local testnet config (multivalidator Alice + Bob)
|
||||
pub fn integration_test_config_with_two_authorities() -> ChainSpec {
|
||||
ChainSpec::from_genesis(
|
||||
"Integration Test",
|
||||
"test",
|
||||
local_testnet_genesis,
|
||||
vec![],
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_connectivity() {
|
||||
service_test::connectivity(
|
||||
integration_test_config_with_two_authorities(),
|
||||
|config| new_full(config),
|
||||
|mut config| {
|
||||
// light nodes are unsupported
|
||||
config.roles = Roles::FULL;
|
||||
new_full(config)
|
||||
},
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub use substrate_cli::error;
|
||||
use tokio::prelude::Future;
|
||||
use tokio::runtime::{Builder as RuntimeBuilder, Runtime};
|
||||
pub use substrate_cli::{VersionInfo, IntoExit, NoCustom, SharedParams, ExecutionStrategyParam};
|
||||
use substrate_service::{AbstractService, Roles as ServiceRoles, Configuration};
|
||||
use log::info;
|
||||
use structopt::{StructOpt, clap::App};
|
||||
use substrate_cli::{display_role, parse_and_prepare, AugmentClap, GetLogFilter, ParseAndPrepare};
|
||||
use crate::{service, ChainSpec, load_spec};
|
||||
use crate::factory_impl::FactoryState;
|
||||
use transaction_factory::RuntimeAdapter;
|
||||
use client::ExecutionStrategies;
|
||||
|
||||
/// Custom subcommands.
|
||||
#[derive(Clone, Debug, StructOpt)]
|
||||
pub enum CustomSubcommands {
|
||||
/// The custom factory subcommmand for manufacturing transactions.
|
||||
#[structopt(
|
||||
name = "factory",
|
||||
about = "Manufactures num transactions from Alice to random accounts. \
|
||||
Only supported for development or local testnet."
|
||||
)]
|
||||
Factory(FactoryCmd),
|
||||
}
|
||||
|
||||
impl GetLogFilter for CustomSubcommands {
|
||||
fn get_log_filter(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// The `factory` command used to generate transactions.
|
||||
/// Please note: this command currently only works on an empty database!
|
||||
#[derive(Debug, StructOpt, Clone)]
|
||||
pub struct FactoryCmd {
|
||||
/// How often to repeat. This option only has an effect in mode `MasterToNToM`.
|
||||
#[structopt(long="rounds", default_value = "1")]
|
||||
pub rounds: u64,
|
||||
|
||||
/// MasterToN: Manufacture `num` transactions from the master account
|
||||
/// to `num` randomly created accounts, one each.
|
||||
///
|
||||
/// MasterTo1: Manufacture `num` transactions from the master account
|
||||
/// to exactly one other randomly created account.
|
||||
///
|
||||
/// MasterToNToM: Manufacture `num` transactions from the master account
|
||||
/// to `num` randomly created accounts.
|
||||
/// From each of these randomly created accounts manufacture
|
||||
/// a transaction to another randomly created account.
|
||||
/// Repeat this `rounds` times. If `rounds` = 1 the behavior
|
||||
/// is the same as `MasterToN`.{n}
|
||||
/// A -> B, A -> C, A -> D, ... x `num`{n}
|
||||
/// B -> E, C -> F, D -> G, ...{n}
|
||||
/// ... x `rounds`
|
||||
///
|
||||
/// These three modes control manufacturing.
|
||||
#[structopt(long="mode", default_value = "MasterToN")]
|
||||
pub mode: transaction_factory::Mode,
|
||||
|
||||
/// Number of transactions to generate. In mode `MasterNToNToM` this is
|
||||
/// the number of transactions per round.
|
||||
#[structopt(long="num", default_value = "8")]
|
||||
pub num: u64,
|
||||
|
||||
#[allow(missing_docs)]
|
||||
#[structopt(flatten)]
|
||||
pub shared_params: SharedParams,
|
||||
|
||||
/// The means of execution used when calling into the runtime while importing blocks.
|
||||
#[structopt(
|
||||
long = "execution",
|
||||
value_name = "STRATEGY",
|
||||
possible_values = &ExecutionStrategyParam::variants(),
|
||||
case_insensitive = true,
|
||||
default_value = "NativeElseWasm"
|
||||
)]
|
||||
pub execution: ExecutionStrategyParam,
|
||||
}
|
||||
|
||||
impl AugmentClap for FactoryCmd {
|
||||
fn augment_clap<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
||||
FactoryCmd::augment_clap(app)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse command line arguments into service configuration.
|
||||
pub fn run<I, T, E>(args: I, exit: E, version: substrate_cli::VersionInfo) -> error::Result<()> where
|
||||
I: IntoIterator<Item = T>,
|
||||
T: Into<std::ffi::OsString> + Clone,
|
||||
E: IntoExit,
|
||||
{
|
||||
type Config<A, B> = Configuration<(), A, B>;
|
||||
|
||||
match parse_and_prepare::<CustomSubcommands, NoCustom, _>(&version, "substrate-node", 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 Parity Technologies, 2017-2019");
|
||||
info!("Chain specification: {}", config.chain_spec.name());
|
||||
info!("Node name: {}", config.name);
|
||||
info!("Roles: {}", display_role(&config));
|
||||
let runtime = RuntimeBuilder::new().name_prefix("main-tokio-").build()
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
match config.roles {
|
||||
ServiceRoles::LIGHT => run_until_exit(
|
||||
runtime,
|
||||
service::new_light(config)?,
|
||||
exit
|
||||
),
|
||||
_ => run_until_exit(
|
||||
runtime,
|
||||
service::new_full(config)?,
|
||||
exit
|
||||
),
|
||||
}
|
||||
}),
|
||||
ParseAndPrepare::BuildSpec(cmd) => cmd.run::<NoCustom, _, _, _>(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(CustomSubcommands::Factory(cli_args)) => {
|
||||
let mut config: Config<_, _> = substrate_cli::create_config_with_db_path(
|
||||
load_spec,
|
||||
&cli_args.shared_params,
|
||||
&version,
|
||||
)?;
|
||||
config.execution_strategies = ExecutionStrategies {
|
||||
importing: cli_args.execution.into(),
|
||||
block_construction: cli_args.execution.into(),
|
||||
other: cli_args.execution.into(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
match ChainSpec::from(config.chain_spec.id()) {
|
||||
Some(ref c) if c == &ChainSpec::Development || c == &ChainSpec::LocalTestnet => {},
|
||||
_ => panic!("Factory is only supported for development and local testnet."),
|
||||
}
|
||||
|
||||
let factory_state = FactoryState::new(
|
||||
cli_args.mode.clone(),
|
||||
cli_args.num,
|
||||
cli_args.rounds,
|
||||
);
|
||||
|
||||
let service_builder = new_full_start!(config).0;
|
||||
transaction_factory::factory::<FactoryState<_>, _, _, _, _, _>(
|
||||
factory_state,
|
||||
service_builder.client(),
|
||||
service_builder.select_chain()
|
||||
.expect("The select_chain is always initialized by new_full_start!; QED")
|
||||
).map_err(|e| format!("Error in transaction factory: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_until_exit<T, E>(
|
||||
mut runtime: Runtime,
|
||||
service: T,
|
||||
e: E,
|
||||
) -> error::Result<()>
|
||||
where
|
||||
T: AbstractService,
|
||||
E: IntoExit,
|
||||
{
|
||||
let (exit_send, exit) = exit_future::signal();
|
||||
|
||||
let informant = substrate_cli::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();
|
||||
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,268 @@
|
||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Implementation of the transaction factory trait, which enables
|
||||
//! using the cli to manufacture transactions and distribute them
|
||||
//! to accounts.
|
||||
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand::rngs::StdRng;
|
||||
|
||||
use codec::{Encode, Decode};
|
||||
use keyring::sr25519::Keyring;
|
||||
use node_runtime::{
|
||||
Call, CheckedExtrinsic, UncheckedExtrinsic, SignedExtra, BalancesCall, ExistentialDeposit,
|
||||
MinimumPeriod
|
||||
};
|
||||
use node_primitives::Signature;
|
||||
use primitives::{sr25519, crypto::Pair};
|
||||
use sr_primitives::{
|
||||
generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension, Verify, IdentifyAccount}
|
||||
};
|
||||
use transaction_factory::RuntimeAdapter;
|
||||
use transaction_factory::modes::Mode;
|
||||
use inherents::InherentData;
|
||||
use timestamp;
|
||||
use finality_tracker;
|
||||
|
||||
type AccountPublic = <Signature as Verify>::Signer;
|
||||
|
||||
pub struct FactoryState<N> {
|
||||
block_no: N,
|
||||
|
||||
mode: Mode,
|
||||
start_number: u32,
|
||||
rounds: u32,
|
||||
round: u32,
|
||||
block_in_round: u32,
|
||||
num: u32,
|
||||
}
|
||||
|
||||
type Number = <<node_primitives::Block as BlockT>::Header as HeaderT>::Number;
|
||||
|
||||
impl<Number> FactoryState<Number> {
|
||||
fn build_extra(index: node_primitives::Index, phase: u64) -> node_runtime::SignedExtra {
|
||||
(
|
||||
system::CheckVersion::new(),
|
||||
system::CheckGenesis::new(),
|
||||
system::CheckEra::from(Era::mortal(256, phase)),
|
||||
system::CheckNonce::from(index),
|
||||
system::CheckWeight::new(),
|
||||
transaction_payment::ChargeTransactionPayment::from(0),
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl RuntimeAdapter for FactoryState<Number> {
|
||||
type AccountId = node_primitives::AccountId;
|
||||
type Balance = node_primitives::Balance;
|
||||
type Block = node_primitives::Block;
|
||||
type Phase = sr_primitives::generic::Phase;
|
||||
type Secret = sr25519::Pair;
|
||||
type Index = node_primitives::Index;
|
||||
|
||||
type Number = Number;
|
||||
|
||||
fn new(
|
||||
mode: Mode,
|
||||
num: u64,
|
||||
rounds: u64,
|
||||
) -> FactoryState<Self::Number> {
|
||||
FactoryState {
|
||||
mode,
|
||||
num: num as u32,
|
||||
round: 0,
|
||||
rounds: rounds as u32,
|
||||
block_in_round: 0,
|
||||
block_no: 0,
|
||||
start_number: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn block_no(&self) -> Self::Number {
|
||||
self.block_no
|
||||
}
|
||||
|
||||
fn block_in_round(&self) -> Self::Number {
|
||||
self.block_in_round
|
||||
}
|
||||
|
||||
fn rounds(&self) -> Self::Number {
|
||||
self.rounds
|
||||
}
|
||||
|
||||
fn num(&self) -> Self::Number {
|
||||
self.num
|
||||
}
|
||||
|
||||
fn round(&self) -> Self::Number {
|
||||
self.round
|
||||
}
|
||||
|
||||
fn start_number(&self) -> Self::Number {
|
||||
self.start_number
|
||||
}
|
||||
|
||||
fn mode(&self) -> &Mode {
|
||||
&self.mode
|
||||
}
|
||||
|
||||
fn set_block_no(&mut self, val: Self::Number) {
|
||||
self.block_no = val;
|
||||
}
|
||||
|
||||
fn set_block_in_round(&mut self, val: Self::Number) {
|
||||
self.block_in_round = val;
|
||||
}
|
||||
|
||||
fn set_round(&mut self, val: Self::Number) {
|
||||
self.round = val;
|
||||
}
|
||||
|
||||
fn transfer_extrinsic(
|
||||
&self,
|
||||
sender: &Self::AccountId,
|
||||
key: &Self::Secret,
|
||||
destination: &Self::AccountId,
|
||||
amount: &Self::Balance,
|
||||
version: u32,
|
||||
genesis_hash: &<Self::Block as BlockT>::Hash,
|
||||
prior_block_hash: &<Self::Block as BlockT>::Hash,
|
||||
) -> <Self::Block as BlockT>::Extrinsic {
|
||||
let index = self.extract_index(&sender, prior_block_hash);
|
||||
let phase = self.extract_phase(*prior_block_hash);
|
||||
sign::<Self>(CheckedExtrinsic {
|
||||
signed: Some((sender.clone(), Self::build_extra(index, phase))),
|
||||
function: Call::Balances(
|
||||
BalancesCall::transfer(
|
||||
indices::address::Address::Id(destination.clone().into()),
|
||||
(*amount).into()
|
||||
)
|
||||
)
|
||||
}, key, (version, genesis_hash.clone(), prior_block_hash.clone(), (), (), (), ()))
|
||||
}
|
||||
|
||||
fn inherent_extrinsics(&self) -> InherentData {
|
||||
let timestamp = (self.block_no as u64 + 1) * MinimumPeriod::get();
|
||||
|
||||
let mut inherent = InherentData::new();
|
||||
inherent.put_data(timestamp::INHERENT_IDENTIFIER, ×tamp)
|
||||
.expect("Failed putting timestamp inherent");
|
||||
inherent.put_data(finality_tracker::INHERENT_IDENTIFIER, &self.block_no)
|
||||
.expect("Failed putting finalized number inherent");
|
||||
inherent
|
||||
}
|
||||
|
||||
fn minimum_balance() -> Self::Balance {
|
||||
ExistentialDeposit::get()
|
||||
}
|
||||
|
||||
fn master_account_id() -> Self::AccountId {
|
||||
Keyring::Alice.to_account_id()
|
||||
}
|
||||
|
||||
fn master_account_secret() -> Self::Secret {
|
||||
Keyring::Alice.pair()
|
||||
}
|
||||
|
||||
/// Generates a random `AccountId` from `seed`.
|
||||
fn gen_random_account_id(seed: &Self::Number) -> Self::AccountId {
|
||||
let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed));
|
||||
AccountPublic::from(pair.public()).into_account()
|
||||
}
|
||||
|
||||
/// Generates a random `Secret` from `seed`.
|
||||
fn gen_random_account_secret(seed: &Self::Number) -> Self::Secret {
|
||||
let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed));
|
||||
pair
|
||||
}
|
||||
|
||||
fn extract_index(
|
||||
&self,
|
||||
_account_id: &Self::AccountId,
|
||||
_block_hash: &<Self::Block as BlockT>::Hash,
|
||||
) -> Self::Index {
|
||||
// TODO get correct index for account via api. See #2587.
|
||||
// This currently prevents the factory from being used
|
||||
// without a preceding purge of the database.
|
||||
if self.mode == Mode::MasterToN || self.mode == Mode::MasterTo1 {
|
||||
self.block_no() as Self::Index
|
||||
} else {
|
||||
match self.round() {
|
||||
0 =>
|
||||
// if round is 0 all transactions will be done with master as a sender
|
||||
self.block_no() as Self::Index,
|
||||
_ =>
|
||||
// if round is e.g. 1 every sender account will be new and not yet have
|
||||
// any transactions done
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_phase(
|
||||
&self,
|
||||
_block_hash: <Self::Block as BlockT>::Hash
|
||||
) -> Self::Phase {
|
||||
// TODO get correct phase via api. See #2587.
|
||||
// This currently prevents the factory from being used
|
||||
// without a preceding purge of the database.
|
||||
self.block_no() as Self::Phase
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_seed_bytes(seed: u32) -> [u8; 32] {
|
||||
let mut rng: StdRng = SeedableRng::seed_from_u64(seed as u64);
|
||||
|
||||
let mut seed_bytes = [0u8; 32];
|
||||
for i in 0..32 {
|
||||
seed_bytes[i] = rng.gen::<u8>();
|
||||
}
|
||||
seed_bytes
|
||||
}
|
||||
|
||||
/// Creates an `UncheckedExtrinsic` containing the appropriate signature for
|
||||
/// a `CheckedExtrinsics`.
|
||||
fn sign<RA: RuntimeAdapter>(
|
||||
xt: CheckedExtrinsic,
|
||||
key: &sr25519::Pair,
|
||||
additional_signed: <SignedExtra as SignedExtension>::AdditionalSigned,
|
||||
) -> <RA::Block as BlockT>::Extrinsic {
|
||||
let s = match xt.signed {
|
||||
Some((signed, extra)) => {
|
||||
let payload = (xt.function, extra.clone(), additional_signed);
|
||||
let signature = payload.using_encoded(|b| {
|
||||
if b.len() > 256 {
|
||||
key.sign(&runtime_io::hashing::blake2_256(b))
|
||||
} else {
|
||||
key.sign(b)
|
||||
}
|
||||
}).into();
|
||||
UncheckedExtrinsic {
|
||||
signature: Some((indices::address::Address::Id(signed), signature, extra)),
|
||||
function: payload.0,
|
||||
}
|
||||
}
|
||||
None => UncheckedExtrinsic {
|
||||
signature: None,
|
||||
function: xt.function,
|
||||
},
|
||||
};
|
||||
|
||||
let e = Encode::encode(&s);
|
||||
Decode::decode(&mut &e[..]).expect("Failed to decode signed unchecked extrinsic")
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate CLI library.
|
||||
//!
|
||||
//! This package has two Cargo features:
|
||||
//!
|
||||
//! - `cli` (default): exposes functions that parse command-line options, then start and run the
|
||||
//! node as a CLI application.
|
||||
//!
|
||||
//! - `browser`: exposes the content of the `browser` module, which consists of exported symbols
|
||||
//! that are meant to be passed through the `wasm-bindgen` utility and called from JavaScript.
|
||||
//! Despite its name the produced WASM can theoretically also be used from NodeJS, although this
|
||||
//! hasn't been tested.
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(unused_extern_crates)]
|
||||
|
||||
pub mod chain_spec;
|
||||
|
||||
#[macro_use]
|
||||
mod service;
|
||||
#[cfg(feature = "browser")]
|
||||
mod browser;
|
||||
#[cfg(feature = "cli")]
|
||||
mod cli;
|
||||
#[cfg(feature = "cli")]
|
||||
mod factory_impl;
|
||||
|
||||
#[cfg(feature = "browser")]
|
||||
pub use browser::*;
|
||||
#[cfg(feature = "cli")]
|
||||
pub use cli::*;
|
||||
|
||||
/// The chain specification option.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ChainSpec {
|
||||
/// Whatever the current runtime is, with just Alice as an auth.
|
||||
Development,
|
||||
/// Whatever the current runtime is, with simple Alice/Bob auths.
|
||||
LocalTestnet,
|
||||
/// The Flaming Fir testnet.
|
||||
FlamingFir,
|
||||
/// Whatever the current runtime is with the "global testnet" defaults.
|
||||
StagingTestnet,
|
||||
}
|
||||
|
||||
/// Get a chain config from a spec setting.
|
||||
impl ChainSpec {
|
||||
pub(crate) fn load(self) -> Result<chain_spec::ChainSpec, String> {
|
||||
Ok(match self {
|
||||
ChainSpec::FlamingFir => chain_spec::flaming_fir_config()?,
|
||||
ChainSpec::Development => chain_spec::development_config(),
|
||||
ChainSpec::LocalTestnet => chain_spec::local_testnet_config(),
|
||||
ChainSpec::StagingTestnet => chain_spec::staging_testnet_config(),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn from(s: &str) -> Option<Self> {
|
||||
match s {
|
||||
"dev" => Some(ChainSpec::Development),
|
||||
"local" => Some(ChainSpec::LocalTestnet),
|
||||
"" | "fir" | "flaming-fir" => Some(ChainSpec::FlamingFir),
|
||||
"staging" => Some(ChainSpec::StagingTestnet),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_spec(id: &str) -> Result<Option<chain_spec::ChainSpec>, String> {
|
||||
Ok(match ChainSpec::from(id) {
|
||||
Some(spec) => Some(spec.load()?),
|
||||
None => None,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
// Copyright 2018-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate 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.
|
||||
|
||||
// Substrate 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 Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![warn(unused_extern_crates)]
|
||||
|
||||
//! Service implementation. Specialized wrapper over substrate service.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use babe;
|
||||
use client::{self, LongestChain};
|
||||
use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider};
|
||||
use node_executor;
|
||||
use node_primitives::Block;
|
||||
use node_runtime::{GenesisConfig, RuntimeApi};
|
||||
use substrate_service::{
|
||||
AbstractService, ServiceBuilder, config::Configuration, error::{Error as ServiceError},
|
||||
};
|
||||
use transaction_pool::{self, txpool::{Pool as TransactionPool}};
|
||||
use inherents::InherentDataProviders;
|
||||
use network::construct_simple_protocol;
|
||||
|
||||
use substrate_service::{Service, NetworkStatus};
|
||||
use client::{Client, LocalCallExecutor};
|
||||
use client_db::Backend;
|
||||
use sr_primitives::traits::Block as BlockT;
|
||||
use node_executor::NativeExecutor;
|
||||
use network::NetworkService;
|
||||
use offchain::OffchainWorkers;
|
||||
use primitives::Blake2Hasher;
|
||||
|
||||
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) => {{
|
||||
type RpcExtension = jsonrpc_core::IoHandler<substrate_rpc::Metadata>;
|
||||
let mut import_setup = None;
|
||||
let inherent_data_providers = inherents::InherentDataProviders::new();
|
||||
|
||||
let builder = substrate_service::ServiceBuilder::new_full::<
|
||||
node_primitives::Block, node_runtime::RuntimeApi, node_executor::Executor
|
||||
>($config)?
|
||||
.with_select_chain(|_config, backend| {
|
||||
Ok(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, mut select_chain, _transaction_pool| {
|
||||
let select_chain = select_chain.take()
|
||||
.ok_or_else(|| substrate_service::Error::SelectChainRequired)?;
|
||||
let (grandpa_block_import, grandpa_link) = grandpa::block_import(
|
||||
client.clone(),
|
||||
&*client,
|
||||
select_chain,
|
||||
)?;
|
||||
let justification_import = grandpa_block_import.clone();
|
||||
|
||||
let (block_import, babe_link) = babe::block_import(
|
||||
babe::Config::get_or_compute(&*client)?,
|
||||
grandpa_block_import,
|
||||
client.clone(),
|
||||
client.clone(),
|
||||
)?;
|
||||
|
||||
let import_queue = babe::import_queue(
|
||||
babe_link.clone(),
|
||||
block_import.clone(),
|
||||
Some(Box::new(justification_import)),
|
||||
None,
|
||||
client.clone(),
|
||||
client,
|
||||
inherent_data_providers.clone(),
|
||||
)?;
|
||||
|
||||
import_setup = Some((block_import, grandpa_link, babe_link));
|
||||
Ok(import_queue)
|
||||
})?
|
||||
.with_rpc_extensions(|client, pool, _backend| -> RpcExtension {
|
||||
node_rpc::create(client, pool)
|
||||
})?;
|
||||
|
||||
(builder, import_setup, inherent_data_providers)
|
||||
}}
|
||||
}
|
||||
|
||||
/// Creates a full service from the configuration.
|
||||
///
|
||||
/// We need to use a macro because the test suit doesn't work with an opaque service. It expects
|
||||
/// concrete types instead.
|
||||
macro_rules! new_full {
|
||||
($config:expr, $with_startup_data: expr) => {{
|
||||
use futures::sync::mpsc;
|
||||
use network::DhtEvent;
|
||||
use futures03::{
|
||||
compat::Stream01CompatExt,
|
||||
stream::StreamExt,
|
||||
future::{FutureExt, TryFutureExt},
|
||||
};
|
||||
|
||||
let (
|
||||
is_authority,
|
||||
force_authoring,
|
||||
name,
|
||||
disable_grandpa
|
||||
) = (
|
||||
$config.roles.is_authority(),
|
||||
$config.force_authoring,
|
||||
$config.name.clone(),
|
||||
$config.disable_grandpa
|
||||
);
|
||||
|
||||
// sentry nodes announce themselves as authorities to the network
|
||||
// and should run the same protocols authorities do, but it should
|
||||
// never actively participate in any consensus process.
|
||||
let participates_in_consensus = is_authority && !$config.sentry_mode;
|
||||
|
||||
let (builder, mut import_setup, inherent_data_providers) = new_full_start!($config);
|
||||
|
||||
// Dht event channel from the network to the authority discovery module. Use bounded channel to ensure
|
||||
// back-pressure. Authority discovery is triggering one event per authority within the current authority set.
|
||||
// This estimates the authority set size to be somewhere below 10 000 thereby setting the channel buffer size to
|
||||
// 10 000.
|
||||
let (dht_event_tx, dht_event_rx) =
|
||||
mpsc::channel::<DhtEvent>(10_000);
|
||||
|
||||
let service = builder.with_network_protocol(|_| Ok(crate::service::NodeProtocol::new()))?
|
||||
.with_finality_proof_provider(|client, backend|
|
||||
Ok(Arc::new(grandpa::FinalityProofProvider::new(backend, client)) as _)
|
||||
)?
|
||||
.with_dht_event_tx(dht_event_tx)?
|
||||
.build()?;
|
||||
|
||||
let (block_import, grandpa_link, babe_link) = import_setup.take()
|
||||
.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
|
||||
|
||||
($with_startup_data)(&block_import, &babe_link);
|
||||
|
||||
if participates_in_consensus {
|
||||
let proposer = substrate_basic_authorship::ProposerFactory {
|
||||
client: service.client(),
|
||||
transaction_pool: service.transaction_pool(),
|
||||
};
|
||||
|
||||
let client = service.client();
|
||||
let select_chain = service.select_chain()
|
||||
.ok_or(substrate_service::Error::SelectChainRequired)?;
|
||||
|
||||
let babe_config = babe::BabeParams {
|
||||
keystore: service.keystore(),
|
||||
client,
|
||||
select_chain,
|
||||
env: proposer,
|
||||
block_import,
|
||||
sync_oracle: service.network(),
|
||||
inherent_data_providers: inherent_data_providers.clone(),
|
||||
force_authoring,
|
||||
babe_link,
|
||||
};
|
||||
|
||||
let babe = babe::start_babe(babe_config)?;
|
||||
service.spawn_essential_task(babe);
|
||||
|
||||
let future03_dht_event_rx = dht_event_rx.compat()
|
||||
.map(|x| x.expect("<mpsc::channel::Receiver as Stream> never returns an error; qed"))
|
||||
.boxed();
|
||||
let authority_discovery = authority_discovery::AuthorityDiscovery::new(
|
||||
service.client(),
|
||||
service.network(),
|
||||
service.keystore(),
|
||||
future03_dht_event_rx,
|
||||
);
|
||||
let future01_authority_discovery = authority_discovery.map(|x| Ok(x)).compat();
|
||||
|
||||
service.spawn_task(future01_authority_discovery);
|
||||
}
|
||||
|
||||
// if the node isn't actively participating in consensus then it doesn't
|
||||
// need a keystore, regardless of which protocol we use below.
|
||||
let keystore = if participates_in_consensus {
|
||||
Some(service.keystore())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let config = grandpa::Config {
|
||||
// FIXME #1578 make this available through chainspec
|
||||
gossip_duration: std::time::Duration::from_millis(333),
|
||||
justification_period: 512,
|
||||
name: Some(name),
|
||||
observer_enabled: true,
|
||||
keystore,
|
||||
is_authority,
|
||||
};
|
||||
|
||||
match (is_authority, disable_grandpa) {
|
||||
(false, false) => {
|
||||
// start the lightweight GRANDPA observer
|
||||
service.spawn_task(grandpa::run_grandpa_observer(
|
||||
config,
|
||||
grandpa_link,
|
||||
service.network(),
|
||||
service.on_exit(),
|
||||
)?);
|
||||
},
|
||||
(true, false) => {
|
||||
// start the full GRANDPA voter
|
||||
let grandpa_config = grandpa::GrandpaParams {
|
||||
config: config,
|
||||
link: grandpa_link,
|
||||
network: service.network(),
|
||||
inherent_data_providers: inherent_data_providers.clone(),
|
||||
on_exit: service.on_exit(),
|
||||
telemetry_on_connect: Some(service.telemetry_on_connect_stream()),
|
||||
voting_rule: grandpa::VotingRulesBuilder::default().build(),
|
||||
};
|
||||
// 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, inherent_data_providers))
|
||||
}};
|
||||
($config:expr) => {{
|
||||
new_full!($config, |_, _| {})
|
||||
}}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
type ConcreteBlock = node_primitives::Block;
|
||||
#[allow(dead_code)]
|
||||
type ConcreteClient =
|
||||
Client<
|
||||
Backend<ConcreteBlock>,
|
||||
LocalCallExecutor<Backend<ConcreteBlock>,
|
||||
NativeExecutor<node_executor::Executor>>,
|
||||
ConcreteBlock,
|
||||
node_runtime::RuntimeApi
|
||||
>;
|
||||
#[allow(dead_code)]
|
||||
type ConcreteBackend = Backend<ConcreteBlock>;
|
||||
|
||||
/// A specialized configuration object for setting up the node..
|
||||
pub type NodeConfiguration<C> = Configuration<C, GenesisConfig, crate::chain_spec::Extensions>;
|
||||
|
||||
/// Builds a new service for a full client.
|
||||
pub fn new_full<C: Send + Default + 'static>(config: NodeConfiguration<C>)
|
||||
-> Result<
|
||||
Service<
|
||||
ConcreteBlock,
|
||||
ConcreteClient,
|
||||
LongestChain<ConcreteBackend, ConcreteBlock>,
|
||||
NetworkStatus<ConcreteBlock>,
|
||||
NetworkService<ConcreteBlock, crate::service::NodeProtocol, <ConcreteBlock as BlockT>::Hash>,
|
||||
TransactionPool<transaction_pool::FullChainApi<ConcreteClient, ConcreteBlock>>,
|
||||
OffchainWorkers<
|
||||
ConcreteClient,
|
||||
<ConcreteBackend as client_api::backend::Backend<Block, Blake2Hasher>>::OffchainStorage,
|
||||
ConcreteBlock,
|
||||
>
|
||||
>,
|
||||
ServiceError,
|
||||
>
|
||||
{
|
||||
new_full!(config).map(|(service, _)| service)
|
||||
}
|
||||
|
||||
/// Builds a new service for a light client.
|
||||
pub fn new_light<C: Send + Default + 'static>(config: NodeConfiguration<C>)
|
||||
-> Result<impl AbstractService, ServiceError> {
|
||||
type RpcExtension = jsonrpc_core::IoHandler<substrate_rpc::Metadata>;
|
||||
let inherent_data_providers = InherentDataProviders::new();
|
||||
|
||||
let service = ServiceBuilder::new_light::<Block, RuntimeApi, node_executor::Executor>(config)?
|
||||
.with_select_chain(|_config, backend| {
|
||||
Ok(LongestChain::new(backend.clone()))
|
||||
})?
|
||||
.with_transaction_pool(|config, client|
|
||||
Ok(TransactionPool::new(config, transaction_pool::FullChainApi::new(client)))
|
||||
)?
|
||||
.with_import_queue_and_fprb(|_config, client, backend, fetcher, _select_chain, _tx_pool| {
|
||||
let fetch_checker = fetcher
|
||||
.map(|fetcher| fetcher.checker().clone())
|
||||
.ok_or_else(|| "Trying to start light import queue without active fetch checker")?;
|
||||
let grandpa_block_import = grandpa::light_block_import::<_, _, _, RuntimeApi>(
|
||||
client.clone(),
|
||||
backend,
|
||||
&*client,
|
||||
Arc::new(fetch_checker),
|
||||
)?;
|
||||
|
||||
let finality_proof_import = grandpa_block_import.clone();
|
||||
let finality_proof_request_builder =
|
||||
finality_proof_import.create_finality_proof_request_builder();
|
||||
|
||||
let (babe_block_import, babe_link) = babe::block_import(
|
||||
babe::Config::get_or_compute(&*client)?,
|
||||
grandpa_block_import,
|
||||
client.clone(),
|
||||
client.clone(),
|
||||
)?;
|
||||
|
||||
let import_queue = babe::import_queue(
|
||||
babe_link,
|
||||
babe_block_import,
|
||||
None,
|
||||
Some(Box::new(finality_proof_import)),
|
||||
client.clone(),
|
||||
client,
|
||||
inherent_data_providers.clone(),
|
||||
)?;
|
||||
|
||||
Ok((import_queue, finality_proof_request_builder))
|
||||
})?
|
||||
.with_network_protocol(|_| Ok(NodeProtocol::new()))?
|
||||
.with_finality_proof_provider(|client, backend|
|
||||
Ok(Arc::new(GrandpaFinalityProofProvider::new(backend, client)) as _)
|
||||
)?
|
||||
.with_rpc_extensions(|client, pool, _backend| -> RpcExtension {
|
||||
node_rpc::create(client, pool)
|
||||
})?
|
||||
.build()?;
|
||||
|
||||
Ok(service)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use babe::CompatibleDigestItem;
|
||||
use consensus_common::{
|
||||
Environment, Proposer, BlockImportParams, BlockOrigin, ForkChoiceStrategy, BlockImport,
|
||||
};
|
||||
use node_primitives::{Block, DigestItem, Signature};
|
||||
use node_runtime::{BalancesCall, Call, UncheckedExtrinsic, Address};
|
||||
use node_runtime::constants::{currency::CENTS, time::SLOT_DURATION};
|
||||
use codec::{Encode, Decode};
|
||||
use primitives::{crypto::Pair as CryptoPair, H256};
|
||||
use sr_primitives::{
|
||||
generic::{BlockId, Era, Digest, SignedPayload},
|
||||
traits::Block as BlockT,
|
||||
traits::Verify,
|
||||
OpaqueExtrinsic,
|
||||
};
|
||||
use timestamp;
|
||||
use finality_tracker;
|
||||
use keyring::AccountKeyring;
|
||||
use substrate_service::{AbstractService, Roles};
|
||||
use crate::service::new_full;
|
||||
use sr_primitives::traits::IdentifyAccount;
|
||||
|
||||
type AccountPublic = <Signature as Verify>::Signer;
|
||||
|
||||
#[cfg(feature = "rhd")]
|
||||
fn test_sync() {
|
||||
use primitives::ed25519::Pair;
|
||||
|
||||
use {service_test, Factory};
|
||||
use client::{BlockImportParams, BlockOrigin};
|
||||
|
||||
let alice: Arc<ed25519::Pair> = Arc::new(Keyring::Alice.into());
|
||||
let bob: Arc<ed25519::Pair> = Arc::new(Keyring::Bob.into());
|
||||
let validators = vec![alice.public().0.into(), bob.public().0.into()];
|
||||
let keys: Vec<&ed25519::Pair> = vec![&*alice, &*bob];
|
||||
let dummy_runtime = ::tokio::runtime::Runtime::new().unwrap();
|
||||
let block_factory = |service: &<Factory as service::ServiceFactory>::FullService| {
|
||||
let block_id = BlockId::number(service.client().info().chain.best_number);
|
||||
let parent_header = service.client().header(&block_id).unwrap().unwrap();
|
||||
let consensus_net = ConsensusNetwork::new(service.network(), service.client().clone());
|
||||
let proposer_factory = consensus::ProposerFactory {
|
||||
client: service.client().clone(),
|
||||
transaction_pool: service.transaction_pool().clone(),
|
||||
network: consensus_net,
|
||||
force_delay: 0,
|
||||
handle: dummy_runtime.executor(),
|
||||
};
|
||||
let (proposer, _, _) = proposer_factory.init(&parent_header, &validators, alice.clone()).unwrap();
|
||||
let block = proposer.propose().expect("Error making test block");
|
||||
BlockImportParams {
|
||||
origin: BlockOrigin::File,
|
||||
justification: Vec::new(),
|
||||
internal_justification: Vec::new(),
|
||||
finalized: false,
|
||||
body: Some(block.extrinsics),
|
||||
header: block.header,
|
||||
auxiliary: Vec::new(),
|
||||
}
|
||||
};
|
||||
let extrinsic_factory =
|
||||
|service: &SyncService<<Factory as service::ServiceFactory>::FullService>|
|
||||
{
|
||||
let payload = (
|
||||
0,
|
||||
Call::Balances(BalancesCall::transfer(RawAddress::Id(bob.public().0.into()), 69.into())),
|
||||
Era::immortal(),
|
||||
service.client().genesis_hash()
|
||||
);
|
||||
let signature = alice.sign(&payload.encode()).into();
|
||||
let id = alice.public().0.into();
|
||||
let xt = UncheckedExtrinsic {
|
||||
signature: Some((RawAddress::Id(id), signature, payload.0, Era::immortal())),
|
||||
function: payload.1,
|
||||
}.encode();
|
||||
let v: Vec<u8> = Decode::decode(&mut xt.as_slice()).unwrap();
|
||||
OpaqueExtrinsic(v)
|
||||
};
|
||||
service_test::sync(
|
||||
chain_spec::integration_test_config(),
|
||||
|config| new_full(config),
|
||||
|mut config| {
|
||||
// light nodes are unsupported
|
||||
config.roles = Roles::FULL;
|
||||
new_full(config)
|
||||
},
|
||||
block_factory,
|
||||
extrinsic_factory,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_sync() {
|
||||
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
|
||||
let keystore = keystore::Store::open(keystore_path.path(), None)
|
||||
.expect("Creates keystore");
|
||||
let alice = keystore.write().insert_ephemeral_from_seed::<babe::AuthorityPair>("//Alice")
|
||||
.expect("Creates authority pair");
|
||||
|
||||
let chain_spec = crate::chain_spec::tests::integration_test_config_with_single_authority();
|
||||
|
||||
// For the block factory
|
||||
let mut slot_num = 1u64;
|
||||
|
||||
// For the extrinsics factory
|
||||
let bob = Arc::new(AccountKeyring::Bob.pair());
|
||||
let charlie = Arc::new(AccountKeyring::Charlie.pair());
|
||||
let mut index = 0;
|
||||
|
||||
service_test::sync(
|
||||
chain_spec,
|
||||
|config| {
|
||||
let mut setup_handles = None;
|
||||
new_full!(config, |
|
||||
block_import: &babe::BabeBlockImport<_, _, Block, _, _, _>,
|
||||
babe_link: &babe::BabeLink<Block>,
|
||||
| {
|
||||
setup_handles = Some((block_import.clone(), babe_link.clone()));
|
||||
}).map(move |(node, x)| (node, (x, setup_handles.unwrap())))
|
||||
},
|
||||
|mut config| {
|
||||
// light nodes are unsupported
|
||||
config.roles = Roles::FULL;
|
||||
new_full(config)
|
||||
},
|
||||
|service, &mut (ref inherent_data_providers, (ref mut block_import, ref babe_link))| {
|
||||
let mut inherent_data = inherent_data_providers
|
||||
.create_inherent_data()
|
||||
.expect("Creates inherent data.");
|
||||
inherent_data.replace_data(finality_tracker::INHERENT_IDENTIFIER, &1u64);
|
||||
|
||||
let parent_id = BlockId::number(service.client().info().chain.best_number);
|
||||
let parent_header = service.client().header(&parent_id).unwrap().unwrap();
|
||||
let mut proposer_factory = substrate_basic_authorship::ProposerFactory {
|
||||
client: service.client(),
|
||||
transaction_pool: service.transaction_pool(),
|
||||
};
|
||||
|
||||
let mut digest = Digest::<H256>::default();
|
||||
|
||||
// even though there's only one authority some slots might be empty,
|
||||
// so we must keep trying the next slots until we can claim one.
|
||||
let babe_pre_digest = loop {
|
||||
inherent_data.replace_data(timestamp::INHERENT_IDENTIFIER, &(slot_num * SLOT_DURATION));
|
||||
if let Some(babe_pre_digest) = babe::test_helpers::claim_slot(
|
||||
slot_num,
|
||||
&parent_header,
|
||||
&*service.client(),
|
||||
&keystore,
|
||||
&babe_link,
|
||||
) {
|
||||
break babe_pre_digest;
|
||||
}
|
||||
|
||||
slot_num += 1;
|
||||
};
|
||||
|
||||
digest.push(<DigestItem as CompatibleDigestItem>::babe_pre_digest(babe_pre_digest));
|
||||
|
||||
let mut proposer = proposer_factory.init(&parent_header).unwrap();
|
||||
let new_block = futures03::executor::block_on(proposer.propose(
|
||||
inherent_data,
|
||||
digest,
|
||||
std::time::Duration::from_secs(1),
|
||||
)).expect("Error making test block");
|
||||
|
||||
let (new_header, new_body) = new_block.deconstruct();
|
||||
let pre_hash = new_header.hash();
|
||||
// sign the pre-sealed hash of the block and then
|
||||
// add it to a digest item.
|
||||
let to_sign = pre_hash.encode();
|
||||
let signature = alice.sign(&to_sign[..]);
|
||||
let item = <DigestItem as CompatibleDigestItem>::babe_seal(
|
||||
signature.into(),
|
||||
);
|
||||
slot_num += 1;
|
||||
|
||||
let params = BlockImportParams {
|
||||
origin: BlockOrigin::File,
|
||||
header: new_header,
|
||||
justification: None,
|
||||
post_digests: vec![item],
|
||||
body: Some(new_body),
|
||||
finalized: false,
|
||||
auxiliary: Vec::new(),
|
||||
fork_choice: ForkChoiceStrategy::LongestChain,
|
||||
allow_missing_state: false,
|
||||
};
|
||||
|
||||
block_import.import_block(params, Default::default())
|
||||
.expect("error importing test block");
|
||||
},
|
||||
|service, _| {
|
||||
let amount = 5 * CENTS;
|
||||
let to: Address = AccountPublic::from(bob.public()).into_account().into();
|
||||
let from: Address = AccountPublic::from(charlie.public()).into_account().into();
|
||||
let genesis_hash = service.client().block_hash(0).unwrap().unwrap();
|
||||
let best_block_id = BlockId::number(service.client().info().chain.best_number);
|
||||
let version = service.client().runtime_version_at(&best_block_id).unwrap().spec_version;
|
||||
let signer = charlie.clone();
|
||||
|
||||
let function = Call::Balances(BalancesCall::transfer(to.into(), amount));
|
||||
|
||||
let check_version = system::CheckVersion::new();
|
||||
let check_genesis = system::CheckGenesis::new();
|
||||
let check_era = system::CheckEra::from(Era::Immortal);
|
||||
let check_nonce = system::CheckNonce::from(index);
|
||||
let check_weight = system::CheckWeight::new();
|
||||
let payment = transaction_payment::ChargeTransactionPayment::from(0);
|
||||
let extra = (
|
||||
check_version,
|
||||
check_genesis,
|
||||
check_era,
|
||||
check_nonce,
|
||||
check_weight,
|
||||
payment,
|
||||
Default::default(),
|
||||
);
|
||||
let raw_payload = SignedPayload::from_raw(
|
||||
function,
|
||||
extra,
|
||||
(version, genesis_hash, genesis_hash, (), (), (), ())
|
||||
);
|
||||
let signature = raw_payload.using_encoded(|payload| {
|
||||
signer.sign(payload)
|
||||
});
|
||||
let (function, extra, _) = raw_payload.deconstruct();
|
||||
let xt = UncheckedExtrinsic::new_signed(
|
||||
function,
|
||||
from.into(),
|
||||
signature.into(),
|
||||
extra,
|
||||
).encode();
|
||||
let v: Vec<u8> = Decode::decode(&mut xt.as_slice()).unwrap();
|
||||
|
||||
index += 1;
|
||||
OpaqueExtrinsic(v)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_consensus() {
|
||||
service_test::consensus(
|
||||
crate::chain_spec::tests::integration_test_config_with_two_authorities(),
|
||||
|config| new_full(config),
|
||||
|mut config| {
|
||||
// light nodes are unsupported
|
||||
config.roles = Roles::FULL;
|
||||
new_full(config)
|
||||
},
|
||||
vec![
|
||||
"//Alice".into(),
|
||||
"//Bob".into(),
|
||||
],
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user