mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 05:51:02 +00:00
Merge branch 'master' into gui-candidate-justification
This commit is contained in:
@@ -36,6 +36,12 @@ variables:
|
|||||||
- schedules
|
- schedules
|
||||||
- web
|
- web
|
||||||
- /^[0-9]+$/ # PRs
|
- /^[0-9]+$/ # PRs
|
||||||
|
retry:
|
||||||
|
max: 2
|
||||||
|
when:
|
||||||
|
- runner_system_failure
|
||||||
|
- unknown_failure
|
||||||
|
- api_failure
|
||||||
tags:
|
tags:
|
||||||
- linux-docker
|
- linux-docker
|
||||||
|
|
||||||
|
|||||||
Generated
+1700
-398
File diff suppressed because it is too large
Load Diff
+10
-5
@@ -1,8 +1,13 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"consensus",
|
"consensus",
|
||||||
"runtime",
|
"network",
|
||||||
"network",
|
"runtime",
|
||||||
"test/runtime",
|
"test/runtime",
|
||||||
"test/client",
|
"test/client",
|
||||||
|
"test/parachain/runtime",
|
||||||
|
"test/parachain/",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = 'unwind'
|
||||||
|
|||||||
+43
-2
@@ -10,10 +10,51 @@ Cumulus clouds are shaped sort of like dots and are up in the air, like this pro
|
|||||||
|
|
||||||
For now, this is only project contained in this repo. *cumulus-consensus* is a consensus engine for Substrate which follows a Polkadot relay chain. This will run a Polkadot node internally, and dictate to the client and synchronization algorithms which chain to follow, finalize, and treat as best.
|
For now, this is only project contained in this repo. *cumulus-consensus* is a consensus engine for Substrate which follows a Polkadot relay chain. This will run a Polkadot node internally, and dictate to the client and synchronization algorithms which chain to follow, finalize, and treat as best.
|
||||||
|
|
||||||
## cumulus-runtime
|
## cumulus-runtime
|
||||||
|
|
||||||
A planned wrapper around substrate runtimes to turn them into parachain validation code and to provide proof-generation routines.
|
A planned wrapper around substrate runtimes to turn them into parachain validation code and to provide proof-generation routines.
|
||||||
|
|
||||||
## cumulus-collator
|
## cumulus-collator
|
||||||
|
|
||||||
A planned Polkadot collator for the parachain.
|
A planned Polkadot collator for the parachain.
|
||||||
|
|
||||||
|
## Running a collator
|
||||||
|
|
||||||
|
1. Checkout polkadot at `bkchr-cumulus-branch`.
|
||||||
|
2. Build the polkadot chainspec:
|
||||||
|
|
||||||
|
`cargo run --release -- build-spec --chain=local --raw > polkadot_chainspec.json`
|
||||||
|
|
||||||
|
Open the `polkadot_chainspec.json` and replace the `bootnodes` with: `"bootNodes": [],`
|
||||||
|
|
||||||
|
3. Run `Alice` and `Bob`:
|
||||||
|
|
||||||
|
`cargo run --release -- --chain=local --base-path=cumulus_relay_chain_node_0 --alice`
|
||||||
|
|
||||||
|
`cargo run --release -- --chain=local --base-path=cumulus_relay_chain_node_1 --bob --port 50666`
|
||||||
|
|
||||||
|
3. Switch back to this repository and generate the parachain genesis state:
|
||||||
|
|
||||||
|
`cargo run --release -p cumulus-test-parachain-collator -- export-genesis-state genesis-state`
|
||||||
|
|
||||||
|
4. Run the collator:
|
||||||
|
|
||||||
|
`cargo run --release -p cumulus-test-parachain-collator -- -d cumulus_collator_path --chain polkadot_chainspec.json --bootnodes /ip4/127.0.0.1/tcp/30333/p2p/PEER_ID`
|
||||||
|
|
||||||
|
`PEER_ID` needs to be replaced with the peer id of the polkadot validator that uses `Alice`
|
||||||
|
as authority.
|
||||||
|
|
||||||
|
5. Open `https://polkadot.js.org/apps/#/sudo` and register the parachain.
|
||||||
|
|
||||||
|
`id`: `100`
|
||||||
|
|
||||||
|
`initial_head_data`: Use the file you generated in 3. (name: genesis-state)
|
||||||
|
|
||||||
|
`code`: `CUMULUS_REPO/target/release/wbuild/cumulus-test-parachain-runtime/cumulus_test_parachain_runtime.compact.wasm`
|
||||||
|
|
||||||
|
Now your parachain should be registered and the collator should start building blocks and sending
|
||||||
|
them to the relay chain.
|
||||||
|
|
||||||
|
6. ...
|
||||||
|
|
||||||
|
7. PROFIT!
|
||||||
|
|||||||
@@ -7,6 +7,9 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
# Substrate dependencies
|
# Substrate dependencies
|
||||||
sr-primitives = { package = "sr-primitives", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
sr-primitives = { package = "sr-primitives", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-primitives = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-client = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-service = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
consensus-common = { package = "substrate-consensus-common", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
consensus-common = { package = "substrate-consensus-common", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
inherents = { package = "substrate-inherents", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
inherents = { package = "substrate-inherents", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
cli = { package = "substrate-cli", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
cli = { package = "substrate-cli", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
@@ -15,6 +18,10 @@ cli = { package = "substrate-cli", git = "https://github.com/paritytech/substrat
|
|||||||
polkadot-collator = { git = "https://github.com/paritytech/polkadot", branch = "bkchr-cumulus-branch" }
|
polkadot-collator = { git = "https://github.com/paritytech/polkadot", branch = "bkchr-cumulus-branch" }
|
||||||
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "bkchr-cumulus-branch" }
|
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "bkchr-cumulus-branch" }
|
||||||
|
|
||||||
|
# Cumulus dependencies
|
||||||
|
cumulus-consensus = { path = "../consensus" }
|
||||||
|
cumulus-runtime = { path = "../runtime" }
|
||||||
|
|
||||||
# other deps
|
# other deps
|
||||||
log = "0.4.8"
|
log = "0.4.8"
|
||||||
codec = { package = "parity-scale-codec", version = "1.0.6", features = [ "derive" ] }
|
codec = { package = "parity-scale-codec", version = "1.0.6", features = [ "derive" ] }
|
||||||
@@ -24,4 +31,8 @@ parking_lot = "0.9"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
test-runtime = { package = "cumulus-test-runtime", path = "../test/runtime" }
|
test-runtime = { package = "cumulus-test-runtime", path = "../test/runtime" }
|
||||||
|
test-client = { package = "cumulus-test-client", path = "../test/client" }
|
||||||
|
substrate-test-client = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
polkadot-executor = { git = "https://github.com/paritytech/polkadot", branch = "bkchr-cumulus-branch" }
|
||||||
keyring = { package = "substrate-keyring", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
keyring = { package = "substrate-keyring", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
env_logger = "0.7.1"
|
||||||
|
|||||||
+249
-77
@@ -1,5 +1,5 @@
|
|||||||
// Copyright 2019 Parity Technologies (UK) Ltd.
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
// This file is part of Substrate.
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
// Substrate is free software: you can redistribute it and/or modify
|
// Substrate is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General Public License as published by
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -16,15 +16,22 @@
|
|||||||
|
|
||||||
//! Cumulus Collator implementation for Substrate.
|
//! Cumulus Collator implementation for Substrate.
|
||||||
|
|
||||||
use sr_primitives::traits::Block as BlockT;
|
use cumulus_runtime::ParachainBlockData;
|
||||||
use consensus_common::{Environment, Proposer};
|
|
||||||
|
use sr_primitives::traits::{Block as BlockT, Header as HeaderT};
|
||||||
|
use consensus_common::{
|
||||||
|
BlockImport, Environment, Proposer, ForkChoiceStrategy, BlockImportParams, BlockOrigin,
|
||||||
|
Error as ConsensusError,
|
||||||
|
};
|
||||||
use inherents::InherentDataProviders;
|
use inherents::InherentDataProviders;
|
||||||
|
use substrate_primitives::Blake2Hasher;
|
||||||
|
|
||||||
use polkadot_collator::{
|
use polkadot_collator::{
|
||||||
InvalidHead, ParachainContext, BuildParachainContext, Network as CollatorNetwork, VersionInfo,
|
InvalidHead, ParachainContext, BuildParachainContext, Network as CollatorNetwork, VersionInfo,
|
||||||
|
TaskExecutor, PolkadotClient,
|
||||||
};
|
};
|
||||||
use polkadot_primitives::{
|
use polkadot_primitives::{
|
||||||
Hash,
|
Hash as PHash, Block as PBlock,
|
||||||
parachain::{
|
parachain::{
|
||||||
self, BlockData, Message, Id as ParaId, OutgoingMessages, Status as ParachainStatus,
|
self, BlockData, Message, Id as ParaId, OutgoingMessages, Status as ParachainStatus,
|
||||||
CollatorPair,
|
CollatorPair,
|
||||||
@@ -33,12 +40,12 @@ use polkadot_primitives::{
|
|||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
|
|
||||||
use log::error;
|
use log::{error, trace};
|
||||||
|
|
||||||
use futures03::TryFutureExt;
|
use futures03::{TryFutureExt, future};
|
||||||
use futures::{Future, future::IntoFuture};
|
use futures::{Future, future::IntoFuture};
|
||||||
|
|
||||||
use std::{sync::Arc, marker::PhantomData, time::Duration};
|
use std::{sync::Arc, marker::PhantomData, time::Duration, fmt::Debug};
|
||||||
|
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
@@ -49,100 +56,184 @@ struct HeadData<Block: BlockT> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The implementation of the Cumulus `Collator`.
|
/// The implementation of the Cumulus `Collator`.
|
||||||
pub struct Collator<Block, PF> {
|
pub struct Collator<Block, PF, BI> {
|
||||||
proposer_factory: Arc<Mutex<PF>>,
|
proposer_factory: Arc<Mutex<PF>>,
|
||||||
_phantom: PhantomData<Block>,
|
_phantom: PhantomData<Block>,
|
||||||
inherent_data_providers: InherentDataProviders,
|
inherent_data_providers: InherentDataProviders,
|
||||||
collator_network: Arc<dyn CollatorNetwork>,
|
collator_network: Arc<dyn CollatorNetwork>,
|
||||||
|
block_import: Arc<Mutex<BI>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block: BlockT, PF: Environment<Block>> Collator<Block, PF> {
|
impl<Block, PF, BI> Collator<Block, PF, BI> {
|
||||||
/// Create a new instance.
|
/// Create a new instance.
|
||||||
fn new(
|
fn new(
|
||||||
proposer_factory: PF,
|
proposer_factory: PF,
|
||||||
inherent_data_providers: InherentDataProviders,
|
inherent_data_providers: InherentDataProviders,
|
||||||
collator_network: Arc<dyn CollatorNetwork>,
|
collator_network: Arc<dyn CollatorNetwork>,
|
||||||
|
block_import: BI,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
proposer_factory: Arc::new(Mutex::new(proposer_factory)),
|
proposer_factory: Arc::new(Mutex::new(proposer_factory)),
|
||||||
inherent_data_providers,
|
inherent_data_providers,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
collator_network,
|
collator_network,
|
||||||
|
block_import: Arc::new(Mutex::new(block_import)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block, PF> Clone for Collator<Block, PF> {
|
impl<Block, PF, BI> Clone for Collator<Block, PF, BI> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
proposer_factory: self.proposer_factory.clone(),
|
proposer_factory: self.proposer_factory.clone(),
|
||||||
inherent_data_providers: self.inherent_data_providers.clone(),
|
inherent_data_providers: self.inherent_data_providers.clone(),
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
collator_network: self.collator_network.clone(),
|
collator_network: self.collator_network.clone(),
|
||||||
|
block_import: self.block_import.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block, PF> ParachainContext for Collator<Block, PF> where
|
impl<Block, PF, BI> ParachainContext for Collator<Block, PF, BI>
|
||||||
Block: BlockT,
|
where
|
||||||
PF: Environment<Block> + 'static + Send + Sync,
|
Block: BlockT,
|
||||||
PF::Error: std::fmt::Debug,
|
PF: Environment<Block> + 'static + Send,
|
||||||
PF::Proposer: Send + Sync,
|
BI: BlockImport<Block, Error=ConsensusError> + Send + Sync + 'static,
|
||||||
<PF::Proposer as Proposer<Block>>::Create: Unpin + Send + Sync,
|
|
||||||
{
|
{
|
||||||
type ProduceCandidate = Box<
|
type ProduceCandidate = Box<
|
||||||
dyn Future<Item=(BlockData, parachain::HeadData, OutgoingMessages), Error=InvalidHead>
|
dyn Future<Item=(BlockData, parachain::HeadData, OutgoingMessages), Error=InvalidHead>
|
||||||
+ Send + Sync
|
+ Send
|
||||||
>;
|
>;
|
||||||
|
|
||||||
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
|
fn produce_candidate<I: IntoIterator<Item=(ParaId, Message)>>(
|
||||||
&self,
|
&mut self,
|
||||||
_relay_chain_parent: Hash,
|
_relay_chain_parent: PHash,
|
||||||
status: ParachainStatus,
|
status: ParachainStatus,
|
||||||
_: I,
|
_: I,
|
||||||
) -> Self::ProduceCandidate {
|
) -> Self::ProduceCandidate {
|
||||||
|
trace!(target: "cumulus-collator", "Producing candidate");
|
||||||
|
|
||||||
let factory = self.proposer_factory.clone();
|
let factory = self.proposer_factory.clone();
|
||||||
let inherent_providers = self.inherent_data_providers.clone();
|
let inherent_providers = self.inherent_data_providers.clone();
|
||||||
|
let block_import = self.block_import.clone();
|
||||||
|
|
||||||
let res = HeadData::<Block>::decode(&mut &status.head_data.0[..])
|
let res = HeadData::<Block>::decode(&mut &status.head_data.0[..])
|
||||||
.map_err(|_| InvalidHead)
|
.map_err(|e| {
|
||||||
|
error!(target: "cumulus-collator", "Could not decode the head data: {:?}", e);
|
||||||
|
InvalidHead
|
||||||
|
})
|
||||||
.into_future()
|
.into_future()
|
||||||
.and_then(move |last_head|
|
.and_then(move |last_head| {
|
||||||
|
let parent_state_root = *last_head.header.state_root();
|
||||||
|
|
||||||
factory.lock()
|
factory.lock()
|
||||||
.init(&last_head.header)
|
.init(&last_head.header)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
//TODO: Do we want to return the real error?
|
error!(
|
||||||
error!("Could not create proposer: {:?}", e);
|
target: "cumulus-collator",
|
||||||
|
"Could not create proposer: {:?}",
|
||||||
|
e,
|
||||||
|
);
|
||||||
InvalidHead
|
InvalidHead
|
||||||
})
|
})
|
||||||
)
|
.and_then(|mut proposer| {
|
||||||
.and_then(move |proposer|
|
let inherent_data = inherent_providers.create_inherent_data()
|
||||||
inherent_providers.create_inherent_data()
|
.map_err(|e| {
|
||||||
.map(|id| (proposer, id))
|
error!(
|
||||||
.map_err(|e| {
|
target: "cumulus-collator",
|
||||||
error!("Failed to create inherent data: {:?}", e);
|
"Failed to create inherent data: {:?}",
|
||||||
InvalidHead
|
e,
|
||||||
|
);
|
||||||
|
InvalidHead
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let future = proposer.propose(
|
||||||
|
inherent_data,
|
||||||
|
Default::default(),
|
||||||
|
//TODO: Fix this.
|
||||||
|
Duration::from_secs(6),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(
|
||||||
|
target: "cumulus-collator",
|
||||||
|
"Proposing failed: {:?}",
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
InvalidHead
|
||||||
|
})
|
||||||
|
.and_then(move |(block, proof)| {
|
||||||
|
let res = match proof {
|
||||||
|
Some(proof) => {
|
||||||
|
let (header, extrinsics) = block.deconstruct();
|
||||||
|
|
||||||
|
// Create the parachain block data for the validators.
|
||||||
|
Ok(
|
||||||
|
ParachainBlockData::<Block>::new(
|
||||||
|
header,
|
||||||
|
extrinsics,
|
||||||
|
proof,
|
||||||
|
parent_state_root,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!(
|
||||||
|
target: "cumulus-collator",
|
||||||
|
"Proposer did not return the requested proof.",
|
||||||
|
);
|
||||||
|
Err(InvalidHead)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
future::ready(res)
|
||||||
|
})
|
||||||
|
.compat();
|
||||||
|
|
||||||
|
Ok(future)
|
||||||
})
|
})
|
||||||
)
|
})
|
||||||
.and_then(|(mut proposer, inherent_data)| {
|
.flatten()
|
||||||
proposer.propose(
|
.and_then(move |b| {
|
||||||
inherent_data,
|
let block_import_params = BlockImportParams {
|
||||||
|
origin: BlockOrigin::Own,
|
||||||
|
header: b.header().clone(),
|
||||||
|
justification: None,
|
||||||
|
post_digests: vec![],
|
||||||
|
body: Some(b.extrinsics().to_vec()),
|
||||||
|
finalized: false,
|
||||||
|
auxiliary: vec![], // block-weight is written in block import.
|
||||||
|
// TODO: block-import handles fork choice and this shouldn't even have the
|
||||||
|
// option to specify one.
|
||||||
|
// https://github.com/paritytech/substrate/issues/3623
|
||||||
|
fork_choice: ForkChoiceStrategy::LongestChain,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(err) = block_import.lock().import_block(
|
||||||
|
block_import_params,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
//TODO: Fix this.
|
) {
|
||||||
Duration::from_secs(6),
|
error!(
|
||||||
)
|
target: "cumulus-collator",
|
||||||
.map_err(|e| {
|
"Error importing build block (at {:?}): {:?}",
|
||||||
error!("Proposing failed: {:?}", e);
|
b.header().parent_hash(),
|
||||||
InvalidHead
|
err,
|
||||||
})
|
);
|
||||||
.compat()
|
Err(InvalidHead)
|
||||||
|
} else {
|
||||||
|
Ok(b)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.map(|b| {
|
.map(|b| {
|
||||||
let block_data = BlockData(b.encode());
|
let block_data = BlockData(b.encode());
|
||||||
let head_data = HeadData::<Block> { header: b.deconstruct().0 };
|
let head_data = HeadData::<Block> { header: b.into_header() };
|
||||||
let messages = OutgoingMessages { outgoing_messages: Vec::new() };
|
let messages = OutgoingMessages { outgoing_messages: Vec::new() };
|
||||||
|
|
||||||
(block_data, parachain::HeadData(head_data.encode()), messages)
|
(block_data, parachain::HeadData(head_data.encode()), messages)
|
||||||
|
})
|
||||||
|
.then(|r| {
|
||||||
|
trace!(target: "cumulus-collator", "Produced candidate: {:?}", r);
|
||||||
|
r
|
||||||
});
|
});
|
||||||
|
|
||||||
Box::new(res)
|
Box::new(res)
|
||||||
@@ -150,41 +241,60 @@ impl<Block, PF> ParachainContext for Collator<Block, PF> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Implements `BuildParachainContext` to build a collator instance.
|
/// Implements `BuildParachainContext` to build a collator instance.
|
||||||
struct CollatorBuilder<Block, PF> {
|
struct CollatorBuilder<Block, SP> {
|
||||||
inherent_data_providers: InherentDataProviders,
|
setup_parachain: SP,
|
||||||
proposer_factory: PF,
|
_marker: PhantomData<Block>,
|
||||||
_phantom: PhantomData<Block>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block, PF> CollatorBuilder<Block, PF> {
|
impl<Block, SP> CollatorBuilder<Block, SP> {
|
||||||
/// Create a new instance of self.
|
/// Create a new instance of self.
|
||||||
fn new(proposer_factory: PF, inherent_data_providers: InherentDataProviders) -> Self {
|
fn new(setup_parachain: SP) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inherent_data_providers,
|
setup_parachain,
|
||||||
proposer_factory,
|
_marker: PhantomData,
|
||||||
_phantom: Default::default(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Block, PF> BuildParachainContext for CollatorBuilder<Block, PF> where
|
impl<Block: BlockT, SP: SetupParachain<Block>> BuildParachainContext for CollatorBuilder<Block, SP> {
|
||||||
Block: BlockT,
|
type ParachainContext = Collator<Block, SP::ProposerFactory, SP::BlockImport>;
|
||||||
PF: Environment<Block> + 'static + Send + Sync,
|
|
||||||
PF::Error: std::fmt::Debug,
|
|
||||||
PF::Proposer: Send + Sync,
|
|
||||||
<PF::Proposer as Proposer<Block>>::Create: Unpin + Send + Sync,
|
|
||||||
{
|
|
||||||
type ParachainContext = Collator<Block, PF>;
|
|
||||||
|
|
||||||
fn build(self, network: Arc<dyn CollatorNetwork>) -> Result<Self::ParachainContext, ()> {
|
fn build<B, E>(
|
||||||
Ok(Collator::new(self.proposer_factory, self.inherent_data_providers, network))
|
self,
|
||||||
|
client: Arc<PolkadotClient<B, E>>,
|
||||||
|
task_executor: TaskExecutor,
|
||||||
|
network: Arc<dyn CollatorNetwork>,
|
||||||
|
) -> Result<Self::ParachainContext, ()>
|
||||||
|
where
|
||||||
|
B: substrate_client::backend::Backend<PBlock, Blake2Hasher> + 'static,
|
||||||
|
E: substrate_client::CallExecutor<PBlock, Blake2Hasher> + Clone + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
let (proposer_factory, block_import, inherent_data_providers) = self.setup_parachain
|
||||||
|
.setup_parachain(client, task_executor)
|
||||||
|
.map_err(|e| error!("Error setting up the parachain: {}", e))?;
|
||||||
|
|
||||||
|
Ok(Collator::new(proposer_factory, inherent_data_providers, network, block_import))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Something that can setup a parachain.
|
||||||
|
pub trait SetupParachain<Block: BlockT>: Send {
|
||||||
|
/// The proposer factory of the parachain to build blocks.
|
||||||
|
type ProposerFactory: Environment<Block> + Send + 'static;
|
||||||
|
/// The block import for importing the blocks build by the collator.
|
||||||
|
type BlockImport: BlockImport<Block, Error=ConsensusError> + Send + Sync + 'static;
|
||||||
|
|
||||||
|
/// Setup the parachain.
|
||||||
|
fn setup_parachain<P: cumulus_consensus::PolkadotClient>(
|
||||||
|
self,
|
||||||
|
polkadot_client: P,
|
||||||
|
task_executor: TaskExecutor,
|
||||||
|
) -> Result<(Self::ProposerFactory, Self::BlockImport, InherentDataProviders), String>;
|
||||||
|
}
|
||||||
|
|
||||||
/// Run a collator with the given proposer factory.
|
/// Run a collator with the given proposer factory.
|
||||||
pub fn run_collator<Block, PF, E, I>(
|
pub fn run_collator<Block, SP, E>(
|
||||||
proposer_factory: PF,
|
setup_parachain: SP,
|
||||||
inherent_data_providers: InherentDataProviders,
|
|
||||||
para_id: ParaId,
|
para_id: ParaId,
|
||||||
exit: E,
|
exit: E,
|
||||||
key: Arc<CollatorPair>,
|
key: Arc<CollatorPair>,
|
||||||
@@ -192,14 +302,11 @@ pub fn run_collator<Block, PF, E, I>(
|
|||||||
) -> Result<(), cli::error::Error>
|
) -> Result<(), cli::error::Error>
|
||||||
where
|
where
|
||||||
Block: BlockT,
|
Block: BlockT,
|
||||||
PF: Environment<Block> + 'static + Send + Sync,
|
SP: SetupParachain<Block> + Send + 'static,
|
||||||
PF::Error: std::fmt::Debug,
|
|
||||||
PF::Proposer: Send + Sync,
|
|
||||||
<PF::Proposer as Proposer<Block>>::Create: Unpin + Send + Sync,
|
|
||||||
E: IntoFuture<Item=(), Error=()>,
|
E: IntoFuture<Item=(), Error=()>,
|
||||||
E::Future: Send + Clone + Sync + 'static,
|
E::Future: Send + Clone + Sync + 'static,
|
||||||
{
|
{
|
||||||
let builder = CollatorBuilder::new(proposer_factory, inherent_data_providers);
|
let builder = CollatorBuilder::new(setup_parachain);
|
||||||
polkadot_collator::run_collator(builder, para_id, exit, key, version)
|
polkadot_collator::run_collator(builder, para_id, exit, key, version)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,10 +319,13 @@ mod tests {
|
|||||||
use polkadot_primitives::parachain::{ConsolidatedIngress, HeadData, FeeSchedule};
|
use polkadot_primitives::parachain::{ConsolidatedIngress, HeadData, FeeSchedule};
|
||||||
|
|
||||||
use keyring::Sr25519Keyring;
|
use keyring::Sr25519Keyring;
|
||||||
use sr_primitives::traits::{DigestFor, Header as HeaderT};
|
use sr_primitives::{generic::BlockId, traits::{DigestFor, Header as HeaderT}};
|
||||||
use inherents::InherentData;
|
use inherents::InherentData;
|
||||||
|
use substrate_client::error::Result as ClientResult;
|
||||||
|
use substrate_test_client::{NativeExecutor, WasmExecutionMethod::Interpreted};
|
||||||
|
|
||||||
use test_runtime::{Block, Header};
|
use test_runtime::{Block, Header};
|
||||||
|
use test_client::{Client, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt};
|
||||||
|
|
||||||
use futures03::future;
|
use futures03::future;
|
||||||
use futures::Stream;
|
use futures::Stream;
|
||||||
@@ -244,14 +354,15 @@ mod tests {
|
|||||||
|
|
||||||
impl Proposer<Block> for DummyProposer {
|
impl Proposer<Block> for DummyProposer {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Create = future::Ready<Result<Block, Error>>;
|
type Proposal = future::Ready<Result<(Block, Option<Vec<Vec<u8>>>), Error>>;
|
||||||
|
|
||||||
fn propose(
|
fn propose(
|
||||||
&mut self,
|
&mut self,
|
||||||
_: InherentData,
|
_: InherentData,
|
||||||
digest : DigestFor<Block>,
|
digest : DigestFor<Block>,
|
||||||
_: Duration,
|
_: Duration,
|
||||||
) -> Self::Create {
|
_: bool,
|
||||||
|
) -> Self::Proposal {
|
||||||
let header = Header::new(
|
let header = Header::new(
|
||||||
1337,
|
1337,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
@@ -260,7 +371,7 @@ mod tests {
|
|||||||
digest,
|
digest,
|
||||||
);
|
);
|
||||||
|
|
||||||
future::ready(Ok(Block::new(header, Vec::new())))
|
future::ready(Ok((Block::new(header, Vec::new()), Some(Default::default()))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +384,7 @@ mod tests {
|
|||||||
unimplemented!("Not required in tests")
|
unimplemented!("Not required in tests")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn checked_statements(&self, _: Hash) ->
|
fn checked_statements(&self, _: PHash) ->
|
||||||
Box<dyn Stream<Item=SignedStatement, Error=()>>
|
Box<dyn Stream<Item=SignedStatement, Error=()>>
|
||||||
{
|
{
|
||||||
unimplemented!("Not required in tests")
|
unimplemented!("Not required in tests")
|
||||||
@@ -291,10 +402,71 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct DummyPolkadotClient;
|
||||||
|
|
||||||
|
impl cumulus_consensus::PolkadotClient for DummyPolkadotClient {
|
||||||
|
type Error = Error;
|
||||||
|
type Finalized = Box<dyn futures03::Stream<Item=Vec<u8>> + Send + Unpin>;
|
||||||
|
|
||||||
|
fn finalized_heads(&self, _: ParaId) -> ClientResult<Self::Finalized> {
|
||||||
|
unimplemented!("Not required in tests")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parachain_head_at(
|
||||||
|
&self,
|
||||||
|
_: &BlockId<PBlock>,
|
||||||
|
_: ParaId,
|
||||||
|
) -> ClientResult<Option<Vec<u8>>>{
|
||||||
|
unimplemented!("Not required in tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DummySetup;
|
||||||
|
|
||||||
|
impl SetupParachain<Block> for DummySetup {
|
||||||
|
type ProposerFactory = DummyFactory;
|
||||||
|
type BlockImport = Client;
|
||||||
|
|
||||||
|
fn setup_parachain<P: cumulus_consensus::PolkadotClient>(
|
||||||
|
self,
|
||||||
|
_: P,
|
||||||
|
_: TaskExecutor,
|
||||||
|
) -> Result<(Self::ProposerFactory, Self::BlockImport, InherentDataProviders), String> {
|
||||||
|
Ok((DummyFactory, TestClientBuilder::new().build(), InherentDataProviders::default()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type BoxFuture = Box<dyn Future<Item = (), Error = ()> + Send>;
|
||||||
|
|
||||||
|
struct DummyFutureExecutor;
|
||||||
|
|
||||||
|
impl futures::future::Executor<BoxFuture>
|
||||||
|
for DummyFutureExecutor
|
||||||
|
{
|
||||||
|
fn execute(
|
||||||
|
&self,
|
||||||
|
_: BoxFuture,
|
||||||
|
) -> Result<(), futures::future::ExecuteError<BoxFuture>> {
|
||||||
|
unimplemented!("Not required in tests")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn collates_produces_a_block() {
|
fn collates_produces_a_block() {
|
||||||
let builder = CollatorBuilder::new(DummyFactory, InherentDataProviders::new());
|
let _ = env_logger::try_init();
|
||||||
let context = builder.build(Arc::new(DummyCollatorNetwork)).expect("Creates parachain context");
|
|
||||||
|
let builder = CollatorBuilder::new(DummySetup);
|
||||||
|
let context = builder.build(
|
||||||
|
Arc::new(
|
||||||
|
substrate_test_client::TestClientBuilder::<_, _, ()>::default()
|
||||||
|
.build_with_native_executor(
|
||||||
|
Some(NativeExecutor::<polkadot_executor::Executor>::new(Interpreted, None)),
|
||||||
|
).0
|
||||||
|
),
|
||||||
|
Arc::new(DummyFutureExecutor),
|
||||||
|
Arc::new(DummyCollatorNetwork),
|
||||||
|
).expect("Creates parachain context");
|
||||||
|
|
||||||
let id = ParaId::from(100);
|
let id = ParaId::from(100);
|
||||||
let header = Header::new(
|
let header = Header::new(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ edition = "2018"
|
|||||||
substrate-client = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
substrate-client = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
substrate-consensus-common = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
substrate-consensus-common = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-inherents = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
sr-primitives = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
sr-primitives = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
|
||||||
# polkadot deps
|
# polkadot deps
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// Cumulus 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.
|
||||||
|
|
||||||
|
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use substrate_primitives::{H256, Blake2Hasher};
|
||||||
|
|
||||||
|
use sr_primitives::{
|
||||||
|
traits::{Block as BlockT, ProvideRuntimeApi, Header as HeaderT}, Justification,
|
||||||
|
generic::BlockId,
|
||||||
|
};
|
||||||
|
|
||||||
|
use substrate_client::{
|
||||||
|
block_builder::api::BlockBuilder as BlockBuilderApi, backend::Backend, CallExecutor, Client,
|
||||||
|
error::Result as ClientResult,
|
||||||
|
};
|
||||||
|
|
||||||
|
use substrate_consensus_common::{
|
||||||
|
import_queue::{Verifier as VerifierT, BasicQueue, CacheKeyId}, BlockImportParams,
|
||||||
|
ForkChoiceStrategy, BlockOrigin, error::Error as ConsensusError, BlockImport,
|
||||||
|
};
|
||||||
|
|
||||||
|
use substrate_inherents::InherentDataProviders;
|
||||||
|
|
||||||
|
/// A verifier that just checks the inherents.
|
||||||
|
struct Verifier<B, E, Block: BlockT, RA> {
|
||||||
|
client: Arc<Client<B, E, Block, RA>>,
|
||||||
|
inherent_data_providers: InherentDataProviders,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<B, E, Block, RA> VerifierT<Block> for Verifier<B, E, Block, RA> where
|
||||||
|
Block: BlockT<Hash=H256>,
|
||||||
|
B: Backend<Block, Blake2Hasher> + 'static,
|
||||||
|
E: CallExecutor<Block, Blake2Hasher> + 'static + Clone + Send + Sync,
|
||||||
|
RA: Send + Sync,
|
||||||
|
Client<B, E, Block, RA>: ProvideRuntimeApi + Send + Sync,
|
||||||
|
<Client<B, E, Block, RA> as ProvideRuntimeApi>::Api: BlockBuilderApi<Block>
|
||||||
|
{
|
||||||
|
fn verify(
|
||||||
|
&mut self,
|
||||||
|
origin: BlockOrigin,
|
||||||
|
header: Block::Header,
|
||||||
|
justification: Option<Justification>,
|
||||||
|
mut body: Option<Vec<Block::Extrinsic>>,
|
||||||
|
) -> Result<(BlockImportParams<Block>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
|
||||||
|
if let Some(inner_body) = body.take() {
|
||||||
|
let inherent_data = self.inherent_data_providers
|
||||||
|
.create_inherent_data()
|
||||||
|
.map_err(String::from)?;
|
||||||
|
|
||||||
|
let block = Block::new(header.clone(), inner_body);
|
||||||
|
|
||||||
|
let inherent_res = self.client.runtime_api().check_inherents(
|
||||||
|
&BlockId::Hash(*header.parent_hash()),
|
||||||
|
block.clone(),
|
||||||
|
inherent_data,
|
||||||
|
).map_err(|e| format!("{:?}", e))?;
|
||||||
|
|
||||||
|
if !inherent_res.ok() {
|
||||||
|
inherent_res
|
||||||
|
.into_errors()
|
||||||
|
.try_for_each(|(i, e)| {
|
||||||
|
Err(self.inherent_data_providers.error_to_string(&i, &e))
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (_, inner_body) = block.deconstruct();
|
||||||
|
body = Some(inner_body);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let block_import_params = BlockImportParams {
|
||||||
|
origin,
|
||||||
|
header,
|
||||||
|
post_digests: Vec::new(),
|
||||||
|
body,
|
||||||
|
finalized: false,
|
||||||
|
justification,
|
||||||
|
auxiliary: Vec::new(),
|
||||||
|
fork_choice: ForkChoiceStrategy::LongestChain,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((block_import_params, None))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start an import queue for a Cumulus collator that does not uses any special authoring logic.
|
||||||
|
pub fn import_queue<B, E, Block: BlockT<Hash=H256>, I, RA>(
|
||||||
|
client: Arc<Client<B, E, Block, RA>>,
|
||||||
|
block_import: I,
|
||||||
|
inherent_data_providers: InherentDataProviders,
|
||||||
|
) -> ClientResult<BasicQueue<Block>>
|
||||||
|
where
|
||||||
|
B: Backend<Block, Blake2Hasher> + 'static,
|
||||||
|
I: BlockImport<Block,Error=ConsensusError> + Send + Sync + 'static,
|
||||||
|
E: CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync + 'static,
|
||||||
|
RA: Send + Sync + 'static,
|
||||||
|
Client<B, E, Block, RA>: ProvideRuntimeApi + Send + Sync + 'static,
|
||||||
|
<Client<B, E, Block, RA> as ProvideRuntimeApi>::Api: BlockBuilderApi<Block>,
|
||||||
|
{
|
||||||
|
let verifier = Verifier {
|
||||||
|
client,
|
||||||
|
inherent_data_providers
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(BasicQueue::new(
|
||||||
|
verifier,
|
||||||
|
Box::new(block_import),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
))
|
||||||
|
}
|
||||||
@@ -35,6 +35,8 @@ use log::warn;
|
|||||||
|
|
||||||
use std::{sync::Arc, marker::PhantomData};
|
use std::{sync::Arc, marker::PhantomData};
|
||||||
|
|
||||||
|
pub mod import_queue;
|
||||||
|
|
||||||
/// Helper for the local client.
|
/// Helper for the local client.
|
||||||
pub trait LocalClient {
|
pub trait LocalClient {
|
||||||
/// The block type of the local client.
|
/// The block type of the local client.
|
||||||
@@ -64,12 +66,12 @@ pub struct HeadUpdate {
|
|||||||
|
|
||||||
/// Helper for the Polkadot client. This is expected to be a lightweight handle
|
/// Helper for the Polkadot client. This is expected to be a lightweight handle
|
||||||
/// like an `Arc`.
|
/// like an `Arc`.
|
||||||
pub trait PolkadotClient: Clone {
|
pub trait PolkadotClient: Clone + 'static {
|
||||||
/// The error type for interacting with the Polkadot client.
|
/// The error type for interacting with the Polkadot client.
|
||||||
type Error: std::fmt::Debug + Send;
|
type Error: std::fmt::Debug + Send;
|
||||||
|
|
||||||
/// A stream that yields finalized head-data for a certain parachain.
|
/// A stream that yields finalized head-data for a certain parachain.
|
||||||
type Finalized: Stream<Item = Vec<u8>> + Send;
|
type Finalized: Stream<Item = Vec<u8>> + Send + Unpin;
|
||||||
|
|
||||||
/// Get a stream of finalized heads.
|
/// Get a stream of finalized heads.
|
||||||
fn finalized_heads(&self, para_id: ParaId) -> ClientResult<Self::Finalized>;
|
fn finalized_heads(&self, para_id: ParaId) -> ClientResult<Self::Finalized>;
|
||||||
@@ -83,11 +85,11 @@ pub trait PolkadotClient: Clone {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Spawns a future that follows the Polkadot relay chain for the given parachain.
|
/// Spawns a future that follows the Polkadot relay chain for the given parachain.
|
||||||
pub fn follow_polkadot<'a, L: 'a, P: 'a>(para_id: ParaId, local: Arc<L>, polkadot: P)
|
pub fn follow_polkadot<L, P>(para_id: ParaId, local: Arc<L>, polkadot: P)
|
||||||
-> ClientResult<impl Future<Output = ()> + Send + 'a>
|
-> ClientResult<impl Future<Output = ()> + Send + Unpin>
|
||||||
where
|
where
|
||||||
L: LocalClient + Send + Sync,
|
L: LocalClient + Send + Sync,
|
||||||
P: PolkadotClient + Send + Sync,
|
P: PolkadotClient,
|
||||||
{
|
{
|
||||||
let finalized_heads = polkadot.finalized_heads(para_id)?;
|
let finalized_heads = polkadot.finalized_heads(para_id)?;
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ pub struct ParachainBlockData<B: BlockT> {
|
|||||||
extrinsics: Vec<<B as BlockT>::Extrinsic>,
|
extrinsics: Vec<<B as BlockT>::Extrinsic>,
|
||||||
/// The data that is required to emulate the storage accesses executed by all extrinsics.
|
/// The data that is required to emulate the storage accesses executed by all extrinsics.
|
||||||
witness_data: WitnessData,
|
witness_data: WitnessData,
|
||||||
|
/// The storage root of the witness data.
|
||||||
witness_data_storage_root: <B as BlockT>::Hash,
|
witness_data_storage_root: <B as BlockT>::Hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,4 +59,19 @@ impl<B: BlockT> ParachainBlockData<B> {
|
|||||||
witness_data_storage_root,
|
witness_data_storage_root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/// Convert `self` into the stored header.
|
||||||
|
pub fn into_header(self) -> B::Header {
|
||||||
|
self.header
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the header.
|
||||||
|
pub fn header(&self) -> &B::Header {
|
||||||
|
&self.header
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the extrinsics.
|
||||||
|
pub fn extrinsics(&self) -> &[B::Extrinsic] {
|
||||||
|
&self.extrinsics
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ use rstd::{slice, ptr, cmp, vec::Vec, boxed::Box, mem};
|
|||||||
|
|
||||||
use hash_db::{HashDB, EMPTY_PREFIX};
|
use hash_db::{HashDB, EMPTY_PREFIX};
|
||||||
|
|
||||||
use parachain::ValidationParams;
|
use parachain::{ValidationParams, ValidationResult};
|
||||||
|
|
||||||
static mut STORAGE: Option<Box<dyn Storage>> = None;
|
static mut STORAGE: Option<Box<dyn Storage>> = None;
|
||||||
/// The message to use as expect message while accessing the `STORAGE`.
|
/// The message to use as expect message while accessing the `STORAGE`.
|
||||||
@@ -54,13 +54,15 @@ trait Storage {
|
|||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn validate_block<B: BlockT<Hash = H256>, E: ExecuteBlock<B>>(
|
pub fn validate_block<B: BlockT<Hash = H256>, E: ExecuteBlock<B>>(
|
||||||
params: ValidationParams,
|
params: ValidationParams,
|
||||||
) {
|
) -> ValidationResult {
|
||||||
use codec::Decode;
|
use codec::{Decode, Encode};
|
||||||
|
|
||||||
let block_data = crate::ParachainBlockData::<B>::decode(&mut ¶ms.block_data[..])
|
let block_data = crate::ParachainBlockData::<B>::decode(&mut ¶ms.block_data[..])
|
||||||
.expect("Invalid parachain block data");
|
.expect("Invalid parachain block data");
|
||||||
|
|
||||||
let parent_head = B::Header::decode(&mut ¶ms.parent_head[..]).expect("Invalid parent head");
|
let parent_head = B::Header::decode(&mut ¶ms.parent_head[..]).expect("Invalid parent head");
|
||||||
|
// TODO: Use correct head data
|
||||||
|
let head_data = block_data.header.encode();
|
||||||
|
|
||||||
// TODO: Add `PolkadotInherent`.
|
// TODO: Add `PolkadotInherent`.
|
||||||
let block = B::new(block_data.header, block_data.extrinsics);
|
let block = B::new(block_data.header, block_data.extrinsics);
|
||||||
@@ -85,6 +87,8 @@ pub fn validate_block<B: BlockT<Hash = H256>, E: ExecuteBlock<B>>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
E::execute_block(block);
|
E::execute_block(block);
|
||||||
|
|
||||||
|
ValidationResult { head_data }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The storage implementation used when validating a block that is using the
|
/// The storage implementation used when validating a block that is using the
|
||||||
@@ -141,16 +145,17 @@ impl<B: BlockT<Hash = H256>> Storage for WitnessStorage<B> {
|
|||||||
let root = match delta_trie_root::<Layout<Blake2Hasher>, _, _, _, _>(
|
let root = match delta_trie_root::<Layout<Blake2Hasher>, _, _, _, _>(
|
||||||
&mut self.witness_data,
|
&mut self.witness_data,
|
||||||
self.storage_root.clone(),
|
self.storage_root.clone(),
|
||||||
self.overlay.drain()
|
self.overlay.drain(),
|
||||||
) {
|
) {
|
||||||
Ok(root) => root,
|
Ok(root) => root,
|
||||||
Err(_) => return [0; STORAGE_ROOT_LEN],
|
Err(e) => match *e {
|
||||||
|
trie_db::TrieError::InvalidStateRoot(_) => panic!("Invalid state root"),
|
||||||
|
trie_db::TrieError::IncompleteDatabase(_) => panic!("IncompleteDatabase"),
|
||||||
|
trie_db::TrieError::DecoderError(_, _) => panic!("DecodeError"),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(root.as_ref().len() <= STORAGE_ROOT_LEN);
|
root.into()
|
||||||
let mut res = [0; STORAGE_ROOT_LEN];
|
|
||||||
res.copy_from_slice(root.as_ref());
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,15 +63,17 @@ macro_rules! register_validate_block_impl {
|
|||||||
unsafe fn validate_block(
|
unsafe fn validate_block(
|
||||||
arguments: *const u8,
|
arguments: *const u8,
|
||||||
arguments_len: usize,
|
arguments_len: usize,
|
||||||
) {
|
) -> u64 {
|
||||||
let params = $crate::validate_block::parachain::wasm_api::load_params(
|
let params = $crate::validate_block::parachain::wasm_api::load_params(
|
||||||
arguments,
|
arguments,
|
||||||
arguments_len,
|
arguments_len,
|
||||||
);
|
);
|
||||||
|
|
||||||
$crate::validate_block::implementation::validate_block::<
|
let res = $crate::validate_block::implementation::validate_block::<
|
||||||
$block, $block_executor
|
$block, $block_executor
|
||||||
>(params);
|
>(params);
|
||||||
|
|
||||||
|
$crate::validate_block::parachain::wasm_api::write_result(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -83,4 +85,4 @@ macro_rules! register_validate_block_impl {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! register_validate_block_impl {
|
macro_rules! register_validate_block_impl {
|
||||||
($block:ty, $block_executor:ty) => {};
|
($block:ty, $block_executor:ty) => {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,46 +19,38 @@ use crate::{ParachainBlockData, WitnessData};
|
|||||||
use rio::TestExternalities;
|
use rio::TestExternalities;
|
||||||
use keyring::AccountKeyring;
|
use keyring::AccountKeyring;
|
||||||
use runtime_primitives::{generic::BlockId, traits::{Block as BlockT, Header as HeaderT}};
|
use runtime_primitives::{generic::BlockId, traits::{Block as BlockT, Header as HeaderT}};
|
||||||
use executor::{WasmExecutor, error::Result, wasmi::RuntimeValue::I32};
|
use executor::{call_in_wasm, error::Result, WasmExecutionMethod};
|
||||||
use test_client::{
|
use test_client::{
|
||||||
TestClientBuilder, TestClientBuilderExt, DefaultTestClientBuilderExt, Client, LongestChain,
|
TestClientBuilder, TestClientBuilderExt, DefaultTestClientBuilderExt, Client, LongestChain,
|
||||||
runtime::{Block, Transfer, Hash, WASM_BINARY, Header}
|
runtime::{Block, Transfer, Hash, WASM_BINARY, Header}
|
||||||
};
|
};
|
||||||
use consensus_common::SelectChain;
|
use consensus_common::SelectChain;
|
||||||
use parachain::ValidationParams;
|
use parachain::{ValidationParams, ValidationResult};
|
||||||
|
|
||||||
use codec::Encode;
|
use codec::{Encode, Decode};
|
||||||
|
|
||||||
fn call_validate_block(parent_head: Header, block_data: ParachainBlockData<Block>) -> Result<()> {
|
fn call_validate_block(
|
||||||
|
parent_head: Header,
|
||||||
|
block_data: ParachainBlockData<Block>,
|
||||||
|
) -> Result<Header> {
|
||||||
let mut ext = TestExternalities::default();
|
let mut ext = TestExternalities::default();
|
||||||
WasmExecutor::new().call_with_custom_signature(
|
let mut ext_ext = ext.ext();
|
||||||
&mut ext,
|
let params = ValidationParams {
|
||||||
1024,
|
block_data: block_data.encode(),
|
||||||
&WASM_BINARY,
|
parent_head: parent_head.encode(),
|
||||||
"validate_block",
|
ingress: Vec::new(),
|
||||||
|alloc| {
|
}.encode();
|
||||||
let params = ValidationParams {
|
|
||||||
block_data: block_data.encode(),
|
|
||||||
parent_head: parent_head.encode(),
|
|
||||||
ingress: Vec::new(),
|
|
||||||
}.encode();
|
|
||||||
let params_offset = alloc(¶ms)?;
|
|
||||||
|
|
||||||
Ok(
|
call_in_wasm(
|
||||||
vec![
|
"validate_block",
|
||||||
I32(params_offset as i32),
|
¶ms,
|
||||||
I32(params.len() as i32),
|
WasmExecutionMethod::Interpreted,
|
||||||
]
|
&mut ext_ext,
|
||||||
)
|
&WASM_BINARY,
|
||||||
},
|
1024,
|
||||||
|res, _| {
|
|
||||||
if res.is_none() {
|
|
||||||
Ok(Some(()))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
.map(|v| ValidationResult::decode(&mut &v[..]).expect("Decode `ValidationResult`."))
|
||||||
|
.map(|v| Header::decode(&mut &v.head_data[..]).expect("Decode `Header`."))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_extrinsics() -> Vec<<Block as BlockT>::Extrinsic> {
|
fn create_extrinsics() -> Vec<<Block as BlockT>::Extrinsic> {
|
||||||
@@ -99,9 +91,10 @@ fn build_block_with_proof(
|
|||||||
extrinsics: Vec<<Block as BlockT>::Extrinsic>,
|
extrinsics: Vec<<Block as BlockT>::Extrinsic>,
|
||||||
) -> (Block, WitnessData) {
|
) -> (Block, WitnessData) {
|
||||||
let block_id = BlockId::Hash(client.info().chain.best_hash);
|
let block_id = BlockId::Hash(client.info().chain.best_hash);
|
||||||
let mut builder = client.new_block_at_with_proof_recording(
|
let mut builder = client.new_block_at(
|
||||||
&block_id,
|
&block_id,
|
||||||
Default::default()
|
Default::default(),
|
||||||
|
true,
|
||||||
).expect("Initializes new block");
|
).expect("Initializes new block");
|
||||||
|
|
||||||
extrinsics.into_iter().for_each(|e| builder.push(e).expect("Pushes an extrinsic"));
|
extrinsics.into_iter().for_each(|e| builder.push(e).expect("Pushes an extrinsic"));
|
||||||
@@ -122,12 +115,14 @@ fn validate_block_with_no_extrinsics() {
|
|||||||
let (header, extrinsics) = block.deconstruct();
|
let (header, extrinsics) = block.deconstruct();
|
||||||
|
|
||||||
let block_data = ParachainBlockData::new(
|
let block_data = ParachainBlockData::new(
|
||||||
header,
|
header.clone(),
|
||||||
extrinsics,
|
extrinsics,
|
||||||
witness_data,
|
witness_data,
|
||||||
witness_data_storage_root
|
witness_data_storage_root
|
||||||
);
|
);
|
||||||
call_validate_block(parent_head, block_data).expect("Calls `validate_block`");
|
|
||||||
|
let res_header = call_validate_block(parent_head, block_data).expect("Calls `validate_block`");
|
||||||
|
assert_eq!(header, res_header);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -139,12 +134,14 @@ fn validate_block_with_extrinsics() {
|
|||||||
let (header, extrinsics) = block.deconstruct();
|
let (header, extrinsics) = block.deconstruct();
|
||||||
|
|
||||||
let block_data = ParachainBlockData::new(
|
let block_data = ParachainBlockData::new(
|
||||||
header,
|
header.clone(),
|
||||||
extrinsics,
|
extrinsics,
|
||||||
witness_data,
|
witness_data,
|
||||||
witness_data_storage_root
|
witness_data_storage_root
|
||||||
);
|
);
|
||||||
call_validate_block(parent_head, block_data).expect("Calls `validate_block`");
|
|
||||||
|
let res_header = call_validate_block(parent_head, block_data).expect("Calls `validate_block`");
|
||||||
|
assert_eq!(header, res_header);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
[package]
|
||||||
|
name = 'cumulus-test-parachain-collator'
|
||||||
|
version = '0.1.0'
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
build = 'build.rs'
|
||||||
|
edition = '2018'
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = 'cumulus-test-parachain-collator'
|
||||||
|
path = 'src/main.rs'
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
derive_more = '0.15.0'
|
||||||
|
exit-future = '0.1.4'
|
||||||
|
futures = '0.1.29'
|
||||||
|
futures03 = { package = "futures-preview", version = "0.3.0-alpha.19", features = ["compat"] }
|
||||||
|
log = '0.4.8'
|
||||||
|
parking_lot = '0.9.0'
|
||||||
|
tokio = '0.1.22'
|
||||||
|
trie-root = '0.15.2'
|
||||||
|
codec = { package = 'parity-scale-codec', version = '1.0.0' }
|
||||||
|
parachain-runtime = { package = "cumulus-test-parachain-runtime", path = "runtime" }
|
||||||
|
structopt = "0.3.3"
|
||||||
|
ctrlc = { version = "3.1.3", features = ["termination"] }
|
||||||
|
|
||||||
|
# Substrate dependencies
|
||||||
|
sr-primitives = { git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
sr-io = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-cli = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-primitives = { package = "substrate-primitives", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-executor = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-service = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
inherents = { package = "substrate-inherents", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
transaction-pool = { package = "substrate-transaction-pool", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
network = { package = "substrate-network", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-client = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
srml-timestamp = { git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
basic-authorship = { package = "substrate-basic-authorship", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
consensus-common = { package = "substrate-consensus-common", git = "https://github.com/paritytech/substrate", branch = "bkchr-cumulus-branch" }
|
||||||
|
|
||||||
|
# Cumulus dependencies
|
||||||
|
cumulus-consensus = { path = "../../consensus" }
|
||||||
|
cumulus-collator = { path = "../../collator" }
|
||||||
|
|
||||||
|
# Polkadot dependencies
|
||||||
|
polkadot-primitives = { git = "https://github.com/paritytech/polkadot", branch = "bkchr-cumulus-branch" }
|
||||||
|
polkadot-collator = { git = "https://github.com/paritytech/polkadot", branch = "bkchr-cumulus-branch" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
vergen = '3.0.4'
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::{env, path::PathBuf};
|
||||||
|
|
||||||
|
use vergen::{ConstantsFlags, generate_cargo_keys};
|
||||||
|
|
||||||
|
const ERROR_MSG: &str = "Failed to generate metadata files";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
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!");
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
[package]
|
||||||
|
name = 'cumulus-test-parachain-runtime'
|
||||||
|
version = '0.1.0'
|
||||||
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
edition = '2018'
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0.101", optional = true, features = ["derive"] }
|
||||||
|
safe-mix = { version = "1.0.0", default-features = false }
|
||||||
|
codec = { package = "parity-scale-codec", version = "1.0.6", default-features = false, features = ["derive"] }
|
||||||
|
|
||||||
|
# Substrate dependencies
|
||||||
|
rstd = { package = "sr-std", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
runtime-io = { package = "sr-io", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
version = { package = "sr-version", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
support = { package = "srml-support", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
primitives = { package = "substrate-primitives", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
substrate-session = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
balances = { package = "srml-balances", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
executive = { package = "srml-executive", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
indices = { package = "srml-indices", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
randomness-collective-flip = { package = "srml-randomness-collective-flip", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
system = { package = "srml-system", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
timestamp = { package = "srml-timestamp", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
sudo = { package = "srml-sudo", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
sr-primitives = { git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
client = { package = "substrate-client", git = "https://github.com/paritytech/substrate", default_features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
offchain-primitives = { package = "substrate-offchain-primitives", git = "https://github.com/paritytech/substrate", default-features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
transaction-payment = { package = "srml-transaction-payment", git = "https://github.com/paritytech/substrate", default-features = false, branch = "bkchr-cumulus-branch" }
|
||||||
|
|
||||||
|
# Cumulus dependencies
|
||||||
|
cumulus-runtime = { path = "../../../runtime", default-features = false }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
wasm-builder-runner = { package = "substrate-wasm-builder-runner", version = "1.0.3" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ['std']
|
||||||
|
std = [
|
||||||
|
'codec/std',
|
||||||
|
'client/std',
|
||||||
|
'rstd/std',
|
||||||
|
'runtime-io/std',
|
||||||
|
'support/std',
|
||||||
|
'balances/std',
|
||||||
|
'executive/std',
|
||||||
|
'indices/std',
|
||||||
|
'primitives/std',
|
||||||
|
'sr-primitives/std',
|
||||||
|
'randomness-collective-flip/std',
|
||||||
|
'system/std',
|
||||||
|
'timestamp/std',
|
||||||
|
'sudo/std',
|
||||||
|
'version/std',
|
||||||
|
'serde',
|
||||||
|
'safe-mix/std',
|
||||||
|
'offchain-primitives/std',
|
||||||
|
'substrate-session/std',
|
||||||
|
'cumulus-runtime/std',
|
||||||
|
'transaction-payment/std',
|
||||||
|
]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use wasm_builder_runner::{build_current_project_with_rustflags, WasmBuilderSource};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
build_current_project_with_rustflags(
|
||||||
|
"wasm_binary.rs",
|
||||||
|
WasmBuilderSource::Crates("1.0.7"),
|
||||||
|
// This instructs LLD to export __heap_base as a global variable, which is used by the
|
||||||
|
// external memory allocator.
|
||||||
|
"-Clink-arg=--export=__heap_base",
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,336 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// Cumulus 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.
|
||||||
|
|
||||||
|
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
|
||||||
|
#![recursion_limit="256"]
|
||||||
|
|
||||||
|
// Make the WASM binary available.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
|
||||||
|
|
||||||
|
use rstd::prelude::*;
|
||||||
|
use primitives::OpaqueMetadata;
|
||||||
|
use sr_primitives::{
|
||||||
|
ApplyResult, transaction_validity::TransactionValidity, generic, create_runtime_str,
|
||||||
|
impl_opaque_keys, AnySignature
|
||||||
|
};
|
||||||
|
use sr_primitives::traits::{NumberFor, BlakeTwo256, Block as BlockT, StaticLookup, Verify, ConvertInto};
|
||||||
|
use sr_primitives::weights::Weight;
|
||||||
|
use client::{
|
||||||
|
block_builder::api::{CheckInherentsResult, InherentData, self as block_builder_api},
|
||||||
|
runtime_api as client_api, impl_runtime_apis
|
||||||
|
};
|
||||||
|
use version::RuntimeVersion;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use version::NativeVersion;
|
||||||
|
|
||||||
|
// A few exports that help ease life for downstream crates.
|
||||||
|
#[cfg(any(feature = "std", test))]
|
||||||
|
pub use sr_primitives::BuildStorage;
|
||||||
|
pub use timestamp::Call as TimestampCall;
|
||||||
|
pub use balances::Call as BalancesCall;
|
||||||
|
pub use sr_primitives::{Permill, Perbill};
|
||||||
|
pub use support::{StorageValue, construct_runtime, parameter_types, traits::Randomness};
|
||||||
|
|
||||||
|
/// An index to a block.
|
||||||
|
pub type BlockNumber = u32;
|
||||||
|
|
||||||
|
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
|
||||||
|
pub type Signature = AnySignature;
|
||||||
|
|
||||||
|
/// Some way of identifying an account on the chain. We intentionally make it equivalent
|
||||||
|
/// to the public key of our transaction signing scheme.
|
||||||
|
pub type AccountId = <Signature as Verify>::Signer;
|
||||||
|
|
||||||
|
/// The type for looking up accounts. We don't expect more than 4 billion of them, but you
|
||||||
|
/// never know...
|
||||||
|
pub type AccountIndex = u32;
|
||||||
|
|
||||||
|
/// Balance of an account.
|
||||||
|
pub type Balance = u128;
|
||||||
|
|
||||||
|
/// Index of a transaction in the chain.
|
||||||
|
pub type Index = u32;
|
||||||
|
|
||||||
|
/// A hash of some data used by the chain.
|
||||||
|
pub type Hash = primitives::H256;
|
||||||
|
|
||||||
|
/// Digest item type.
|
||||||
|
pub type DigestItem = generic::DigestItem<Hash>;
|
||||||
|
|
||||||
|
/// Opaque types. These are used by the CLI to instantiate machinery that don't need to know
|
||||||
|
/// the specifics of the runtime. They can then be made to be agnostic over specific formats
|
||||||
|
/// of data like extrinsics, allowing for them to continue syncing the network through upgrades
|
||||||
|
/// to even the core datastructures.
|
||||||
|
pub mod opaque {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub use sr_primitives::OpaqueExtrinsic as UncheckedExtrinsic;
|
||||||
|
|
||||||
|
/// Opaque block header type.
|
||||||
|
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||||
|
/// Opaque block type.
|
||||||
|
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||||
|
/// Opaque block identifier type.
|
||||||
|
pub type BlockId = generic::BlockId<Block>;
|
||||||
|
|
||||||
|
pub type SessionHandlers = ();
|
||||||
|
|
||||||
|
impl_opaque_keys! {
|
||||||
|
pub struct SessionKeys {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This runtime version.
|
||||||
|
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||||
|
spec_name: create_runtime_str!("wasm-test-parachain"),
|
||||||
|
impl_name: create_runtime_str!("wasm-test-parachain"),
|
||||||
|
authoring_version: 3,
|
||||||
|
spec_version: 4,
|
||||||
|
impl_version: 4,
|
||||||
|
apis: RUNTIME_API_VERSIONS,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const MILLISECS_PER_BLOCK: u64 = 6000;
|
||||||
|
|
||||||
|
pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK;
|
||||||
|
|
||||||
|
pub const EPOCH_DURATION_IN_BLOCKS: u32 = 10 * MINUTES;
|
||||||
|
|
||||||
|
// These time units are defined in number of blocks.
|
||||||
|
pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber);
|
||||||
|
pub const HOURS: BlockNumber = MINUTES * 60;
|
||||||
|
pub const DAYS: BlockNumber = HOURS * 24;
|
||||||
|
|
||||||
|
// 1 in 4 blocks (on average, not counting collisions) will be primary babe blocks.
|
||||||
|
pub const PRIMARY_PROBABILITY: (u64, u64) = (1, 4);
|
||||||
|
|
||||||
|
/// The version infromation used to identify this runtime when compiled natively.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub fn native_version() -> NativeVersion {
|
||||||
|
NativeVersion {
|
||||||
|
runtime_version: VERSION,
|
||||||
|
can_author_with: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub const BlockHashCount: BlockNumber = 250;
|
||||||
|
pub const MaximumBlockWeight: Weight = 1_000_000;
|
||||||
|
pub const AvailableBlockRatio: Perbill = Perbill::from_percent(75);
|
||||||
|
pub const MaximumBlockLength: u32 = 5 * 1024 * 1024;
|
||||||
|
pub const Version: RuntimeVersion = VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl system::Trait for Runtime {
|
||||||
|
/// The identifier used to distinguish between accounts.
|
||||||
|
type AccountId = AccountId;
|
||||||
|
/// The aggregated dispatch type that is available for extrinsics.
|
||||||
|
type Call = Call;
|
||||||
|
/// The lookup mechanism to get account ID from whatever is passed in dispatchers.
|
||||||
|
type Lookup = Indices;
|
||||||
|
/// The index type for storing how many extrinsics an account has signed.
|
||||||
|
type Index = Index;
|
||||||
|
/// The index type for blocks.
|
||||||
|
type BlockNumber = BlockNumber;
|
||||||
|
/// The type for hashing blocks and tries.
|
||||||
|
type Hash = Hash;
|
||||||
|
/// The hashing algorithm used.
|
||||||
|
type Hashing = BlakeTwo256;
|
||||||
|
/// The header type.
|
||||||
|
type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||||
|
/// The ubiquitous event type.
|
||||||
|
type Event = Event;
|
||||||
|
/// The ubiquitous origin type.
|
||||||
|
type Origin = Origin;
|
||||||
|
/// Maximum number of block number to block hash mappings to keep (oldest pruned first).
|
||||||
|
type BlockHashCount = BlockHashCount;
|
||||||
|
/// Maximum weight of each block. With a default weight system of 1byte == 1weight, 4mb is ok.
|
||||||
|
type MaximumBlockWeight = MaximumBlockWeight;
|
||||||
|
/// Maximum size of all encoded transactions (in bytes) that are allowed in one block.
|
||||||
|
type MaximumBlockLength = MaximumBlockLength;
|
||||||
|
/// Portion of the block weight that is available to all normal transactions.
|
||||||
|
type AvailableBlockRatio = AvailableBlockRatio;
|
||||||
|
type Version = Version;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl indices::Trait for Runtime {
|
||||||
|
/// The type for recording indexing into the account enumeration. If this ever overflows, there
|
||||||
|
/// will be problems!
|
||||||
|
type AccountIndex = u32;
|
||||||
|
/// Use the standard means of resolving an index hint from an id.
|
||||||
|
type ResolveHint = indices::SimpleResolveHint<Self::AccountId, Self::AccountIndex>;
|
||||||
|
/// Determine whether an account is dead.
|
||||||
|
type IsDeadAccount = Balances;
|
||||||
|
/// The ubiquitous event type.
|
||||||
|
type Event = Event;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub const MinimumPeriod: u64 = SLOT_DURATION / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl timestamp::Trait for Runtime {
|
||||||
|
/// A timestamp: milliseconds since the unix epoch.
|
||||||
|
type Moment = u64;
|
||||||
|
type OnTimestampSet = ();
|
||||||
|
type MinimumPeriod = MinimumPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub const ExistentialDeposit: u128 = 500;
|
||||||
|
pub const TransferFee: u128 = 0;
|
||||||
|
pub const CreationFee: u128 = 0;
|
||||||
|
pub const TransactionBaseFee: u128 = 0;
|
||||||
|
pub const TransactionByteFee: u128 = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl balances::Trait for Runtime {
|
||||||
|
/// The type for recording an account's balance.
|
||||||
|
type Balance = Balance;
|
||||||
|
/// What to do if an account's free balance gets zeroed.
|
||||||
|
type OnFreeBalanceZero = ();
|
||||||
|
/// What to do if a new account is created.
|
||||||
|
type OnNewAccount = Indices;
|
||||||
|
/// The ubiquitous event type.
|
||||||
|
type Event = Event;
|
||||||
|
type DustRemoval = ();
|
||||||
|
type TransferPayment = ();
|
||||||
|
type ExistentialDeposit = ExistentialDeposit;
|
||||||
|
type TransferFee = TransferFee;
|
||||||
|
type CreationFee = CreationFee;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl transaction_payment::Trait for Runtime {
|
||||||
|
type Currency = Balances;
|
||||||
|
type OnTransactionPayment = ();
|
||||||
|
type TransactionBaseFee = TransactionBaseFee;
|
||||||
|
type TransactionByteFee = TransactionByteFee;
|
||||||
|
type WeightToFee = ConvertInto;
|
||||||
|
type FeeMultiplierUpdate = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sudo::Trait for Runtime {
|
||||||
|
type Event = Event;
|
||||||
|
type Proposal = Call;
|
||||||
|
}
|
||||||
|
|
||||||
|
construct_runtime! {
|
||||||
|
pub enum Runtime where
|
||||||
|
Block = Block,
|
||||||
|
NodeBlock = opaque::Block,
|
||||||
|
UncheckedExtrinsic = UncheckedExtrinsic
|
||||||
|
{
|
||||||
|
System: system::{Module, Call, Storage, Config, Event},
|
||||||
|
Timestamp: timestamp::{Module, Call, Storage, Inherent},
|
||||||
|
Indices: indices::{default, Config<T>},
|
||||||
|
Balances: balances::{default, Error},
|
||||||
|
Sudo: sudo,
|
||||||
|
RandomnessCollectiveFlip: randomness_collective_flip::{Module, Call, Storage},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The address format for describing accounts.
|
||||||
|
pub type Address = <Indices as StaticLookup>::Source;
|
||||||
|
/// Block header type as expected by this runtime.
|
||||||
|
pub type Header = generic::Header<BlockNumber, BlakeTwo256>;
|
||||||
|
/// Block type as expected by this runtime.
|
||||||
|
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||||
|
/// A Block signed with a Justification
|
||||||
|
pub type SignedBlock = generic::SignedBlock<Block>;
|
||||||
|
/// BlockId type as expected by this runtime.
|
||||||
|
pub type BlockId = generic::BlockId<Block>;
|
||||||
|
/// The SignedExtension to the basic transaction logic.
|
||||||
|
pub type SignedExtra = (
|
||||||
|
system::CheckVersion<Runtime>,
|
||||||
|
system::CheckGenesis<Runtime>,
|
||||||
|
system::CheckEra<Runtime>,
|
||||||
|
system::CheckNonce<Runtime>,
|
||||||
|
system::CheckWeight<Runtime>,
|
||||||
|
transaction_payment::ChargeTransactionPayment<Runtime>,
|
||||||
|
);
|
||||||
|
/// Unchecked extrinsic type as expected by this runtime.
|
||||||
|
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Call, Signature, SignedExtra>;
|
||||||
|
/// Extrinsic type that has already been checked.
|
||||||
|
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Call, SignedExtra>;
|
||||||
|
/// Executive: handles dispatch to the various modules.
|
||||||
|
pub type Executive = executive::Executive<Runtime, Block, system::ChainContext<Runtime>, Runtime, AllModules>;
|
||||||
|
|
||||||
|
impl_runtime_apis! {
|
||||||
|
impl client_api::Core<Block> for Runtime {
|
||||||
|
fn version() -> RuntimeVersion {
|
||||||
|
VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_block(block: Block) {
|
||||||
|
Executive::execute_block(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialize_block(header: &<Block as BlockT>::Header) {
|
||||||
|
Executive::initialize_block(header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl client_api::Metadata<Block> for Runtime {
|
||||||
|
fn metadata() -> OpaqueMetadata {
|
||||||
|
Runtime::metadata().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl block_builder_api::BlockBuilder<Block> for Runtime {
|
||||||
|
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult {
|
||||||
|
Executive::apply_extrinsic(extrinsic)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalize_block() -> <Block as BlockT>::Header {
|
||||||
|
Executive::finalize_block()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inherent_extrinsics(data: InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
|
||||||
|
data.create_extrinsics()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_inherents(block: Block, data: InherentData) -> CheckInherentsResult {
|
||||||
|
data.check_extrinsics(&block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_seed() -> <Block as BlockT>::Hash {
|
||||||
|
RandomnessCollectiveFlip::random_seed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl client_api::TaggedTransactionQueue<Block> for Runtime {
|
||||||
|
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
|
||||||
|
Executive::validate_transaction(tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl offchain_primitives::OffchainWorkerApi<Block> for Runtime {
|
||||||
|
fn offchain_worker(number: NumberFor<Block>) {
|
||||||
|
Executive::offchain_worker(number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl substrate_session::SessionKeys<Block> for Runtime {
|
||||||
|
fn generate_session_keys(seed: Option<Vec<u8>>) -> Vec<u8> {
|
||||||
|
let seed = seed.as_ref().map(|s| rstd::str::from_utf8(&s).expect("Seed is an utf8 string"));
|
||||||
|
opaque::SessionKeys::generate(seed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cumulus_runtime::register_validate_block!(Block, Executive);
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// Cumulus 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.
|
||||||
|
|
||||||
|
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use substrate_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",
|
||||||
|
"parachain_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,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// Cumulus 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.
|
||||||
|
|
||||||
|
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use crate::chain_spec;
|
||||||
|
|
||||||
|
use parachain_runtime::Block;
|
||||||
|
|
||||||
|
pub use substrate_cli::{VersionInfo, IntoExit, error::{self, Result}};
|
||||||
|
use substrate_cli::{parse_and_prepare, ParseAndPrepare, NoCustom};
|
||||||
|
use substrate_service::{Roles as ServiceRoles, Configuration};
|
||||||
|
use sr_primitives::{traits::{Block as BlockT, Header as HeaderT, Hash as HashT}, BuildStorage};
|
||||||
|
use substrate_client::genesis;
|
||||||
|
use substrate_primitives::hexdisplay::HexDisplay;
|
||||||
|
|
||||||
|
use codec::Encode;
|
||||||
|
|
||||||
|
use log::info;
|
||||||
|
|
||||||
|
use std::{path::PathBuf, cell::RefCell, sync::Arc};
|
||||||
|
|
||||||
|
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 + Send + 'static,
|
||||||
|
{
|
||||||
|
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, mut 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);
|
||||||
|
info!("Parachain id: {:?}", crate::PARA_ID);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
let key = Arc::new(substrate_primitives::Pair::from_seed(&[10; 32]));
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
config.network.listen_addresses = Vec::new();
|
||||||
|
config.network.boot_nodes = vec![];
|
||||||
|
config.chain_spec = chain_spec::get_chain_spec();
|
||||||
|
|
||||||
|
match config.roles {
|
||||||
|
ServiceRoles::LIGHT => unimplemented!("Light client not supported!"),
|
||||||
|
_ => crate::service::run_collator(config, exit, key, version.clone()),
|
||||||
|
}.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(_: &str) -> std::result::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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// Cumulus 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.
|
||||||
|
|
||||||
|
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Cumulus test parachain collator
|
||||||
|
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![warn(unused_extern_crates)]
|
||||||
|
|
||||||
|
use polkadot_primitives::parachain::Id as ParaId;
|
||||||
|
|
||||||
|
mod chain_spec;
|
||||||
|
#[macro_use]
|
||||||
|
mod service;
|
||||||
|
mod cli;
|
||||||
|
|
||||||
|
pub use substrate_cli::{VersionInfo, IntoExit, error};
|
||||||
|
|
||||||
|
/// The parachain id of this parachain.
|
||||||
|
pub const PARA_ID: ParaId = ParaId::new(100);
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2019 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Cumulus.
|
||||||
|
|
||||||
|
// Cumulus 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.
|
||||||
|
|
||||||
|
// Cumulus 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 Cumulus. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use parachain_runtime::{self, GenesisConfig, opaque::Block};
|
||||||
|
|
||||||
|
use inherents::InherentDataProviders;
|
||||||
|
use substrate_service::{AbstractService, Configuration};
|
||||||
|
use network::construct_simple_protocol;
|
||||||
|
use substrate_executor::native_executor_instance;
|
||||||
|
|
||||||
|
use futures::prelude::*;
|
||||||
|
|
||||||
|
use futures03::FutureExt;
|
||||||
|
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
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)
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the collator with the given `config`.
|
||||||
|
pub fn run_collator<C: Send + Default + 'static, E: crate::cli::IntoExit + Send + 'static>(
|
||||||
|
config: Configuration<C, GenesisConfig>,
|
||||||
|
exit: E,
|
||||||
|
key: Arc<polkadot_primitives::parachain::CollatorPair>,
|
||||||
|
version: crate::cli::VersionInfo,
|
||||||
|
) -> crate::cli::Result<()> {
|
||||||
|
let (builder, inherent_data_providers) = new_full_start!(config);
|
||||||
|
inherent_data_providers.register_provider(srml_timestamp::InherentDataProvider).unwrap();
|
||||||
|
|
||||||
|
let service = builder.with_network_protocol(|_| Ok(NodeProtocol::new()))?.build()?;
|
||||||
|
let proposer_factory = basic_authorship::ProposerFactory {
|
||||||
|
client: service.client(),
|
||||||
|
transaction_pool: service.transaction_pool(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let on_exit = service.on_exit();
|
||||||
|
let block_import = service.client();
|
||||||
|
|
||||||
|
let setup_parachain = SetupParachain {
|
||||||
|
service,
|
||||||
|
inherent_data_providers,
|
||||||
|
proposer_factory,
|
||||||
|
exit,
|
||||||
|
block_import,
|
||||||
|
};
|
||||||
|
|
||||||
|
cumulus_collator::run_collator(setup_parachain, crate::PARA_ID, on_exit, key, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SetupParachain<S, PF, E, BI> {
|
||||||
|
service: S,
|
||||||
|
proposer_factory: PF,
|
||||||
|
exit: E,
|
||||||
|
inherent_data_providers: InherentDataProviders,
|
||||||
|
block_import: BI,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, PF, E, BI> cumulus_collator::SetupParachain<Block> for SetupParachain<S, PF, E, BI>
|
||||||
|
where
|
||||||
|
S: AbstractService,
|
||||||
|
E: Send + crate::cli::IntoExit,
|
||||||
|
PF: consensus_common::Environment<Block> + Send + 'static,
|
||||||
|
BI: consensus_common::BlockImport<Block, Error=consensus_common::Error> + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
type ProposerFactory = PF;
|
||||||
|
type BlockImport = BI;
|
||||||
|
|
||||||
|
fn setup_parachain<P: cumulus_consensus::PolkadotClient>(
|
||||||
|
self,
|
||||||
|
polkadot_client: P,
|
||||||
|
task_executor: polkadot_collator::TaskExecutor,
|
||||||
|
) -> Result<(Self::ProposerFactory, Self::BlockImport, InherentDataProviders), String> {
|
||||||
|
let client = self.service.client();
|
||||||
|
|
||||||
|
let follow = match cumulus_consensus::follow_polkadot(crate::PARA_ID, client, polkadot_client) {
|
||||||
|
Ok(follow) => follow,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(format!("Could not start following polkadot: {:?}", e));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
task_executor.execute(
|
||||||
|
Box::new(
|
||||||
|
self.service
|
||||||
|
.map_err(|e| error!("Parachain service error: {:?}", e))
|
||||||
|
.select(futures03::compat::Compat::new(follow.map(|_| Ok::<(), ()>(()))))
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| ())
|
||||||
|
.select(self.exit.into_exit())
|
||||||
|
.map(|_| ())
|
||||||
|
.map_err(|_| ())
|
||||||
|
),
|
||||||
|
).map_err(|_| "Could not spawn parachain server!")?;
|
||||||
|
|
||||||
|
Ok((self.proposer_factory, self.block_import, self.inherent_data_providers))
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user