mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 10:31:03 +00:00
Make work with Substrate master (#36)
* Fix up wasm runtime build * Fixes for runtime * Fix. * More fixes * Runtime builds on native. * Native and wasm both build without warnings. * Fix runtime tests. * Merge #20 * Final fix for native runtime. * Compile polkadot wo consensus * Reverted changes to polkadot-consensus * reintroduce minimal subset of consensus * reintroduce checked_block to runtime for std * polkadot_consensus compiles without most of the code * remove checked_block again and do more checks in parachains for runtime * uncomment proposer * remove offline tracker * extract out parachain-attestation logic from proposal directly * reintroduce transaction_pool * write some custom aura verification logic for the block verifier * use transaction pool in more generic way * service compiles again * polkadot-network and tests pass * remove unused session_key function from router * everything but CLI compiles due to service hell * Fixes compilation of `polkadot_cli` * everything compiles * update adder wasm
This commit is contained in:
Generated
+1762
-1464
File diff suppressed because it is too large
Load Diff
+1
-6
@@ -19,7 +19,6 @@ vergen = "0.1"
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"api",
|
|
||||||
"availability-store",
|
"availability-store",
|
||||||
"cli",
|
"cli",
|
||||||
"collator",
|
"collator",
|
||||||
@@ -30,7 +29,6 @@ members = [
|
|||||||
"runtime",
|
"runtime",
|
||||||
"service",
|
"service",
|
||||||
"statement-table",
|
"statement-table",
|
||||||
"transaction-pool",
|
|
||||||
"service",
|
"service",
|
||||||
|
|
||||||
"test-parachains/adder",
|
"test-parachains/adder",
|
||||||
@@ -42,11 +40,8 @@ exclude = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[badges]
|
[badges]
|
||||||
travis-ci = { repository = "paritytech/substrate", branch = "master" }
|
|
||||||
maintenance = { status = "actively-developed" }
|
maintenance = { status = "actively-developed" }
|
||||||
is-it-maintained-issue-resolution = { repository = "paritytech/substrate" }
|
|
||||||
is-it-maintained-open-issues = { repository = "paritytech/substrate" }
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
# Substrate runtime requires unwinding.
|
# Polkadot runtime requires unwinding.
|
||||||
panic = "unwind"
|
panic = "unwind"
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "polkadot-api"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
error-chain = "0.12"
|
|
||||||
log = "0.3"
|
|
||||||
polkadot-executor = { path = "../executor" }
|
|
||||||
polkadot-runtime = { path = "../runtime" }
|
|
||||||
polkadot-primitives = { path = "../primitives" }
|
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
sr-io = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
srml-executive = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
sr-primitives = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
substrate-executor = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
substrate-state-machine = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
substrate-keyring = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
= Polkadot API
|
|
||||||
|
|
||||||
placeholder
|
|
||||||
//TODO Write content :)
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Polkadot.
|
|
||||||
|
|
||||||
// Polkadot 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.
|
|
||||||
|
|
||||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Strongly typed API for full Polkadot client.
|
|
||||||
|
|
||||||
use client::backend::LocalBackend;
|
|
||||||
use client::block_builder::BlockBuilder as ClientBlockBuilder;
|
|
||||||
use client::{Client, LocalCallExecutor};
|
|
||||||
use polkadot_executor::Executor as LocalDispatch;
|
|
||||||
use substrate_executor::NativeExecutor;
|
|
||||||
|
|
||||||
//use runtime::{Block, Header, Address, BlockId};
|
|
||||||
use runtime::Address;
|
|
||||||
use primitives::{
|
|
||||||
Block, BlockId,
|
|
||||||
AccountId, Hash, Index, InherentData,
|
|
||||||
SessionKey, Timestamp, UncheckedExtrinsic,
|
|
||||||
};
|
|
||||||
use primitives::parachain::{DutyRoster, Id as ParaId};
|
|
||||||
use substrate_primitives::{Blake2Hasher, RlpCodec};
|
|
||||||
use {BlockBuilder, PolkadotApi, LocalPolkadotApi, ErrorKind, Result};
|
|
||||||
|
|
||||||
impl<B: LocalBackend<Block, Blake2Hasher, RlpCodec>> BlockBuilder for ClientBlockBuilder<B, LocalCallExecutor<B, NativeExecutor<LocalDispatch>>, Block, Blake2Hasher, RlpCodec> {
|
|
||||||
fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()> {
|
|
||||||
self.push(extrinsic).map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bake the block with provided extrinsics.
|
|
||||||
fn bake(self) -> Result<Block> {
|
|
||||||
ClientBlockBuilder::bake(self).map_err(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: LocalBackend<Block, Blake2Hasher, RlpCodec>> PolkadotApi for Client<B, LocalCallExecutor<B, NativeExecutor<LocalDispatch>>, Block> {
|
|
||||||
type BlockBuilder = ClientBlockBuilder<B, LocalCallExecutor<B, NativeExecutor<LocalDispatch>>, Block, Blake2Hasher, RlpCodec>;
|
|
||||||
|
|
||||||
fn session_keys(&self, at: &BlockId) -> Result<Vec<SessionKey>> {
|
|
||||||
Ok(self.authorities_at(at)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validators(&self, at: &BlockId) -> Result<Vec<AccountId>> {
|
|
||||||
Ok(self.call_api_at(at, "validators", &())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_seed(&self, at: &BlockId) -> Result<Hash> {
|
|
||||||
Ok(self.call_api_at(at, "random_seed", &())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duty_roster(&self, at: &BlockId) -> Result<DutyRoster> {
|
|
||||||
Ok(self.call_api_at(at, "duty_roster", &())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn timestamp(&self, at: &BlockId) -> Result<Timestamp> {
|
|
||||||
Ok(self.call_api_at(at, "timestamp", &())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate_block(&self, at: &BlockId, block: Block) -> Result<bool> {
|
|
||||||
let res: Result<()> = self.call_api_at(at, "execute_block", &block).map_err(From::from);
|
|
||||||
match res {
|
|
||||||
Ok(_) => Ok(true),
|
|
||||||
Err(err) => match err.kind() {
|
|
||||||
&ErrorKind::Execution(_) => Ok(false),
|
|
||||||
_ => Err(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(&self, at: &BlockId, account: AccountId) -> Result<Index> {
|
|
||||||
Ok(self.call_api_at(at, "account_nonce", &account)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(&self, at: &BlockId, address: Address) -> Result<Option<AccountId>> {
|
|
||||||
Ok(self.call_api_at(at, "lookup_address", &address)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn active_parachains(&self, at: &BlockId) -> Result<Vec<ParaId>> {
|
|
||||||
Ok(self.call_api_at(at, "active_parachains", &())?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parachain_code(&self, at: &BlockId, parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
|
||||||
Ok(self.call_api_at(at, "parachain_code", ¶chain)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parachain_head(&self, at: &BlockId, parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
|
||||||
Ok(self.call_api_at(at, "parachain_head", ¶chain)?)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result<Self::BlockBuilder> {
|
|
||||||
let mut block_builder = self.new_block_at(at)?;
|
|
||||||
for inherent in self.inherent_extrinsics(at, inherent_data)? {
|
|
||||||
block_builder.push(inherent)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(block_builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result<Vec<UncheckedExtrinsic>> {
|
|
||||||
let runtime_version = self.runtime_version_at(at)?;
|
|
||||||
Ok(self.call_api_at(at, "inherent_extrinsics", &(inherent_data, runtime_version.spec_version))?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: LocalBackend<Block, Blake2Hasher, RlpCodec>> LocalPolkadotApi for Client<B, LocalCallExecutor<B, NativeExecutor<LocalDispatch>>, Block>
|
|
||||||
{}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use keyring::Keyring;
|
|
||||||
use client::LocalCallExecutor;
|
|
||||||
use client::in_mem::Backend as InMemory;
|
|
||||||
use substrate_executor::NativeExecutionDispatch;
|
|
||||||
use runtime::{GenesisConfig, ConsensusConfig, SessionConfig};
|
|
||||||
|
|
||||||
fn validators() -> Vec<AccountId> {
|
|
||||||
vec![
|
|
||||||
Keyring::One.to_raw_public().into(),
|
|
||||||
Keyring::Two.to_raw_public().into(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn session_keys() -> Vec<SessionKey> {
|
|
||||||
vec![
|
|
||||||
Keyring::One.to_raw_public().into(),
|
|
||||||
Keyring::Two.to_raw_public().into(),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn client() -> Client<InMemory<Block, Blake2Hasher, RlpCodec>, LocalCallExecutor<InMemory<Block, Blake2Hasher, RlpCodec>, NativeExecutor<LocalDispatch>>, Block> {
|
|
||||||
let genesis_config = GenesisConfig {
|
|
||||||
consensus: Some(ConsensusConfig {
|
|
||||||
code: LocalDispatch::native_equivalent().to_vec(),
|
|
||||||
authorities: session_keys(),
|
|
||||||
}),
|
|
||||||
system: None,
|
|
||||||
balances: Some(Default::default()),
|
|
||||||
session: Some(SessionConfig {
|
|
||||||
validators: validators(),
|
|
||||||
session_length: 100,
|
|
||||||
}),
|
|
||||||
council: Some(Default::default()),
|
|
||||||
democracy: Some(Default::default()),
|
|
||||||
parachains: Some(Default::default()),
|
|
||||||
staking: Some(Default::default()),
|
|
||||||
timestamp: Some(Default::default()),
|
|
||||||
treasury: Some(Default::default()),
|
|
||||||
};
|
|
||||||
|
|
||||||
::client::new_in_mem(LocalDispatch::new(), genesis_config).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gets_session_and_validator_keys() {
|
|
||||||
let client = client();
|
|
||||||
let id = BlockId::number(0);
|
|
||||||
assert_eq!(client.session_keys(&id).unwrap(), session_keys());
|
|
||||||
assert_eq!(client.validators(&id).unwrap(), validators());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn build_block_implicit_succeeds() {
|
|
||||||
let client = client();
|
|
||||||
|
|
||||||
let id = BlockId::number(0);
|
|
||||||
let block_builder = client.build_block(&id, InherentData {
|
|
||||||
timestamp: 1_000_000,
|
|
||||||
parachain_heads: Vec::new(),
|
|
||||||
offline_indices: Vec::new(),
|
|
||||||
}).unwrap();
|
|
||||||
let block = block_builder.bake().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(block.header.number, 1);
|
|
||||||
assert!(block.header.extrinsics_root != Default::default());
|
|
||||||
assert!(client.evaluate_block(&id, block).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn build_block_with_inherent_succeeds() {
|
|
||||||
let client = client();
|
|
||||||
|
|
||||||
let id = BlockId::number(0);
|
|
||||||
let inherent = client.inherent_extrinsics(&id, InherentData {
|
|
||||||
timestamp: 1_000_000,
|
|
||||||
parachain_heads: Vec::new(),
|
|
||||||
offline_indices: Vec::new(),
|
|
||||||
}).unwrap();
|
|
||||||
|
|
||||||
let mut block_builder = client.new_block_at(&id).unwrap();
|
|
||||||
for extrinsic in inherent {
|
|
||||||
block_builder.push(extrinsic).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
let block = block_builder.bake().unwrap();
|
|
||||||
|
|
||||||
assert_eq!(block.header.number, 1);
|
|
||||||
assert!(block.header.extrinsics_root != Default::default());
|
|
||||||
assert!(client.evaluate_block(&id, block).unwrap());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gets_random_seed_with_genesis() {
|
|
||||||
let client = client();
|
|
||||||
|
|
||||||
let id = BlockId::number(0);
|
|
||||||
client.random_seed(&id).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Polkadot.
|
|
||||||
|
|
||||||
// Polkadot 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.
|
|
||||||
|
|
||||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Strongly typed API for Polkadot based around the locally-compiled native
|
|
||||||
//! runtime.
|
|
||||||
|
|
||||||
extern crate polkadot_executor;
|
|
||||||
extern crate polkadot_primitives as primitives;
|
|
||||||
extern crate polkadot_runtime as runtime;
|
|
||||||
extern crate parity_codec as codec;
|
|
||||||
extern crate sr_io as runtime_io;
|
|
||||||
extern crate substrate_client as client;
|
|
||||||
extern crate substrate_executor as substrate_executor;
|
|
||||||
extern crate srml_executive;
|
|
||||||
extern crate substrate_primitives;
|
|
||||||
extern crate sr_primitives as runtime_primitives;
|
|
||||||
extern crate substrate_state_machine as state_machine;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate error_chain;
|
|
||||||
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate substrate_keyring as keyring;
|
|
||||||
|
|
||||||
pub mod full;
|
|
||||||
pub mod light;
|
|
||||||
|
|
||||||
use primitives::{
|
|
||||||
AccountId, Block, BlockId, Hash, Index, SessionKey, Timestamp,
|
|
||||||
UncheckedExtrinsic, InherentData,
|
|
||||||
};
|
|
||||||
use runtime::Address;
|
|
||||||
use primitives::parachain::{DutyRoster, Id as ParaId};
|
|
||||||
|
|
||||||
error_chain! {
|
|
||||||
errors {
|
|
||||||
/// Unknown runtime code.
|
|
||||||
UnknownRuntime {
|
|
||||||
description("Unknown runtime code")
|
|
||||||
display("Unknown runtime code")
|
|
||||||
}
|
|
||||||
/// Unknown block ID.
|
|
||||||
UnknownBlock(b: String) {
|
|
||||||
description("Unknown block")
|
|
||||||
display("Unknown block {}", b)
|
|
||||||
}
|
|
||||||
/// Execution error.
|
|
||||||
Execution(e: String) {
|
|
||||||
description("Execution error")
|
|
||||||
display("Execution error: {}", e)
|
|
||||||
}
|
|
||||||
/// Some other error.
|
|
||||||
// TODO: allow to be specified as associated type of PolkadotApi
|
|
||||||
Other(e: Box<::std::error::Error + Send>) {
|
|
||||||
description("Other error")
|
|
||||||
display("Other error: {}", e.description())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<client::error::Error> for Error {
|
|
||||||
fn from(e: client::error::Error) -> Error {
|
|
||||||
match e {
|
|
||||||
client::error::Error(client::error::ErrorKind::UnknownBlock(b), _) => Error::from_kind(ErrorKind::UnknownBlock(b)),
|
|
||||||
client::error::Error(client::error::ErrorKind::Execution(e), _) =>
|
|
||||||
Error::from_kind(ErrorKind::Execution(format!("{}", e))),
|
|
||||||
other => Error::from_kind(ErrorKind::Other(Box::new(other) as Box<_>)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build new blocks.
|
|
||||||
pub trait BlockBuilder {
|
|
||||||
/// Push an extrinsic onto the block. Fails if the extrinsic is invalid.
|
|
||||||
fn push_extrinsic(&mut self, extrinsic: UncheckedExtrinsic) -> Result<()>;
|
|
||||||
|
|
||||||
/// Bake the block with provided extrinsics.
|
|
||||||
fn bake(self) -> Result<Block>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait encapsulating the Polkadot API.
|
|
||||||
///
|
|
||||||
/// All calls should fail when the exact runtime is unknown.
|
|
||||||
pub trait PolkadotApi {
|
|
||||||
/// The block builder for this API type.
|
|
||||||
type BlockBuilder: BlockBuilder;
|
|
||||||
|
|
||||||
/// Get session keys at a given block.
|
|
||||||
fn session_keys(&self, at: &BlockId) -> Result<Vec<SessionKey>>;
|
|
||||||
|
|
||||||
/// Get validators at a given block.
|
|
||||||
fn validators(&self, at: &BlockId) -> Result<Vec<AccountId>>;
|
|
||||||
|
|
||||||
/// Get the value of the randomness beacon at a given block.
|
|
||||||
fn random_seed(&self, at: &BlockId) -> Result<Hash>;
|
|
||||||
|
|
||||||
/// Get the authority duty roster at a block.
|
|
||||||
fn duty_roster(&self, at: &BlockId) -> Result<DutyRoster>;
|
|
||||||
|
|
||||||
/// Get the timestamp registered at a block.
|
|
||||||
fn timestamp(&self, at: &BlockId) -> Result<Timestamp>;
|
|
||||||
|
|
||||||
/// Get the nonce (né index) of an account at a block.
|
|
||||||
fn index(&self, at: &BlockId, account: AccountId) -> Result<Index>;
|
|
||||||
|
|
||||||
/// Get the account id of an address at a block.
|
|
||||||
fn lookup(&self, at: &BlockId, address: Address) -> Result<Option<AccountId>>;
|
|
||||||
|
|
||||||
/// Get the active parachains at a block.
|
|
||||||
fn active_parachains(&self, at: &BlockId) -> Result<Vec<ParaId>>;
|
|
||||||
|
|
||||||
/// Get the validation code of a parachain at a block. If the parachain is active, this will always return `Some`.
|
|
||||||
fn parachain_code(&self, at: &BlockId, parachain: ParaId) -> Result<Option<Vec<u8>>>;
|
|
||||||
|
|
||||||
/// Get the chain head of a parachain. If the parachain is active, this will always return `Some`.
|
|
||||||
fn parachain_head(&self, at: &BlockId, parachain: ParaId) -> Result<Option<Vec<u8>>>;
|
|
||||||
|
|
||||||
/// Evaluate a block. Returns true if the block is good, false if it is known to be bad,
|
|
||||||
/// and an error if we can't evaluate for some reason.
|
|
||||||
fn evaluate_block(&self, at: &BlockId, block: Block) -> Result<bool>;
|
|
||||||
|
|
||||||
/// Build a block on top of the given, with inherent extrinsics pre-pushed.
|
|
||||||
fn build_block(&self, at: &BlockId, inherent_data: InherentData) -> Result<Self::BlockBuilder>;
|
|
||||||
|
|
||||||
/// Attempt to produce the (encoded) inherent extrinsics for a block being built upon the given.
|
|
||||||
/// This may vary by runtime and will fail if a runtime doesn't follow the same API.
|
|
||||||
fn inherent_extrinsics(&self, at: &BlockId, inherent_data: InherentData) -> Result<Vec<UncheckedExtrinsic>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Mark for all Polkadot API implementations, that are making use of state data, stored locally.
|
|
||||||
pub trait LocalPolkadotApi: PolkadotApi {}
|
|
||||||
|
|
||||||
/// Mark for all Polkadot API implementations, that are fetching required state data from remote nodes.
|
|
||||||
pub trait RemotePolkadotApi: PolkadotApi {}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Polkadot.
|
|
||||||
|
|
||||||
// Polkadot 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.
|
|
||||||
|
|
||||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Strongly typed API for light Polkadot client.
|
|
||||||
|
|
||||||
use std::sync::Arc;
|
|
||||||
use client::backend::{Backend, RemoteBackend};
|
|
||||||
use client::{Client, CallExecutor};
|
|
||||||
use codec::Decode;
|
|
||||||
use primitives::{
|
|
||||||
AccountId, Block, BlockId, Hash, Index, InherentData,
|
|
||||||
SessionKey, Timestamp, UncheckedExtrinsic,
|
|
||||||
};
|
|
||||||
use runtime::Address;
|
|
||||||
use primitives::parachain::{DutyRoster, Id as ParaId};
|
|
||||||
use {PolkadotApi, BlockBuilder, RemotePolkadotApi, Result, ErrorKind};
|
|
||||||
use substrate_primitives::{Blake2Hasher, RlpCodec};
|
|
||||||
|
|
||||||
/// Light block builder. TODO: make this work (efficiently)
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub struct LightBlockBuilder;
|
|
||||||
|
|
||||||
impl BlockBuilder for LightBlockBuilder {
|
|
||||||
fn push_extrinsic(&mut self, _xt: UncheckedExtrinsic) -> Result<()> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bake(self) -> Result<Block> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remote polkadot API implementation.
|
|
||||||
pub struct RemotePolkadotApiWrapper<B: Backend<Block, Blake2Hasher, RlpCodec>, E: CallExecutor<Block, Blake2Hasher, RlpCodec>>(pub Arc<Client<B, E, Block>>);
|
|
||||||
|
|
||||||
impl<B: Backend<Block, Blake2Hasher, RlpCodec>, E: CallExecutor<Block, Blake2Hasher, RlpCodec>> PolkadotApi for RemotePolkadotApiWrapper<B, E> {
|
|
||||||
type BlockBuilder = LightBlockBuilder;
|
|
||||||
|
|
||||||
fn session_keys(&self, at: &BlockId) -> Result<Vec<SessionKey>> {
|
|
||||||
self.0.executor().call(at, "authorities", &[])
|
|
||||||
.and_then(|r| Vec::<SessionKey>::decode(&mut &r.return_data[..])
|
|
||||||
.ok_or("error decoding session keys".into()))
|
|
||||||
.map_err(Into::into)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn validators(&self, _at: &BlockId) -> Result<Vec<AccountId>> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn random_seed(&self, _at: &BlockId) -> Result<Hash> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn duty_roster(&self, _at: &BlockId) -> Result<DutyRoster> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn timestamp(&self, _at: &BlockId) -> Result<Timestamp> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn evaluate_block(&self, _at: &BlockId, _block: Block) -> Result<bool> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn index(&self, _at: &BlockId, _account: AccountId) -> Result<Index> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(&self, _at: &BlockId, _address: Address) -> Result<Option<AccountId>> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn active_parachains(&self, _at: &BlockId) -> Result<Vec<ParaId>> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parachain_code(&self, _at: &BlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parachain_head(&self, _at: &BlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_block(&self, _at: &BlockId, _inherent: InherentData) -> Result<Self::BlockBuilder> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inherent_extrinsics(&self, _at: &BlockId, _inherent: InherentData) -> Result<Vec<UncheckedExtrinsic>> {
|
|
||||||
Err(ErrorKind::UnknownRuntime.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: RemoteBackend<Block, Blake2Hasher, RlpCodec>, E: CallExecutor<Block, Blake2Hasher, RlpCodec>> RemotePolkadotApi for RemotePolkadotApiWrapper<B, E> {}
|
|
||||||
@@ -8,7 +8,7 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
polkadot-primitives = { path = "../primitives" }
|
polkadot-primitives = { path = "../primitives" }
|
||||||
parking_lot = "0.4"
|
parking_lot = "0.4"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate" }
|
parity-codec = "2.1"
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
kvdb = { git = "https://github.com/paritytech/parity-common.git" }
|
kvdb = { git = "https://github.com/paritytech/parity-common.git" }
|
||||||
kvdb-rocksdb = { git = "https://github.com/paritytech/parity-common.git" }
|
kvdb-rocksdb = { git = "https://github.com/paritytech/parity-common.git" }
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ impl Store {
|
|||||||
let mut tx = DBTransaction::new();
|
let mut tx = DBTransaction::new();
|
||||||
|
|
||||||
// note the meta key.
|
// note the meta key.
|
||||||
let mut v = match self.inner.get(columns::META, &*data.relay_parent) {
|
let mut v = match self.inner.get(columns::META, data.relay_parent.as_ref()) {
|
||||||
Ok(Some(raw)) => Vec::decode(&mut &raw[..]).expect("all stored data serialized correctly; qed"),
|
Ok(Some(raw)) => Vec::decode(&mut &raw[..]).expect("all stored data serialized correctly; qed"),
|
||||||
Ok(None) => Vec::new(),
|
Ok(None) => Vec::new(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
|||||||
@@ -11,3 +11,4 @@ futures = "0.1.17"
|
|||||||
exit-future = "0.1"
|
exit-future = "0.1"
|
||||||
substrate-cli = { git = "https://github.com/paritytech/substrate" }
|
substrate-cli = { git = "https://github.com/paritytech/substrate" }
|
||||||
polkadot-service = { path = "../service" }
|
polkadot-service = { path = "../service" }
|
||||||
|
structopt = "0.2.13"
|
||||||
|
|||||||
+38
-12
@@ -25,20 +25,27 @@ extern crate tokio;
|
|||||||
extern crate substrate_cli as cli;
|
extern crate substrate_cli as cli;
|
||||||
extern crate polkadot_service as service;
|
extern crate polkadot_service as service;
|
||||||
extern crate exit_future;
|
extern crate exit_future;
|
||||||
|
extern crate structopt;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
mod chain_spec;
|
mod chain_spec;
|
||||||
|
|
||||||
pub use cli::error;
|
use std::ops::Deref;
|
||||||
|
|
||||||
use chain_spec::ChainSpec;
|
use chain_spec::ChainSpec;
|
||||||
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
pub use service::{Components as ServiceComponents, Service, CustomConfiguration};
|
use structopt::StructOpt;
|
||||||
|
use service::Service as BareService;
|
||||||
|
|
||||||
|
pub use service::{
|
||||||
|
Components as ServiceComponents, PolkadotService, CustomConfiguration, ServiceFactory, Factory,
|
||||||
|
ProvideRuntimeApi, CoreApi, ParachainHost,
|
||||||
|
};
|
||||||
|
|
||||||
pub use cli::{VersionInfo, IntoExit};
|
pub use cli::{VersionInfo, IntoExit};
|
||||||
|
pub use cli::error;
|
||||||
|
|
||||||
fn load_spec(id: &str) -> Result<Option<service::ChainSpec>, String> {
|
fn load_spec(id: &str) -> Result<Option<service::ChainSpec>, String> {
|
||||||
Ok(match ChainSpec::from(id) {
|
Ok(match ChainSpec::from(id) {
|
||||||
@@ -62,7 +69,7 @@ pub trait Worker: IntoExit {
|
|||||||
fn configuration(&self) -> service::CustomConfiguration { Default::default() }
|
fn configuration(&self) -> service::CustomConfiguration { Default::default() }
|
||||||
|
|
||||||
/// Do work and schedule exit.
|
/// Do work and schedule exit.
|
||||||
fn work<C: service::Components>(self, service: &service::Service<C>) -> Self::Work;
|
fn work<S: PolkadotService>(self, service: &S) -> Self::Work;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse command line arguments into service configuration.
|
/// Parse command line arguments into service configuration.
|
||||||
@@ -78,10 +85,26 @@ pub fn run<I, T, W>(args: I, worker: W, version: cli::VersionInfo) -> error::Res
|
|||||||
T: Into<std::ffi::OsString> + Clone,
|
T: Into<std::ffi::OsString> + Clone,
|
||||||
W: Worker,
|
W: Worker,
|
||||||
{
|
{
|
||||||
|
let full_version = polkadot_service::full_version_from_strs(
|
||||||
|
version.version,
|
||||||
|
version.commit
|
||||||
|
);
|
||||||
|
|
||||||
match cli::prepare_execution::<service::Factory, _, _, _, _>(args, worker, version, load_spec, "parity-polkadot")? {
|
let matches = match cli::CoreParams::clap()
|
||||||
|
.name(version.executable_name)
|
||||||
|
.author(version.author)
|
||||||
|
.about(version.description)
|
||||||
|
.version(&(full_version + "\n")[..])
|
||||||
|
.get_matches_from_safe(args) {
|
||||||
|
Ok(m) => m,
|
||||||
|
Err(e) => e.exit(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (spec, mut config) = cli::parse_matches::<service::Factory, _>(load_spec, version, "parity-polkadot", &matches)?;
|
||||||
|
|
||||||
|
match cli::execute_default::<service::Factory, _,>(spec, worker, &matches)? {
|
||||||
cli::Action::ExecutedInternally => (),
|
cli::Action::ExecutedInternally => (),
|
||||||
cli::Action::RunService((mut config, worker)) => {
|
cli::Action::RunService(worker) => {
|
||||||
info!("Parity ·:· Polkadot");
|
info!("Parity ·:· Polkadot");
|
||||||
info!(" version {}", config.full_version());
|
info!(" version {}", config.full_version());
|
||||||
info!(" by Parity Technologies, 2017, 2018");
|
info!(" by Parity Technologies, 2017, 2018");
|
||||||
@@ -92,20 +115,23 @@ pub fn run<I, T, W>(args: I, worker: W, version: cli::VersionInfo) -> error::Res
|
|||||||
let mut runtime = Runtime::new()?;
|
let mut runtime = Runtime::new()?;
|
||||||
let executor = runtime.executor();
|
let executor = runtime.executor();
|
||||||
match config.roles == service::Roles::LIGHT {
|
match config.roles == service::Roles::LIGHT {
|
||||||
true => run_until_exit(&mut runtime, service::new_light(config, executor)?, worker)?,
|
true => run_until_exit(&mut runtime, Factory::new_light(config, executor)?, worker)?,
|
||||||
false => run_until_exit(&mut runtime, service::new_full(config, executor)?, worker)?,
|
false => run_until_exit(&mut runtime, Factory::new_full(config, executor)?, worker)?,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
fn run_until_exit<C, W>(
|
|
||||||
|
fn run_until_exit<T, C, W>(
|
||||||
runtime: &mut Runtime,
|
runtime: &mut Runtime,
|
||||||
service: service::Service<C>,
|
service: T,
|
||||||
worker: W,
|
worker: W,
|
||||||
) -> error::Result<()>
|
) -> error::Result<()>
|
||||||
where
|
where
|
||||||
|
T: Deref<Target=BareService<C>>,
|
||||||
C: service::Components,
|
C: service::Components,
|
||||||
|
BareService<C>: PolkadotService,
|
||||||
W: Worker,
|
W: Worker,
|
||||||
{
|
{
|
||||||
let (exit_send, exit) = exit_future::signal();
|
let (exit_send, exit) = exit_future::signal();
|
||||||
@@ -113,7 +139,7 @@ fn run_until_exit<C, W>(
|
|||||||
let executor = runtime.executor();
|
let executor = runtime.executor();
|
||||||
cli::informant::start(&service, exit.clone(), executor.clone());
|
cli::informant::start(&service, exit.clone(), executor.clone());
|
||||||
|
|
||||||
let _ = runtime.block_on(worker.work(&service));
|
let _ = runtime.block_on(worker.work(&*service));
|
||||||
exit_send.fire();
|
exit_send.fire();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ description = "Collator node implementation"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
futures = "0.1.17"
|
futures = "0.1.17"
|
||||||
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate" }
|
parity-codec = "2.1"
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
polkadot-api = { path = "../api" }
|
|
||||||
polkadot-runtime = { path = "../runtime", version = "0.1" }
|
polkadot-runtime = { path = "../runtime", version = "0.1" }
|
||||||
polkadot-primitives = { path = "../primitives", version = "0.1" }
|
polkadot-primitives = { path = "../primitives", version = "0.1" }
|
||||||
polkadot-cli = { path = "../cli" }
|
polkadot-cli = { path = "../cli" }
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ extern crate parity_codec as codec;
|
|||||||
extern crate substrate_primitives as primitives;
|
extern crate substrate_primitives as primitives;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
|
|
||||||
extern crate polkadot_api;
|
|
||||||
extern crate polkadot_cli;
|
extern crate polkadot_cli;
|
||||||
extern crate polkadot_runtime;
|
extern crate polkadot_runtime;
|
||||||
extern crate polkadot_primitives;
|
extern crate polkadot_primitives;
|
||||||
@@ -61,17 +60,16 @@ extern crate log;
|
|||||||
use std::collections::{BTreeSet, BTreeMap, HashSet};
|
use std::collections::{BTreeSet, BTreeMap, HashSet};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::Duration;
|
||||||
|
|
||||||
use futures::{future, stream, Stream, Future, IntoFuture};
|
use futures::{future, stream, Stream, Future, IntoFuture};
|
||||||
use client::BlockchainEvents;
|
use client::BlockchainEvents;
|
||||||
use polkadot_api::PolkadotApi;
|
|
||||||
use primitives::ed25519;
|
use primitives::ed25519;
|
||||||
use polkadot_primitives::{AccountId, BlockId, SessionKey};
|
use polkadot_primitives::{AccountId, BlockId, SessionKey};
|
||||||
use polkadot_primitives::parachain::{self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId};
|
use polkadot_primitives::parachain::{self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId};
|
||||||
use polkadot_cli::{ServiceComponents, Service, CustomConfiguration};
|
use polkadot_cli::{PolkadotService, CustomConfiguration, CoreApi, ParachainHost};
|
||||||
use polkadot_cli::{Worker, IntoExit};
|
use polkadot_cli::{Worker, IntoExit, ProvideRuntimeApi};
|
||||||
use tokio::timer::Deadline;
|
use tokio::timer::Timeout;
|
||||||
|
|
||||||
pub use polkadot_cli::VersionInfo;
|
pub use polkadot_cli::VersionInfo;
|
||||||
|
|
||||||
@@ -193,7 +191,7 @@ pub fn collate<'a, R, P>(
|
|||||||
).map_err(Error::Collator)?;
|
).map_err(Error::Collator)?;
|
||||||
|
|
||||||
let block_data_hash = block_data.hash();
|
let block_data_hash = block_data.hash();
|
||||||
let signature = key.sign(&block_data_hash.0[..]).into();
|
let signature = key.sign(block_data_hash.as_ref()).into();
|
||||||
|
|
||||||
let receipt = parachain::CandidateReceipt {
|
let receipt = parachain::CandidateReceipt {
|
||||||
parachain_index: local_id,
|
parachain_index: local_id,
|
||||||
@@ -217,7 +215,7 @@ pub fn collate<'a, R, P>(
|
|||||||
struct ApiContext;
|
struct ApiContext;
|
||||||
|
|
||||||
impl RelayChainContext for ApiContext {
|
impl RelayChainContext for ApiContext {
|
||||||
type Error = ::polkadot_api::Error;
|
type Error = client::error::Error;
|
||||||
type FutureEgress = Result<Vec<Vec<Message>>, Self::Error>;
|
type FutureEgress = Result<Vec<Vec<Message>>, Self::Error>;
|
||||||
|
|
||||||
fn routing_parachains(&self) -> BTreeSet<ParaId> {
|
fn routing_parachains(&self) -> BTreeSet<ParaId> {
|
||||||
@@ -261,10 +259,12 @@ impl<P, E> Worker for CollationNode<P, E> where
|
|||||||
config
|
config
|
||||||
}
|
}
|
||||||
|
|
||||||
fn work<C: ServiceComponents>(self, service: &Service<C>) -> Self::Work {
|
fn work<S>(self, service: &S) -> Self::Work
|
||||||
|
where S: PolkadotService,
|
||||||
|
{
|
||||||
|
|
||||||
let CollationNode { parachain_context, exit, para_id, key } = self;
|
let CollationNode { parachain_context, exit, para_id, key } = self;
|
||||||
let client = service.client();
|
let client = service.client();
|
||||||
let api = service.api();
|
|
||||||
let network = service.network();
|
let network = service.network();
|
||||||
|
|
||||||
let work = client.import_notification_stream()
|
let work = client.import_notification_stream()
|
||||||
@@ -282,19 +282,20 @@ impl<P, E> Worker for CollationNode<P, E> where
|
|||||||
let id = BlockId::hash(relay_parent);
|
let id = BlockId::hash(relay_parent);
|
||||||
|
|
||||||
let network = network.clone();
|
let network = network.clone();
|
||||||
let api = api.clone();
|
let client = client.clone();
|
||||||
let key = key.clone();
|
let key = key.clone();
|
||||||
let parachain_context = parachain_context.clone();
|
let parachain_context = parachain_context.clone();
|
||||||
|
|
||||||
let work = future::lazy(move || {
|
let work = future::lazy(move || {
|
||||||
let last_head = match try_fr!(api.parachain_head(&id, para_id)) {
|
let api = client.runtime_api();
|
||||||
|
let last_head = match try_fr!(api.parachain_head(&id, ¶_id)) {
|
||||||
Some(last_head) => last_head,
|
Some(last_head) => last_head,
|
||||||
None => return future::Either::A(future::ok(())),
|
None => return future::Either::A(future::ok(())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let targets = compute_targets(
|
let targets = compute_targets(
|
||||||
para_id,
|
para_id,
|
||||||
try_fr!(api.session_keys(&id)).as_slice(),
|
try_fr!(api.authorities(&id)).as_slice(),
|
||||||
try_fr!(api.duty_roster(&id)),
|
try_fr!(api.duty_roster(&id)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -315,11 +316,11 @@ impl<P, E> Worker for CollationNode<P, E> where
|
|||||||
|
|
||||||
future::Either::B(collation_work)
|
future::Either::B(collation_work)
|
||||||
});
|
});
|
||||||
let deadlined = Deadline::new(work, Instant::now() + COLLATION_TIMEOUT);
|
let deadlined = Timeout::new(work, COLLATION_TIMEOUT);
|
||||||
let silenced = deadlined.then(|res| match res {
|
let silenced = deadlined.then(|res| match res {
|
||||||
Ok(()) => Ok(()),
|
Ok(()) => Ok(()),
|
||||||
Err(e) => {
|
Err(_) => {
|
||||||
warn!("Collation failure: {}", e);
|
warn!("Collation failure: timeout");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ tokio = "0.1.7"
|
|||||||
error-chain = "0.12"
|
error-chain = "0.12"
|
||||||
log = "0.3"
|
log = "0.3"
|
||||||
exit-future = "0.1"
|
exit-future = "0.1"
|
||||||
rhododendron = "0.3"
|
parity-codec = "2.1"
|
||||||
polkadot-api = { path = "../api" }
|
|
||||||
polkadot-availability-store = { path = "../availability-store" }
|
polkadot-availability-store = { path = "../availability-store" }
|
||||||
polkadot-parachain = { path = "../parachain" }
|
polkadot-parachain = { path = "../parachain" }
|
||||||
polkadot-primitives = { path = "../primitives" }
|
polkadot-primitives = { path = "../primitives" }
|
||||||
polkadot-runtime = { path = "../runtime" }
|
polkadot-runtime = { path = "../runtime" }
|
||||||
polkadot-statement-table = { path = "../statement-table" }
|
polkadot-statement-table = { path = "../statement-table" }
|
||||||
polkadot-transaction-pool = { path = "../transaction-pool" }
|
substrate-consensus-aura = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-bft = { git = "https://github.com/paritytech/substrate" }
|
substrate-finality-grandpa = { git = "https://github.com/paritytech/substrate" }
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate" }
|
substrate-consensus-common = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
substrate-transaction-pool = { git = "https://github.com/paritytech/substrate" }
|
||||||
srml-support = { git = "https://github.com/paritytech/substrate" }
|
srml-support = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
||||||
sr-primitives = { git = "https://github.com/paritytech/substrate" }
|
sr-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
|||||||
@@ -21,9 +21,10 @@
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use polkadot_api::PolkadotApi;
|
use polkadot_primitives::{Block, Hash, AccountId, BlockId};
|
||||||
use polkadot_primitives::{Hash, AccountId, BlockId};
|
|
||||||
use polkadot_primitives::parachain::{Id as ParaId, Collation, Extrinsic};
|
use polkadot_primitives::parachain::{Id as ParaId, Collation, Extrinsic};
|
||||||
|
use polkadot_primitives::parachain::ParachainHost;
|
||||||
|
use runtime_primitives::traits::ProvideRuntimeApi;
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
|
|
||||||
@@ -52,7 +53,7 @@ pub trait Collators: Clone {
|
|||||||
/// A future which resolves when a collation is available.
|
/// A future which resolves when a collation is available.
|
||||||
///
|
///
|
||||||
/// This future is fused.
|
/// This future is fused.
|
||||||
pub struct CollationFetch<C: Collators, P: PolkadotApi> {
|
pub struct CollationFetch<C: Collators, P: ProvideRuntimeApi> {
|
||||||
parachain: ParaId,
|
parachain: ParaId,
|
||||||
relay_parent_hash: Hash,
|
relay_parent_hash: Hash,
|
||||||
relay_parent: BlockId,
|
relay_parent: BlockId,
|
||||||
@@ -61,7 +62,7 @@ pub struct CollationFetch<C: Collators, P: PolkadotApi> {
|
|||||||
client: Arc<P>,
|
client: Arc<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Collators, P: PolkadotApi> CollationFetch<C, P> {
|
impl<C: Collators, P: ProvideRuntimeApi> CollationFetch<C, P> {
|
||||||
/// Create a new collation fetcher for the given chain.
|
/// Create a new collation fetcher for the given chain.
|
||||||
pub fn new(parachain: ParaId, relay_parent: BlockId, relay_parent_hash: Hash, collators: C, client: Arc<P>) -> Self {
|
pub fn new(parachain: ParaId, relay_parent: BlockId, relay_parent_hash: Hash, collators: C, client: Arc<P>) -> Self {
|
||||||
CollationFetch {
|
CollationFetch {
|
||||||
@@ -80,7 +81,9 @@ impl<C: Collators, P: PolkadotApi> CollationFetch<C, P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: Collators, P: PolkadotApi> Future for CollationFetch<C, P> {
|
impl<C: Collators, P: ProvideRuntimeApi> Future for CollationFetch<C, P>
|
||||||
|
where P::Api: ParachainHost<Block>,
|
||||||
|
{
|
||||||
type Item = (Collation, Extrinsic);
|
type Item = (Collation, Extrinsic);
|
||||||
type Error = C::Error;
|
type Error = C::Error;
|
||||||
|
|
||||||
@@ -133,19 +136,27 @@ error_chain! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
links {
|
links {
|
||||||
PolkadotApi(::polkadot_api::Error, ::polkadot_api::ErrorKind);
|
Client(::client::error::Error, ::client::error::ErrorKind);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check whether a given collation is valid. Returns `Ok` on success, error otherwise.
|
/// Check whether a given collation is valid. Returns `Ok` on success, error otherwise.
|
||||||
pub fn validate_collation<P: PolkadotApi>(client: &P, relay_parent: &BlockId, collation: &Collation) -> Result<(), Error> {
|
pub fn validate_collation<P>(
|
||||||
|
client: &P,
|
||||||
|
relay_parent: &BlockId,
|
||||||
|
collation: &Collation
|
||||||
|
) -> Result<(), Error> where
|
||||||
|
P: ProvideRuntimeApi,
|
||||||
|
P::Api: ParachainHost<Block>
|
||||||
|
{
|
||||||
use parachain::{self, ValidationParams};
|
use parachain::{self, ValidationParams};
|
||||||
|
|
||||||
|
let api = client.runtime_api();
|
||||||
let para_id = collation.receipt.parachain_index;
|
let para_id = collation.receipt.parachain_index;
|
||||||
let validation_code = client.parachain_code(relay_parent, para_id)?
|
let validation_code = api.parachain_code(relay_parent, ¶_id)?
|
||||||
.ok_or_else(|| ErrorKind::InactiveParachain(para_id))?;
|
.ok_or_else(|| ErrorKind::InactiveParachain(para_id))?;
|
||||||
|
|
||||||
let chain_head = client.parachain_head(relay_parent, para_id)?
|
let chain_head = api.parachain_head(relay_parent, ¶_id)?
|
||||||
.ok_or_else(|| ErrorKind::InactiveParachain(para_id))?;
|
.ok_or_else(|| ErrorKind::InactiveParachain(para_id))?;
|
||||||
|
|
||||||
let params = ValidationParams {
|
let params = ValidationParams {
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ use primitives::AuthorityId;
|
|||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
links {
|
links {
|
||||||
PolkadotApi(::polkadot_api::Error, ::polkadot_api::ErrorKind);
|
Client(::client::error::Error, ::client::error::ErrorKind);
|
||||||
Bft(::bft::Error, ::bft::ErrorKind);
|
Consensus(::consensus::error::Error, ::consensus::error::ErrorKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
errors {
|
errors {
|
||||||
@@ -48,8 +48,8 @@ error_chain! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<::bft::InputStreamConcluded> for Error {
|
// impl From<::bft::InputStreamConcluded> for Error {
|
||||||
fn from(err: ::bft::InputStreamConcluded) -> Self {
|
// fn from(err: ::bft::InputStreamConcluded) -> Self {
|
||||||
::bft::Error::from(err).into()
|
// ::bft::Error::from(err).into()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -18,14 +18,13 @@
|
|||||||
|
|
||||||
use super::MAX_TRANSACTIONS_SIZE;
|
use super::MAX_TRANSACTIONS_SIZE;
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
use codec::Encode;
|
||||||
use polkadot_runtime::{Block as PolkadotGenericBlock, CheckedBlock};
|
|
||||||
use polkadot_primitives::{Block, Hash, BlockNumber, Timestamp};
|
use polkadot_primitives::{Block, Hash, BlockNumber, Timestamp};
|
||||||
use polkadot_primitives::parachain::Id as ParaId;
|
use polkadot_primitives::parachain::Id as ParaId;
|
||||||
|
|
||||||
error_chain! {
|
error_chain! {
|
||||||
links {
|
links {
|
||||||
PolkadotApi(::polkadot_api::Error, ::polkadot_api::ErrorKind);
|
Client(::client::error::Error, ::client::error::ErrorKind);
|
||||||
}
|
}
|
||||||
|
|
||||||
errors {
|
errors {
|
||||||
@@ -33,10 +32,6 @@ error_chain! {
|
|||||||
description("Proposal provided not a Polkadot block."),
|
description("Proposal provided not a Polkadot block."),
|
||||||
display("Proposal provided not a Polkadot block."),
|
display("Proposal provided not a Polkadot block."),
|
||||||
}
|
}
|
||||||
TimestampInFuture {
|
|
||||||
description("Proposal had timestamp too far in the future."),
|
|
||||||
display("Proposal had timestamp too far in the future."),
|
|
||||||
}
|
|
||||||
TooManyCandidates(expected: usize, got: usize) {
|
TooManyCandidates(expected: usize, got: usize) {
|
||||||
description("Proposal included more candidates than is possible."),
|
description("Proposal included more candidates than is possible."),
|
||||||
display("Proposal included {} candidates for {} parachains", got, expected),
|
display("Proposal included {} candidates for {} parachains", got, expected),
|
||||||
@@ -71,18 +66,11 @@ error_chain! {
|
|||||||
/// upon any initial validity checks failing.
|
/// upon any initial validity checks failing.
|
||||||
pub fn evaluate_initial(
|
pub fn evaluate_initial(
|
||||||
proposal: &Block,
|
proposal: &Block,
|
||||||
now: Timestamp,
|
_now: Timestamp,
|
||||||
parent_hash: &Hash,
|
parent_hash: &Hash,
|
||||||
parent_number: BlockNumber,
|
parent_number: BlockNumber,
|
||||||
active_parachains: &[ParaId],
|
_active_parachains: &[ParaId],
|
||||||
) -> Result<CheckedBlock> {
|
) -> Result<()> {
|
||||||
const MAX_TIMESTAMP_DRIFT: Timestamp = 60;
|
|
||||||
|
|
||||||
let encoded = Encode::encode(proposal);
|
|
||||||
let proposal = PolkadotGenericBlock::decode(&mut &encoded[..])
|
|
||||||
.and_then(|b| CheckedBlock::new(b).ok())
|
|
||||||
.ok_or_else(|| ErrorKind::ProposalNotForPolkadot)?;
|
|
||||||
|
|
||||||
let transactions_size = proposal.extrinsics.iter().fold(0, |a, tx| {
|
let transactions_size = proposal.extrinsics.iter().fold(0, |a, tx| {
|
||||||
a + Encode::encode(tx).len()
|
a + Encode::encode(tx).len()
|
||||||
});
|
});
|
||||||
@@ -99,35 +87,5 @@ pub fn evaluate_initial(
|
|||||||
bail!(ErrorKind::WrongNumber(parent_number + 1, proposal.header.number));
|
bail!(ErrorKind::WrongNumber(parent_number + 1, proposal.header.number));
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_timestamp = proposal.timestamp();
|
Ok(())
|
||||||
|
|
||||||
// lenient maximum -- small drifts will just be delayed using a timer.
|
|
||||||
if block_timestamp > now + MAX_TIMESTAMP_DRIFT {
|
|
||||||
bail!(ErrorKind::TimestampInFuture)
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let n_parachains = active_parachains.len();
|
|
||||||
if proposal.parachain_heads().len() > n_parachains {
|
|
||||||
bail!(ErrorKind::TooManyCandidates(n_parachains, proposal.parachain_heads().len()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut last_id = None;
|
|
||||||
let mut iter = active_parachains.iter();
|
|
||||||
for head in proposal.parachain_heads() {
|
|
||||||
// proposed heads must be ascending order by parachain ID without duplicate.
|
|
||||||
if last_id.as_ref().map_or(false, |x| x >= &head.parachain_index) {
|
|
||||||
bail!(ErrorKind::ParachainOutOfOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !iter.any(|x| x == &head.parachain_index) {
|
|
||||||
// must be unknown since active parachains are always sorted.
|
|
||||||
bail!(ErrorKind::UnknownParachain(head.parachain_index))
|
|
||||||
}
|
|
||||||
|
|
||||||
last_id = Some(head.parachain_index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(proposal)
|
|
||||||
}
|
}
|
||||||
|
|||||||
+235
-341
@@ -30,15 +30,12 @@
|
|||||||
//! Groups themselves may be compromised by malicious authorities.
|
//! Groups themselves may be compromised by malicious authorities.
|
||||||
|
|
||||||
extern crate parking_lot;
|
extern crate parking_lot;
|
||||||
extern crate polkadot_api;
|
|
||||||
extern crate polkadot_availability_store as extrinsic_store;
|
extern crate polkadot_availability_store as extrinsic_store;
|
||||||
extern crate polkadot_statement_table as table;
|
extern crate polkadot_statement_table as table;
|
||||||
extern crate polkadot_parachain as parachain;
|
extern crate polkadot_parachain as parachain;
|
||||||
extern crate polkadot_transaction_pool as transaction_pool;
|
|
||||||
extern crate polkadot_runtime;
|
extern crate polkadot_runtime;
|
||||||
extern crate polkadot_primitives;
|
extern crate polkadot_primitives;
|
||||||
|
|
||||||
extern crate substrate_bft as bft;
|
|
||||||
extern crate parity_codec as codec;
|
extern crate parity_codec as codec;
|
||||||
extern crate substrate_primitives as primitives;
|
extern crate substrate_primitives as primitives;
|
||||||
extern crate srml_support as runtime_support;
|
extern crate srml_support as runtime_support;
|
||||||
@@ -47,7 +44,10 @@ extern crate substrate_client as client;
|
|||||||
|
|
||||||
extern crate exit_future;
|
extern crate exit_future;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
extern crate rhododendron;
|
extern crate substrate_consensus_common as consensus;
|
||||||
|
extern crate substrate_consensus_aura as aura;
|
||||||
|
extern crate substrate_finality_grandpa as grandpa;
|
||||||
|
extern crate substrate_transaction_pool as transaction_pool;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
@@ -65,40 +65,40 @@ use std::collections::{HashMap, HashSet};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::{self, Duration, Instant};
|
use std::time::{self, Duration, Instant};
|
||||||
|
|
||||||
|
use aura::ExtraVerification;
|
||||||
|
use client::blockchain::HeaderBackend;
|
||||||
|
use client::block_builder::api::BlockBuilder;
|
||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use extrinsic_store::Store as ExtrinsicStore;
|
use extrinsic_store::Store as ExtrinsicStore;
|
||||||
use polkadot_api::PolkadotApi;
|
use parking_lot::Mutex;
|
||||||
use polkadot_primitives::{AccountId, Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey};
|
use polkadot_primitives::{Hash, Block, BlockId, BlockNumber, Header, Timestamp, SessionKey};
|
||||||
|
use polkadot_primitives::{Compact, UncheckedExtrinsic};
|
||||||
use polkadot_primitives::parachain::{Id as ParaId, Chain, DutyRoster, BlockData, Extrinsic as ParachainExtrinsic, CandidateReceipt, CandidateSignature};
|
use polkadot_primitives::parachain::{Id as ParaId, Chain, DutyRoster, BlockData, Extrinsic as ParachainExtrinsic, CandidateReceipt, CandidateSignature};
|
||||||
|
use polkadot_primitives::parachain::ParachainHost;
|
||||||
use primitives::{AuthorityId, ed25519};
|
use primitives::{AuthorityId, ed25519};
|
||||||
use transaction_pool::TransactionPool;
|
use runtime_primitives::traits::ProvideRuntimeApi;
|
||||||
use tokio::runtime::TaskExecutor;
|
use tokio::runtime::TaskExecutor;
|
||||||
use tokio::timer::{Delay, Interval};
|
use tokio::timer::{Delay, Interval};
|
||||||
|
use transaction_pool::txpool::{Pool, ChainApi as PoolChainApi};
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use futures::future;
|
use futures::future::{self, Either};
|
||||||
use collation::CollationFetch;
|
use collation::CollationFetch;
|
||||||
use dynamic_inclusion::DynamicInclusion;
|
use dynamic_inclusion::DynamicInclusion;
|
||||||
use parking_lot::RwLock;
|
|
||||||
|
|
||||||
pub use self::collation::{validate_collation, Collators};
|
pub use self::collation::{validate_collation, Collators};
|
||||||
pub use self::error::{ErrorKind, Error};
|
pub use self::error::{ErrorKind, Error};
|
||||||
pub use self::offline_tracker::OfflineTracker;
|
|
||||||
pub use self::shared_table::{SharedTable, StatementProducer, ProducedStatements, Statement, SignedStatement, GenericStatement};
|
pub use self::shared_table::{SharedTable, StatementProducer, ProducedStatements, Statement, SignedStatement, GenericStatement};
|
||||||
pub use service::Service;
|
pub use service::Service;
|
||||||
|
|
||||||
mod dynamic_inclusion;
|
mod dynamic_inclusion;
|
||||||
mod evaluation;
|
mod evaluation;
|
||||||
mod error;
|
mod error;
|
||||||
mod offline_tracker;
|
|
||||||
mod service;
|
mod service;
|
||||||
mod shared_table;
|
mod shared_table;
|
||||||
|
|
||||||
pub mod collation;
|
pub mod collation;
|
||||||
|
|
||||||
/// Shared offline validator tracker.
|
|
||||||
pub type SharedOfflineTracker = Arc<RwLock<OfflineTracker>>;
|
|
||||||
|
|
||||||
// block size limit.
|
// block size limit.
|
||||||
const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024;
|
const MAX_TRANSACTIONS_SIZE: usize = 4 * 1024 * 1024;
|
||||||
|
|
||||||
@@ -129,14 +129,14 @@ pub trait Network {
|
|||||||
/// The table router type. This should handle importing of any statements,
|
/// The table router type. This should handle importing of any statements,
|
||||||
/// routing statements to peers, and driving completion of any `StatementProducers`.
|
/// routing statements to peers, and driving completion of any `StatementProducers`.
|
||||||
type TableRouter: TableRouter;
|
type TableRouter: TableRouter;
|
||||||
/// The input stream of BFT messages. Should never logically conclude.
|
|
||||||
type Input: Stream<Item=bft::Communication<Block>,Error=Error>;
|
|
||||||
/// The output sink of BFT messages. Messages sent here should eventually pass to all
|
|
||||||
/// current authorities.
|
|
||||||
type Output: Sink<SinkItem=bft::Communication<Block>,SinkError=Error>;
|
|
||||||
|
|
||||||
/// Instantiate a table router using the given shared table and task executor.
|
/// Instantiate a table router using the given shared table and task executor.
|
||||||
fn communication_for(&self, validators: &[SessionKey], table: Arc<SharedTable>, task_executor: TaskExecutor) -> (Self::TableRouter, Self::Input, Self::Output);
|
fn communication_for(
|
||||||
|
&self,
|
||||||
|
validators: &[SessionKey],
|
||||||
|
table: Arc<SharedTable>,
|
||||||
|
task_executor: TaskExecutor
|
||||||
|
) -> Self::TableRouter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a specific group.
|
/// Information about a specific group.
|
||||||
@@ -157,7 +157,7 @@ pub struct GroupInfo {
|
|||||||
/// parent hash.
|
/// parent hash.
|
||||||
pub fn sign_table_statement(statement: &Statement, key: &ed25519::Pair, parent_hash: &Hash) -> CandidateSignature {
|
pub fn sign_table_statement(statement: &Statement, key: &ed25519::Pair, parent_hash: &Hash) -> CandidateSignature {
|
||||||
let mut encoded = statement.encode();
|
let mut encoded = statement.encode();
|
||||||
encoded.extend(&parent_hash.0);
|
encoded.extend(parent_hash.as_ref());
|
||||||
|
|
||||||
key.sign(&encoded).into()
|
key.sign(&encoded).into()
|
||||||
}
|
}
|
||||||
@@ -167,7 +167,7 @@ pub fn check_statement(statement: &Statement, signature: &CandidateSignature, si
|
|||||||
use runtime_primitives::traits::Verify;
|
use runtime_primitives::traits::Verify;
|
||||||
|
|
||||||
let mut encoded = statement.encode();
|
let mut encoded = statement.encode();
|
||||||
encoded.extend(&parent_hash.0);
|
encoded.extend(parent_hash.as_ref());
|
||||||
|
|
||||||
signature.verify(&encoded[..], &signer.into())
|
signature.verify(&encoded[..], &signer.into())
|
||||||
}
|
}
|
||||||
@@ -229,62 +229,58 @@ fn make_group_info(roster: DutyRoster, authorities: &[AuthorityId], local_id: Au
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Polkadot proposer factory.
|
/// Constructs parachain-agreement instances.
|
||||||
pub struct ProposerFactory<C, N, P>
|
struct ParachainConsensus<C, N, P> {
|
||||||
where
|
|
||||||
P: PolkadotApi + Send + Sync + 'static
|
|
||||||
{
|
|
||||||
/// The client instance.
|
/// The client instance.
|
||||||
pub client: Arc<P>,
|
client: Arc<P>,
|
||||||
/// The transaction pool.
|
|
||||||
pub transaction_pool: Arc<TransactionPool<P>>,
|
|
||||||
/// The backing network handle.
|
/// The backing network handle.
|
||||||
pub network: N,
|
network: N,
|
||||||
/// Parachain collators.
|
/// Parachain collators.
|
||||||
pub collators: C,
|
collators: C,
|
||||||
/// handle to remote task executor
|
/// handle to remote task executor
|
||||||
pub handle: TaskExecutor,
|
handle: TaskExecutor,
|
||||||
/// The duration after which parachain-empty blocks will be allowed.
|
|
||||||
pub parachain_empty_duration: Duration,
|
|
||||||
/// Store for extrinsic data.
|
/// Store for extrinsic data.
|
||||||
pub extrinsic_store: ExtrinsicStore,
|
extrinsic_store: ExtrinsicStore,
|
||||||
/// Offline-tracker.
|
/// The time after which no parachains may be included.
|
||||||
pub offline: SharedOfflineTracker,
|
parachain_empty_duration: Duration,
|
||||||
|
/// Live agreements.
|
||||||
|
live_instances: Mutex<HashMap<Hash, Arc<AttestationTracker>>>,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, N, P> bft::Environment<Block> for ProposerFactory<C, N, P>
|
impl<C, N, P> ParachainConsensus<C, N, P> where
|
||||||
where
|
|
||||||
C: Collators + Send + 'static,
|
C: Collators + Send + 'static,
|
||||||
N: Network,
|
N: Network,
|
||||||
P: PolkadotApi + Send + Sync + 'static,
|
P: ProvideRuntimeApi + HeaderBackend<Block> + Send + Sync + 'static,
|
||||||
|
P::Api: ParachainHost<Block> + BlockBuilder<Block>,
|
||||||
<C::Collation as IntoFuture>::Future: Send + 'static,
|
<C::Collation as IntoFuture>::Future: Send + 'static,
|
||||||
N::TableRouter: Send + 'static,
|
N::TableRouter: Send + 'static,
|
||||||
{
|
{
|
||||||
type Proposer = Proposer<P>;
|
/// Get an attestation table for given parent hash.
|
||||||
type Input = N::Input;
|
///
|
||||||
type Output = N::Output;
|
/// This starts a parachain agreement process for given parent hash if
|
||||||
type Error = Error;
|
/// one has not already started.
|
||||||
|
fn get_or_instantiate(
|
||||||
fn init(
|
|
||||||
&self,
|
&self,
|
||||||
parent_header: &Header,
|
parent_hash: Hash,
|
||||||
authorities: &[AuthorityId],
|
authorities: &[AuthorityId],
|
||||||
sign_with: Arc<ed25519::Pair>,
|
sign_with: Arc<ed25519::Pair>,
|
||||||
) -> Result<(Self::Proposer, Self::Input, Self::Output), Error> {
|
)
|
||||||
|
-> Result<Arc<AttestationTracker>, Error>
|
||||||
|
{
|
||||||
use runtime_primitives::traits::{Hash as HashT, BlakeTwo256};
|
use runtime_primitives::traits::{Hash as HashT, BlakeTwo256};
|
||||||
|
|
||||||
// force delay in evaluation this long.
|
let mut live_instances = self.live_instances.lock();
|
||||||
const FORCE_DELAY: Timestamp = 5;
|
if let Some(tracker) = live_instances.get(&parent_hash) {
|
||||||
|
return Ok(tracker.clone());
|
||||||
let parent_hash = parent_header.hash().into();
|
}
|
||||||
|
|
||||||
let id = BlockId::hash(parent_hash);
|
let id = BlockId::hash(parent_hash);
|
||||||
let duty_roster = self.client.duty_roster(&id)?;
|
let duty_roster = self.client.runtime_api().duty_roster(&id)?;
|
||||||
let random_seed = self.client.random_seed(&id)?;
|
let random_seed = self.client.runtime_api().random_seed(&id)?;
|
||||||
let random_seed = BlakeTwo256::hash(&*random_seed);
|
let _random_seed = BlakeTwo256::hash(random_seed.as_ref());
|
||||||
|
|
||||||
let validators = self.client.validators(&id)?;
|
let _validators = self.client.runtime_api().validators(&id)?;
|
||||||
self.offline.write().note_new_block(&validators[..]);
|
|
||||||
|
|
||||||
let (group_info, local_duty) = make_group_info(
|
let (group_info, local_duty) = make_group_info(
|
||||||
duty_roster,
|
duty_roster,
|
||||||
@@ -292,28 +288,21 @@ impl<C, N, P> bft::Environment<Block> for ProposerFactory<C, N, P>
|
|||||||
sign_with.public().into(),
|
sign_with.public().into(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
info!("Starting consensus session on top of parent {:?}. Local parachain duty is {:?}",
|
info!("Starting parachain attestation session on top of parent {:?}. Local parachain duty is {:?}",
|
||||||
parent_hash, local_duty.validation);
|
parent_hash, local_duty.validation);
|
||||||
|
|
||||||
let active_parachains = self.client.active_parachains(&id)?;
|
let active_parachains = self.client.runtime_api().active_parachains(&id)?;
|
||||||
|
|
||||||
debug!(target: "consensus", "Active parachains: {:?}", active_parachains);
|
debug!(target: "consensus", "Active parachains: {:?}", active_parachains);
|
||||||
|
|
||||||
let n_parachains = active_parachains.len();
|
let _n_parachains = active_parachains.len();
|
||||||
let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash, self.extrinsic_store.clone()));
|
let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash, self.extrinsic_store.clone()));
|
||||||
let (router, input, output) = self.network.communication_for(
|
let router = self.network.communication_for(
|
||||||
authorities,
|
authorities,
|
||||||
table.clone(),
|
table.clone(),
|
||||||
self.handle.clone()
|
self.handle.clone()
|
||||||
);
|
);
|
||||||
|
|
||||||
let now = Instant::now();
|
|
||||||
let dynamic_inclusion = DynamicInclusion::new(
|
|
||||||
n_parachains,
|
|
||||||
now,
|
|
||||||
self.parachain_empty_duration.clone(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let validation_para = match local_duty.validation {
|
let validation_para = match local_duty.validation {
|
||||||
Chain::Relay => None,
|
Chain::Relay => None,
|
||||||
Chain::Parachain(id) => Some(id),
|
Chain::Parachain(id) => Some(id),
|
||||||
@@ -326,6 +315,7 @@ impl<C, N, P> bft::Environment<Block> for ProposerFactory<C, N, P>
|
|||||||
self.collators.clone(),
|
self.collators.clone(),
|
||||||
self.client.clone(),
|
self.client.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let drop_signal = dispatch_collation_work(
|
let drop_signal = dispatch_collation_work(
|
||||||
router.clone(),
|
router.clone(),
|
||||||
&self.handle,
|
&self.handle,
|
||||||
@@ -333,23 +323,96 @@ impl<C, N, P> bft::Environment<Block> for ProposerFactory<C, N, P>
|
|||||||
self.extrinsic_store.clone(),
|
self.extrinsic_store.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let proposer = Proposer {
|
let now = Instant::now();
|
||||||
client: self.client.clone(),
|
let dynamic_inclusion = DynamicInclusion::new(
|
||||||
dynamic_inclusion,
|
table.num_parachains(),
|
||||||
local_key: sign_with,
|
now,
|
||||||
parent_hash,
|
self.parachain_empty_duration.clone(),
|
||||||
parent_id: id,
|
);
|
||||||
parent_number: parent_header.number,
|
|
||||||
random_seed,
|
|
||||||
table,
|
|
||||||
transaction_pool: self.transaction_pool.clone(),
|
|
||||||
offline: self.offline.clone(),
|
|
||||||
validators,
|
|
||||||
minimum_timestamp: current_timestamp() + FORCE_DELAY,
|
|
||||||
_drop_signal: drop_signal,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((proposer, input, output))
|
let tracker = Arc::new(AttestationTracker {
|
||||||
|
table,
|
||||||
|
dynamic_inclusion,
|
||||||
|
_drop_signal: drop_signal
|
||||||
|
});
|
||||||
|
|
||||||
|
live_instances.insert(parent_hash, tracker.clone());
|
||||||
|
|
||||||
|
Ok(tracker)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retain consensus sessions matching predicate.
|
||||||
|
fn retain<F: FnMut(&Hash) -> bool>(&self, mut pred: F) {
|
||||||
|
self.live_instances.lock().retain(|k, _| pred(k))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parachain consensus for a single block.
|
||||||
|
struct AttestationTracker {
|
||||||
|
_drop_signal: exit_future::Signal,
|
||||||
|
table: Arc<SharedTable>,
|
||||||
|
dynamic_inclusion: DynamicInclusion,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Polkadot proposer factory.
|
||||||
|
struct ProposerFactory<C, N, P, TxApi: PoolChainApi> {
|
||||||
|
parachain_consensus: Arc<ParachainConsensus<C, N, P>>,
|
||||||
|
transaction_pool: Arc<Pool<TxApi>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, N, P, TxApi> ProposerFactory<C, N, P, TxApi> where
|
||||||
|
TxApi: PoolChainApi,
|
||||||
|
{
|
||||||
|
/// Create a new proposer factory.
|
||||||
|
fn new(
|
||||||
|
parachain_consensus: Arc<ParachainConsensus<C, N, P>>,
|
||||||
|
transaction_pool: Arc<Pool<TxApi>>,
|
||||||
|
) -> Self {
|
||||||
|
ProposerFactory {
|
||||||
|
parachain_consensus,
|
||||||
|
transaction_pool,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, N, P, TxApi> consensus::Environment<Block> for ProposerFactory<C, N, P, TxApi> where
|
||||||
|
C: Collators + Send + 'static,
|
||||||
|
N: Network,
|
||||||
|
TxApi: PoolChainApi<Block=Block>,
|
||||||
|
P: ProvideRuntimeApi + HeaderBackend<Block> + Send + Sync + 'static,
|
||||||
|
P::Api: ParachainHost<Block> + BlockBuilder<Block>,
|
||||||
|
<C::Collation as IntoFuture>::Future: Send + 'static,
|
||||||
|
N::TableRouter: Send + 'static,
|
||||||
|
{
|
||||||
|
type Proposer = Proposer<P, TxApi>;
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn init(
|
||||||
|
&self,
|
||||||
|
parent_header: &Header,
|
||||||
|
authorities: &[AuthorityId],
|
||||||
|
sign_with: Arc<ed25519::Pair>,
|
||||||
|
) -> Result<Self::Proposer, Error> {
|
||||||
|
// force delay in evaluation this long.
|
||||||
|
const FORCE_DELAY: Timestamp = Compact(5);
|
||||||
|
|
||||||
|
let parent_hash = parent_header.hash();
|
||||||
|
let parent_id = BlockId::hash(parent_hash);
|
||||||
|
let tracker = self.parachain_consensus.get_or_instantiate(
|
||||||
|
parent_hash,
|
||||||
|
authorities,
|
||||||
|
sign_with,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Proposer {
|
||||||
|
client: self.parachain_consensus.client.clone(),
|
||||||
|
tracker,
|
||||||
|
parent_hash,
|
||||||
|
parent_id,
|
||||||
|
parent_number: parent_header.number,
|
||||||
|
transaction_pool: self.transaction_pool.clone(),
|
||||||
|
minimum_timestamp: current_timestamp().0 + FORCE_DELAY.0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +425,8 @@ fn dispatch_collation_work<R, C, P>(
|
|||||||
extrinsic_store: ExtrinsicStore,
|
extrinsic_store: ExtrinsicStore,
|
||||||
) -> exit_future::Signal where
|
) -> exit_future::Signal where
|
||||||
C: Collators + Send + 'static,
|
C: Collators + Send + 'static,
|
||||||
P: PolkadotApi + Send + Sync + 'static,
|
P: ProvideRuntimeApi + HeaderBackend<Block> + Send + Sync + 'static,
|
||||||
|
P::Api: ParachainHost<Block>,
|
||||||
<C::Collation as IntoFuture>::Future: Send + 'static,
|
<C::Collation as IntoFuture>::Future: Send + 'static,
|
||||||
R: TableRouter + Send + 'static,
|
R: TableRouter + Send + 'static,
|
||||||
{
|
{
|
||||||
@@ -413,50 +477,35 @@ struct LocalDuty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The Polkadot proposer logic.
|
/// The Polkadot proposer logic.
|
||||||
pub struct Proposer<C: PolkadotApi + Send + Sync> {
|
pub struct Proposer<C: Send + Sync, TxApi: PoolChainApi> where
|
||||||
|
C: ProvideRuntimeApi + HeaderBackend<Block>,
|
||||||
|
{
|
||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
dynamic_inclusion: DynamicInclusion,
|
|
||||||
local_key: Arc<ed25519::Pair>,
|
|
||||||
parent_hash: Hash,
|
parent_hash: Hash,
|
||||||
parent_id: BlockId,
|
parent_id: BlockId,
|
||||||
parent_number: BlockNumber,
|
parent_number: BlockNumber,
|
||||||
random_seed: Hash,
|
tracker: Arc<AttestationTracker>,
|
||||||
table: Arc<SharedTable>,
|
transaction_pool: Arc<Pool<TxApi>>,
|
||||||
transaction_pool: Arc<TransactionPool<C>>,
|
|
||||||
offline: SharedOfflineTracker,
|
|
||||||
validators: Vec<AccountId>,
|
|
||||||
minimum_timestamp: u64,
|
minimum_timestamp: u64,
|
||||||
_drop_signal: exit_future::Signal,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: PolkadotApi + Send + Sync> Proposer<C> {
|
impl<C, TxApi> consensus::Proposer<Block> for Proposer<C, TxApi> where
|
||||||
fn primary_index(&self, round_number: usize, len: usize) -> usize {
|
TxApi: PoolChainApi<Block=Block>,
|
||||||
use primitives::uint::U256;
|
C: ProvideRuntimeApi + HeaderBackend<Block> + Send + Sync,
|
||||||
|
C::Api: ParachainHost<Block> + BlockBuilder<Block>,
|
||||||
let big_len = U256::from(len);
|
|
||||||
let offset = U256::from_big_endian(&self.random_seed.0) % big_len;
|
|
||||||
let offset = offset.low_u64() as usize + round_number;
|
|
||||||
offset % len
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<C> bft::Proposer<Block> for Proposer<C>
|
|
||||||
where
|
|
||||||
C: PolkadotApi + Send + Sync,
|
|
||||||
{
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
type Create = future::Either<
|
type Create = Either<
|
||||||
CreateProposal<C>,
|
CreateProposal<C, TxApi>,
|
||||||
future::FutureResult<Block, Error>,
|
future::FutureResult<Block, Error>,
|
||||||
>;
|
>;
|
||||||
type Evaluate = Box<Future<Item=bool, Error=Error>>;
|
|
||||||
|
|
||||||
fn propose(&self) -> Self::Create {
|
fn propose(&self) -> Self::Create {
|
||||||
const ATTEMPT_PROPOSE_EVERY: Duration = Duration::from_millis(100);
|
const ATTEMPT_PROPOSE_EVERY: Duration = Duration::from_millis(100);
|
||||||
|
|
||||||
let initial_included = self.table.includable_count();
|
let initial_included = self.tracker.table.includable_count();
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let enough_candidates = self.dynamic_inclusion.acceptable_in(
|
let enough_candidates = self.tracker.dynamic_inclusion.acceptable_in(
|
||||||
now,
|
now,
|
||||||
initial_included,
|
initial_included,
|
||||||
).unwrap_or_else(|| now + Duration::from_millis(1));
|
).unwrap_or_else(|| now + Duration::from_millis(1));
|
||||||
@@ -464,216 +513,69 @@ impl<C> bft::Proposer<Block> for Proposer<C>
|
|||||||
let timing = ProposalTiming {
|
let timing = ProposalTiming {
|
||||||
attempt_propose: Interval::new(now + ATTEMPT_PROPOSE_EVERY, ATTEMPT_PROPOSE_EVERY),
|
attempt_propose: Interval::new(now + ATTEMPT_PROPOSE_EVERY, ATTEMPT_PROPOSE_EVERY),
|
||||||
enough_candidates: Delay::new(enough_candidates),
|
enough_candidates: Delay::new(enough_candidates),
|
||||||
dynamic_inclusion: self.dynamic_inclusion.clone(),
|
dynamic_inclusion: self.tracker.dynamic_inclusion.clone(),
|
||||||
last_included: initial_included,
|
last_included: initial_included,
|
||||||
};
|
};
|
||||||
|
|
||||||
future::Either::A(CreateProposal {
|
Either::A(CreateProposal {
|
||||||
parent_hash: self.parent_hash.clone(),
|
parent_hash: self.parent_hash.clone(),
|
||||||
parent_number: self.parent_number.clone(),
|
parent_number: self.parent_number.clone(),
|
||||||
parent_id: self.parent_id.clone(),
|
parent_id: self.parent_id.clone(),
|
||||||
client: self.client.clone(),
|
client: self.client.clone(),
|
||||||
transaction_pool: self.transaction_pool.clone(),
|
transaction_pool: self.transaction_pool.clone(),
|
||||||
table: self.table.clone(),
|
table: self.tracker.table.clone(),
|
||||||
offline: self.offline.clone(),
|
minimum_timestamp: self.minimum_timestamp.into(),
|
||||||
validators: self.validators.clone(),
|
|
||||||
minimum_timestamp: self.minimum_timestamp,
|
|
||||||
timing,
|
timing,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn evaluate(&self, unchecked_proposal: &Block) -> Self::Evaluate {
|
/// Does verification before importing blocks.
|
||||||
debug!(target: "bft", "evaluating block on top of parent ({}, {:?})", self.parent_number, self.parent_hash);
|
/// Should be used for further verification in aura.
|
||||||
|
pub struct BlockVerifier;
|
||||||
|
|
||||||
let active_parachains = match self.client.active_parachains(&self.parent_id) {
|
impl ExtraVerification<Block> for BlockVerifier {
|
||||||
Ok(x) => x,
|
type Verified = Either<
|
||||||
Err(e) => return Box::new(future::err(e.into())) as Box<_>,
|
future::FutureResult<(), String>,
|
||||||
|
Box<dyn Future<Item=(), Error=String>>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
fn verify(&self, _header: &Header, body: Option<&[UncheckedExtrinsic]>) -> Self::Verified {
|
||||||
|
use polkadot_runtime::{Call, UncheckedExtrinsic, TimestampCall};
|
||||||
|
|
||||||
|
let body = match body {
|
||||||
|
None => return Either::A(future::ok(())),
|
||||||
|
Some(body) => body,
|
||||||
};
|
};
|
||||||
|
|
||||||
let current_timestamp = current_timestamp();
|
// TODO: reintroduce or revisit necessaity for includability tracker.
|
||||||
|
let timestamp = current_timestamp();
|
||||||
|
|
||||||
// do initial serialization and structural integrity checks.
|
let maybe_in_block = body.iter()
|
||||||
let maybe_proposal = evaluation::evaluate_initial(
|
.filter_map(|ex| {
|
||||||
unchecked_proposal,
|
let encoded = ex.encode();
|
||||||
current_timestamp,
|
let runtime_ex = UncheckedExtrinsic::decode(&mut &encoded[..])?;
|
||||||
&self.parent_hash,
|
match runtime_ex.function {
|
||||||
self.parent_number,
|
Call::Timestamp(TimestampCall::set(t)) => Some(t),
|
||||||
&active_parachains,
|
_ => None,
|
||||||
);
|
|
||||||
|
|
||||||
let proposal = match maybe_proposal {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
// TODO: these errors are easily re-checked in runtime.
|
|
||||||
debug!(target: "bft", "Invalid proposal: {:?}", e);
|
|
||||||
return Box::new(future::ok(false));
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.next();
|
||||||
|
|
||||||
|
let timestamp_in_block = match maybe_in_block {
|
||||||
|
None => return Either::A(future::ok(())),
|
||||||
|
Some(t) => t,
|
||||||
};
|
};
|
||||||
|
|
||||||
let vote_delays = {
|
// we wait until the block timestamp is earlier than current.
|
||||||
let now = Instant::now();
|
if timestamp.0 < timestamp_in_block.0 {
|
||||||
|
let diff_secs = timestamp_in_block.0 - timestamp.0;
|
||||||
let included_candidate_hashes = proposal
|
let delay = Delay::new(Instant::now() + Duration::from_secs(diff_secs))
|
||||||
.parachain_heads()
|
.map_err(move |e| format!("Error waiting for {} seconds: {:?}", diff_secs, e));
|
||||||
.iter()
|
Either::B(Box::new(delay))
|
||||||
.map(|candidate| candidate.hash());
|
|
||||||
|
|
||||||
// delay casting vote until we have proof that all candidates are
|
|
||||||
// includable.
|
|
||||||
let includability_tracker = self.table.track_includability(included_candidate_hashes)
|
|
||||||
.map_err(|_| ErrorKind::PrematureDestruction.into());
|
|
||||||
|
|
||||||
// the duration at which the given number of parachains is acceptable.
|
|
||||||
let count_delay = self.dynamic_inclusion.acceptable_in(
|
|
||||||
now,
|
|
||||||
proposal.parachain_heads().len(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// the duration until the given timestamp is current
|
|
||||||
let proposed_timestamp = ::std::cmp::max(self.minimum_timestamp, proposal.timestamp());
|
|
||||||
let timestamp_delay = if proposed_timestamp > current_timestamp {
|
|
||||||
let delay_s = proposed_timestamp - current_timestamp;
|
|
||||||
debug!(target: "bft", "Delaying evaluation of proposal for {} seconds", delay_s);
|
|
||||||
Some(now + Duration::from_secs(delay_s))
|
|
||||||
} else {
|
} else {
|
||||||
None
|
Either::A(future::ok(()))
|
||||||
};
|
|
||||||
|
|
||||||
// delay casting vote until able according to minimum block time,
|
|
||||||
// timestamp delay, and count delay.
|
|
||||||
// construct a future from the maximum of the two durations.
|
|
||||||
let max_delay = ::std::cmp::max(timestamp_delay, count_delay);
|
|
||||||
|
|
||||||
let temporary_delay = match max_delay {
|
|
||||||
Some(duration) => future::Either::A(
|
|
||||||
Delay::new(duration).map_err(|e| Error::from(ErrorKind::Timer(e)))
|
|
||||||
),
|
|
||||||
None => future::Either::B(future::ok(())),
|
|
||||||
};
|
|
||||||
|
|
||||||
includability_tracker.join(temporary_delay)
|
|
||||||
};
|
|
||||||
|
|
||||||
// refuse to vote if this block says a validator is offline that we
|
|
||||||
// think isn't.
|
|
||||||
let offline = proposal.noted_offline();
|
|
||||||
if !self.offline.read().check_consistency(&self.validators[..], offline) {
|
|
||||||
return Box::new(futures::empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// evaluate whether the block is actually valid.
|
|
||||||
// TODO: is it better to delay this until the delays are finished?
|
|
||||||
let evaluated = self.client
|
|
||||||
.evaluate_block(&self.parent_id, unchecked_proposal.clone())
|
|
||||||
.map_err(Into::into);
|
|
||||||
|
|
||||||
let future = future::result(evaluated).and_then(move |good| {
|
|
||||||
let end_result = future::ok(good);
|
|
||||||
if good {
|
|
||||||
// delay a "good" vote.
|
|
||||||
future::Either::A(vote_delays.and_then(|_| end_result))
|
|
||||||
} else {
|
|
||||||
// don't delay a "bad" evaluation.
|
|
||||||
future::Either::B(end_result)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Box::new(future) as Box<_>
|
|
||||||
}
|
|
||||||
|
|
||||||
fn round_proposer(&self, round_number: usize, authorities: &[AuthorityId]) -> AuthorityId {
|
|
||||||
let offset = self.primary_index(round_number, authorities.len());
|
|
||||||
let proposer = authorities[offset].clone();
|
|
||||||
trace!(target: "bft", "proposer for round {} is {}", round_number, proposer);
|
|
||||||
|
|
||||||
proposer
|
|
||||||
}
|
|
||||||
|
|
||||||
fn import_misbehavior(&self, misbehavior: Vec<(AuthorityId, bft::Misbehavior<Hash>)>) {
|
|
||||||
use rhododendron::Misbehavior as GenericMisbehavior;
|
|
||||||
use runtime_primitives::bft::{MisbehaviorKind, MisbehaviorReport};
|
|
||||||
use polkadot_runtime::{Call, UncheckedExtrinsic, ConsensusCall, RawAddress};
|
|
||||||
|
|
||||||
let local_id = self.local_key.public().0.into();
|
|
||||||
let mut next_index = {
|
|
||||||
let cur_index = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending| pending
|
|
||||||
.filter(|tx| tx.verified.sender().map(|s| s == local_id).unwrap_or(false))
|
|
||||||
.last()
|
|
||||||
.map(|tx| Ok(tx.verified.index()))
|
|
||||||
.unwrap_or_else(|| self.client.index(&self.parent_id, local_id))
|
|
||||||
);
|
|
||||||
|
|
||||||
match cur_index {
|
|
||||||
Ok(Ok(cur_index)) => cur_index + 1,
|
|
||||||
Ok(Err(e)) => {
|
|
||||||
warn!(target: "consensus", "Error computing next transaction index: {}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!(target: "consensus", "Error computing next transaction index: {}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for (target, misbehavior) in misbehavior {
|
|
||||||
let report = MisbehaviorReport {
|
|
||||||
parent_hash: self.parent_hash,
|
|
||||||
parent_number: self.parent_number,
|
|
||||||
target,
|
|
||||||
misbehavior: match misbehavior {
|
|
||||||
GenericMisbehavior::ProposeOutOfTurn(_, _, _) => continue,
|
|
||||||
GenericMisbehavior::DoublePropose(_, _, _) => continue,
|
|
||||||
GenericMisbehavior::DoublePrepare(round, (h1, s1), (h2, s2))
|
|
||||||
=> MisbehaviorKind::BftDoublePrepare(round as u32, (h1, s1.signature), (h2, s2.signature)),
|
|
||||||
GenericMisbehavior::DoubleCommit(round, (h1, s1), (h2, s2))
|
|
||||||
=> MisbehaviorKind::BftDoubleCommit(round as u32, (h1, s1.signature), (h2, s2.signature)),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let payload = (next_index, Call::Consensus(ConsensusCall::report_misbehavior(report)));
|
|
||||||
let signature = self.local_key.sign(&payload.encode()).into();
|
|
||||||
next_index += 1;
|
|
||||||
|
|
||||||
let local_id = self.local_key.public().0.into();
|
|
||||||
let extrinsic = UncheckedExtrinsic {
|
|
||||||
signature: Some((RawAddress::Id(local_id), signature)),
|
|
||||||
index: payload.0,
|
|
||||||
function: payload.1,
|
|
||||||
};
|
|
||||||
let uxt: Vec<u8> = Decode::decode(&mut extrinsic.encode().as_slice()).expect("Encoded extrinsic is valid");
|
|
||||||
self.transaction_pool.submit_one(&BlockId::hash(self.parent_hash), polkadot_primitives::UncheckedExtrinsic(uxt))
|
|
||||||
.expect("locally signed extrinsic is valid; qed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_round_end(&self, round_number: usize, was_proposed: bool) {
|
|
||||||
let primary_validator = self.validators[
|
|
||||||
self.primary_index(round_number, self.validators.len())
|
|
||||||
];
|
|
||||||
|
|
||||||
|
|
||||||
// alter the message based on whether we think the empty proposer was forced to skip the round.
|
|
||||||
// this is determined by checking if our local validator would have been forced to skip the round.
|
|
||||||
let consider_online = was_proposed || {
|
|
||||||
let forced_delay = self.dynamic_inclusion.acceptable_in(Instant::now(), self.table.includable_count());
|
|
||||||
let public = ed25519::Public::from_raw(primary_validator.0);
|
|
||||||
match forced_delay {
|
|
||||||
None => info!(
|
|
||||||
"Potential Offline Validator: {} failed to propose during assigned slot: {}",
|
|
||||||
public,
|
|
||||||
round_number,
|
|
||||||
),
|
|
||||||
Some(_) => info!(
|
|
||||||
"Potential Offline Validator {} potentially forced to skip assigned slot: {}",
|
|
||||||
public,
|
|
||||||
round_number,
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
forced_delay.is_some()
|
|
||||||
};
|
|
||||||
|
|
||||||
self.offline.write().note_round_end(primary_validator, consider_online);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -681,6 +583,7 @@ fn current_timestamp() -> Timestamp {
|
|||||||
time::SystemTime::now().duration_since(time::UNIX_EPOCH)
|
time::SystemTime::now().duration_since(time::UNIX_EPOCH)
|
||||||
.expect("now always later than unix epoch; qed")
|
.expect("now always later than unix epoch; qed")
|
||||||
.as_secs()
|
.as_secs()
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProposalTiming {
|
struct ProposalTiming {
|
||||||
@@ -721,75 +624,62 @@ impl ProposalTiming {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Future which resolves upon the creation of a proposal.
|
/// Future which resolves upon the creation of a proposal.
|
||||||
pub struct CreateProposal<C: PolkadotApi + Send + Sync> {
|
pub struct CreateProposal<C: Send + Sync, TxApi: PoolChainApi> {
|
||||||
parent_hash: Hash,
|
parent_hash: Hash,
|
||||||
parent_number: BlockNumber,
|
parent_number: BlockNumber,
|
||||||
parent_id: BlockId,
|
parent_id: BlockId,
|
||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
transaction_pool: Arc<TransactionPool<C>>,
|
transaction_pool: Arc<Pool<TxApi>>,
|
||||||
table: Arc<SharedTable>,
|
table: Arc<SharedTable>,
|
||||||
timing: ProposalTiming,
|
timing: ProposalTiming,
|
||||||
validators: Vec<AccountId>,
|
|
||||||
offline: SharedOfflineTracker,
|
|
||||||
minimum_timestamp: Timestamp,
|
minimum_timestamp: Timestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> CreateProposal<C> where C: PolkadotApi + Send + Sync {
|
impl<C, TxApi> CreateProposal<C, TxApi> where
|
||||||
|
TxApi: PoolChainApi<Block=Block>,
|
||||||
|
C: ProvideRuntimeApi + HeaderBackend<Block> + Send + Sync,
|
||||||
|
C::Api: ParachainHost<Block> + BlockBuilder<Block>,
|
||||||
|
{
|
||||||
fn propose_with(&self, candidates: Vec<CandidateReceipt>) -> Result<Block, Error> {
|
fn propose_with(&self, candidates: Vec<CandidateReceipt>) -> Result<Block, Error> {
|
||||||
use polkadot_api::BlockBuilder;
|
use client::block_builder::BlockBuilder;
|
||||||
use runtime_primitives::traits::{Hash as HashT, BlakeTwo256};
|
use runtime_primitives::traits::{Hash as HashT, BlakeTwo256};
|
||||||
use polkadot_primitives::InherentData;
|
use polkadot_primitives::InherentData;
|
||||||
|
|
||||||
const MAX_VOTE_OFFLINE_SECONDS: Duration = Duration::from_secs(60);
|
|
||||||
|
|
||||||
// TODO: handle case when current timestamp behind that in state.
|
// TODO: handle case when current timestamp behind that in state.
|
||||||
let timestamp = ::std::cmp::max(self.minimum_timestamp, current_timestamp());
|
let timestamp = ::std::cmp::max(self.minimum_timestamp.0, current_timestamp().0).into();
|
||||||
|
|
||||||
let elapsed_since_start = self.timing.dynamic_inclusion.started_at().elapsed();
|
let _elapsed_since_start = self.timing.dynamic_inclusion.started_at().elapsed();
|
||||||
let offline_indices = if elapsed_since_start > MAX_VOTE_OFFLINE_SECONDS {
|
|
||||||
Vec::new()
|
|
||||||
} else {
|
|
||||||
self.offline.read().reports(&self.validators[..])
|
|
||||||
};
|
|
||||||
|
|
||||||
if !offline_indices.is_empty() {
|
let _inherent_data = InherentData {
|
||||||
info!(
|
|
||||||
"Submitting offline validators {:?} for slash-vote",
|
|
||||||
offline_indices.iter().map(|&i| self.validators[i as usize]).collect::<Vec<_>>(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
let inherent_data = InherentData {
|
|
||||||
timestamp,
|
timestamp,
|
||||||
parachain_heads: candidates,
|
parachain_heads: candidates,
|
||||||
offline_indices,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut block_builder = self.client.build_block(&self.parent_id, inherent_data)?;
|
let mut block_builder = BlockBuilder::at_block(&self.parent_id, &*self.client)?;
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut unqueue_invalid = Vec::new();
|
let mut unqueue_invalid = Vec::new();
|
||||||
let result = self.transaction_pool.cull_and_get_pending(&BlockId::hash(self.parent_hash), |pending_iterator| {
|
|
||||||
let mut pending_size = 0;
|
let mut pending_size = 0;
|
||||||
for pending in pending_iterator {
|
|
||||||
if pending_size + pending.verified.encoded_size() >= MAX_TRANSACTIONS_SIZE { break }
|
|
||||||
|
|
||||||
match block_builder.push_extrinsic(pending.original.clone()) {
|
let ready_iter = self.transaction_pool.ready();
|
||||||
|
for ready in ready_iter {
|
||||||
|
let encoded_size = ready.data.encode().len();
|
||||||
|
if pending_size + encoded_size >= MAX_TRANSACTIONS_SIZE {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
match block_builder.push(ready.data.clone()) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
pending_size += pending.verified.encoded_size();
|
pending_size += encoded_size;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
trace!(target: "transaction-pool", "Invalid transaction: {}", e);
|
trace!(target: "transaction-pool", "Invalid transaction: {}", e);
|
||||||
unqueue_invalid.push(pending.verified.hash().clone());
|
unqueue_invalid.push(ready.hash.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
if let Err(e) = result {
|
|
||||||
warn!("Unable to get the pending set: {:?}", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.transaction_pool.remove(&unqueue_invalid, false);
|
self.transaction_pool.remove_invalid(&unqueue_invalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
let polkadot_block = block_builder.bake()?;
|
let polkadot_block = block_builder.bake()?;
|
||||||
@@ -808,7 +698,7 @@ impl<C> CreateProposal<C> where C: PolkadotApi + Send + Sync {
|
|||||||
.expect("polkadot blocks defined to serialize to substrate blocks correctly; qed");
|
.expect("polkadot blocks defined to serialize to substrate blocks correctly; qed");
|
||||||
|
|
||||||
// TODO: full re-evaluation
|
// TODO: full re-evaluation
|
||||||
let active_parachains = self.client.active_parachains(&self.parent_id)?;
|
let active_parachains = self.client.runtime_api().active_parachains(&self.parent_id)?;
|
||||||
assert!(evaluation::evaluate_initial(
|
assert!(evaluation::evaluate_initial(
|
||||||
&substrate_block,
|
&substrate_block,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -821,7 +711,11 @@ impl<C> CreateProposal<C> where C: PolkadotApi + Send + Sync {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> Future for CreateProposal<C> where C: PolkadotApi + Send + Sync {
|
impl<C, TxApi> Future for CreateProposal<C, TxApi> where
|
||||||
|
TxApi: PoolChainApi<Block=Block>,
|
||||||
|
C: ProvideRuntimeApi + HeaderBackend<Block> + Send + Sync,
|
||||||
|
C::Api: ParachainHost<Block> + BlockBuilder<Block>,
|
||||||
|
{
|
||||||
type Item = Block;
|
type Item = Block;
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
|
|||||||
@@ -1,167 +0,0 @@
|
|||||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Polkadot.
|
|
||||||
|
|
||||||
// Polkadot 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.
|
|
||||||
|
|
||||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Tracks offline validators.
|
|
||||||
|
|
||||||
use polkadot_primitives::AccountId;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::time::{Instant, Duration};
|
|
||||||
|
|
||||||
struct Observed {
|
|
||||||
last_round_end: Instant,
|
|
||||||
offline_since: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, PartialEq)]
|
|
||||||
enum Activity {
|
|
||||||
Offline,
|
|
||||||
StillOffline(Duration),
|
|
||||||
Online,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Observed {
|
|
||||||
fn new() -> Observed {
|
|
||||||
let now = Instant::now();
|
|
||||||
Observed {
|
|
||||||
last_round_end: now,
|
|
||||||
offline_since: now,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn note_round_end(&mut self, now: Instant, was_online: Option<bool>) {
|
|
||||||
self.last_round_end = now;
|
|
||||||
if let Some(false) = was_online {
|
|
||||||
self.offline_since = now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns what we have observed about the online/offline state of the validator.
|
|
||||||
fn activity(&self) -> Activity {
|
|
||||||
// can happen if clocks are not monotonic
|
|
||||||
if self.offline_since > self.last_round_end { return Activity::Online }
|
|
||||||
if self.offline_since == self.last_round_end { return Activity::Offline }
|
|
||||||
Activity::StillOffline(self.last_round_end.duration_since(self.offline_since))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tracks offline validators and can issue a report for those offline.
|
|
||||||
pub struct OfflineTracker {
|
|
||||||
observed: HashMap<AccountId, Observed>,
|
|
||||||
block_instant: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OfflineTracker {
|
|
||||||
/// Create a new tracker.
|
|
||||||
pub fn new() -> Self {
|
|
||||||
OfflineTracker { observed: HashMap::new(), block_instant: Instant::now() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Note new consensus is starting with the given set of validators.
|
|
||||||
pub fn note_new_block(&mut self, validators: &[AccountId]) {
|
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
let set: HashSet<_> = validators.iter().cloned().collect();
|
|
||||||
self.observed.retain(|k, _| set.contains(k));
|
|
||||||
|
|
||||||
self.block_instant = Instant::now();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Note that a round has ended.
|
|
||||||
pub fn note_round_end(&mut self, validator: AccountId, was_online: bool) {
|
|
||||||
self.observed.entry(validator).or_insert_with(Observed::new);
|
|
||||||
for (val, obs) in self.observed.iter_mut() {
|
|
||||||
obs.note_round_end(
|
|
||||||
self.block_instant,
|
|
||||||
if val == &validator {
|
|
||||||
Some(was_online)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a vector of indices for offline account IDs.
|
|
||||||
pub fn reports(&self, validators: &[AccountId]) -> Vec<u32> {
|
|
||||||
validators.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, v)| if self.is_known_offline_now(v) {
|
|
||||||
Some(i as u32)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether reports on a validator set are consistent with our view of things.
|
|
||||||
pub fn check_consistency(&self, validators: &[AccountId], reports: &[u32]) -> bool {
|
|
||||||
reports.iter().cloned().all(|r| {
|
|
||||||
let v = match validators.get(r as usize) {
|
|
||||||
Some(v) => v,
|
|
||||||
None => return false,
|
|
||||||
};
|
|
||||||
|
|
||||||
// we must think all validators reported externally are offline.
|
|
||||||
self.is_known_offline_now(v)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Rwturns true only if we have seen the validator miss the last round. For further
|
|
||||||
/// rounds where we can't say for sure that they're still offline, we give them the
|
|
||||||
/// benefit of the doubt.
|
|
||||||
fn is_known_offline_now(&self, v: &AccountId) -> bool {
|
|
||||||
self.observed.get(v).map(|o| o.activity() == Activity::Offline).unwrap_or(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn validator_offline() {
|
|
||||||
let mut tracker = OfflineTracker::new();
|
|
||||||
let v = [0; 32].into();
|
|
||||||
let v2 = [1; 32].into();
|
|
||||||
let v3 = [2; 32].into();
|
|
||||||
tracker.note_new_block(&[v, v2, v3]);
|
|
||||||
tracker.note_round_end(v, true);
|
|
||||||
tracker.note_round_end(v2, true);
|
|
||||||
tracker.note_round_end(v3, true);
|
|
||||||
assert_eq!(tracker.reports(&[v, v2, v3]), vec![0u32; 0]);
|
|
||||||
|
|
||||||
tracker.note_new_block(&[v, v2, v3]);
|
|
||||||
tracker.note_round_end(v, true);
|
|
||||||
tracker.note_round_end(v2, false);
|
|
||||||
tracker.note_round_end(v3, true);
|
|
||||||
assert_eq!(tracker.reports(&[v, v2, v3]), vec![1]);
|
|
||||||
|
|
||||||
tracker.note_new_block(&[v, v2, v3]);
|
|
||||||
tracker.note_round_end(v, false);
|
|
||||||
assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]);
|
|
||||||
|
|
||||||
tracker.note_new_block(&[v, v2, v3]);
|
|
||||||
tracker.note_round_end(v, false);
|
|
||||||
tracker.note_round_end(v2, true);
|
|
||||||
tracker.note_round_end(v3, false);
|
|
||||||
assert_eq!(tracker.reports(&[v, v2, v3]), vec![0, 2]);
|
|
||||||
|
|
||||||
tracker.note_new_block(&[v, v2]);
|
|
||||||
tracker.note_round_end(v, false);
|
|
||||||
assert_eq!(tracker.reports(&[v, v2, v3]), vec![0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,47 +27,23 @@ use std::thread;
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use bft::{self, BftService};
|
|
||||||
use client::{BlockchainEvents, ChainHead, BlockBody};
|
use client::{BlockchainEvents, ChainHead, BlockBody};
|
||||||
|
use client::block_builder::api::BlockBuilder;
|
||||||
|
use client::blockchain::HeaderBackend;
|
||||||
|
use client::runtime_api::Core;
|
||||||
use primitives::ed25519;
|
use primitives::ed25519;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use polkadot_api::LocalPolkadotApi;
|
use polkadot_primitives::{Block, BlockId};
|
||||||
use polkadot_primitives::{Block, Header};
|
use polkadot_primitives::parachain::ParachainHost;
|
||||||
use transaction_pool::TransactionPool;
|
|
||||||
use extrinsic_store::Store as ExtrinsicStore;
|
use extrinsic_store::Store as ExtrinsicStore;
|
||||||
|
use runtime_primitives::traits::ProvideRuntimeApi;
|
||||||
|
use transaction_pool::txpool::{ChainApi as PoolChainApi, Pool};
|
||||||
|
|
||||||
use tokio::executor::current_thread::TaskExecutor as LocalThreadHandle;
|
|
||||||
use tokio::runtime::TaskExecutor as ThreadPoolHandle;
|
use tokio::runtime::TaskExecutor as ThreadPoolHandle;
|
||||||
use tokio::runtime::current_thread::Runtime as LocalRuntime;
|
use tokio::runtime::current_thread::Runtime as LocalRuntime;
|
||||||
use tokio::timer::Interval;
|
use tokio::timer::Interval;
|
||||||
|
|
||||||
use super::{Network, Collators, ProposerFactory};
|
use super::{Network, Collators, ProposerFactory};
|
||||||
use error;
|
|
||||||
|
|
||||||
const TIMER_DELAY_MS: u64 = 5000;
|
|
||||||
const TIMER_INTERVAL_MS: u64 = 500;
|
|
||||||
|
|
||||||
// spin up an instance of BFT agreement on the current thread's executor.
|
|
||||||
// panics if there is no current thread executor.
|
|
||||||
fn start_bft<F, C>(
|
|
||||||
header: Header,
|
|
||||||
bft_service: Arc<BftService<Block, F, C>>,
|
|
||||||
) where
|
|
||||||
F: bft::Environment<Block> + 'static,
|
|
||||||
C: bft::BlockImport<Block> + bft::Authorities<Block> + 'static,
|
|
||||||
F::Error: ::std::fmt::Debug,
|
|
||||||
<F::Proposer as bft::Proposer<Block>>::Error: ::std::fmt::Display + Into<error::Error>,
|
|
||||||
<F as bft::Environment<Block>>::Error: ::std::fmt::Display
|
|
||||||
{
|
|
||||||
let mut handle = LocalThreadHandle::current();
|
|
||||||
match bft_service.build_upon(&header) {
|
|
||||||
Ok(Some(bft_work)) => if let Err(e) = handle.spawn_local(Box::new(bft_work)) {
|
|
||||||
warn!(target: "bft", "Couldn't initialize BFT agreement: {:?}", e);
|
|
||||||
}
|
|
||||||
Ok(None) => trace!(target: "bft", "Could not start agreement on top of {}", header.hash()),
|
|
||||||
Err(e) => warn!(target: "bft", "BFT agreement error: {}", e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// creates a task to prune redundant entries in availability store upon block finalization
|
// creates a task to prune redundant entries in availability store upon block finalization
|
||||||
//
|
//
|
||||||
@@ -79,13 +55,11 @@ fn prune_unneeded_availability<C>(client: Arc<C>, extrinsic_store: ExtrinsicStor
|
|||||||
{
|
{
|
||||||
use codec::{Encode, Decode};
|
use codec::{Encode, Decode};
|
||||||
use polkadot_primitives::BlockId;
|
use polkadot_primitives::BlockId;
|
||||||
use polkadot_runtime::CheckedBlock;
|
|
||||||
|
|
||||||
enum NotifyError {
|
enum NotifyError {
|
||||||
NoBody,
|
NoBody,
|
||||||
BodyFetch(::client::error::Error),
|
BodyFetch(::client::error::Error),
|
||||||
UnexpectedFormat,
|
UnexpectedFormat,
|
||||||
ExtrinsicsWrong,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NotifyError {
|
impl NotifyError {
|
||||||
@@ -94,32 +68,44 @@ fn prune_unneeded_availability<C>(client: Arc<C>, extrinsic_store: ExtrinsicStor
|
|||||||
NotifyError::NoBody => warn!("No block body for imported block {:?}", hash),
|
NotifyError::NoBody => warn!("No block body for imported block {:?}", hash),
|
||||||
NotifyError::BodyFetch(ref err) => warn!("Failed to fetch block body for imported block {:?}: {:?}", hash, err),
|
NotifyError::BodyFetch(ref err) => warn!("Failed to fetch block body for imported block {:?}: {:?}", hash, err),
|
||||||
NotifyError::UnexpectedFormat => warn!("Consensus outdated: Block {:?} has unexpected body format", hash),
|
NotifyError::UnexpectedFormat => warn!("Consensus outdated: Block {:?} has unexpected body format", hash),
|
||||||
NotifyError::ExtrinsicsWrong => warn!("Consensus outdated: Extrinsics cannot be decoded for {:?}", hash),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.import_notification_stream()
|
client.finality_notification_stream()
|
||||||
.for_each(move |notification| {
|
.for_each(move |notification| {
|
||||||
|
use polkadot_runtime::{Call, ParachainsCall};
|
||||||
|
|
||||||
let hash = notification.hash;
|
let hash = notification.hash;
|
||||||
let parent_hash = notification.header.parent_hash;
|
let parent_hash = notification.header.parent_hash;
|
||||||
let checked_block = client.block_body(&BlockId::hash(hash))
|
let runtime_block = client.block_body(&BlockId::hash(hash))
|
||||||
.map_err(NotifyError::BodyFetch)
|
.map_err(NotifyError::BodyFetch)
|
||||||
.and_then(|maybe_body| maybe_body.ok_or(NotifyError::NoBody))
|
.and_then(|maybe_body| maybe_body.ok_or(NotifyError::NoBody))
|
||||||
.map(|extrinsics| Block { header: notification.header, extrinsics })
|
.map(|extrinsics| Block { header: notification.header, extrinsics })
|
||||||
.map(|b: Block| ::polkadot_runtime::Block::decode(&mut b.encode().as_slice()))
|
.map(|b: Block| ::polkadot_runtime::Block::decode(&mut b.encode().as_slice()))
|
||||||
.and_then(|maybe_block| maybe_block.ok_or(NotifyError::UnexpectedFormat))
|
.and_then(|maybe_block| maybe_block.ok_or(NotifyError::UnexpectedFormat));
|
||||||
.and_then(|block| CheckedBlock::new(block).map_err(|_| NotifyError::ExtrinsicsWrong));
|
|
||||||
|
let runtime_block = match runtime_block {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => { e.log(&hash); return Ok(()) }
|
||||||
|
};
|
||||||
|
|
||||||
|
let candidate_hashes = match runtime_block.extrinsics
|
||||||
|
.iter()
|
||||||
|
.filter_map(|ex| match ex.function {
|
||||||
|
Call::Parachains(ParachainsCall::set_heads(ref heads)) =>
|
||||||
|
Some(heads.iter().map(|c| c.hash()).collect()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
{
|
||||||
|
Some(x) => x,
|
||||||
|
None => return Ok(()),
|
||||||
|
};
|
||||||
|
|
||||||
match checked_block {
|
|
||||||
Ok(block) => {
|
|
||||||
let candidate_hashes = block.parachain_heads().iter().map(|c| c.hash()).collect();
|
|
||||||
if let Err(e) = extrinsic_store.candidates_finalized(parent_hash, candidate_hashes) {
|
if let Err(e) = extrinsic_store.candidates_finalized(parent_hash, candidate_hashes) {
|
||||||
warn!(target: "consensus", "Failed to prune unneeded available data: {:?}", e);
|
warn!(target: "consensus", "Failed to prune unneeded available data: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
Err(e) => e.log(&hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@@ -133,90 +119,102 @@ pub struct Service {
|
|||||||
|
|
||||||
impl Service {
|
impl Service {
|
||||||
/// Create and start a new instance.
|
/// Create and start a new instance.
|
||||||
pub fn new<A, C, N>(
|
pub fn new<C, N, TxApi>(
|
||||||
client: Arc<C>,
|
client: Arc<C>,
|
||||||
api: Arc<A>,
|
|
||||||
network: N,
|
network: N,
|
||||||
transaction_pool: Arc<TransactionPool<A>>,
|
transaction_pool: Arc<Pool<TxApi>>,
|
||||||
thread_pool: ThreadPoolHandle,
|
thread_pool: ThreadPoolHandle,
|
||||||
parachain_empty_duration: Duration,
|
parachain_empty_duration: Duration,
|
||||||
key: ed25519::Pair,
|
key: ed25519::Pair,
|
||||||
extrinsic_store: ExtrinsicStore,
|
extrinsic_store: ExtrinsicStore,
|
||||||
) -> Service
|
) -> Service
|
||||||
where
|
where
|
||||||
A: LocalPolkadotApi + Send + Sync + 'static,
|
|
||||||
C: BlockchainEvents<Block> + ChainHead<Block> + BlockBody<Block>,
|
C: BlockchainEvents<Block> + ChainHead<Block> + BlockBody<Block>,
|
||||||
C: bft::BlockImport<Block> + bft::Authorities<Block> + Send + Sync + 'static,
|
C: ProvideRuntimeApi + HeaderBackend<Block> + Send + Sync + 'static,
|
||||||
N: Network + Collators + Send + 'static,
|
C::Api: ParachainHost<Block> + Core<Block> + BlockBuilder<Block>,
|
||||||
|
N: Network + Collators + Send + Sync + 'static,
|
||||||
N::TableRouter: Send + 'static,
|
N::TableRouter: Send + 'static,
|
||||||
<N::Collation as IntoFuture>::Future: Send + 'static,
|
<N::Collation as IntoFuture>::Future: Send + 'static,
|
||||||
|
TxApi: PoolChainApi<Block=Block> + Send + 'static,
|
||||||
{
|
{
|
||||||
use parking_lot::RwLock;
|
use parking_lot::Mutex;
|
||||||
use super::OfflineTracker;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
const TIMER_DELAY: Duration = Duration::from_secs(5);
|
||||||
|
const TIMER_INTERVAL: Duration = Duration::from_secs(30);
|
||||||
|
|
||||||
let (signal, exit) = ::exit_future::signal();
|
let (signal, exit) = ::exit_future::signal();
|
||||||
let thread = thread::spawn(move || {
|
let thread = thread::spawn(move || {
|
||||||
let mut runtime = LocalRuntime::new().expect("Could not create local runtime");
|
let mut runtime = LocalRuntime::new().expect("Could not create local runtime");
|
||||||
let key = Arc::new(key);
|
let key = Arc::new(key);
|
||||||
|
|
||||||
let factory = ProposerFactory {
|
let parachain_consensus = Arc::new(::ParachainConsensus{
|
||||||
client: api.clone(),
|
client: client.clone(),
|
||||||
transaction_pool: transaction_pool.clone(),
|
network: network.clone(),
|
||||||
collators: network.clone(),
|
collators: network.clone(),
|
||||||
network,
|
|
||||||
parachain_empty_duration,
|
|
||||||
handle: thread_pool.clone(),
|
handle: thread_pool.clone(),
|
||||||
extrinsic_store: extrinsic_store.clone(),
|
extrinsic_store: extrinsic_store.clone(),
|
||||||
offline: Arc::new(RwLock::new(OfflineTracker::new())),
|
parachain_empty_duration,
|
||||||
};
|
live_instances: Mutex::new(HashMap::new()),
|
||||||
let bft_service = Arc::new(BftService::new(client.clone(), key, factory));
|
});
|
||||||
|
|
||||||
|
let factory = ProposerFactory::new(
|
||||||
|
parachain_consensus.clone(),
|
||||||
|
transaction_pool
|
||||||
|
);
|
||||||
|
|
||||||
let notifications = {
|
let notifications = {
|
||||||
let client = client.clone();
|
let client = client.clone();
|
||||||
let bft_service = bft_service.clone();
|
let consensus = parachain_consensus.clone();
|
||||||
|
let key = key.clone();
|
||||||
|
|
||||||
client.import_notification_stream().for_each(move |notification| {
|
client.import_notification_stream().for_each(move |notification| {
|
||||||
|
let parent_hash = notification.hash;
|
||||||
if notification.is_new_best {
|
if notification.is_new_best {
|
||||||
trace!(target: "bft", "Attempting to start new consensus round after import notification of {:?}", notification.hash);
|
let res = client
|
||||||
start_bft(notification.header, bft_service.clone());
|
.runtime_api()
|
||||||
|
.authorities(&BlockId::hash(parent_hash))
|
||||||
|
.map_err(Into::into)
|
||||||
|
.and_then(|authorities| {
|
||||||
|
consensus.get_or_instantiate(
|
||||||
|
parent_hash,
|
||||||
|
&authorities,
|
||||||
|
key.clone(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Err(e) = res {
|
||||||
|
warn!("Unable to start parachain consensus on top of {:?}: {}",
|
||||||
|
parent_hash, e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let prune_old_sessions = {
|
||||||
|
let client = client.clone();
|
||||||
let interval = Interval::new(
|
let interval = Interval::new(
|
||||||
Instant::now() + Duration::from_millis(TIMER_DELAY_MS),
|
Instant::now() + TIMER_DELAY,
|
||||||
Duration::from_millis(TIMER_INTERVAL_MS),
|
TIMER_INTERVAL,
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut prev_best = match client.best_block_header() {
|
interval
|
||||||
Ok(header) => header.hash(),
|
.for_each(move |_| match client.leaves() {
|
||||||
Err(e) => {
|
Ok(leaves) => {
|
||||||
warn!("Can't start consensus service. Error reading best block header: {:?}", e);
|
parachain_consensus.retain(|h| leaves.contains(h));
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let timed = {
|
|
||||||
let c = client.clone();
|
|
||||||
let s = bft_service.clone();
|
|
||||||
|
|
||||||
interval.map_err(|e| debug!(target: "bft", "Timer error: {:?}", e)).for_each(move |_| {
|
|
||||||
if let Ok(best_block) = c.best_block_header() {
|
|
||||||
let hash = best_block.hash();
|
|
||||||
|
|
||||||
if hash == prev_best {
|
|
||||||
debug!(target: "bft", "Starting consensus round after a timeout");
|
|
||||||
start_bft(best_block, s.clone());
|
|
||||||
}
|
|
||||||
prev_best = hash;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
warn!("Error fetching leaves from client: {:?}", e);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
.map_err(|e| warn!("Timer error {:?}", e))
|
||||||
};
|
};
|
||||||
|
|
||||||
runtime.spawn(notifications);
|
runtime.spawn(notifications);
|
||||||
runtime.spawn(timed);
|
thread_pool.spawn(prune_old_sessions);
|
||||||
|
|
||||||
let prune_available = prune_unneeded_availability(client, extrinsic_store)
|
let prune_available = prune_unneeded_availability(client, extrinsic_store)
|
||||||
.select(exit.clone())
|
.select(exit.clone())
|
||||||
|
|||||||
@@ -449,6 +449,11 @@ impl SharedTable {
|
|||||||
f(inner.table.proposed_candidates(&*self.context))
|
f(inner.table.proposed_candidates(&*self.context))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the number of total parachains.
|
||||||
|
pub fn num_parachains(&self) -> usize {
|
||||||
|
self.group_info().len()
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the number of parachains which have available candidates.
|
/// Get the number of parachains which have available candidates.
|
||||||
pub fn includable_count(&self) -> usize {
|
pub fn includable_count(&self) -> usize {
|
||||||
self.inner.lock().table.includable_count()
|
self.inner.lock().table.includable_count()
|
||||||
|
|||||||
@@ -21,4 +21,9 @@ extern crate polkadot_runtime;
|
|||||||
#[macro_use] extern crate substrate_executor;
|
#[macro_use] extern crate substrate_executor;
|
||||||
extern crate substrate_primitives as primitives;
|
extern crate substrate_primitives as primitives;
|
||||||
|
|
||||||
native_executor_instance!(pub Executor, polkadot_runtime::api::dispatch, polkadot_runtime::VERSION, include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm"));
|
native_executor_instance!(
|
||||||
|
pub Executor,
|
||||||
|
polkadot_runtime::api::dispatch,
|
||||||
|
polkadot_runtime::native_version,
|
||||||
|
include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm")
|
||||||
|
);
|
||||||
|
|||||||
@@ -6,15 +6,14 @@ description = "Polkadot-specific networking protocol"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parking_lot = "0.4"
|
parking_lot = "0.4"
|
||||||
polkadot-api = { path = "../api" }
|
|
||||||
polkadot-availability-store = { path = "../availability-store" }
|
polkadot-availability-store = { path = "../availability-store" }
|
||||||
polkadot-consensus = { path = "../consensus" }
|
polkadot-consensus = { path = "../consensus" }
|
||||||
polkadot-primitives = { path = "../primitives" }
|
polkadot-primitives = { path = "../primitives" }
|
||||||
substrate-bft = { git = "https://github.com/paritytech/substrate" }
|
parity-codec = "2.1"
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate" }
|
parity-codec-derive = "2.1"
|
||||||
parity-codec-derive = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
substrate-network = { git = "https://github.com/paritytech/substrate" }
|
substrate-network = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
sr-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
futures = "0.1"
|
futures = "0.1"
|
||||||
tokio = "0.1.7"
|
tokio = "0.1.7"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|||||||
@@ -15,17 +15,15 @@
|
|||||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//! The "consensus" networking code built on top of the base network service.
|
//! The "consensus" networking code built on top of the base network service.
|
||||||
|
//!
|
||||||
//! This fulfills the `polkadot_consensus::Network` trait, providing a hook to be called
|
//! This fulfills the `polkadot_consensus::Network` trait, providing a hook to be called
|
||||||
//! each time consensus begins on a new chain head.
|
//! each time consensus begins on a new chain head.
|
||||||
|
|
||||||
use bft;
|
use sr_primitives::traits::ProvideRuntimeApi;
|
||||||
use substrate_primitives::ed25519;
|
|
||||||
use substrate_network::{self as net, generic_message as msg};
|
|
||||||
use substrate_network::consensus_gossip::ConsensusMessage;
|
use substrate_network::consensus_gossip::ConsensusMessage;
|
||||||
use polkadot_api::{PolkadotApi, LocalPolkadotApi};
|
|
||||||
use polkadot_consensus::{Network, SharedTable, Collators};
|
use polkadot_consensus::{Network, SharedTable, Collators};
|
||||||
use polkadot_primitives::{AccountId, Block, Hash, SessionKey};
|
use polkadot_primitives::{AccountId, Block, Hash, SessionKey};
|
||||||
use polkadot_primitives::parachain::{Id as ParaId, Collation};
|
use polkadot_primitives::parachain::{Id as ParaId, Collation, ParachainHost};
|
||||||
use codec::Decode;
|
use codec::Decode;
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
@@ -36,160 +34,42 @@ use std::sync::Arc;
|
|||||||
use tokio::runtime::TaskExecutor;
|
use tokio::runtime::TaskExecutor;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use super::{Message, NetworkService, Knowledge, CurrentConsensus};
|
use super::{NetworkService, Knowledge, CurrentConsensus};
|
||||||
use router::Router;
|
use router::Router;
|
||||||
|
|
||||||
/// Sink for output BFT messages.
|
|
||||||
pub struct BftSink<E> {
|
|
||||||
network: Arc<NetworkService>,
|
|
||||||
parent_hash: Hash,
|
|
||||||
_marker: ::std::marker::PhantomData<E>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E> Sink for BftSink<E> {
|
|
||||||
type SinkItem = bft::Communication<Block>;
|
|
||||||
// TODO: replace this with the ! type when that's stabilized
|
|
||||||
type SinkError = E;
|
|
||||||
|
|
||||||
fn start_send(&mut self, message: bft::Communication<Block>) -> ::futures::StartSend<bft::Communication<Block>, E> {
|
|
||||||
let network_message = net::LocalizedBftMessage {
|
|
||||||
message: match message {
|
|
||||||
::rhododendron::Communication::Consensus(c) => msg::BftMessage::Consensus(match c {
|
|
||||||
::rhododendron::LocalizedMessage::Propose(proposal) => msg::SignedConsensusMessage::Propose(msg::SignedConsensusProposal {
|
|
||||||
round_number: proposal.round_number as u32,
|
|
||||||
proposal: proposal.proposal,
|
|
||||||
digest: proposal.digest,
|
|
||||||
sender: proposal.sender,
|
|
||||||
digest_signature: proposal.digest_signature.signature,
|
|
||||||
full_signature: proposal.full_signature.signature,
|
|
||||||
}),
|
|
||||||
::rhododendron::LocalizedMessage::Vote(vote) => msg::SignedConsensusMessage::Vote(msg::SignedConsensusVote {
|
|
||||||
sender: vote.sender,
|
|
||||||
signature: vote.signature.signature,
|
|
||||||
vote: match vote.vote {
|
|
||||||
::rhododendron::Vote::Prepare(r, h) => msg::ConsensusVote::Prepare(r as u32, h),
|
|
||||||
::rhododendron::Vote::Commit(r, h) => msg::ConsensusVote::Commit(r as u32, h),
|
|
||||||
::rhododendron::Vote::AdvanceRound(r) => msg::ConsensusVote::AdvanceRound(r as u32),
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
::rhododendron::Communication::Auxiliary(justification) => {
|
|
||||||
let unchecked: bft::UncheckedJustification<_> = justification.uncheck().into();
|
|
||||||
msg::BftMessage::Auxiliary(unchecked.into())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
parent_hash: self.parent_hash,
|
|
||||||
};
|
|
||||||
self.network.with_spec(
|
|
||||||
move |spec, ctx| spec.consensus_gossip.multicast_bft_message(ctx, network_message)
|
|
||||||
);
|
|
||||||
Ok(::futures::AsyncSink::Ready)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_complete(&mut self) -> ::futures::Poll<(), E> {
|
|
||||||
Ok(Async::Ready(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check signature and authority validity of message.
|
|
||||||
fn process_bft_message(msg: msg::LocalizedBftMessage<Block, Hash>, local_id: &SessionKey, authorities: &[SessionKey]) -> Result<Option<bft::Communication<Block>>, bft::Error> {
|
|
||||||
Ok(Some(match msg.message {
|
|
||||||
msg::BftMessage::Consensus(c) => ::rhododendron::Communication::Consensus(match c {
|
|
||||||
msg::SignedConsensusMessage::Propose(proposal) => ::rhododendron::LocalizedMessage::Propose({
|
|
||||||
if &proposal.sender == local_id { return Ok(None) }
|
|
||||||
let proposal = ::rhododendron::LocalizedProposal {
|
|
||||||
round_number: proposal.round_number as usize,
|
|
||||||
proposal: proposal.proposal,
|
|
||||||
digest: proposal.digest,
|
|
||||||
sender: proposal.sender,
|
|
||||||
digest_signature: ed25519::LocalizedSignature {
|
|
||||||
signature: proposal.digest_signature,
|
|
||||||
signer: ed25519::Public(proposal.sender.into()),
|
|
||||||
},
|
|
||||||
full_signature: ed25519::LocalizedSignature {
|
|
||||||
signature: proposal.full_signature,
|
|
||||||
signer: ed25519::Public(proposal.sender.into()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
bft::check_proposal(authorities, &msg.parent_hash, &proposal)?;
|
|
||||||
|
|
||||||
trace!(target: "bft", "importing proposal message for round {} from {}", proposal.round_number, Hash::from(proposal.sender.0));
|
|
||||||
proposal
|
|
||||||
}),
|
|
||||||
msg::SignedConsensusMessage::Vote(vote) => ::rhododendron::LocalizedMessage::Vote({
|
|
||||||
if &vote.sender == local_id { return Ok(None) }
|
|
||||||
let vote = ::rhododendron::LocalizedVote {
|
|
||||||
sender: vote.sender,
|
|
||||||
signature: ed25519::LocalizedSignature {
|
|
||||||
signature: vote.signature,
|
|
||||||
signer: ed25519::Public(vote.sender.0),
|
|
||||||
},
|
|
||||||
vote: match vote.vote {
|
|
||||||
msg::ConsensusVote::Prepare(r, h) => ::rhododendron::Vote::Prepare(r as usize, h),
|
|
||||||
msg::ConsensusVote::Commit(r, h) => ::rhododendron::Vote::Commit(r as usize, h),
|
|
||||||
msg::ConsensusVote::AdvanceRound(r) => ::rhododendron::Vote::AdvanceRound(r as usize),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
bft::check_vote::<Block>(authorities, &msg.parent_hash, &vote)?;
|
|
||||||
|
|
||||||
trace!(target: "bft", "importing vote {:?} from {}", vote.vote, Hash::from(vote.sender.0));
|
|
||||||
vote
|
|
||||||
}),
|
|
||||||
}),
|
|
||||||
msg::BftMessage::Auxiliary(a) => {
|
|
||||||
let justification = bft::UncheckedJustification::from(a);
|
|
||||||
// TODO: get proper error
|
|
||||||
let justification: Result<_, bft::Error> = bft::check_prepare_justification::<Block>(authorities, msg.parent_hash, justification)
|
|
||||||
.map_err(|_| bft::ErrorKind::InvalidJustification.into());
|
|
||||||
::rhododendron::Communication::Auxiliary(justification?)
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
// task that processes all gossipped consensus messages,
|
// task that processes all gossipped consensus messages,
|
||||||
// checking signatures
|
// checking signatures
|
||||||
struct MessageProcessTask<P: PolkadotApi> {
|
struct MessageProcessTask<P> {
|
||||||
inner_stream: mpsc::UnboundedReceiver<ConsensusMessage<Block>>,
|
inner_stream: mpsc::UnboundedReceiver<ConsensusMessage>,
|
||||||
bft_messages: mpsc::UnboundedSender<bft::Communication<Block>>,
|
parent_hash: Hash,
|
||||||
validators: Vec<SessionKey>,
|
|
||||||
table_router: Router<P>,
|
table_router: Router<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: LocalPolkadotApi + Send + Sync + 'static> MessageProcessTask<P> {
|
impl<P: ProvideRuntimeApi + Send + Sync + 'static> MessageProcessTask<P>
|
||||||
fn process_message(&self, msg: ConsensusMessage<Block>) -> Option<Async<()>> {
|
where P::Api: ParachainHost<Block>,
|
||||||
match msg {
|
{
|
||||||
ConsensusMessage::Bft(msg) => {
|
fn process_message(&self, msg: ConsensusMessage) -> Option<Async<()>> {
|
||||||
let local_id = self.table_router.session_key();
|
use polkadot_consensus::SignedStatement;
|
||||||
match process_bft_message(msg, &local_id, &self.validators[..]) {
|
|
||||||
Ok(Some(msg)) => {
|
|
||||||
if let Err(_) = self.bft_messages.unbounded_send(msg) {
|
|
||||||
// if the BFT receiving stream has ended then
|
|
||||||
// we should just bail.
|
|
||||||
trace!(target: "bft", "BFT message stream appears to have closed");
|
|
||||||
return Some(Async::Ready(()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(None) => {} // ignored local message
|
|
||||||
Err(e) => {
|
|
||||||
debug!("Message validation failed: {:?}", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConsensusMessage::ChainSpecific(msg, _) => {
|
|
||||||
debug!(target: "consensus", "Processing consensus statement for live consensus");
|
debug!(target: "consensus", "Processing consensus statement for live consensus");
|
||||||
if let Some(Message::Statement(parent_hash, statement)) = Decode::decode(&mut msg.as_slice()) {
|
if let Some(statement) = SignedStatement::decode(&mut msg.as_slice()) {
|
||||||
if ::polkadot_consensus::check_statement(&statement.statement, &statement.signature, statement.sender, &parent_hash) {
|
if ::polkadot_consensus::check_statement(
|
||||||
|
&statement.statement,
|
||||||
|
&statement.signature,
|
||||||
|
statement.sender,
|
||||||
|
&self.parent_hash
|
||||||
|
) {
|
||||||
self.table_router.import_statement(statement);
|
self.table_router.import_statement(statement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: LocalPolkadotApi + Send + Sync + 'static> Future for MessageProcessTask<P> {
|
impl<P: ProvideRuntimeApi + Send + Sync + 'static> Future for MessageProcessTask<P>
|
||||||
|
where P::Api: ParachainHost<Block>,
|
||||||
|
{
|
||||||
type Item = ();
|
type Item = ();
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
@@ -207,23 +87,6 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Future for MessageProcessTask<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Input stream from the consensus network.
|
|
||||||
pub struct InputAdapter {
|
|
||||||
input: mpsc::UnboundedReceiver<bft::Communication<Block>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stream for InputAdapter {
|
|
||||||
type Item = bft::Communication<Block>;
|
|
||||||
type Error = ::polkadot_consensus::Error;
|
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
|
|
||||||
match self.input.poll() {
|
|
||||||
Err(_) | Ok(Async::Ready(None)) => Err(bft::InputStreamConcluded.into()),
|
|
||||||
Ok(x) => Ok(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Wrapper around the network service
|
/// Wrapper around the network service
|
||||||
pub struct ConsensusNetwork<P> {
|
pub struct ConsensusNetwork<P> {
|
||||||
network: Arc<NetworkService>,
|
network: Arc<NetworkService>,
|
||||||
@@ -246,27 +109,21 @@ impl<P> Clone for ConsensusNetwork<P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A long-lived network which can create parachain statement and BFT message routing processes on demand.
|
/// A long-lived network which can create parachain statement routing processes on demand.
|
||||||
impl<P: LocalPolkadotApi + Send + Sync + 'static> Network for ConsensusNetwork<P> {
|
impl<P: ProvideRuntimeApi + Send + Sync + 'static> Network for ConsensusNetwork<P>
|
||||||
|
where P::Api: ParachainHost<Block>,
|
||||||
|
{
|
||||||
type TableRouter = Router<P>;
|
type TableRouter = Router<P>;
|
||||||
/// The input stream of BFT messages. Should never logically conclude.
|
|
||||||
type Input = InputAdapter;
|
|
||||||
/// The output sink of BFT messages. Messages sent here should eventually pass to all
|
|
||||||
/// current validators.
|
|
||||||
type Output = BftSink<::polkadot_consensus::Error>;
|
|
||||||
|
|
||||||
/// Instantiate a table router using the given shared table.
|
/// Instantiate a table router using the given shared table.
|
||||||
fn communication_for(&self, validators: &[SessionKey], table: Arc<SharedTable>, task_executor: TaskExecutor) -> (Self::TableRouter, Self::Input, Self::Output) {
|
fn communication_for(
|
||||||
|
&self,
|
||||||
|
_validators: &[SessionKey],
|
||||||
|
table: Arc<SharedTable>,
|
||||||
|
task_executor: TaskExecutor
|
||||||
|
) -> Self::TableRouter {
|
||||||
let parent_hash = table.consensus_parent_hash().clone();
|
let parent_hash = table.consensus_parent_hash().clone();
|
||||||
|
|
||||||
let sink = BftSink {
|
|
||||||
network: self.network.clone(),
|
|
||||||
parent_hash,
|
|
||||||
_marker: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let (bft_send, bft_recv) = mpsc::unbounded();
|
|
||||||
|
|
||||||
let knowledge = Arc::new(Mutex::new(Knowledge::new()));
|
let knowledge = Arc::new(Mutex::new(Knowledge::new()));
|
||||||
|
|
||||||
let local_session_key = table.session_key();
|
let local_session_key = table.session_key();
|
||||||
@@ -279,9 +136,12 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Network for ConsensusNetwork<P
|
|||||||
knowledge.clone(),
|
knowledge.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let attestation_topic = table_router.gossip_topic();
|
||||||
|
|
||||||
// spin up a task in the background that processes all incoming statements
|
// spin up a task in the background that processes all incoming statements
|
||||||
// TODO: propagate statements on a timer?
|
// TODO: propagate statements on a timer?
|
||||||
let process_task = self.network.with_spec(|spec, ctx| {
|
let inner_stream = self.network.consensus_gossip().write().messages_for(attestation_topic);
|
||||||
|
task_executor.spawn(self.network.with_spec(|spec, ctx| {
|
||||||
spec.new_consensus(ctx, CurrentConsensus {
|
spec.new_consensus(ctx, CurrentConsensus {
|
||||||
knowledge,
|
knowledge,
|
||||||
parent_hash,
|
parent_hash,
|
||||||
@@ -289,19 +149,13 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Network for ConsensusNetwork<P
|
|||||||
});
|
});
|
||||||
|
|
||||||
MessageProcessTask {
|
MessageProcessTask {
|
||||||
inner_stream: spec.consensus_gossip.messages_for(parent_hash),
|
inner_stream,
|
||||||
bft_messages: bft_send,
|
parent_hash,
|
||||||
validators: validators.to_vec(),
|
|
||||||
table_router: table_router.clone(),
|
table_router: table_router.clone(),
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
|
||||||
match process_task {
|
table_router
|
||||||
Some(task) => task_executor.spawn(task),
|
|
||||||
None => warn!(target: "p_net", "Cannot process incoming messages: network appears to be down"),
|
|
||||||
}
|
|
||||||
|
|
||||||
(table_router, InputAdapter { input: bft_recv }, sink)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,23 +164,21 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Network for ConsensusNetwork<P
|
|||||||
pub struct NetworkDown;
|
pub struct NetworkDown;
|
||||||
|
|
||||||
/// A future that resolves when a collation is received.
|
/// A future that resolves when a collation is received.
|
||||||
pub struct AwaitingCollation(Option<::futures::sync::oneshot::Receiver<Collation>>);
|
pub struct AwaitingCollation(::futures::sync::oneshot::Receiver<Collation>);
|
||||||
|
|
||||||
impl Future for AwaitingCollation {
|
impl Future for AwaitingCollation {
|
||||||
type Item = Collation;
|
type Item = Collation;
|
||||||
type Error = NetworkDown;
|
type Error = NetworkDown;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<Collation, NetworkDown> {
|
fn poll(&mut self) -> Poll<Collation, NetworkDown> {
|
||||||
match self.0.poll().map_err(|_| NetworkDown)? {
|
self.0.poll().map_err(|_| NetworkDown)
|
||||||
Async::Ready(None) => Err(NetworkDown),
|
|
||||||
Async::Ready(Some(x)) => Ok(Async::Ready(x)),
|
|
||||||
Async::NotReady => Ok(Async::NotReady),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<P: LocalPolkadotApi + Send + Sync + 'static> Collators for ConsensusNetwork<P> {
|
impl<P: ProvideRuntimeApi + Send + Sync + 'static> Collators for ConsensusNetwork<P>
|
||||||
|
where P::Api: ParachainHost<Block>,
|
||||||
|
{
|
||||||
type Error = NetworkDown;
|
type Error = NetworkDown;
|
||||||
type Collation = AwaitingCollation;
|
type Collation = AwaitingCollation;
|
||||||
|
|
||||||
|
|||||||
+13
-37
@@ -20,14 +20,13 @@
|
|||||||
//! parachain block and extrinsic data fetching, communication between collators and validators,
|
//! parachain block and extrinsic data fetching, communication between collators and validators,
|
||||||
//! and more.
|
//! and more.
|
||||||
|
|
||||||
extern crate substrate_bft as bft;
|
|
||||||
extern crate parity_codec as codec;
|
extern crate parity_codec as codec;
|
||||||
extern crate substrate_network;
|
extern crate substrate_network;
|
||||||
extern crate substrate_primitives;
|
extern crate substrate_primitives;
|
||||||
|
extern crate sr_primitives;
|
||||||
|
|
||||||
extern crate polkadot_api;
|
|
||||||
extern crate polkadot_availability_store as av_store;
|
|
||||||
extern crate polkadot_consensus;
|
extern crate polkadot_consensus;
|
||||||
|
extern crate polkadot_availability_store as av_store;
|
||||||
extern crate polkadot_primitives;
|
extern crate polkadot_primitives;
|
||||||
|
|
||||||
extern crate futures;
|
extern crate futures;
|
||||||
@@ -48,13 +47,12 @@ pub mod consensus;
|
|||||||
use codec::{Decode, Encode};
|
use codec::{Decode, Encode};
|
||||||
use futures::sync::oneshot;
|
use futures::sync::oneshot;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use polkadot_consensus::{Statement, SignedStatement, GenericStatement};
|
use polkadot_consensus::{Statement, GenericStatement};
|
||||||
use polkadot_primitives::{AccountId, Block, SessionKey, Hash, Header};
|
use polkadot_primitives::{AccountId, Block, SessionKey, Hash, Header};
|
||||||
use polkadot_primitives::parachain::{Id as ParaId, BlockData, Extrinsic, CandidateReceipt, Collation};
|
use polkadot_primitives::parachain::{Id as ParaId, BlockData, Extrinsic, CandidateReceipt, Collation};
|
||||||
use substrate_network::{NodeIndex, RequestId, Context, Severity};
|
use substrate_network::{NodeIndex, RequestId, Context, Severity};
|
||||||
use substrate_network::consensus_gossip::ConsensusGossip;
|
|
||||||
use substrate_network::{message, generic_message};
|
use substrate_network::{message, generic_message};
|
||||||
use substrate_network::specialization::Specialization;
|
use substrate_network::specialization::NetworkSpecialization as Specialization;
|
||||||
use substrate_network::StatusMessage as GenericFullStatus;
|
use substrate_network::StatusMessage as GenericFullStatus;
|
||||||
use self::collator_pool::{CollatorPool, Role, Action};
|
use self::collator_pool::{CollatorPool, Role, Action};
|
||||||
use self::local_collations::LocalCollations;
|
use self::local_collations::LocalCollations;
|
||||||
@@ -185,8 +183,6 @@ impl CurrentConsensus {
|
|||||||
/// Polkadot-specific messages.
|
/// Polkadot-specific messages.
|
||||||
#[derive(Debug, Encode, Decode)]
|
#[derive(Debug, Encode, Decode)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
/// signed statement and localized parent hash.
|
|
||||||
Statement(Hash, SignedStatement),
|
|
||||||
/// As a validator, tell the peer your current session key.
|
/// As a validator, tell the peer your current session key.
|
||||||
// TODO: do this with a cryptographic proof of some kind
|
// TODO: do this with a cryptographic proof of some kind
|
||||||
SessionKey(SessionKey),
|
SessionKey(SessionKey),
|
||||||
@@ -210,7 +206,6 @@ fn send_polkadot_message(ctx: &mut Context<Block>, to: NodeIndex, message: Messa
|
|||||||
pub struct PolkadotProtocol {
|
pub struct PolkadotProtocol {
|
||||||
peers: HashMap<NodeIndex, PeerInfo>,
|
peers: HashMap<NodeIndex, PeerInfo>,
|
||||||
collating_for: Option<(AccountId, ParaId)>,
|
collating_for: Option<(AccountId, ParaId)>,
|
||||||
consensus_gossip: ConsensusGossip<Block>,
|
|
||||||
collators: CollatorPool,
|
collators: CollatorPool,
|
||||||
validators: HashMap<SessionKey, NodeIndex>,
|
validators: HashMap<SessionKey, NodeIndex>,
|
||||||
local_collations: LocalCollations<Collation>,
|
local_collations: LocalCollations<Collation>,
|
||||||
@@ -226,7 +221,6 @@ impl PolkadotProtocol {
|
|||||||
pub fn new(collating_for: Option<(AccountId, ParaId)>) -> Self {
|
pub fn new(collating_for: Option<(AccountId, ParaId)>) -> Self {
|
||||||
PolkadotProtocol {
|
PolkadotProtocol {
|
||||||
peers: HashMap::new(),
|
peers: HashMap::new(),
|
||||||
consensus_gossip: ConsensusGossip::new(),
|
|
||||||
collators: CollatorPool::new(),
|
collators: CollatorPool::new(),
|
||||||
collating_for,
|
collating_for,
|
||||||
validators: HashMap::new(),
|
validators: HashMap::new(),
|
||||||
@@ -239,13 +233,6 @@ impl PolkadotProtocol {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gossip a consensus statement.
|
|
||||||
fn gossip_statement(&mut self, ctx: &mut Context<Block>, parent_hash: Hash, statement: SignedStatement) {
|
|
||||||
// TODO: something more targeted than gossip.
|
|
||||||
let raw = Message::Statement(parent_hash, statement).encode();
|
|
||||||
self.consensus_gossip.multicast_chain_specific(ctx, raw, parent_hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fetch block data by candidate receipt.
|
/// Fetch block data by candidate receipt.
|
||||||
fn fetch_block_data(&mut self, ctx: &mut Context<Block>, candidate: &CandidateReceipt, relay_parent: Hash) -> oneshot::Receiver<BlockData> {
|
fn fetch_block_data(&mut self, ctx: &mut Context<Block>, candidate: &CandidateReceipt, relay_parent: Hash) -> oneshot::Receiver<BlockData> {
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
@@ -279,7 +266,6 @@ impl PolkadotProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.live_consensus = Some(consensus);
|
self.live_consensus = Some(consensus);
|
||||||
self.consensus_gossip.collect_garbage(old_data.as_ref().map(|&(ref hash, _)| hash));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_pending_requests(&mut self, ctx: &mut Context<Block>) {
|
fn dispatch_pending_requests(&mut self, ctx: &mut Context<Block>) {
|
||||||
@@ -332,11 +318,9 @@ impl PolkadotProtocol {
|
|||||||
self.pending = new_pending;
|
self.pending = new_pending;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_polkadot_message(&mut self, ctx: &mut Context<Block>, who: NodeIndex, raw: Vec<u8>, msg: Message) {
|
fn on_polkadot_message(&mut self, ctx: &mut Context<Block>, who: NodeIndex, msg: Message) {
|
||||||
trace!(target: "p_net", "Polkadot message from {}: {:?}", who, msg);
|
trace!(target: "p_net", "Polkadot message from {}: {:?}", who, msg);
|
||||||
match msg {
|
match msg {
|
||||||
Message::Statement(parent_hash, _statement) =>
|
|
||||||
self.consensus_gossip.on_chain_specific(ctx, who, raw, parent_hash),
|
|
||||||
Message::SessionKey(key) => self.on_session_key(ctx, who, key),
|
Message::SessionKey(key) => self.on_session_key(ctx, who, key),
|
||||||
Message::RequestBlockData(req_id, relay_parent, candidate_hash) => {
|
Message::RequestBlockData(req_id, relay_parent, candidate_hash) => {
|
||||||
let block_data = self.live_consensus.as_ref()
|
let block_data = self.live_consensus.as_ref()
|
||||||
@@ -445,7 +429,7 @@ impl Specialization<Block> for PolkadotProtocol {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let validator = status.roles.contains(substrate_network::Roles::AUTHORITY);
|
let validator = status.roles.contains(substrate_network::config::Roles::AUTHORITY);
|
||||||
let send_key = validator || local_status.collating_for.is_some();
|
let send_key = validator || local_status.collating_for.is_some();
|
||||||
|
|
||||||
let mut peer_info = PeerInfo {
|
let mut peer_info = PeerInfo {
|
||||||
@@ -479,7 +463,6 @@ impl Specialization<Block> for PolkadotProtocol {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.peers.insert(who, peer_info);
|
self.peers.insert(who, peer_info);
|
||||||
self.consensus_gossip.new_peer(ctx, who, status.roles);
|
|
||||||
self.dispatch_pending_requests(ctx);
|
self.dispatch_pending_requests(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -521,37 +504,30 @@ impl Specialization<Block> for PolkadotProtocol {
|
|||||||
retain
|
retain
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
self.consensus_gossip.peer_disconnected(ctx, who);
|
|
||||||
self.dispatch_pending_requests(ctx);
|
self.dispatch_pending_requests(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_message(&mut self, ctx: &mut Context<Block>, who: NodeIndex, message: message::Message<Block>) {
|
fn on_message(&mut self, ctx: &mut Context<Block>, who: NodeIndex, message: &mut Option<message::Message<Block>>) {
|
||||||
match message {
|
match message.take() {
|
||||||
generic_message::Message::BftMessage(msg) => {
|
Some(generic_message::Message::ChainSpecific(raw)) => {
|
||||||
trace!(target: "p_net", "Polkadot BFT message from {}: {:?}", who, msg);
|
|
||||||
// TODO: check signature here? what if relevant block is unknown?
|
|
||||||
self.consensus_gossip.on_bft_message(ctx, who, msg)
|
|
||||||
}
|
|
||||||
generic_message::Message::ChainSpecific(raw) => {
|
|
||||||
match Message::decode(&mut raw.as_slice()) {
|
match Message::decode(&mut raw.as_slice()) {
|
||||||
Some(msg) => self.on_polkadot_message(ctx, who, raw, msg),
|
Some(msg) => self.on_polkadot_message(ctx, who, msg),
|
||||||
None => {
|
None => {
|
||||||
trace!(target: "p_net", "Bad message from {}", who);
|
trace!(target: "p_net", "Bad message from {}", who);
|
||||||
ctx.report_peer(who, Severity::Bad("Invalid polkadot protocol message format"));
|
ctx.report_peer(who, Severity::Bad("Invalid polkadot protocol message format"));
|
||||||
|
*message = Some(generic_message::Message::ChainSpecific(raw));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(other) => *message = Some(other),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_abort(&mut self) {
|
fn on_abort(&mut self) { }
|
||||||
self.consensus_gossip.abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn maintain_peers(&mut self, ctx: &mut Context<Block>) {
|
fn maintain_peers(&mut self, ctx: &mut Context<Block>) {
|
||||||
self.consensus_gossip.collect_garbage(None);
|
|
||||||
self.collators.collect_garbage(None);
|
self.collators.collect_garbage(None);
|
||||||
self.local_collations.collect_garbage(None);
|
self.local_collations.collect_garbage(None);
|
||||||
self.dispatch_pending_requests(ctx);
|
self.dispatch_pending_requests(ctx);
|
||||||
|
|||||||
@@ -22,11 +22,12 @@
|
|||||||
//! the `TableRouter` trait from `polkadot-consensus`, which is expected to call into a shared statement table
|
//! the `TableRouter` trait from `polkadot-consensus`, which is expected to call into a shared statement table
|
||||||
//! and dispatch evaluation work as necessary when new statements come in.
|
//! and dispatch evaluation work as necessary when new statements come in.
|
||||||
|
|
||||||
use polkadot_api::{PolkadotApi, LocalPolkadotApi};
|
use sr_primitives::traits::{ProvideRuntimeApi, BlakeTwo256, Hash as HashT};
|
||||||
use polkadot_consensus::{SharedTable, TableRouter, SignedStatement, GenericStatement, StatementProducer};
|
use polkadot_consensus::{SharedTable, TableRouter, SignedStatement, GenericStatement, StatementProducer};
|
||||||
use polkadot_primitives::{Hash, BlockId, SessionKey};
|
use polkadot_primitives::{Block, Hash, BlockId, SessionKey};
|
||||||
use polkadot_primitives::parachain::{BlockData, Extrinsic, CandidateReceipt};
|
use polkadot_primitives::parachain::{BlockData, Extrinsic, CandidateReceipt, ParachainHost};
|
||||||
|
|
||||||
|
use codec::Encode;
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use tokio::runtime::TaskExecutor;
|
use tokio::runtime::TaskExecutor;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
@@ -37,18 +38,26 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use super::{NetworkService, Knowledge};
|
use super::{NetworkService, Knowledge};
|
||||||
|
|
||||||
|
fn attestation_topic(parent_hash: Hash) -> Hash {
|
||||||
|
let mut v = parent_hash.as_ref().to_vec();
|
||||||
|
v.extend(b"attestations");
|
||||||
|
|
||||||
|
BlakeTwo256::hash(&v[..])
|
||||||
|
}
|
||||||
|
|
||||||
/// Table routing implementation.
|
/// Table routing implementation.
|
||||||
pub struct Router<P: PolkadotApi> {
|
pub struct Router<P> {
|
||||||
table: Arc<SharedTable>,
|
table: Arc<SharedTable>,
|
||||||
network: Arc<NetworkService>,
|
network: Arc<NetworkService>,
|
||||||
api: Arc<P>,
|
api: Arc<P>,
|
||||||
task_executor: TaskExecutor,
|
task_executor: TaskExecutor,
|
||||||
parent_hash: Hash,
|
parent_hash: Hash,
|
||||||
|
attestation_topic: Hash,
|
||||||
knowledge: Arc<Mutex<Knowledge>>,
|
knowledge: Arc<Mutex<Knowledge>>,
|
||||||
deferred_statements: Arc<Mutex<DeferredStatements>>,
|
deferred_statements: Arc<Mutex<DeferredStatements>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: PolkadotApi> Router<P> {
|
impl<P> Router<P> {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
table: Arc<SharedTable>,
|
table: Arc<SharedTable>,
|
||||||
network: Arc<NetworkService>,
|
network: Arc<NetworkService>,
|
||||||
@@ -63,17 +72,19 @@ impl<P: PolkadotApi> Router<P> {
|
|||||||
api,
|
api,
|
||||||
task_executor,
|
task_executor,
|
||||||
parent_hash,
|
parent_hash,
|
||||||
|
attestation_topic: attestation_topic(parent_hash),
|
||||||
knowledge,
|
knowledge,
|
||||||
deferred_statements: Arc::new(Mutex::new(DeferredStatements::new())),
|
deferred_statements: Arc::new(Mutex::new(DeferredStatements::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn session_key(&self) -> SessionKey {
|
/// Get the attestation topic for gossip.
|
||||||
self.table.session_key()
|
pub(crate) fn gossip_topic(&self) -> Hash {
|
||||||
|
self.attestation_topic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: PolkadotApi> Clone for Router<P> {
|
impl<P> Clone for Router<P> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Router {
|
Router {
|
||||||
table: self.table.clone(),
|
table: self.table.clone(),
|
||||||
@@ -81,13 +92,16 @@ impl<P: PolkadotApi> Clone for Router<P> {
|
|||||||
api: self.api.clone(),
|
api: self.api.clone(),
|
||||||
task_executor: self.task_executor.clone(),
|
task_executor: self.task_executor.clone(),
|
||||||
parent_hash: self.parent_hash.clone(),
|
parent_hash: self.parent_hash.clone(),
|
||||||
|
attestation_topic: self.attestation_topic.clone(),
|
||||||
deferred_statements: self.deferred_statements.clone(),
|
deferred_statements: self.deferred_statements.clone(),
|
||||||
knowledge: self.knowledge.clone(),
|
knowledge: self.knowledge.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: LocalPolkadotApi + Send + Sync + 'static> Router<P> {
|
impl<P: ProvideRuntimeApi + Send + Sync + 'static> Router<P>
|
||||||
|
where P::Api: ParachainHost<Block>
|
||||||
|
{
|
||||||
/// Import a statement whose signature has been checked already.
|
/// Import a statement whose signature has been checked already.
|
||||||
pub(crate) fn import_statement(&self, statement: SignedStatement) {
|
pub(crate) fn import_statement(&self, statement: SignedStatement) {
|
||||||
trace!(target: "p_net", "importing consensus statement {:?}", statement.statement);
|
trace!(target: "p_net", "importing consensus statement {:?}", statement.statement);
|
||||||
@@ -156,6 +170,7 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Router<P> {
|
|||||||
let table = self.table.clone();
|
let table = self.table.clone();
|
||||||
let network = self.network.clone();
|
let network = self.network.clone();
|
||||||
let knowledge = self.knowledge.clone();
|
let knowledge = self.knowledge.clone();
|
||||||
|
let attestation_topic = self.attestation_topic.clone();
|
||||||
|
|
||||||
let work = producer.prime(validate)
|
let work = producer.prime(validate)
|
||||||
.map(move |produced| {
|
.map(move |produced| {
|
||||||
@@ -166,15 +181,26 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Router<P> {
|
|||||||
produced.extrinsic
|
produced.extrinsic
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if produced.validity.is_none() && produced.availability.is_none() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut gossip = network.consensus_gossip().write();
|
||||||
|
|
||||||
// propagate the statements
|
// propagate the statements
|
||||||
|
// consider something more targeted than gossip in the future.
|
||||||
if let Some(validity) = produced.validity {
|
if let Some(validity) = produced.validity {
|
||||||
let signed = table.sign_and_import(validity.clone()).0;
|
let signed = table.sign_and_import(validity.clone()).0;
|
||||||
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
|
network.with_spec(|_, ctx|
|
||||||
|
gossip.multicast(ctx, attestation_topic, signed.encode())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(availability) = produced.availability {
|
if let Some(availability) = produced.availability {
|
||||||
let signed = table.sign_and_import(availability).0;
|
let signed = table.sign_and_import(availability).0;
|
||||||
network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed));
|
network.with_spec(|_, ctx|
|
||||||
|
gossip.multicast(ctx, attestation_topic, signed.encode())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.map_err(|e| debug!(target: "p_net", "Failed to produce statements: {:?}", e));
|
.map_err(|e| debug!(target: "p_net", "Failed to produce statements: {:?}", e));
|
||||||
@@ -183,7 +209,9 @@ impl<P: LocalPolkadotApi + Send + Sync + 'static> Router<P> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: LocalPolkadotApi + Send> TableRouter for Router<P> {
|
impl<P: ProvideRuntimeApi + Send> TableRouter for Router<P>
|
||||||
|
where P::Api: ParachainHost<Block>
|
||||||
|
{
|
||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
type FetchCandidate = BlockDataReceiver;
|
type FetchCandidate = BlockDataReceiver;
|
||||||
type FetchExtrinsic = Result<Extrinsic, Self::Error>;
|
type FetchExtrinsic = Result<Extrinsic, Self::Error>;
|
||||||
@@ -194,10 +222,11 @@ impl<P: LocalPolkadotApi + Send> TableRouter for Router<P> {
|
|||||||
let (candidate, availability) = self.table.sign_and_import(GenericStatement::Candidate(receipt));
|
let (candidate, availability) = self.table.sign_and_import(GenericStatement::Candidate(receipt));
|
||||||
|
|
||||||
self.knowledge.lock().note_candidate(hash, Some(block_data), Some(extrinsic));
|
self.knowledge.lock().note_candidate(hash, Some(block_data), Some(extrinsic));
|
||||||
self.network.with_spec(|spec, ctx| {
|
let mut gossip = self.network.consensus_gossip().write();
|
||||||
spec.gossip_statement(ctx, self.parent_hash, candidate);
|
self.network.with_spec(|_spec, ctx| {
|
||||||
|
gossip.multicast(ctx, self.attestation_topic, candidate.encode());
|
||||||
if let Some(availability) = availability {
|
if let Some(availability) = availability {
|
||||||
spec.gossip_statement(ctx, self.parent_hash, availability);
|
gossip.multicast(ctx, self.attestation_topic, availability.encode());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -215,7 +244,7 @@ impl<P: LocalPolkadotApi + Send> TableRouter for Router<P> {
|
|||||||
|
|
||||||
/// Receiver for block data.
|
/// Receiver for block data.
|
||||||
pub struct BlockDataReceiver {
|
pub struct BlockDataReceiver {
|
||||||
inner: Option<::futures::sync::oneshot::Receiver<BlockData>>,
|
inner: ::futures::sync::oneshot::Receiver<BlockData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Future for BlockDataReceiver {
|
impl Future for BlockDataReceiver {
|
||||||
@@ -223,16 +252,10 @@ impl Future for BlockDataReceiver {
|
|||||||
type Error = io::Error;
|
type Error = io::Error;
|
||||||
|
|
||||||
fn poll(&mut self) -> Poll<BlockData, io::Error> {
|
fn poll(&mut self) -> Poll<BlockData, io::Error> {
|
||||||
match self.inner {
|
self.inner.poll().map_err(|_| io::Error::new(
|
||||||
Some(ref mut inner) => inner.poll().map_err(|_| io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
io::ErrorKind::Other,
|
||||||
"Sending end of channel hung up",
|
"Sending end of channel hung up",
|
||||||
)),
|
))
|
||||||
None => return Err(io::Error::new(
|
|
||||||
io::ErrorKind::Other,
|
|
||||||
"Network service is unavailable",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,7 +326,7 @@ mod tests {
|
|||||||
fn deferred_statements_works() {
|
fn deferred_statements_works() {
|
||||||
let mut deferred = DeferredStatements::new();
|
let mut deferred = DeferredStatements::new();
|
||||||
let hash = [1; 32].into();
|
let hash = [1; 32].into();
|
||||||
let sig = H512([2; 64]).into();
|
let sig = H512::from([2; 64]).into();
|
||||||
let sender = [255; 32].into();
|
let sender = [255; 32].into();
|
||||||
|
|
||||||
let statement = SignedStatement {
|
let statement = SignedStatement {
|
||||||
|
|||||||
@@ -24,7 +24,11 @@ use polkadot_primitives::{Block, Hash, SessionKey};
|
|||||||
use polkadot_primitives::parachain::{CandidateReceipt, HeadData, BlockData};
|
use polkadot_primitives::parachain::{CandidateReceipt, HeadData, BlockData};
|
||||||
use substrate_primitives::H512;
|
use substrate_primitives::H512;
|
||||||
use codec::Encode;
|
use codec::Encode;
|
||||||
use substrate_network::{Severity, NodeIndex, PeerInfo, ClientHandle, Context, Roles, message::Message as SubstrateMessage, specialization::Specialization, generic_message::Message as GenericMessage};
|
use substrate_network::{
|
||||||
|
Severity, NodeIndex, PeerInfo, ClientHandle, Context, config::Roles,
|
||||||
|
message::Message as SubstrateMessage, specialization::NetworkSpecialization,
|
||||||
|
generic_message::Message as GenericMessage
|
||||||
|
};
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
@@ -93,7 +97,7 @@ fn make_consensus(parent_hash: Hash, local_key: SessionKey) -> (CurrentConsensus
|
|||||||
|
|
||||||
fn on_message(protocol: &mut PolkadotProtocol, ctx: &mut TestContext, from: NodeIndex, message: Message) {
|
fn on_message(protocol: &mut PolkadotProtocol, ctx: &mut TestContext, from: NodeIndex, message: Message) {
|
||||||
let encoded = message.encode();
|
let encoded = message.encode();
|
||||||
protocol.on_message(ctx, from, GenericMessage::ChainSpecific(encoded));
|
protocol.on_message(ctx, from, &mut Some(GenericMessage::ChainSpecific(encoded)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
description = "Types and utilities for creating and working with parachains"
|
description = "Types and utilities for creating and working with parachains"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate", default-features = false }
|
parity-codec = { version = "2.1", default-features = false }
|
||||||
parity-codec-derive = { git = "https://github.com/paritytech/substrate", default-features = false }
|
parity-codec-derive = { version = "2.1", default-features = false }
|
||||||
wasmi = { version = "0.4", optional = true }
|
wasmi = { version = "0.4", optional = true }
|
||||||
error-chain = { version = "0.12", optional = true }
|
error-chain = { version = "0.12", optional = true }
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -4,13 +4,15 @@ version = "0.1.0"
|
|||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", default_features = false }
|
serde = { version = "1.0", default-features = false }
|
||||||
serde_derive = { version = "1.0", optional = true }
|
serde_derive = { version = "1.0", optional = true }
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate", default_features = false }
|
parity-codec = { version = "2.1", default-features = false }
|
||||||
parity-codec-derive = { git = "https://github.com/paritytech/substrate", default_features = false }
|
parity-codec-derive = { version = "2.1", default-features = false }
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate", default_features = false }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
sr-std = { git = "https://github.com/paritytech/substrate", default_features = false }
|
substrate-client = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
sr-primitives = { git = "https://github.com/paritytech/substrate", default_features = false }
|
sr-version = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
|
sr-std = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
|
sr-primitives = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
substrate-serializer = { git = "https://github.com/paritytech/substrate" }
|
substrate-serializer = { git = "https://github.com/paritytech/substrate" }
|
||||||
@@ -20,8 +22,11 @@ pretty_assertions = "0.4"
|
|||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = [
|
std = [
|
||||||
"parity-codec/std",
|
"parity-codec/std",
|
||||||
|
"parity-codec-derive/std",
|
||||||
"substrate-primitives/std",
|
"substrate-primitives/std",
|
||||||
|
"substrate-client/std",
|
||||||
"sr-std/std",
|
"sr-std/std",
|
||||||
|
"sr-version/std",
|
||||||
"sr-primitives/std",
|
"sr-primitives/std",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde/std",
|
"serde/std",
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ extern crate parity_codec as codec;
|
|||||||
extern crate substrate_primitives as primitives;
|
extern crate substrate_primitives as primitives;
|
||||||
extern crate sr_primitives as runtime_primitives;
|
extern crate sr_primitives as runtime_primitives;
|
||||||
extern crate sr_std as rstd;
|
extern crate sr_std as rstd;
|
||||||
|
extern crate sr_version;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate substrate_serializer;
|
extern crate substrate_serializer;
|
||||||
@@ -39,10 +40,16 @@ extern crate serde_derive;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate substrate_client;
|
||||||
|
|
||||||
use rstd::prelude::*;
|
use rstd::prelude::*;
|
||||||
use runtime_primitives::{generic, traits::BlakeTwo256};
|
use runtime_primitives::{generic, traits::{Extrinsic, BlakeTwo256}};
|
||||||
|
|
||||||
pub mod parachain;
|
pub mod parachain;
|
||||||
|
|
||||||
|
pub use codec::Compact;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use primitives::bytes;
|
use primitives::bytes;
|
||||||
|
|
||||||
@@ -76,7 +83,7 @@ pub type Index = u32;
|
|||||||
pub type Signature = runtime_primitives::Ed25519Signature;
|
pub type Signature = runtime_primitives::Ed25519Signature;
|
||||||
|
|
||||||
/// A timestamp: seconds since the unix epoch.
|
/// A timestamp: seconds since the unix epoch.
|
||||||
pub type Timestamp = u64;
|
pub type Timestamp = Compact<u64>;
|
||||||
|
|
||||||
/// The balance of an account.
|
/// The balance of an account.
|
||||||
/// 128-bits (or 38 significant decimal figures) will allow for 10m currency (10^7) at a resolution
|
/// 128-bits (or 38 significant decimal figures) will allow for 10m currency (10^7) at a resolution
|
||||||
@@ -88,7 +95,7 @@ pub type Timestamp = u64;
|
|||||||
pub type Balance = u128;
|
pub type Balance = u128;
|
||||||
|
|
||||||
/// Header type.
|
/// Header type.
|
||||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256, generic::DigestItem<()>>;
|
pub type Header = generic::Header<BlockNumber, BlakeTwo256, generic::DigestItem<Hash, AccountId>>;
|
||||||
/// Block type.
|
/// Block type.
|
||||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||||
/// Block ID.
|
/// Block ID.
|
||||||
@@ -99,6 +106,8 @@ pub type BlockId = generic::BlockId<Block>;
|
|||||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))]
|
||||||
pub struct UncheckedExtrinsic(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec<u8>);
|
pub struct UncheckedExtrinsic(#[cfg_attr(feature = "std", serde(with="bytes"))] pub Vec<u8>);
|
||||||
|
|
||||||
|
impl Extrinsic for UncheckedExtrinsic {}
|
||||||
|
|
||||||
/// Inherent data to include in a block.
|
/// Inherent data to include in a block.
|
||||||
#[derive(Encode, Decode)]
|
#[derive(Encode, Decode)]
|
||||||
pub struct InherentData {
|
pub struct InherentData {
|
||||||
@@ -106,6 +115,4 @@ pub struct InherentData {
|
|||||||
pub timestamp: Timestamp,
|
pub timestamp: Timestamp,
|
||||||
/// Parachain heads update.
|
/// Parachain heads update.
|
||||||
pub parachain_heads: Vec<::parachain::CandidateReceipt>,
|
pub parachain_heads: Vec<::parachain::CandidateReceipt>,
|
||||||
/// Indices of offline validators.
|
|
||||||
pub offline_indices: Vec<u32>,
|
|
||||||
}
|
}
|
||||||
@@ -20,6 +20,8 @@ use rstd::prelude::*;
|
|||||||
use rstd::cmp::Ordering;
|
use rstd::cmp::Ordering;
|
||||||
use super::Hash;
|
use super::Hash;
|
||||||
|
|
||||||
|
use {AccountId};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use primitives::bytes;
|
use primitives::bytes;
|
||||||
|
|
||||||
@@ -110,7 +112,7 @@ impl CandidateReceipt {
|
|||||||
pub fn check_signature(&self) -> Result<(), ()> {
|
pub fn check_signature(&self) -> Result<(), ()> {
|
||||||
use runtime_primitives::traits::Verify;
|
use runtime_primitives::traits::Verify;
|
||||||
|
|
||||||
if self.signature.verify(&self.block_data_hash.0[..], &self.collator) {
|
if self.signature.verify(self.block_data_hash.as_ref(), &self.collator) {
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(())
|
Err(())
|
||||||
@@ -205,3 +207,27 @@ pub enum Statement {
|
|||||||
/// Vote to advance round after inactive primary.
|
/// Vote to advance round after inactive primary.
|
||||||
Available(Hash),
|
Available(Hash),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decl_runtime_apis! {
|
||||||
|
/// The API for querying the state of parachains on-chain.
|
||||||
|
pub trait ParachainHost {
|
||||||
|
/// Get the current validators.
|
||||||
|
fn validators() -> Vec<AccountId>;
|
||||||
|
/// Get the current duty roster.
|
||||||
|
fn duty_roster() -> DutyRoster;
|
||||||
|
/// Get the currently active parachains.
|
||||||
|
fn active_parachains() -> Vec<Id>;
|
||||||
|
/// Get the given parachain's head data blob.
|
||||||
|
fn parachain_head(id: Id) -> Option<Vec<u8>>;
|
||||||
|
/// Get the given parachain's head code blob.
|
||||||
|
fn parachain_code(id: Id) -> Option<Vec<u8>>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runtime ID module.
|
||||||
|
pub mod id {
|
||||||
|
use sr_version::ApiId;
|
||||||
|
|
||||||
|
/// Parachain host runtime API id.
|
||||||
|
pub const PARACHAIN_HOST: ApiId = *b"parahost";
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,18 +6,19 @@ authors = ["Parity Technologies <admin@parity.io>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
rustc-hex = "1.0"
|
rustc-hex = "1.0"
|
||||||
log = { version = "0.3", optional = true }
|
log = { version = "0.3", optional = true }
|
||||||
serde = { version = "1.0", default_features = false }
|
serde = { version = "1.0", default-features = false }
|
||||||
serde_derive = { version = "1.0", optional = true }
|
serde_derive = { version = "1.0", optional = true }
|
||||||
safe-mix = { version = "1.0", default_features = false}
|
safe-mix = { version = "1.0", default-features = false}
|
||||||
polkadot-primitives = { path = "../primitives", default_features = false }
|
polkadot-primitives = { path = "../primitives", default-features = false }
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate" }
|
parity-codec = "2.1"
|
||||||
parity-codec-derive = { git = "https://github.com/paritytech/substrate" }
|
parity-codec-derive = "2.1"
|
||||||
substrate-serializer = { git = "https://github.com/paritytech/substrate" }
|
substrate-serializer = { git = "https://github.com/paritytech/substrate" }
|
||||||
sr-std = { git = "https://github.com/paritytech/substrate" }
|
sr-std = { git = "https://github.com/paritytech/substrate" }
|
||||||
sr-io = { git = "https://github.com/paritytech/substrate" }
|
sr-io = { git = "https://github.com/paritytech/substrate" }
|
||||||
srml-support = { git = "https://github.com/paritytech/substrate" }
|
srml-support = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-keyring = { git = "https://github.com/paritytech/substrate" }
|
substrate-keyring = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
||||||
srml-balances = { git = "https://github.com/paritytech/substrate" }
|
srml-balances = { git = "https://github.com/paritytech/substrate" }
|
||||||
srml-consensus = { git = "https://github.com/paritytech/substrate" }
|
srml-consensus = { git = "https://github.com/paritytech/substrate" }
|
||||||
srml-council = { git = "https://github.com/paritytech/substrate" }
|
srml-council = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Polkadot.
|
|
||||||
|
|
||||||
// Polkadot 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.
|
|
||||||
|
|
||||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Typesafe block interaction.
|
|
||||||
|
|
||||||
use super::{Call, Block, TIMESTAMP_SET_POSITION, PARACHAINS_SET_POSITION, NOTE_OFFLINE_POSITION};
|
|
||||||
use timestamp::Call as TimestampCall;
|
|
||||||
use parachains::Call as ParachainsCall;
|
|
||||||
use consensus::Call as ConsensusCall;
|
|
||||||
use primitives::parachain::CandidateReceipt;
|
|
||||||
|
|
||||||
/// Provides a type-safe wrapper around a structurally valid block.
|
|
||||||
pub struct CheckedBlock {
|
|
||||||
inner: Block,
|
|
||||||
file_line: Option<(&'static str, u32)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CheckedBlock {
|
|
||||||
/// Create a new checked block. Fails if the block is not structurally valid.
|
|
||||||
pub fn new(block: Block) -> Result<Self, Block> {
|
|
||||||
let has_timestamp = block.extrinsics.get(TIMESTAMP_SET_POSITION as usize).map_or(false, |xt| {
|
|
||||||
!xt.is_signed() && match xt.function {
|
|
||||||
Call::Timestamp(TimestampCall::set(_)) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if !has_timestamp { return Err(block) }
|
|
||||||
|
|
||||||
let has_heads = block.extrinsics.get(PARACHAINS_SET_POSITION as usize).map_or(false, |xt| {
|
|
||||||
!xt.is_signed() && match xt.function {
|
|
||||||
Call::Parachains(ParachainsCall::set_heads(_)) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if !has_heads { return Err(block) }
|
|
||||||
|
|
||||||
Ok(CheckedBlock {
|
|
||||||
inner: block,
|
|
||||||
file_line: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new checked block, asserting that it is valid.
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn new_unchecked(block: Block, file: &'static str, line: u32) -> Self {
|
|
||||||
CheckedBlock {
|
|
||||||
inner: block,
|
|
||||||
file_line: Some((file, line)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the timestamp from the block.
|
|
||||||
pub fn timestamp(&self) -> ::primitives::Timestamp {
|
|
||||||
let x = self.inner.extrinsics.get(TIMESTAMP_SET_POSITION as usize).and_then(|xt| match xt.function {
|
|
||||||
Call::Timestamp(TimestampCall::set(x)) => Some(x),
|
|
||||||
_ => None
|
|
||||||
});
|
|
||||||
|
|
||||||
match x {
|
|
||||||
Some(x) => x,
|
|
||||||
None => panic!("Invalid polkadot block asserted at {:?}", self.file_line),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the parachain heads from the block.
|
|
||||||
pub fn parachain_heads(&self) -> &[CandidateReceipt] {
|
|
||||||
let x = self.inner.extrinsics.get(PARACHAINS_SET_POSITION as usize).and_then(|xt| match xt.function {
|
|
||||||
Call::Parachains(ParachainsCall::set_heads(ref x)) => Some(&x[..]),
|
|
||||||
_ => None
|
|
||||||
});
|
|
||||||
|
|
||||||
match x {
|
|
||||||
Some(x) => x,
|
|
||||||
None => panic!("Invalid polkadot block asserted at {:?}", self.file_line),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Extract the noted missed proposal validator indices (if any) from the block.
|
|
||||||
pub fn noted_offline(&self) -> &[u32] {
|
|
||||||
self.inner.extrinsics.get(NOTE_OFFLINE_POSITION as usize).and_then(|xt| match xt.function {
|
|
||||||
Call::Consensus(ConsensusCall::note_offline(ref x)) => Some(&x[..]),
|
|
||||||
_ => None,
|
|
||||||
}).unwrap_or(&[])
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert into inner block.
|
|
||||||
pub fn into_inner(self) -> Block { self.inner }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::ops::Deref for CheckedBlock {
|
|
||||||
type Target = Block;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Block { &self.inner }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assert that a block is structurally valid. May lead to panic in the future
|
|
||||||
/// in case it isn't.
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! assert_polkadot_block {
|
|
||||||
($block: expr) => {
|
|
||||||
$crate::CheckedBlock::new_unchecked($block, file!(), line!())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+331
-321
@@ -17,39 +17,28 @@
|
|||||||
//! The Polkadot runtime. This can be compiled with ``#[no_std]`, ready for Wasm.
|
//! The Polkadot runtime. This can be compiled with ``#[no_std]`, ready for Wasm.
|
||||||
|
|
||||||
#![cfg_attr(not(feature = "std"), no_std)]
|
#![cfg_attr(not(feature = "std"), no_std)]
|
||||||
|
// `construct_runtime!` does a lot of recursion and requires us to increase the limit to 256.
|
||||||
#[cfg(feature = "std")]
|
#![recursion_limit="256"]
|
||||||
#[macro_use]
|
|
||||||
extern crate serde_derive;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
extern crate serde;
|
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate sr_io as runtime_io;
|
extern crate parity_codec_derive;
|
||||||
|
extern crate parity_codec as codec;
|
||||||
#[macro_use]
|
|
||||||
extern crate srml_support;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate sr_primitives as runtime_primitives;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
#[macro_use]
|
|
||||||
extern crate hex_literal;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate substrate_serializer;
|
|
||||||
|
|
||||||
extern crate substrate_primitives;
|
extern crate substrate_primitives;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate substrate_client as client;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate sr_std as rstd;
|
extern crate sr_std as rstd;
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate sr_io;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate parity_codec_derive;
|
extern crate sr_version as version;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate sr_primitives;
|
||||||
|
|
||||||
extern crate polkadot_primitives as primitives;
|
#[macro_use]
|
||||||
extern crate parity_codec as codec;
|
extern crate srml_support;
|
||||||
extern crate srml_balances as balances;
|
extern crate srml_balances as balances;
|
||||||
extern crate srml_consensus as consensus;
|
extern crate srml_consensus as consensus;
|
||||||
extern crate srml_council as council;
|
extern crate srml_council as council;
|
||||||
@@ -60,69 +49,78 @@ extern crate srml_staking as staking;
|
|||||||
extern crate srml_system as system;
|
extern crate srml_system as system;
|
||||||
extern crate srml_timestamp as timestamp;
|
extern crate srml_timestamp as timestamp;
|
||||||
extern crate srml_treasury as treasury;
|
extern crate srml_treasury as treasury;
|
||||||
#[macro_use]
|
|
||||||
extern crate sr_version as version;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
extern crate polkadot_primitives as primitives;
|
||||||
mod checked_block;
|
|
||||||
mod parachains;
|
mod parachains;
|
||||||
mod utils;
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use checked_block::CheckedBlock;
|
use codec::{Encode, Decode};
|
||||||
pub use utils::{inherent_extrinsics, check_extrinsic};
|
|
||||||
pub use balances::address::Address as RawAddress;
|
|
||||||
|
|
||||||
use rstd::prelude::*;
|
use rstd::prelude::*;
|
||||||
use codec::{Encode, Decode, Input};
|
|
||||||
use substrate_primitives::u32_trait::{_2, _4};
|
use substrate_primitives::u32_trait::{_2, _4};
|
||||||
use primitives::{AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, SessionKey, Signature};
|
use primitives::{
|
||||||
use runtime_primitives::{generic, traits::{Convert, BlakeTwo256, DigestItem}};
|
AccountId, AccountIndex, Balance, BlockNumber, Hash, Index, SessionKey, Signature,
|
||||||
|
parachain, parachain::runtime::ParachainHost, parachain::id::PARACHAIN_HOST,
|
||||||
|
};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use primitives::Block as GBlock;
|
||||||
|
use client::{block_builder::api::runtime::*, runtime_api::{runtime::*, id::*}};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use client::runtime_api::ApiExt;
|
||||||
|
use sr_primitives::ApplyResult;
|
||||||
|
use sr_primitives::transaction_validity::TransactionValidity;
|
||||||
|
use sr_primitives::generic;
|
||||||
|
use sr_primitives::traits::{Convert, BlakeTwo256, Block as BlockT};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use sr_primitives::traits::ApiRef;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use substrate_primitives::AuthorityId;
|
||||||
use version::RuntimeVersion;
|
use version::RuntimeVersion;
|
||||||
use council::{motions as council_motions, voting as council_voting};
|
use council::{motions as council_motions, voting as council_voting};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use runtime_primitives::BuildStorage;
|
use council::seats as council_seats;
|
||||||
|
#[cfg(any(feature = "std", test))]
|
||||||
|
use version::NativeVersion;
|
||||||
|
use substrate_primitives::OpaqueMetadata;
|
||||||
|
|
||||||
|
#[cfg(any(feature = "std", test))]
|
||||||
|
pub use sr_primitives::BuildStorage;
|
||||||
pub use consensus::Call as ConsensusCall;
|
pub use consensus::Call as ConsensusCall;
|
||||||
pub use timestamp::Call as TimestampCall;
|
pub use timestamp::Call as TimestampCall;
|
||||||
|
pub use balances::Call as BalancesCall;
|
||||||
pub use parachains::Call as ParachainsCall;
|
pub use parachains::Call as ParachainsCall;
|
||||||
|
pub use sr_primitives::{Permill, Perbill};
|
||||||
|
pub use timestamp::BlockPeriod;
|
||||||
|
pub use srml_support::{StorageValue, RuntimeMetadata};
|
||||||
|
|
||||||
/// The position of the timestamp set extrinsic.
|
const TIMESTAMP_SET_POSITION: u32 = 0;
|
||||||
pub const TIMESTAMP_SET_POSITION: u32 = 0;
|
const NOTE_OFFLINE_POSITION: u32 = 1;
|
||||||
/// The position of the parachains set extrinsic.
|
const PARACHAINS_SET_POSITION: u32 = 2;
|
||||||
pub const PARACHAINS_SET_POSITION: u32 = 1;
|
|
||||||
/// The position of the note_offline in the block, if it exists.
|
|
||||||
pub const NOTE_OFFLINE_POSITION: u32 = 2;
|
|
||||||
|
|
||||||
/// Block header type as expected by this runtime.
|
/// Runtime version.
|
||||||
pub type Header = generic::Header<BlockNumber, BlakeTwo256, Log>;
|
|
||||||
/// The address format for describing accounts.
|
|
||||||
pub type Address = balances::Address<Runtime>;
|
|
||||||
/// Block Id type for this block.
|
|
||||||
pub type BlockId = generic::BlockId<Block>;
|
|
||||||
/// Unchecked extrinsic type as expected by this runtime.
|
|
||||||
pub type UncheckedExtrinsic = generic::UncheckedExtrinsic<Address, Index, Call, Signature>;
|
|
||||||
/// Extrinsic type that has already been checked.
|
|
||||||
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Index, Call>;
|
|
||||||
/// Block type as expected by this runtime.
|
|
||||||
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
|
||||||
|
|
||||||
/// Runtime runtime type used to parameterize the various modules.
|
|
||||||
// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted.
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "std", derive(Debug, Serialize, Deserialize))]
|
|
||||||
pub struct Runtime;
|
|
||||||
|
|
||||||
/// Polkadot runtime version.
|
|
||||||
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
pub const VERSION: RuntimeVersion = RuntimeVersion {
|
||||||
spec_name: ver_str!("polkadot"),
|
spec_name: ver_str!("polkadot"),
|
||||||
impl_name: ver_str!("parity-polkadot"),
|
impl_name: ver_str!("parity-polkadot"),
|
||||||
authoring_version: 1,
|
authoring_version: 1,
|
||||||
spec_version: 101,
|
spec_version: 101,
|
||||||
impl_version: 0,
|
impl_version: 0,
|
||||||
|
apis: apis_vec!([
|
||||||
|
(BLOCK_BUILDER, 1),
|
||||||
|
(TAGGED_TRANSACTION_QUEUE, 1),
|
||||||
|
(METADATA, 1),
|
||||||
|
(PARACHAIN_HOST, 1),
|
||||||
|
]),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Native version.
|
||||||
|
#[cfg(any(feature = "std", test))]
|
||||||
|
pub fn native_version() -> NativeVersion {
|
||||||
|
NativeVersion {
|
||||||
|
runtime_version: VERSION,
|
||||||
|
can_author_with: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl system::Trait for Runtime {
|
impl system::Trait for Runtime {
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Index = Index;
|
type Index = Index;
|
||||||
@@ -131,11 +129,10 @@ impl system::Trait for Runtime {
|
|||||||
type Hashing = BlakeTwo256;
|
type Hashing = BlakeTwo256;
|
||||||
type Digest = generic::Digest<Log>;
|
type Digest = generic::Digest<Log>;
|
||||||
type AccountId = AccountId;
|
type AccountId = AccountId;
|
||||||
type Header = Header;
|
type Header = generic::Header<BlockNumber, BlakeTwo256, Log>;
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
|
type Log = Log;
|
||||||
}
|
}
|
||||||
/// System module for this concrete runtime.
|
|
||||||
pub type System = system::Module<Runtime>;
|
|
||||||
|
|
||||||
impl balances::Trait for Runtime {
|
impl balances::Trait for Runtime {
|
||||||
type Balance = Balance;
|
type Balance = Balance;
|
||||||
@@ -144,8 +141,6 @@ impl balances::Trait for Runtime {
|
|||||||
type EnsureAccountLiquid = Staking;
|
type EnsureAccountLiquid = Staking;
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
/// Staking module for this concrete runtime.
|
|
||||||
pub type Balances = balances::Module<Runtime>;
|
|
||||||
|
|
||||||
impl consensus::Trait for Runtime {
|
impl consensus::Trait for Runtime {
|
||||||
const NOTE_OFFLINE_POSITION: u32 = NOTE_OFFLINE_POSITION;
|
const NOTE_OFFLINE_POSITION: u32 = NOTE_OFFLINE_POSITION;
|
||||||
@@ -153,21 +148,17 @@ impl consensus::Trait for Runtime {
|
|||||||
type SessionKey = SessionKey;
|
type SessionKey = SessionKey;
|
||||||
type OnOfflineValidator = Staking;
|
type OnOfflineValidator = Staking;
|
||||||
}
|
}
|
||||||
/// Consensus module for this concrete runtime.
|
|
||||||
pub type Consensus = consensus::Module<Runtime>;
|
|
||||||
|
|
||||||
impl timestamp::Trait for Runtime {
|
impl timestamp::Trait for Runtime {
|
||||||
const TIMESTAMP_SET_POSITION: u32 = TIMESTAMP_SET_POSITION;
|
const TIMESTAMP_SET_POSITION: u32 = TIMESTAMP_SET_POSITION;
|
||||||
type Moment = u64;
|
type Moment = u64;
|
||||||
}
|
}
|
||||||
/// Timestamp module for this concrete runtime.
|
|
||||||
pub type Timestamp = timestamp::Module<Runtime>;
|
|
||||||
|
|
||||||
/// Session key conversion.
|
/// Session key conversion.
|
||||||
pub struct SessionKeyConversion;
|
pub struct SessionKeyConversion;
|
||||||
impl Convert<AccountId, SessionKey> for SessionKeyConversion {
|
impl Convert<AccountId, SessionKey> for SessionKeyConversion {
|
||||||
fn convert(a: AccountId) -> SessionKey {
|
fn convert(a: AccountId) -> SessionKey {
|
||||||
a.0.into()
|
a.to_fixed_bytes().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,297 +167,316 @@ impl session::Trait for Runtime {
|
|||||||
type OnSessionChange = Staking;
|
type OnSessionChange = Staking;
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
/// Session module for this concrete runtime.
|
|
||||||
pub type Session = session::Module<Runtime>;
|
|
||||||
|
|
||||||
impl staking::Trait for Runtime {
|
impl staking::Trait for Runtime {
|
||||||
type OnRewardMinted = Treasury;
|
type OnRewardMinted = Treasury;
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
/// Staking module for this concrete runtime.
|
|
||||||
pub type Staking = staking::Module<Runtime>;
|
|
||||||
|
|
||||||
impl democracy::Trait for Runtime {
|
impl democracy::Trait for Runtime {
|
||||||
type Proposal = Call;
|
type Proposal = Call;
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
/// Democracy module for this concrete runtime.
|
|
||||||
pub type Democracy = democracy::Module<Runtime>;
|
|
||||||
|
|
||||||
impl council::Trait for Runtime {
|
impl council::Trait for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Council module for this concrete runtime.
|
|
||||||
pub type Council = council::Module<Runtime>;
|
|
||||||
|
|
||||||
impl council::voting::Trait for Runtime {
|
impl council::voting::Trait for Runtime {
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Council voting module for this concrete runtime.
|
|
||||||
pub type CouncilVoting = council::voting::Module<Runtime>;
|
|
||||||
|
|
||||||
impl council::motions::Trait for Runtime {
|
impl council::motions::Trait for Runtime {
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Proposal = Call;
|
type Proposal = Call;
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Council motions module for this concrete runtime.
|
|
||||||
pub type CouncilMotions = council_motions::Module<Runtime>;
|
|
||||||
|
|
||||||
impl treasury::Trait for Runtime {
|
impl treasury::Trait for Runtime {
|
||||||
type ApproveOrigin = council_motions::EnsureMembers<_4>;
|
type ApproveOrigin = council_motions::EnsureMembers<_4>;
|
||||||
type RejectOrigin = council_motions::EnsureMembers<_2>;
|
type RejectOrigin = council_motions::EnsureMembers<_2>;
|
||||||
type Event = Event;
|
type Event = Event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Treasury module for this concrete runtime.
|
|
||||||
pub type Treasury = treasury::Module<Runtime>;
|
|
||||||
|
|
||||||
impl parachains::Trait for Runtime {
|
impl parachains::Trait for Runtime {
|
||||||
const SET_POSITION: u32 = PARACHAINS_SET_POSITION;
|
const SET_POSITION: u32 = PARACHAINS_SET_POSITION;
|
||||||
}
|
}
|
||||||
pub type Parachains = parachains::Module<Runtime>;
|
|
||||||
|
|
||||||
impl_outer_event! {
|
construct_runtime!(
|
||||||
pub enum Event for Runtime {
|
pub enum Runtime with Log(InternalLog: DigestItem<Hash, SessionKey>) where
|
||||||
//consensus,
|
Block = Block,
|
||||||
balances,
|
UncheckedExtrinsic = UncheckedExtrinsic
|
||||||
//timetstamp,
|
{
|
||||||
session,
|
System: system::{default, Log(ChangesTrieRoot)},
|
||||||
staking,
|
Timestamp: timestamp::{Module, Call, Storage, Config<T>, Inherent},
|
||||||
democracy,
|
Consensus: consensus::{Module, Call, Storage, Config<T>, Log(AuthoritiesChange), Inherent},
|
||||||
council,
|
Balances: balances,
|
||||||
council_voting,
|
Session: session,
|
||||||
council_motions,
|
Staking: staking,
|
||||||
treasury
|
Democracy: democracy,
|
||||||
|
Council: council::{Module, Call, Storage, Event<T>},
|
||||||
|
CouncilVoting: council_voting,
|
||||||
|
CouncilMotions: council_motions::{Module, Call, Storage, Event<T>, Origin},
|
||||||
|
CouncilSeats: council_seats::{Config<T>},
|
||||||
|
Treasury: treasury,
|
||||||
|
Parachains: parachains::{Module, Call, Storage, Config<T>, Inherent},
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl_outer_log! {
|
|
||||||
pub enum Log(InternalLog: DigestItem<SessionKey>) for Runtime {
|
|
||||||
consensus(AuthoritiesChange)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_outer_origin! {
|
|
||||||
pub enum Origin for Runtime {
|
|
||||||
council_motions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_outer_dispatch! {
|
|
||||||
/// Call type for polkadot transactions.
|
|
||||||
pub enum Call where origin: <Runtime as system::Trait>::Origin {
|
|
||||||
Consensus,
|
|
||||||
Balances,
|
|
||||||
Session,
|
|
||||||
Staking,
|
|
||||||
Timestamp,
|
|
||||||
Democracy,
|
|
||||||
Council,
|
|
||||||
CouncilVoting,
|
|
||||||
CouncilMotions,
|
|
||||||
Parachains,
|
|
||||||
Treasury,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_outer_config! {
|
|
||||||
pub struct GenesisConfig for Runtime {
|
|
||||||
ConsensusConfig => consensus,
|
|
||||||
SystemConfig => system,
|
|
||||||
BalancesConfig => balances,
|
|
||||||
SessionConfig => session,
|
|
||||||
StakingConfig => staking,
|
|
||||||
DemocracyConfig => democracy,
|
|
||||||
CouncilConfig => council,
|
|
||||||
TimestampConfig => timestamp,
|
|
||||||
TreasuryConfig => treasury,
|
|
||||||
ParachainsConfig => parachains,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type AllModules = (
|
|
||||||
Consensus,
|
|
||||||
Balances,
|
|
||||||
Session,
|
|
||||||
Staking,
|
|
||||||
Timestamp,
|
|
||||||
Democracy,
|
|
||||||
Council,
|
|
||||||
CouncilVoting,
|
|
||||||
CouncilMotions,
|
|
||||||
Parachains,
|
|
||||||
Treasury,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
impl_json_metadata!(
|
/// The address format for describing accounts.
|
||||||
for Runtime with modules
|
pub use balances::address::Address as RawAddress;
|
||||||
system::Module with Storage,
|
/// The address format for describing accounts.
|
||||||
consensus::Module with Storage,
|
pub type Address = balances::Address<Runtime>;
|
||||||
balances::Module with Storage,
|
/// Block header type as expected by this runtime.
|
||||||
timestamp::Module with Storage,
|
pub type Header = generic::Header<BlockNumber, BlakeTwo256, Log>;
|
||||||
session::Module with Storage,
|
/// Block type as expected by this runtime.
|
||||||
staking::Module with Storage,
|
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
|
||||||
democracy::Module with Storage,
|
/// A Block signed with a Justification
|
||||||
council::Module with Storage,
|
pub type SignedBlock = generic::SignedBlock<Block>;
|
||||||
council_voting::Module with Storage,
|
/// BlockId type as expected by this runtime.
|
||||||
council_motions::Module with Storage,
|
pub type BlockId = generic::BlockId<Block>;
|
||||||
treasury::Module with Storage,
|
/// Unchecked extrinsic type as expected by this runtime.
|
||||||
parachains::Module with Storage,
|
pub type UncheckedExtrinsic = generic::UncheckedMortalExtrinsic<Address, Index, Call, Signature>;
|
||||||
);
|
/// Extrinsic type that has already been checked.
|
||||||
|
pub type CheckedExtrinsic = generic::CheckedExtrinsic<AccountId, Index, Call>;
|
||||||
impl DigestItem for Log {
|
|
||||||
type AuthorityId = SessionKey;
|
|
||||||
|
|
||||||
fn as_authorities_change(&self) -> Option<&[Self::AuthorityId]> {
|
|
||||||
match self.0 {
|
|
||||||
InternalLog::consensus(ref item) => item.as_authorities_change(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Executive: handles dispatch to the various modules.
|
/// Executive: handles dispatch to the various modules.
|
||||||
pub type Executive = executive::Executive<Runtime, Block, Balances, Balances, AllModules>;
|
pub type Executive = executive::Executive<Runtime, Block, balances::ChainContext<Runtime>, Balances, AllModules>;
|
||||||
|
|
||||||
pub mod api {
|
#[cfg(feature = "std")]
|
||||||
impl_stubs!(
|
pub struct ClientWithApi {
|
||||||
version => |()| super::VERSION,
|
call: ::std::ptr::NonNull<client::runtime_api::CallApiAt<GBlock>>,
|
||||||
json_metadata => |()| super::Runtime::json_metadata(),
|
commit_on_success: ::std::cell::RefCell<bool>,
|
||||||
authorities => |()| super::Consensus::authorities(),
|
initialised_block: ::std::cell::RefCell<Option<GBlockId>>,
|
||||||
initialise_block => |header| super::Executive::initialise_block(&header),
|
changes: ::std::cell::RefCell<client::runtime_api::OverlayedChanges>,
|
||||||
apply_extrinsic => |extrinsic| super::Executive::apply_extrinsic(extrinsic),
|
|
||||||
execute_block => |block| super::Executive::execute_block(block),
|
|
||||||
finalise_block => |()| super::Executive::finalise_block(),
|
|
||||||
inherent_extrinsics => |(inherent, spec_version)| super::inherent_extrinsics(inherent, spec_version),
|
|
||||||
validator_count => |()| super::Session::validator_count(),
|
|
||||||
validators => |()| super::Session::validators(),
|
|
||||||
timestamp => |()| super::Timestamp::get(),
|
|
||||||
random_seed => |()| super::System::random_seed(),
|
|
||||||
account_nonce => |account| super::System::account_nonce(&account),
|
|
||||||
lookup_address => |address| super::Balances::lookup_address(address),
|
|
||||||
duty_roster => |()| super::Parachains::calculate_duty_roster(),
|
|
||||||
active_parachains => |()| super::Parachains::active_parachains(),
|
|
||||||
parachain_head => |id| super::Parachains::parachain_head(&id),
|
|
||||||
parachain_code => |id| super::Parachains::parachain_code(&id)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(feature = "std")]
|
||||||
mod tests {
|
unsafe impl Send for ClientWithApi {}
|
||||||
use super::*;
|
#[cfg(feature = "std")]
|
||||||
use substrate_primitives as primitives;
|
unsafe impl Sync for ClientWithApi {}
|
||||||
use codec::{Encode, Decode};
|
|
||||||
use substrate_primitives::hexdisplay::HexDisplay;
|
|
||||||
use substrate_serializer as ser;
|
|
||||||
use runtime_primitives::traits::Header as HeaderT;
|
|
||||||
type Digest = generic::Digest<Log>;
|
|
||||||
|
|
||||||
#[test]
|
#[cfg(feature = "std")]
|
||||||
fn test_header_serialization() {
|
impl ApiExt for ClientWithApi {
|
||||||
let header = Header {
|
fn map_api_result<F: FnOnce(&Self) -> Result<R, E>, R, E>(&self, map_call: F) -> Result<R, E> {
|
||||||
parent_hash: 5.into(),
|
*self.commit_on_success.borrow_mut() = false;
|
||||||
number: 67,
|
let res = map_call(self);
|
||||||
state_root: 3.into(),
|
*self.commit_on_success.borrow_mut() = true;
|
||||||
extrinsics_root: 6.into(),
|
|
||||||
digest: Digest::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
assert_eq!(ser::to_string_pretty(&header), r#"{
|
self.commit_on_ok(&res);
|
||||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000005",
|
|
||||||
"number": 67,
|
|
||||||
"stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003",
|
|
||||||
"extrinsicsRoot": "0x0000000000000000000000000000000000000000000000000000000000000006",
|
|
||||||
"digest": {
|
|
||||||
"logs": []
|
|
||||||
}
|
|
||||||
}"#);
|
|
||||||
|
|
||||||
let v = header.encode();
|
res
|
||||||
assert_eq!(Header::decode(&mut &v[..]).unwrap(), header);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block_encoding_round_trip() {
|
|
||||||
let mut block = Block {
|
|
||||||
header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()),
|
|
||||||
extrinsics: vec![
|
|
||||||
UncheckedExtrinsic::new_unsigned(
|
|
||||||
Default::default(),
|
|
||||||
Call::Timestamp(timestamp::Call::set(100_000_000))
|
|
||||||
)
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let raw = block.encode();
|
|
||||||
let decoded = Block::decode(&mut &raw[..]).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(block, decoded);
|
|
||||||
|
|
||||||
block.extrinsics.push(UncheckedExtrinsic::new_unsigned(
|
|
||||||
10101,
|
|
||||||
Call::Staking(staking::Call::stake())
|
|
||||||
));
|
|
||||||
|
|
||||||
let raw = block.encode();
|
|
||||||
let decoded = Block::decode(&mut &raw[..]).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(block, decoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn block_encoding_substrate_round_trip() {
|
|
||||||
let mut block = Block {
|
|
||||||
header: Header::new(1, Default::default(), Default::default(), Default::default(), Default::default()),
|
|
||||||
extrinsics: vec![
|
|
||||||
UncheckedExtrinsic::new_unsigned(
|
|
||||||
Default::default(),
|
|
||||||
Call::Timestamp(timestamp::Call::set(100_000_000))
|
|
||||||
)
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
block.extrinsics.push(UncheckedExtrinsic::new_unsigned(
|
|
||||||
10101,
|
|
||||||
Call::Staking(staking::Call::stake())
|
|
||||||
));
|
|
||||||
|
|
||||||
let raw = block.encode();
|
|
||||||
let decoded_primitive = ::primitives::Block::decode(&mut &raw[..]).unwrap();
|
|
||||||
let encoded_primitive = decoded_primitive.encode();
|
|
||||||
let decoded = Block::decode(&mut &encoded_primitive[..]).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(block, decoded);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn serialize_unchecked() {
|
|
||||||
let tx = UncheckedExtrinsic::new_signed(
|
|
||||||
999,
|
|
||||||
Call::Timestamp(TimestampCall::set(135135)),
|
|
||||||
AccountId::from([1; 32]).into(),
|
|
||||||
runtime_primitives::Ed25519Signature(primitives::hash::H512([0; 64])).into()
|
|
||||||
);
|
|
||||||
|
|
||||||
// 6f000000
|
|
||||||
// ff0101010101010101010101010101010101010101010101010101010101010101
|
|
||||||
// e7030000
|
|
||||||
// 0300
|
|
||||||
// df0f0200
|
|
||||||
// 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
||||||
|
|
||||||
let v = Encode::encode(&tx);
|
|
||||||
assert_eq!(&v[..], &hex!["7000000001ff010101010101010101010101010101010101010101010101010101010101010100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e70300000400df0f020000000000"][..]);
|
|
||||||
println!("{}", HexDisplay::from(&v));
|
|
||||||
assert_eq!(UncheckedExtrinsic::decode(&mut &v[..]).unwrap(), tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parachain_calls_are_privcall() {
|
|
||||||
let _register = Call::Parachains(parachains::Call::register_parachain(0.into(), vec![1, 2, 3], vec![]));
|
|
||||||
let _deregister = Call::Parachains(parachains::Call::deregister_parachain(0.into()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl client::runtime_api::ConstructRuntimeApi<GBlock> for ClientWithApi {
|
||||||
|
fn construct_runtime_api<'a, T: client::runtime_api::CallApiAt<GBlock>>(call: &'a T) -> ApiRef<'a, Self> {
|
||||||
|
ClientWithApi {
|
||||||
|
call: unsafe {
|
||||||
|
::std::ptr::NonNull::new_unchecked(
|
||||||
|
::std::mem::transmute(
|
||||||
|
call as &client::runtime_api::CallApiAt<GBlock>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
commit_on_success: true.into(),
|
||||||
|
initialised_block: None.into(),
|
||||||
|
changes: Default::default(),
|
||||||
|
}.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl ClientWithApi {
|
||||||
|
fn call_api_at<A: Encode, R: Decode>(
|
||||||
|
&self,
|
||||||
|
at: &GBlockId,
|
||||||
|
function: &'static str,
|
||||||
|
args: &A
|
||||||
|
) -> client::error::Result<R> {
|
||||||
|
let res = unsafe {
|
||||||
|
self.call.as_ref().call_api_at(
|
||||||
|
at,
|
||||||
|
function,
|
||||||
|
args.encode(),
|
||||||
|
&mut *self.changes.borrow_mut(),
|
||||||
|
&mut *self.initialised_block.borrow_mut()
|
||||||
|
).and_then(|r|
|
||||||
|
R::decode(&mut &r[..])
|
||||||
|
.ok_or_else(||
|
||||||
|
client::error::ErrorKind::CallResultDecode(function).into()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.commit_on_ok(&res);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn commit_on_ok<R, E>(&self, res: &Result<R, E>) {
|
||||||
|
if *self.commit_on_success.borrow() {
|
||||||
|
if res.is_err() {
|
||||||
|
self.changes.borrow_mut().discard_prospective();
|
||||||
|
} else {
|
||||||
|
self.changes.borrow_mut().commit_prospective();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
type GBlockId = generic::BlockId<GBlock>;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl client::runtime_api::Core<GBlock> for ClientWithApi {
|
||||||
|
fn version(&self, at: &GBlockId) -> Result<RuntimeVersion, client::error::Error> {
|
||||||
|
self.call_api_at(at, "version", &())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authorities(&self, at: &GBlockId) -> Result<Vec<AuthorityId>, client::error::Error> {
|
||||||
|
self.call_api_at(at, "authorities", &())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_block(&self, at: &GBlockId, block: &GBlock) -> Result<(), client::error::Error> {
|
||||||
|
self.call_api_at(at, "execute_block", block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialise_block(&self, at: &GBlockId, header: &<GBlock as BlockT>::Header) -> Result<(), client::error::Error> {
|
||||||
|
self.call_api_at(at, "initialise_block", header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl client::block_builder::api::BlockBuilder<GBlock> for ClientWithApi {
|
||||||
|
fn apply_extrinsic(&self, at: &GBlockId, extrinsic: &<GBlock as BlockT>::Extrinsic) -> Result<ApplyResult, client::error::Error> {
|
||||||
|
self.call_api_at(at, "apply_extrinsic", extrinsic)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalise_block(&self, at: &GBlockId) -> Result<<GBlock as BlockT>::Header, client::error::Error> {
|
||||||
|
self.call_api_at(at, "finalise_block", &())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inherent_extrinsics<Inherent: Decode + Encode, Unchecked: Decode + Encode>(
|
||||||
|
&self, at: &GBlockId, inherent: &Inherent
|
||||||
|
) -> Result<Vec<Unchecked>, client::error::Error> {
|
||||||
|
self.call_api_at(at, "inherent_extrinsics", inherent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_inherents<Inherent: Decode + Encode, Error: Decode + Encode>(&self, at: &GBlockId, block: &GBlock, inherent: &Inherent) -> Result<Result<(), Error>, client::error::Error> {
|
||||||
|
self.call_api_at(at, "check_inherents", &(block, inherent))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_seed(&self, at: &GBlockId) -> Result<<GBlock as BlockT>::Hash, client::error::Error> {
|
||||||
|
self.call_api_at(at, "random_seed", &())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl client::runtime_api::TaggedTransactionQueue<GBlock> for ClientWithApi {
|
||||||
|
fn validate_transaction(
|
||||||
|
&self,
|
||||||
|
at: &GBlockId,
|
||||||
|
utx: &<GBlock as BlockT>::Extrinsic
|
||||||
|
) -> Result<TransactionValidity, client::error::Error> {
|
||||||
|
self.call_api_at(at, "validate_transaction", utx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl client::runtime_api::Metadata<GBlock> for ClientWithApi {
|
||||||
|
fn metadata(&self, at: &GBlockId) -> Result<OpaqueMetadata, client::error::Error> {
|
||||||
|
self.call_api_at(at, "metadata", &())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl ::primitives::parachain::ParachainHost<GBlock> for ClientWithApi {
|
||||||
|
fn validators(&self, at: &GBlockId) -> Result<Vec<primitives::AccountId>, client::error::Error> {
|
||||||
|
self.call_api_at(at, "validators", &())
|
||||||
|
}
|
||||||
|
fn duty_roster(&self, at: &GBlockId) -> Result<primitives::parachain::DutyRoster, client::error::Error> {
|
||||||
|
self.call_api_at(at, "calculate_duty_roster", &())
|
||||||
|
}
|
||||||
|
fn active_parachains(&self, at: &GBlockId) -> Result<Vec<parachain::Id>, client::error::Error> {
|
||||||
|
self.call_api_at(at, "active_parachains", &())
|
||||||
|
}
|
||||||
|
fn parachain_head(&self, at: &GBlockId, id: ¶chain::Id) -> Result<Option<Vec<u8>>, client::error::Error> {
|
||||||
|
self.call_api_at(at, "parachain_head", &id)
|
||||||
|
}
|
||||||
|
fn parachain_code(&self, at: &GBlockId, id: ¶chain::Id) -> Result<Option<Vec<u8>>, client::error::Error> {
|
||||||
|
self.call_api_at(at, "parachain_code", &id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_runtime_apis! {
|
||||||
|
impl Core<Block> for Runtime {
|
||||||
|
fn version() -> RuntimeVersion {
|
||||||
|
VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
fn authorities() -> Vec<SessionKey> {
|
||||||
|
Consensus::authorities()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute_block(block: Block) {
|
||||||
|
Executive::execute_block(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialise_block(header: <Block as BlockT>::Header) {
|
||||||
|
Executive::initialise_block(&header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Metadata for Runtime {
|
||||||
|
fn metadata() -> OpaqueMetadata {
|
||||||
|
Runtime::metadata().into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockBuilder<Block, InherentData, UncheckedExtrinsic, InherentData, InherentError> for Runtime {
|
||||||
|
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult {
|
||||||
|
Executive::apply_extrinsic(extrinsic)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finalise_block() -> <Block as BlockT>::Header {
|
||||||
|
Executive::finalise_block()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inherent_extrinsics(data: InherentData) -> Vec<UncheckedExtrinsic> {
|
||||||
|
data.create_inherent_extrinsics()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_inherents(block: Block, data: InherentData) -> Result<(), InherentError> {
|
||||||
|
data.check_inherents(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random_seed() -> <Block as BlockT>::Hash {
|
||||||
|
System::random_seed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TaggedTransactionQueue<Block> for Runtime {
|
||||||
|
fn validate_transaction(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
|
||||||
|
Executive::validate_transaction(tx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ParachainHost for Runtime {
|
||||||
|
fn validators() -> Vec<AccountId> {
|
||||||
|
Session::validators()
|
||||||
|
}
|
||||||
|
fn duty_roster() -> parachain::DutyRoster {
|
||||||
|
Parachains::calculate_duty_roster()
|
||||||
|
}
|
||||||
|
fn active_parachains() -> Vec<parachain::Id> {
|
||||||
|
Parachains::active_parachains()
|
||||||
|
}
|
||||||
|
fn parachain_head(id: parachain::Id) -> Option<Vec<u8>> {
|
||||||
|
Parachains::parachain_head(&id)
|
||||||
|
}
|
||||||
|
fn parachain_code(id: parachain::Id) -> Option<Vec<u8>> {
|
||||||
|
Parachains::parachain_code(&id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
+171
-151
@@ -19,7 +19,8 @@
|
|||||||
use rstd::prelude::*;
|
use rstd::prelude::*;
|
||||||
use codec::Decode;
|
use codec::Decode;
|
||||||
|
|
||||||
use runtime_primitives::traits::{Hash, BlakeTwo256, OnFinalise};
|
use sr_primitives::{RuntimeString, traits::{Extrinsic, Block as BlockT,
|
||||||
|
Hash, BlakeTwo256, ProvideInherent}};
|
||||||
use primitives::parachain::{Id, Chain, DutyRoster, CandidateReceipt};
|
use primitives::parachain::{Id, Chain, DutyRoster, CandidateReceipt};
|
||||||
use {system, session};
|
use {system, session};
|
||||||
|
|
||||||
@@ -27,43 +28,133 @@ use srml_support::{StorageValue, StorageMap};
|
|||||||
use srml_support::dispatch::Result;
|
use srml_support::dispatch::Result;
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
#[cfg(any(feature = "std", test))]
|
||||||
use rstd::marker::PhantomData;
|
use sr_primitives::{self, ChildrenStorageMap};
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
use system::ensure_inherent;
|
||||||
use runtime_primitives;
|
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
pub trait Trait: session::Trait {
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use system::{ensure_root, ensure_inherent};
|
|
||||||
|
|
||||||
pub trait Trait: system::Trait<Hash = ::primitives::Hash> + session::Trait {
|
|
||||||
/// The position of the set_heads call in the block.
|
/// The position of the set_heads call in the block.
|
||||||
const SET_POSITION: u32;
|
const SET_POSITION: u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
decl_storage! {
|
||||||
|
trait Store for Module<T: Trait> as Parachains {
|
||||||
|
// Vector of all parachain IDs.
|
||||||
|
pub Parachains get(active_parachains): Vec<Id>;
|
||||||
|
// The parachains registered at present.
|
||||||
|
pub Code get(parachain_code): map Id => Option<Vec<u8>>;
|
||||||
|
// The heads of the parachains registered at present. these are kept sorted.
|
||||||
|
pub Heads get(parachain_head): map Id => Option<Vec<u8>>;
|
||||||
|
|
||||||
|
// Did the parachain heads get updated in this block?
|
||||||
|
DidUpdate: bool;
|
||||||
|
}
|
||||||
|
add_extra_genesis {
|
||||||
|
config(parachains): Vec<(Id, Vec<u8>, Vec<u8>)>;
|
||||||
|
build(|storage: &mut sr_primitives::StorageMap, _: &mut ChildrenStorageMap, config: &GenesisConfig<T>| {
|
||||||
|
use codec::Encode;
|
||||||
|
|
||||||
|
let mut p = config.parachains.clone();
|
||||||
|
p.sort_unstable_by_key(|&(ref id, _, _)| id.clone());
|
||||||
|
p.dedup_by_key(|&mut (ref id, _, _)| id.clone());
|
||||||
|
|
||||||
|
let only_ids: Vec<_> = p.iter().map(|&(ref id, _, _)| id).cloned().collect();
|
||||||
|
|
||||||
|
storage.insert(GenesisConfig::<T>::hash(<Parachains<T>>::key()).to_vec(), only_ids.encode());
|
||||||
|
|
||||||
|
for (id, code, genesis) in p {
|
||||||
|
let code_key = GenesisConfig::<T>::hash(&<Code<T>>::key_for(&id)).to_vec();
|
||||||
|
let head_key = GenesisConfig::<T>::hash(&<Heads<T>>::key_for(&id)).to_vec();
|
||||||
|
|
||||||
|
storage.insert(code_key, code.encode());
|
||||||
|
storage.insert(head_key, genesis.encode());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
decl_module! {
|
decl_module! {
|
||||||
/// Parachains module.
|
/// Parachains module.
|
||||||
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||||
/// Provide candidate receipts for parachains, in ascending order by id.
|
/// Provide candidate receipts for parachains, in ascending order by id.
|
||||||
fn set_heads(origin, heads: Vec<CandidateReceipt>) -> Result;
|
fn set_heads(origin, heads: Vec<CandidateReceipt>) -> Result {
|
||||||
|
ensure_inherent(origin)?;
|
||||||
|
ensure!(!<DidUpdate<T>>::exists(), "Parachain heads must be updated only once in the block");
|
||||||
|
ensure!(
|
||||||
|
<system::Module<T>>::extrinsic_index() == Some(T::SET_POSITION),
|
||||||
|
"Parachain heads update extrinsic must be at position {} in the block"
|
||||||
|
// , T::SET_POSITION
|
||||||
|
);
|
||||||
|
|
||||||
fn register_parachain(origin, id: Id, code: Vec<u8>, initial_head_data: Vec<u8>) -> Result;
|
let active_parachains = Self::active_parachains();
|
||||||
fn deregister_parachain(origin, id: Id) -> Result;
|
|
||||||
|
// perform integrity checks before writing to storage.
|
||||||
|
{
|
||||||
|
let n_parachains = active_parachains.len();
|
||||||
|
ensure!(heads.len() <= n_parachains, "Too many parachain candidates");
|
||||||
|
|
||||||
|
let mut last_id = None;
|
||||||
|
let mut iter = active_parachains.iter();
|
||||||
|
for head in &heads {
|
||||||
|
// proposed heads must be ascending order by parachain ID without duplicate.
|
||||||
|
ensure!(
|
||||||
|
last_id.as_ref().map_or(true, |x| x < &head.parachain_index),
|
||||||
|
"Parachain candidates out of order by ID"
|
||||||
|
);
|
||||||
|
|
||||||
|
// must be unknown since active parachains are always sorted.
|
||||||
|
ensure!(
|
||||||
|
iter.find(|x| x == &&head.parachain_index).is_some(),
|
||||||
|
"Submitted candidate for unregistered or out-of-order parachain {}"
|
||||||
|
);
|
||||||
|
|
||||||
|
last_id = Some(head.parachain_index);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
decl_storage! {
|
for head in heads {
|
||||||
trait Store for Module<T: Trait> as Parachains {
|
let id = head.parachain_index.clone();
|
||||||
// Vector of all parachain IDs.
|
<Heads<T>>::insert(id, head.head_data.0);
|
||||||
pub Parachains get(active_parachains): default Vec<Id>;
|
}
|
||||||
// The parachains registered at present.
|
|
||||||
pub Code get(parachain_code): map [ Id => Vec<u8> ];
|
|
||||||
// The heads of the parachains registered at present. these are kept sorted.
|
|
||||||
pub Heads get(parachain_head): map [ Id => Vec<u8> ];
|
|
||||||
|
|
||||||
// Did the parachain heads get updated in this block?
|
<DidUpdate<T>>::put(true);
|
||||||
DidUpdate: default bool;
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Register a parachain with given code.
|
||||||
|
/// Fails if given ID is already used.
|
||||||
|
pub fn register_parachain(id: Id, code: Vec<u8>, initial_head_data: Vec<u8>) -> Result {
|
||||||
|
let mut parachains = Self::active_parachains();
|
||||||
|
match parachains.binary_search(&id) {
|
||||||
|
Ok(_) => fail!("Parachain already exists"),
|
||||||
|
Err(idx) => parachains.insert(idx, id),
|
||||||
|
}
|
||||||
|
|
||||||
|
<Code<T>>::insert(id, code);
|
||||||
|
<Parachains<T>>::put(parachains);
|
||||||
|
<Heads<T>>::insert(id, initial_head_data);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deregister a parachain with given id
|
||||||
|
pub fn deregister_parachain(id: Id) -> Result {
|
||||||
|
let mut parachains = Self::active_parachains();
|
||||||
|
match parachains.binary_search(&id) {
|
||||||
|
Ok(idx) => { parachains.remove(idx); }
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
<Code<T>>::remove(id);
|
||||||
|
<Heads<T>>::remove(id);
|
||||||
|
<Parachains<T>>::put(parachains);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_finalise(_n: T::BlockNumber) {
|
||||||
|
assert!(<Self as Store>::DidUpdate::take(), "Parachain heads must be updated once in the block");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +176,7 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
let mut roles_gua = roles_val.clone();
|
let mut roles_gua = roles_val.clone();
|
||||||
|
|
||||||
let mut random_seed = system::Module::<T>::random_seed().to_vec();
|
let mut random_seed = system::Module::<T>::random_seed().as_ref().to_vec();
|
||||||
random_seed.extend(b"validator_role_pairs");
|
random_seed.extend(b"validator_role_pairs");
|
||||||
let mut seed = BlakeTwo256::hash(&random_seed);
|
let mut seed = BlakeTwo256::hash(&random_seed);
|
||||||
|
|
||||||
@@ -103,7 +194,7 @@ impl<T: Trait> Module<T> {
|
|||||||
|
|
||||||
if offset == 24 {
|
if offset == 24 {
|
||||||
// into the last 8 bytes - rehash to gather new entropy
|
// into the last 8 bytes - rehash to gather new entropy
|
||||||
seed = BlakeTwo256::hash(&seed);
|
seed = BlakeTwo256::hash(seed.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
// exchange last item with randomly chosen first.
|
// exchange last item with randomly chosen first.
|
||||||
@@ -117,134 +208,60 @@ impl<T: Trait> Module<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Register a parachain with given code.
|
/*
|
||||||
/// Fails if given ID is already used.
|
// TODO: Consider integrating if needed.
|
||||||
pub fn register_parachain(origin: T::Origin, id: Id, code: Vec<u8>, initial_head_data: Vec<u8>) -> Result {
|
/// Extract the parachain heads from the block.
|
||||||
ensure_root(origin)?;
|
pub fn parachain_heads(&self) -> &[CandidateReceipt] {
|
||||||
let mut parachains = Self::active_parachains();
|
let x = self.inner.extrinsics.get(PARACHAINS_SET_POSITION as usize).and_then(|xt| match xt.function {
|
||||||
match parachains.binary_search(&id) {
|
Call::Parachains(ParachainsCall::set_heads(ref x)) => Some(&x[..]),
|
||||||
Ok(_) => fail!("Parachain already exists"),
|
_ => None
|
||||||
Err(idx) => parachains.insert(idx, id),
|
});
|
||||||
|
|
||||||
|
match x {
|
||||||
|
Some(x) => x,
|
||||||
|
None => panic!("Invalid polkadot block asserted at {:?}", self.file_line),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Trait> ProvideInherent for Module<T> {
|
||||||
|
type Inherent = Vec<CandidateReceipt>;
|
||||||
|
type Call = Call<T>;
|
||||||
|
type Error = RuntimeString;
|
||||||
|
|
||||||
|
fn create_inherent_extrinsics(data: Self::Inherent) -> Vec<(u32, Self::Call)> {
|
||||||
|
vec![(T::SET_POSITION, Call::set_heads(data))]
|
||||||
}
|
}
|
||||||
|
|
||||||
<Code<T>>::insert(id, code);
|
fn check_inherent<Block: BlockT, F: Fn(&Block::Extrinsic) -> Option<&Self::Call>>(
|
||||||
<Parachains<T>>::put(parachains);
|
block: &Block, _data: Self::Inherent, extract_function: &F
|
||||||
<Heads<T>>::insert(id, initial_head_data);
|
) -> ::rstd::result::Result<(), Self::Error> {
|
||||||
|
let has_heads = block
|
||||||
|
.extrinsics()
|
||||||
|
.get(T::SET_POSITION as usize)
|
||||||
|
.map_or(false, |xt| {
|
||||||
|
xt.is_signed() == Some(true) && match extract_function(&xt) {
|
||||||
|
Some(Call::set_heads(_)) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if !has_heads { return Err("No valid parachains inherent in block".into()) }
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deregister a parachain with given id
|
|
||||||
pub fn deregister_parachain(origin: T::Origin, id: Id) -> Result {
|
|
||||||
ensure_root(origin)?;
|
|
||||||
let mut parachains = Self::active_parachains();
|
|
||||||
match parachains.binary_search(&id) {
|
|
||||||
Ok(idx) => { parachains.remove(idx); }
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
<Code<T>>::remove(id);
|
|
||||||
<Heads<T>>::remove(id);
|
|
||||||
<Parachains<T>>::put(parachains);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_heads(origin: T::Origin, heads: Vec<CandidateReceipt>) -> Result {
|
|
||||||
ensure_inherent(origin)?;
|
|
||||||
ensure!(!<DidUpdate<T>>::exists(), "Parachain heads must be updated only once in the block");
|
|
||||||
ensure!(
|
|
||||||
<system::Module<T>>::extrinsic_index() == Some(T::SET_POSITION),
|
|
||||||
"Parachain heads update extrinsic must be at position {} in the block"
|
|
||||||
// , T::SET_POSITION
|
|
||||||
);
|
|
||||||
|
|
||||||
let active_parachains = Self::active_parachains();
|
|
||||||
let mut iter = active_parachains.iter();
|
|
||||||
|
|
||||||
// perform this check before writing to storage.
|
|
||||||
for head in &heads {
|
|
||||||
ensure!(
|
|
||||||
iter.find(|&p| p == &head.parachain_index).is_some(),
|
|
||||||
"Submitted candidate for unregistered or out-of-order parachain {}"
|
|
||||||
// , head.parachain_index.into_inner()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for head in heads {
|
|
||||||
let id = head.parachain_index.clone();
|
|
||||||
<Heads<T>>::insert(id, head.head_data.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
<DidUpdate<T>>::put(true);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Trait> OnFinalise<T::BlockNumber> for Module<T> {
|
|
||||||
fn on_finalise(_n: T::BlockNumber) {
|
|
||||||
assert!(<Self as Store>::DidUpdate::take(), "Parachain heads must be updated once in the block");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parachains module genesis configuration.
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct GenesisConfig<T: Trait> {
|
|
||||||
/// The initial parachains, mapped to code and initial head data
|
|
||||||
pub parachains: Vec<(Id, Vec<u8>, Vec<u8>)>,
|
|
||||||
/// Phantom data.
|
|
||||||
#[serde(skip)]
|
|
||||||
pub phantom: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
impl<T: Trait> Default for GenesisConfig<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
GenesisConfig {
|
|
||||||
parachains: Vec::new(),
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(any(feature = "std", test))]
|
|
||||||
impl<T: Trait> runtime_primitives::BuildStorage for GenesisConfig<T>
|
|
||||||
{
|
|
||||||
fn build_storage(mut self) -> ::std::result::Result<HashMap<Vec<u8>, Vec<u8>>, String> {
|
|
||||||
use codec::Encode;
|
|
||||||
|
|
||||||
self.parachains.sort_unstable_by_key(|&(ref id, _, _)| id.clone());
|
|
||||||
self.parachains.dedup_by_key(|&mut (ref id, _, _)| id.clone());
|
|
||||||
|
|
||||||
let only_ids: Vec<_> = self.parachains.iter().map(|&(ref id, _, _)| id).cloned().collect();
|
|
||||||
|
|
||||||
let mut map: HashMap<_, _> = map![
|
|
||||||
Self::hash(<Parachains<T>>::key()).to_vec() => only_ids.encode()
|
|
||||||
];
|
|
||||||
|
|
||||||
for (id, code, genesis) in self.parachains {
|
|
||||||
let code_key = Self::hash(&<Code<T>>::key_for(&id)).to_vec();
|
|
||||||
let head_key = Self::hash(&<Heads<T>>::key_for(&id)).to_vec();
|
|
||||||
|
|
||||||
map.insert(code_key, code.encode());
|
|
||||||
map.insert(head_key, genesis.encode());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(map)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use runtime_io::{TestExternalities, with_externalities};
|
use rstd::marker::PhantomData;
|
||||||
|
use sr_io::{TestExternalities, with_externalities};
|
||||||
use substrate_primitives::{H256, Blake2Hasher};
|
use substrate_primitives::{H256, Blake2Hasher};
|
||||||
use runtime_primitives::BuildStorage;
|
use sr_primitives::BuildStorage;
|
||||||
use runtime_primitives::traits::{Identity, BlakeTwo256};
|
use sr_primitives::traits::{Identity, BlakeTwo256};
|
||||||
use runtime_primitives::testing::{Digest, Header};
|
use sr_primitives::testing::{Digest, Header, DigestItem};
|
||||||
use {consensus, timestamp};
|
use {consensus, timestamp};
|
||||||
|
|
||||||
impl_outer_origin! {
|
impl_outer_origin! {
|
||||||
@@ -257,7 +274,7 @@ mod tests {
|
|||||||
const NOTE_OFFLINE_POSITION: u32 = 1;
|
const NOTE_OFFLINE_POSITION: u32 = 1;
|
||||||
type SessionKey = u64;
|
type SessionKey = u64;
|
||||||
type OnOfflineValidator = ();
|
type OnOfflineValidator = ();
|
||||||
type Log = u64;
|
type Log = DigestItem;
|
||||||
}
|
}
|
||||||
impl system::Trait for Test {
|
impl system::Trait for Test {
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
@@ -269,6 +286,7 @@ mod tests {
|
|||||||
type AccountId = u64;
|
type AccountId = u64;
|
||||||
type Header = Header;
|
type Header = Header;
|
||||||
type Event = ();
|
type Event = ();
|
||||||
|
type Log = DigestItem;
|
||||||
}
|
}
|
||||||
impl session::Trait for Test {
|
impl session::Trait for Test {
|
||||||
type ConvertAccountIdToSessionKey = Identity;
|
type ConvertAccountIdToSessionKey = Identity;
|
||||||
@@ -286,19 +304,21 @@ mod tests {
|
|||||||
type Parachains = Module<Test>;
|
type Parachains = Module<Test>;
|
||||||
|
|
||||||
fn new_test_ext(parachains: Vec<(Id, Vec<u8>, Vec<u8>)>) -> TestExternalities<Blake2Hasher> {
|
fn new_test_ext(parachains: Vec<(Id, Vec<u8>, Vec<u8>)>) -> TestExternalities<Blake2Hasher> {
|
||||||
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
let mut t = system::GenesisConfig::<Test>::default().build_storage().unwrap().0;
|
||||||
t.extend(consensus::GenesisConfig::<Test>{
|
t.extend(consensus::GenesisConfig::<Test>{
|
||||||
code: vec![],
|
code: vec![],
|
||||||
authorities: vec![1, 2, 3],
|
authorities: vec![1, 2, 3],
|
||||||
}.build_storage().unwrap());
|
_genesis_phantom_data: PhantomData,
|
||||||
|
}.build_storage().unwrap().0);
|
||||||
t.extend(session::GenesisConfig::<Test>{
|
t.extend(session::GenesisConfig::<Test>{
|
||||||
session_length: 1000,
|
session_length: 1000,
|
||||||
validators: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
validators: vec![1, 2, 3, 4, 5, 6, 7, 8],
|
||||||
}.build_storage().unwrap());
|
_genesis_phantom_data: PhantomData,
|
||||||
|
}.build_storage().unwrap().0);
|
||||||
t.extend(GenesisConfig::<Test>{
|
t.extend(GenesisConfig::<Test>{
|
||||||
parachains: parachains,
|
parachains: parachains,
|
||||||
phantom: PhantomData,
|
_genesis_phantom_data: PhantomData,
|
||||||
}.build_storage().unwrap());
|
}.build_storage().unwrap().0);
|
||||||
t.into()
|
t.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -329,12 +349,12 @@ mod tests {
|
|||||||
assert_eq!(Parachains::parachain_code(&5u32.into()), Some(vec![1,2,3]));
|
assert_eq!(Parachains::parachain_code(&5u32.into()), Some(vec![1,2,3]));
|
||||||
assert_eq!(Parachains::parachain_code(&100u32.into()), Some(vec![4,5,6]));
|
assert_eq!(Parachains::parachain_code(&100u32.into()), Some(vec![4,5,6]));
|
||||||
|
|
||||||
assert_ok!(Parachains::register_parachain(Origin::ROOT, 99u32.into(), vec![7,8,9], vec![1, 1, 1]));
|
assert_ok!(Parachains::register_parachain(99u32.into(), vec![7,8,9], vec![1, 1, 1]));
|
||||||
|
|
||||||
assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 99u32.into(), 100u32.into()]);
|
assert_eq!(Parachains::active_parachains(), vec![5u32.into(), 99u32.into(), 100u32.into()]);
|
||||||
assert_eq!(Parachains::parachain_code(&99u32.into()), Some(vec![7,8,9]));
|
assert_eq!(Parachains::parachain_code(&99u32.into()), Some(vec![7,8,9]));
|
||||||
|
|
||||||
assert_ok!(Parachains::deregister_parachain(Origin::ROOT, 5u32.into()));
|
assert_ok!(Parachains::deregister_parachain(5u32.into()));
|
||||||
|
|
||||||
assert_eq!(Parachains::active_parachains(), vec![99u32.into(), 100u32.into()]);
|
assert_eq!(Parachains::active_parachains(), vec![99u32.into(), 100u32.into()]);
|
||||||
assert_eq!(Parachains::parachain_code(&5u32.into()), None);
|
assert_eq!(Parachains::parachain_code(&5u32.into()), None);
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
// Copyright 2017 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Polkadot.
|
|
||||||
|
|
||||||
// Polkadot 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.
|
|
||||||
|
|
||||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Utils for block interaction.
|
|
||||||
|
|
||||||
use rstd::prelude::*;
|
|
||||||
use super::{Call, UncheckedExtrinsic, Balances};
|
|
||||||
use runtime_primitives::traits::{Checkable, Lookup};
|
|
||||||
use timestamp::Call as TimestampCall;
|
|
||||||
use parachains::Call as ParachainsCall;
|
|
||||||
use consensus::Call as ConsensusCall;
|
|
||||||
|
|
||||||
/// Produces the list of inherent extrinsics.
|
|
||||||
pub fn inherent_extrinsics(data: ::primitives::InherentData, spec_version: u32) -> Vec<UncheckedExtrinsic> {
|
|
||||||
let make_inherent = |function| UncheckedExtrinsic::new_unsigned(0, function);
|
|
||||||
|
|
||||||
let mut inherent = vec![
|
|
||||||
make_inherent(Call::Timestamp(TimestampCall::set(data.timestamp))),
|
|
||||||
make_inherent(Call::Parachains(ParachainsCall::set_heads(data.parachain_heads))),
|
|
||||||
];
|
|
||||||
|
|
||||||
if !data.offline_indices.is_empty() && spec_version == 5 {
|
|
||||||
inherent.push(make_inherent(
|
|
||||||
Call::Consensus(ConsensusCall::note_offline(data.offline_indices))
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
inherent
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks an unchecked extrinsic for validity.
|
|
||||||
pub fn check_extrinsic(xt: UncheckedExtrinsic) -> bool {
|
|
||||||
xt.check_with(Balances::lookup).is_ok()
|
|
||||||
}
|
|
||||||
Generated
+189
-160
@@ -3,12 +3,12 @@ name = "arrayvec"
|
|||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.2.6"
|
version = "1.2.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -17,14 +17,30 @@ version = "0.1.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fixed-hash"
|
name = "crunchy"
|
||||||
version = "0.2.2"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashdb"
|
name = "fixed-hash"
|
||||||
version = "0.2.1"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash-db"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "git+https://github.com/paritytech/trie#2616db2a2529098949e5d39aa06dd4e502a9e5f7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash256-std-hasher"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "git+https://github.com/paritytech/trie#2616db2a2529098949e5d39aa06dd4e502a9e5f7"
|
||||||
|
dependencies = [
|
||||||
|
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hex-literal"
|
name = "hex-literal"
|
||||||
@@ -32,7 +48,7 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -40,7 +56,7 @@ name = "hex-literal-impl"
|
|||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -48,51 +64,68 @@ name = "integer-sqrt"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/integer-sqrt-rs.git#886e9cb983c46498003878afe965d55caa762025"
|
source = "git+https://github.com/paritytech/integer-sqrt-rs.git#886e9cb983c46498003878afe965d55caa762025"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "integer-sqrt"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mashup"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"mashup-impl 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mashup-impl"
|
||||||
|
version = "0.1.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"proc-macro2 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nodrop"
|
name = "nodrop"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.5"
|
version = "0.2.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-codec"
|
name = "parity-codec"
|
||||||
version = "0.1.0"
|
version = "2.1.5"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parity-codec-derive"
|
name = "parity-codec-derive"
|
||||||
version = "0.1.0"
|
version = "2.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "plain_hasher"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polkadot-primitives"
|
name = "polkadot-primitives"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"sr-version 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"substrate-client 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -101,8 +134,8 @@ name = "polkadot-runtime"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
|
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"polkadot-primitives 0.1.0",
|
"polkadot-primitives 0.1.0",
|
||||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -120,50 +153,37 @@ dependencies = [
|
|||||||
"srml-system 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-system 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-timestamp 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-timestamp 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-treasury 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-treasury 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"substrate-client 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack"
|
name = "proc-macro-hack"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-hack-impl"
|
name = "proc-macro-hack-impl"
|
||||||
version = "0.4.0"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "0.4.15"
|
version = "0.4.23"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pwasm-alloc"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
|
||||||
dependencies = [
|
|
||||||
"pwasm-libc 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pwasm-libc"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "0.6.8"
|
version = "0.6.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -202,16 +222,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.75"
|
version = "1.0.80"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sr-io"
|
name = "sr-io"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hash-db 0.9.0 (git+https://github.com/paritytech/trie)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -220,24 +240,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "sr-primitives"
|
name = "sr-primitives"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
|
"integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sr-sandbox"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
|
||||||
dependencies = [
|
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -246,34 +254,33 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "sr-std"
|
name = "sr-std"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pwasm-alloc 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"pwasm-libc 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sr-version"
|
name = "sr-version"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-balances"
|
name = "srml-balances"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -285,12 +292,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-consensus"
|
name = "srml-consensus"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -302,19 +309,17 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-council"
|
name = "srml-council"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-balances 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-balances 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-consensus 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"srml-democracy 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-democracy 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-support 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-support 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-system 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-system 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -324,18 +329,17 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-democracy"
|
name = "srml-democracy"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-balances 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-balances 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-consensus 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"srml-support 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-support 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-system 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-system 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -344,11 +348,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-executive"
|
name = "srml-executive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -356,16 +361,27 @@ dependencies = [
|
|||||||
"srml-system 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-system 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "srml-metadata"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
|
dependencies = [
|
||||||
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-session"
|
name = "srml-session"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -379,16 +395,15 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-staking"
|
name = "srml-staking"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-sandbox 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-balances 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-balances 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"srml-consensus 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-consensus 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -402,26 +417,27 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-support"
|
name = "srml-support"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"mashup 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-metadata 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"srml-metadata 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-system"
|
name = "srml-system"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -432,11 +448,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-timestamp"
|
name = "srml-timestamp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -449,12 +466,12 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "srml-treasury"
|
name = "srml-treasury"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-io 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
@@ -465,29 +482,38 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "substrate-metadata"
|
name = "static_assertions"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "substrate-client"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"sr-version 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "substrate-primitives"
|
name = "substrate-primitives"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/paritytech/substrate#b9849fe33203fd34105452ca8e8e638f3918d357"
|
source = "git+https://github.com/paritytech/substrate#c10123e41c2f4bfa7aa82f65c229920111c66d19"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fixed-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hash-db 0.9.0 (git+https://github.com/paritytech/trie)",
|
||||||
"parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"hash256-std-hasher 0.9.0 (git+https://github.com/paritytech/trie)",
|
||||||
"parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"sr-std 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"uint 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -495,18 +521,18 @@ name = "syn"
|
|||||||
version = "0.14.9"
|
version = "0.14.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
"proc-macro2 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uint"
|
name = "uint"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -517,33 +543,34 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
|
"checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
|
||||||
"checksum byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "90492c5858dd7d2e78691cfb89f90d273a2800fc11d98f60786e5d87e2f83781"
|
"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
|
||||||
"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
|
"checksum crunchy 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a2f4a431c5c9f662e1200b7c7f02c34e91361150e382089a8f2dec3ba680cbda"
|
||||||
"checksum fixed-hash 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0d5ec8112f00ea8a483e04748a85522184418fd1cf02890b626d8fc28683f7de"
|
"checksum crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c240f247c278fa08a6d4820a6a222bfc6e0d999e51ba67be94f44c905b2161f2"
|
||||||
"checksum hashdb 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f1c71fc577cde89b3345d5f2880fecaf462a32e96c619f431279bdaf1ba5ddb1"
|
"checksum fixed-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a557e80084b05c32b455963ff565a9de6f2866da023d6671705c6aff6f65e01c"
|
||||||
|
"checksum hash-db 0.9.0 (git+https://github.com/paritytech/trie)" = "<none>"
|
||||||
|
"checksum hash256-std-hasher 0.9.0 (git+https://github.com/paritytech/trie)" = "<none>"
|
||||||
"checksum hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4da5f0e01bd8a71a224a4eedecaacfcabda388dbb7a80faf04d3514287572d95"
|
"checksum hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4da5f0e01bd8a71a224a4eedecaacfcabda388dbb7a80faf04d3514287572d95"
|
||||||
"checksum hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d340b6514f232f6db1bd16db65302a5278a04fef9ce867cb932e7e5fa21130a"
|
"checksum hex-literal-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1d340b6514f232f6db1bd16db65302a5278a04fef9ce867cb932e7e5fa21130a"
|
||||||
"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "<none>"
|
"checksum integer-sqrt 0.1.0 (git+https://github.com/paritytech/integer-sqrt-rs.git)" = "<none>"
|
||||||
"checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2"
|
"checksum integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ea155abb3ba6f382a75f1418988c05fe82959ed9ce727de427f9cfd425b0c903"
|
||||||
"checksum num-traits 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "630de1ef5cc79d0cdd78b7e33b81f083cbfe90de0f4b2b2f07f905867c70e9fe"
|
"checksum mashup 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f2d82b34c7fb11bb41719465c060589e291d505ca4735ea30016a91f6fc79c3b"
|
||||||
"checksum parity-codec 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum mashup-impl 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "aa607bfb674b4efb310512527d64266b065de3f894fc52f84efcbf7eaa5965fb"
|
||||||
"checksum parity-codec-derive 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||||
"checksum plain_hasher 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "95fa6386b1d34aaf0adb9b7dd2885dbe7c34190e6263785e5a7ec2b19044a90f"
|
"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
|
||||||
"checksum proc-macro-hack 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ba8d4f9257b85eb6cdf13f055cea3190520aab1409ca2ab43493ea4820c25f0"
|
"checksum parity-codec 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "dca389ea5e1632c89b2ce54f7e2b4a8a8c9d278042222a91e0bf95451218cb4c"
|
||||||
"checksum proc-macro-hack-impl 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d5cb6f960ad471404618e9817c0e5d10b1ae74cfdf01fab89ea0641fe7fb2892"
|
"checksum parity-codec-derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ffa42c2cb493b60b12c75b26e8c94cb734af4df4d7f2cc229dc04c1953dac189"
|
||||||
"checksum proc-macro2 0.4.15 (registry+https://github.com/rust-lang/crates.io-index)" = "295af93acfb1d5be29c16ca5b3f82d863836efd9cb0c14fd83811eb9a110e452"
|
"checksum proc-macro-hack 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2c725b36c99df7af7bf9324e9c999b9e37d92c8f8caf106d82e1d7953218d2d8"
|
||||||
"checksum pwasm-alloc 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum proc-macro-hack-impl 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2b753ad9ed99dd8efeaa7d2fb8453c8f6bc3e54b97966d35f1bc77ca6865254a"
|
||||||
"checksum pwasm-libc 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum proc-macro2 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)" = "88dae56b29da695d783ea7fc5a90de281f79eb38407e77f6d674dd8befc4ac47"
|
||||||
"checksum quote 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dd636425967c33af890042c483632d33fa7a18f19ad1d7ea72e8998c6ef8dea5"
|
"checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c"
|
||||||
"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8"
|
"checksum rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "403bb3a286107a04825a5f82e1270acc1e14028d3d554d7a1e08914549575ab8"
|
||||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
"checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347"
|
"checksum safe-mix 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7f7bf422d23a88c16d5090d455f182bc99c60af4df6a345c63428acf5129e347"
|
||||||
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
"checksum serde 1.0.75 (registry+https://github.com/rust-lang/crates.io-index)" = "22d340507cea0b7e6632900a176101fea959c7065d93ba555072da90aaaafc87"
|
"checksum serde 1.0.80 (registry+https://github.com/rust-lang/crates.io-index)" = "15c141fc7027dd265a47c090bf864cf62b42c4d228bbcf4e51a0c9e2b0d3f7ef"
|
||||||
"checksum sr-io 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum sr-io 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum sr-primitives 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum sr-sandbox 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
|
||||||
"checksum sr-std 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum sr-std 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum sr-version 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum sr-version 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-balances 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-balances 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
@@ -551,14 +578,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
"checksum srml-council 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-council 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-democracy 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-democracy 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-executive 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-executive 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
|
"checksum srml-metadata 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-session 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-session 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-staking 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-staking 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-support 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-support 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-system 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-system 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-timestamp 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-timestamp 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum srml-treasury 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum srml-treasury 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum substrate-metadata 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c19be23126415861cb3a23e501d34a708f7f9b2183c5252d690941c2e69199d5"
|
||||||
|
"checksum substrate-client 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
"checksum substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)" = "<none>"
|
||||||
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
|
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
|
||||||
"checksum uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "754ba11732b9161b94c41798e5197e5e75388d012f760c42adb5000353e98646"
|
"checksum uint 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "082df6964410f6aa929a61ddfafc997e4f32c62c22490e439ac351cec827f436"
|
||||||
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ crate-type = ["cdylib"]
|
|||||||
integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" }
|
integer-sqrt = { git = "https://github.com/paritytech/integer-sqrt-rs.git", branch = "master" }
|
||||||
polkadot-primitives = { path = "../../primitives", default-features = false }
|
polkadot-primitives = { path = "../../primitives", default-features = false }
|
||||||
safe-mix = { version = "1.0", default-features = false }
|
safe-mix = { version = "1.0", default-features = false }
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate", default-features = false }
|
parity-codec = { version = "2.1", default-features = false }
|
||||||
parity-codec-derive = { git = "https://github.com/paritytech/substrate", default-features = false }
|
parity-codec-derive = { version = "2.1", default-features = false }
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate", default-features = false }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
|
substrate-client = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
sr-std = { git = "https://github.com/paritytech/substrate", default-features = false }
|
sr-std = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
sr-io = { git = "https://github.com/paritytech/substrate", default-features = false }
|
sr-io = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
srml-support = { git = "https://github.com/paritytech/substrate", default-features = false }
|
srml-support = { git = "https://github.com/paritytech/substrate", default-features = false }
|
||||||
|
|||||||
BIN
Binary file not shown.
Binary file not shown.
@@ -14,14 +14,14 @@ hex-literal = "0.1"
|
|||||||
polkadot-availability-store = { path = "../availability-store" }
|
polkadot-availability-store = { path = "../availability-store" }
|
||||||
polkadot-primitives = { path = "../primitives" }
|
polkadot-primitives = { path = "../primitives" }
|
||||||
polkadot-runtime = { path = "../runtime" }
|
polkadot-runtime = { path = "../runtime" }
|
||||||
polkadot-consensus = { path = "../consensus" }
|
|
||||||
polkadot-executor = { path = "../executor" }
|
polkadot-executor = { path = "../executor" }
|
||||||
polkadot-api = { path = "../api" }
|
|
||||||
polkadot-transaction-pool = { path = "../transaction-pool" }
|
|
||||||
polkadot-network = { path = "../network" }
|
polkadot-network = { path = "../network" }
|
||||||
sr-io = { git = "https://github.com/paritytech/substrate" }
|
sr-io = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
sr-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-network = { git = "https://github.com/paritytech/substrate" }
|
substrate-network = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
substrate-consensus-aura = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-service = { git = "https://github.com/paritytech/substrate" }
|
substrate-service = { git = "https://github.com/paritytech/substrate" }
|
||||||
substrate-telemetry = { git = "https://github.com/paritytech/substrate" }
|
substrate-telemetry = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
substrate-transaction-pool = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
|||||||
@@ -17,13 +17,16 @@
|
|||||||
//! Polkadot chain configurations.
|
//! Polkadot chain configurations.
|
||||||
|
|
||||||
use primitives::{AuthorityId, ed25519};
|
use primitives::{AuthorityId, ed25519};
|
||||||
use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilConfig, DemocracyConfig,
|
use polkadot_runtime::{GenesisConfig, ConsensusConfig, CouncilSeatsConfig, DemocracyConfig,
|
||||||
SessionConfig, StakingConfig, TimestampConfig, BalancesConfig};
|
SessionConfig, StakingConfig, TimestampConfig, BalancesConfig, Perbill, CouncilVotingConfig};
|
||||||
use service::ChainSpec;
|
|
||||||
|
|
||||||
const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
|
const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
|
||||||
|
const DEFAULT_PROTOCOL_ID: &str = "dot";
|
||||||
|
|
||||||
pub fn poc_1_testnet_config() -> Result<ChainSpec<GenesisConfig>, String> {
|
/// Specialised `ChainSpec`.
|
||||||
|
pub type ChainSpec = ::service::ChainSpec<GenesisConfig>;
|
||||||
|
|
||||||
|
pub fn poc_1_testnet_config() -> Result<ChainSpec, String> {
|
||||||
ChainSpec::from_embedded(include_bytes!("../res/krummelanke.json"))
|
ChainSpec::from_embedded(include_bytes!("../res/krummelanke.json"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +44,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
|
|||||||
consensus: Some(ConsensusConfig {
|
consensus: Some(ConsensusConfig {
|
||||||
code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(), // TODO change
|
code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(), // TODO change
|
||||||
authorities: initial_authorities.clone(),
|
authorities: initial_authorities.clone(),
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
system: None,
|
system: None,
|
||||||
balances: Some(BalancesConfig {
|
balances: Some(BalancesConfig {
|
||||||
@@ -51,28 +55,34 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
|
|||||||
creation_fee: 0,
|
creation_fee: 0,
|
||||||
reclaim_rebate: 0,
|
reclaim_rebate: 0,
|
||||||
balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(),
|
balances: endowed_accounts.iter().map(|&k|(k, 1u128 << 60)).collect(),
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
session: Some(SessionConfig {
|
session: Some(SessionConfig {
|
||||||
validators: initial_authorities.iter().cloned().map(Into::into).collect(),
|
validators: initial_authorities.iter().cloned().map(Into::into).collect(),
|
||||||
session_length: 60, // that's 5 minutes per session.
|
session_length: 60, // that's 5 minutes per session.
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
staking: Some(StakingConfig {
|
staking: Some(StakingConfig {
|
||||||
current_era: 0,
|
current_era: 0,
|
||||||
intentions: initial_authorities.iter().cloned().map(Into::into).collect(),
|
intentions: initial_authorities.iter().cloned().map(Into::into).collect(),
|
||||||
offline_slash: 10000,
|
offline_slash: Perbill::from_billionths(1_000_000),
|
||||||
session_reward: 100,
|
session_reward: Perbill::from_billionths(60),
|
||||||
|
current_offline_slash: 0,
|
||||||
|
current_session_reward: 0,
|
||||||
validator_count: 12,
|
validator_count: 12,
|
||||||
sessions_per_era: 12, // 1 hour per era
|
sessions_per_era: 12, // 1 hour per era
|
||||||
bonding_duration: 24 * 60 * 12, // 1 day per bond.
|
bonding_duration: 24 * 60 * 12, // 1 day per bond.
|
||||||
offline_slash_grace: 4,
|
offline_slash_grace: 4,
|
||||||
minimum_validator_count: 4,
|
minimum_validator_count: 4,
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
democracy: Some(DemocracyConfig {
|
democracy: Some(DemocracyConfig {
|
||||||
launch_period: 12 * 60 * 24, // 1 day per public referendum
|
launch_period: 12 * 60 * 24, // 1 day per public referendum
|
||||||
voting_period: 12 * 60 * 24 * 3, // 3 days to discuss & vote on an active referendum
|
voting_period: 12 * 60 * 24 * 3, // 3 days to discuss & vote on an active referendum
|
||||||
minimum_deposit: 5000, // 12000 as the minimum deposit for a referendum
|
minimum_deposit: 5000, // 12000 as the minimum deposit for a referendum
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
council: Some(CouncilConfig {
|
council_seats: Some(CouncilSeatsConfig {
|
||||||
active_council: vec![],
|
active_council: vec![],
|
||||||
candidacy_bond: 5000, // 5000 to become a council candidate
|
candidacy_bond: 5000, // 5000 to become a council candidate
|
||||||
voter_bond: 1000, // 1000 down to vote for a candidate
|
voter_bond: 1000, // 1000 down to vote for a candidate
|
||||||
@@ -83,20 +93,24 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
|
|||||||
term_duration: 12 * 60 * 24 * 24, // 24 day term duration for the council.
|
term_duration: 12 * 60 * 24 * 24, // 24 day term duration for the council.
|
||||||
desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit.
|
desired_seats: 0, // start with no council: we'll raise this once the stake has been dispersed a bit.
|
||||||
inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped.
|
inactive_grace_period: 1, // one addition vote should go by before an inactive voter can be reaped.
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
cooloff_period: 12 * 60 * 24 * 4, // 4 day cooling off period if council member vetoes a proposal.
|
}),
|
||||||
voting_period: 12 * 60 * 24, // 1 day voting period for council members.
|
council_voting: Some(CouncilVotingConfig {
|
||||||
|
cooloff_period: 75,
|
||||||
|
voting_period: 20,
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
parachains: Some(Default::default()),
|
parachains: Some(Default::default()),
|
||||||
timestamp: Some(TimestampConfig {
|
timestamp: Some(TimestampConfig {
|
||||||
period: 5, // 5 second block time.
|
period: 5, // 5 second block time.
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
treasury: Some(Default::default()),
|
treasury: Some(Default::default()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Staging testnet config.
|
/// Staging testnet config.
|
||||||
pub fn staging_testnet_config() -> ChainSpec<GenesisConfig> {
|
pub fn staging_testnet_config() -> ChainSpec {
|
||||||
let boot_nodes = vec![];
|
let boot_nodes = vec![];
|
||||||
ChainSpec::from_genesis(
|
ChainSpec::from_genesis(
|
||||||
"Staging Testnet",
|
"Staging Testnet",
|
||||||
@@ -104,6 +118,9 @@ pub fn staging_testnet_config() -> ChainSpec<GenesisConfig> {
|
|||||||
staging_testnet_config_genesis,
|
staging_testnet_config_genesis,
|
||||||
boot_nodes,
|
boot_nodes,
|
||||||
Some(STAGING_TELEMETRY_URL.into()),
|
Some(STAGING_TELEMETRY_URL.into()),
|
||||||
|
Some(DEFAULT_PROTOCOL_ID),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,6 +137,7 @@ fn testnet_genesis(initial_authorities: Vec<AuthorityId>) -> GenesisConfig {
|
|||||||
consensus: Some(ConsensusConfig {
|
consensus: Some(ConsensusConfig {
|
||||||
code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(),
|
code: include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm").to_vec(),
|
||||||
authorities: initial_authorities.clone(),
|
authorities: initial_authorities.clone(),
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
system: None,
|
system: None,
|
||||||
balances: Some(BalancesConfig {
|
balances: Some(BalancesConfig {
|
||||||
@@ -130,10 +148,12 @@ fn testnet_genesis(initial_authorities: Vec<AuthorityId>) -> GenesisConfig {
|
|||||||
creation_fee: 0,
|
creation_fee: 0,
|
||||||
reclaim_rebate: 0,
|
reclaim_rebate: 0,
|
||||||
balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(),
|
balances: endowed_accounts.iter().map(|&k|(k, (1u128 << 60))).collect(),
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
session: Some(SessionConfig {
|
session: Some(SessionConfig {
|
||||||
validators: initial_authorities.iter().cloned().map(Into::into).collect(),
|
validators: initial_authorities.iter().cloned().map(Into::into).collect(),
|
||||||
session_length: 10,
|
session_length: 10,
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
staking: Some(StakingConfig {
|
staking: Some(StakingConfig {
|
||||||
current_era: 0,
|
current_era: 0,
|
||||||
@@ -142,17 +162,21 @@ fn testnet_genesis(initial_authorities: Vec<AuthorityId>) -> GenesisConfig {
|
|||||||
validator_count: 2,
|
validator_count: 2,
|
||||||
sessions_per_era: 5,
|
sessions_per_era: 5,
|
||||||
bonding_duration: 2 * 60 * 12,
|
bonding_duration: 2 * 60 * 12,
|
||||||
offline_slash: 0,
|
offline_slash: Perbill::zero(),
|
||||||
session_reward: 0,
|
session_reward: Perbill::zero(),
|
||||||
|
current_offline_slash: 0,
|
||||||
|
current_session_reward: 0,
|
||||||
offline_slash_grace: 0,
|
offline_slash_grace: 0,
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
democracy: Some(DemocracyConfig {
|
democracy: Some(DemocracyConfig {
|
||||||
launch_period: 9,
|
launch_period: 9,
|
||||||
voting_period: 18,
|
voting_period: 18,
|
||||||
minimum_deposit: 10,
|
minimum_deposit: 10,
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
council: Some(CouncilConfig {
|
council_seats: Some(CouncilSeatsConfig {
|
||||||
active_council: endowed_accounts.iter().filter(|a| initial_authorities.iter().find(|&b| a.0 == b.0).is_none()).map(|a| (a.clone(), 1000000)).collect(),
|
active_council: endowed_accounts.iter().filter(|a| initial_authorities.iter().find(|&b| a[..] == b.0).is_none()).map(|a| (a.clone(), 1000000)).collect(),
|
||||||
candidacy_bond: 10,
|
candidacy_bond: 10,
|
||||||
voter_bond: 2,
|
voter_bond: 2,
|
||||||
present_slash_per_voter: 1,
|
present_slash_per_voter: 1,
|
||||||
@@ -162,13 +186,17 @@ fn testnet_genesis(initial_authorities: Vec<AuthorityId>) -> GenesisConfig {
|
|||||||
term_duration: 1000000,
|
term_duration: 1000000,
|
||||||
desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32,
|
desired_seats: (endowed_accounts.len() - initial_authorities.len()) as u32,
|
||||||
inactive_grace_period: 1,
|
inactive_grace_period: 1,
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
|
}),
|
||||||
|
council_voting: Some(CouncilVotingConfig {
|
||||||
cooloff_period: 75,
|
cooloff_period: 75,
|
||||||
voting_period: 20,
|
voting_period: 20,
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
parachains: Some(Default::default()),
|
parachains: Some(Default::default()),
|
||||||
timestamp: Some(TimestampConfig {
|
timestamp: Some(TimestampConfig {
|
||||||
period: 5, // 5 second block time.
|
period: 5, // 5 second block time.
|
||||||
|
_genesis_phantom_data: Default::default(),
|
||||||
}),
|
}),
|
||||||
treasury: Some(Default::default()),
|
treasury: Some(Default::default()),
|
||||||
}
|
}
|
||||||
@@ -181,8 +209,17 @@ fn development_config_genesis() -> GenesisConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Development config (single validator Alice)
|
/// Development config (single validator Alice)
|
||||||
pub fn development_config() -> ChainSpec<GenesisConfig> {
|
pub fn development_config() -> ChainSpec {
|
||||||
ChainSpec::from_genesis("Development", "development", development_config_genesis, vec![], None)
|
ChainSpec::from_genesis(
|
||||||
|
"Development",
|
||||||
|
"development",
|
||||||
|
development_config_genesis,
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
Some(DEFAULT_PROTOCOL_ID),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn local_testnet_genesis() -> GenesisConfig {
|
fn local_testnet_genesis() -> GenesisConfig {
|
||||||
@@ -193,6 +230,15 @@ fn local_testnet_genesis() -> GenesisConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Local testnet config (multivalidator Alice + Bob)
|
/// Local testnet config (multivalidator Alice + Bob)
|
||||||
pub fn local_testnet_config() -> ChainSpec<GenesisConfig> {
|
pub fn local_testnet_config() -> ChainSpec {
|
||||||
ChainSpec::from_genesis("Local Testnet", "local_testnet", local_testnet_genesis, vec![], None)
|
ChainSpec::from_genesis(
|
||||||
|
"Local Testnet",
|
||||||
|
"local_testnet",
|
||||||
|
local_testnet_genesis,
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
Some(DEFAULT_PROTOCOL_ID),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+133
-201
@@ -22,14 +22,16 @@ extern crate polkadot_availability_store as av_store;
|
|||||||
extern crate polkadot_primitives;
|
extern crate polkadot_primitives;
|
||||||
extern crate polkadot_runtime;
|
extern crate polkadot_runtime;
|
||||||
extern crate polkadot_executor;
|
extern crate polkadot_executor;
|
||||||
extern crate polkadot_api;
|
|
||||||
extern crate polkadot_consensus as consensus;
|
|
||||||
extern crate polkadot_transaction_pool as transaction_pool;
|
|
||||||
extern crate polkadot_network;
|
extern crate polkadot_network;
|
||||||
|
extern crate sr_primitives;
|
||||||
extern crate substrate_primitives as primitives;
|
extern crate substrate_primitives as primitives;
|
||||||
|
#[macro_use]
|
||||||
extern crate substrate_network as network;
|
extern crate substrate_network as network;
|
||||||
extern crate substrate_client as client;
|
extern crate substrate_client as client;
|
||||||
|
#[macro_use]
|
||||||
extern crate substrate_service as service;
|
extern crate substrate_service as service;
|
||||||
|
extern crate substrate_consensus_aura as consensus;
|
||||||
|
extern crate substrate_transaction_pool as transaction_pool;
|
||||||
extern crate tokio;
|
extern crate tokio;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
@@ -40,51 +42,27 @@ extern crate hex_literal;
|
|||||||
pub mod chain_spec;
|
pub mod chain_spec;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tokio::prelude::{Stream, Future};
|
use polkadot_primitives::{parachain, AccountId, Block};
|
||||||
use transaction_pool::TransactionPool;
|
use polkadot_runtime::{GenesisConfig, ClientWithApi};
|
||||||
use polkadot_api::{PolkadotApi, light::RemotePolkadotApiWrapper};
|
|
||||||
use polkadot_primitives::{parachain, AccountId, Block, BlockId, Hash};
|
|
||||||
use polkadot_runtime::GenesisConfig;
|
|
||||||
use client::{Client, BlockchainEvents};
|
|
||||||
use polkadot_network::{PolkadotProtocol, consensus::ConsensusNetwork};
|
|
||||||
use tokio::runtime::TaskExecutor;
|
use tokio::runtime::TaskExecutor;
|
||||||
use service::FactoryFullConfiguration;
|
use service::{FactoryFullConfiguration, FullBackend, LightBackend, FullExecutor, LightExecutor};
|
||||||
use primitives::{Blake2Hasher, RlpCodec};
|
use transaction_pool::txpool::{Pool as TransactionPool};
|
||||||
|
use consensus::{import_queue, start_aura, Config as AuraConfig, AuraImportQueue, NothingExtra};
|
||||||
|
|
||||||
pub use service::{Roles, PruningMode, ExtrinsicPoolOptions,
|
pub use service::{
|
||||||
ErrorKind, Error, ComponentBlock, LightComponents, FullComponents};
|
Roles, PruningMode, TransactionPoolOptions, ComponentClient,
|
||||||
pub use client::ExecutionStrategy;
|
ErrorKind, Error, ComponentBlock, LightComponents, FullComponents,
|
||||||
|
FullClient, LightClient, Components, Service, ServiceFactory
|
||||||
|
};
|
||||||
|
pub use service::config::full_version_from_strs;
|
||||||
|
pub use client::{backend::Backend, runtime_api::Core as CoreApi, ExecutionStrategy};
|
||||||
|
pub use polkadot_network::{PolkadotProtocol, NetworkService};
|
||||||
|
pub use polkadot_primitives::parachain::ParachainHost;
|
||||||
|
pub use primitives::{Blake2Hasher};
|
||||||
|
pub use sr_primitives::traits::ProvideRuntimeApi;
|
||||||
|
pub use chain_spec::ChainSpec;
|
||||||
|
|
||||||
/// Specialised polkadot `ChainSpec`.
|
const AURA_SLOT_DURATION: u64 = 6;
|
||||||
pub type ChainSpec = service::ChainSpec<GenesisConfig>;
|
|
||||||
/// Polkadot client type for specialised `Components`.
|
|
||||||
pub type ComponentClient<C> = Client<<C as Components>::Backend, <C as Components>::Executor, Block>;
|
|
||||||
pub type NetworkService = network::Service<Block, <Factory as service::ServiceFactory>::NetworkProtocol, Hash>;
|
|
||||||
|
|
||||||
/// A collection of type to generalise Polkadot specific components over full / light client.
|
|
||||||
pub trait Components: service::Components {
|
|
||||||
/// Polkadot API.
|
|
||||||
type Api: 'static + PolkadotApi + Send + Sync;
|
|
||||||
/// Client backend.
|
|
||||||
type Backend: 'static + client::backend::Backend<Block, Blake2Hasher, RlpCodec>;
|
|
||||||
/// Client executor.
|
|
||||||
type Executor: 'static + client::CallExecutor<Block, Blake2Hasher, RlpCodec> + Send + Sync;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Components for service::LightComponents<Factory> {
|
|
||||||
type Api = RemotePolkadotApiWrapper<
|
|
||||||
<service::LightComponents<Factory> as service::Components>::Backend,
|
|
||||||
<service::LightComponents<Factory> as service::Components>::Executor,
|
|
||||||
>;
|
|
||||||
type Executor = service::LightExecutor<Factory>;
|
|
||||||
type Backend = service::LightBackend<Factory>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Components for service::FullComponents<Factory> {
|
|
||||||
type Api = service::FullClient<Factory>;
|
|
||||||
type Executor = service::FullExecutor<Factory>;
|
|
||||||
type Backend = service::FullBackend<Factory>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// All configuration for the polkadot node.
|
/// All configuration for the polkadot node.
|
||||||
pub type Configuration = FactoryFullConfiguration<Factory>;
|
pub type Configuration = FactoryFullConfiguration<Factory>;
|
||||||
@@ -97,169 +75,123 @@ pub struct CustomConfiguration {
|
|||||||
pub collating_for: Option<(AccountId, parachain::Id)>,
|
pub collating_for: Option<(AccountId, parachain::Id)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Polkadot config for the substrate service.
|
/// Chain API type for the transaction pool.
|
||||||
pub struct Factory;
|
pub type TxChainApi<Backend, Executor> = transaction_pool::ChainApi<
|
||||||
|
client::Client<Backend, Executor, Block, ClientWithApi>,
|
||||||
|
Block,
|
||||||
|
>;
|
||||||
|
|
||||||
impl service::ServiceFactory for Factory {
|
/// Provides polkadot types.
|
||||||
type Block = Block;
|
pub trait PolkadotService {
|
||||||
type ExtrinsicHash = Hash;
|
/// The client's backend type.
|
||||||
type NetworkProtocol = PolkadotProtocol;
|
type Backend: 'static + client::backend::Backend<Block, Blake2Hasher>;
|
||||||
type RuntimeDispatch = polkadot_executor::Executor;
|
/// The client's call executor type.
|
||||||
type FullExtrinsicPoolApi = transaction_pool::ChainApi<service::FullClient<Self>>;
|
type Executor: 'static + client::CallExecutor<Block, Blake2Hasher> + Send + Sync + Clone;
|
||||||
type LightExtrinsicPoolApi = transaction_pool::ChainApi<
|
|
||||||
RemotePolkadotApiWrapper<service::LightBackend<Self>, service::LightExecutor<Self>>
|
|
||||||
>;
|
|
||||||
type Genesis = GenesisConfig;
|
|
||||||
type Configuration = CustomConfiguration;
|
|
||||||
|
|
||||||
const NETWORK_PROTOCOL_ID: network::ProtocolId = ::polkadot_network::DOT_PROTOCOL_ID;
|
/// Get a handle to the client.
|
||||||
|
fn client(&self) -> Arc<client::Client<Self::Backend, Self::Executor, Block, ClientWithApi>>;
|
||||||
|
|
||||||
fn build_full_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc<service::FullClient<Self>>)
|
/// Get a handle to the network.
|
||||||
-> Result<TransactionPool<service::FullClient<Self>>, Error>
|
fn network(&self) -> Arc<NetworkService>;
|
||||||
|
|
||||||
|
/// Get a handle to the transaction pool.
|
||||||
|
fn transaction_pool(&self) -> Arc<TransactionPool<TxChainApi<Self::Backend, Self::Executor>>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PolkadotService for Service<FullComponents<Factory>> {
|
||||||
|
type Backend = <FullComponents<Factory> as Components>::Backend;
|
||||||
|
type Executor = <FullComponents<Factory> as Components>::Executor;
|
||||||
|
|
||||||
|
fn client(&self) -> Arc<client::Client<Self::Backend, Self::Executor, Block, ClientWithApi>> {
|
||||||
|
Service::client(self)
|
||||||
|
}
|
||||||
|
fn network(&self) -> Arc<NetworkService> {
|
||||||
|
Service::network(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transaction_pool(&self) -> Arc<TransactionPool<TxChainApi<Self::Backend, Self::Executor>>> {
|
||||||
|
Service::transaction_pool(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PolkadotService for Service<LightComponents<Factory>> {
|
||||||
|
type Backend = <LightComponents<Factory> as Components>::Backend;
|
||||||
|
type Executor = <LightComponents<Factory> as Components>::Executor;
|
||||||
|
|
||||||
|
fn client(&self) -> Arc<client::Client<Self::Backend, Self::Executor, Block, ClientWithApi>> {
|
||||||
|
Service::client(self)
|
||||||
|
}
|
||||||
|
fn network(&self) -> Arc<NetworkService> {
|
||||||
|
Service::network(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn transaction_pool(&self) -> Arc<TransactionPool<TxChainApi<Self::Backend, Self::Executor>>> {
|
||||||
|
Service::transaction_pool(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
construct_service_factory! {
|
||||||
|
struct Factory {
|
||||||
|
Block = Block,
|
||||||
|
RuntimeApi = ClientWithApi,
|
||||||
|
NetworkProtocol = PolkadotProtocol { |config: &Configuration| Ok(PolkadotProtocol::new(config.custom.collating_for)) },
|
||||||
|
RuntimeDispatch = polkadot_executor::Executor,
|
||||||
|
FullTransactionPoolApi = TxChainApi<FullBackend<Self>, FullExecutor<Self>>
|
||||||
|
{ |config, client| Ok(TransactionPool::new(config, TxChainApi::new(client))) },
|
||||||
|
LightTransactionPoolApi = TxChainApi<LightBackend<Self>, LightExecutor<Self>>
|
||||||
|
{ |config, client| Ok(TransactionPool::new(config, TxChainApi::new(client))) },
|
||||||
|
Genesis = GenesisConfig,
|
||||||
|
Configuration = CustomConfiguration,
|
||||||
|
FullService = FullComponents<Self>
|
||||||
|
{ |config: FactoryFullConfiguration<Self>, executor: TaskExecutor| {
|
||||||
|
let is_auth = config.roles == Roles::AUTHORITY;
|
||||||
|
FullComponents::<Factory>::new(config, executor.clone()).map(move |service|{
|
||||||
|
if is_auth {
|
||||||
|
if let Ok(Some(Ok(key))) = service.keystore().contents()
|
||||||
|
.map(|keys| keys.get(0).map(|k| service.keystore().load(k, "")))
|
||||||
{
|
{
|
||||||
let api = client.clone();
|
|
||||||
Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(api)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_light_extrinsic_pool(config: ExtrinsicPoolOptions, client: Arc<service::LightClient<Self>>)
|
|
||||||
-> Result<TransactionPool<RemotePolkadotApiWrapper<service::LightBackend<Self>, service::LightExecutor<Self>>>, Error>
|
|
||||||
{
|
|
||||||
let api = Arc::new(RemotePolkadotApiWrapper(client.clone()));
|
|
||||||
Ok(TransactionPool::new(config, transaction_pool::ChainApi::new(api)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_network_protocol(config: &Configuration)
|
|
||||||
-> Result<PolkadotProtocol, Error>
|
|
||||||
{
|
|
||||||
if let Some((_, ref para_id)) = config.custom.collating_for {
|
|
||||||
info!("Starting network in Collator mode for parachain {:?}", para_id);
|
|
||||||
}
|
|
||||||
Ok(PolkadotProtocol::new(config.custom.collating_for))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Polkadot service.
|
|
||||||
pub struct Service<C: Components> {
|
|
||||||
inner: service::Service<C>,
|
|
||||||
client: Arc<ComponentClient<C>>,
|
|
||||||
network: Arc<NetworkService>,
|
|
||||||
api: Arc<<C as Components>::Api>,
|
|
||||||
_consensus: Option<consensus::Service>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl <C: Components> Service<C> {
|
|
||||||
pub fn client(&self) -> Arc<ComponentClient<C>> {
|
|
||||||
self.client.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn network(&self) -> Arc<NetworkService> {
|
|
||||||
self.network.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn api(&self) -> Arc<<C as Components>::Api> {
|
|
||||||
self.api.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates light client and register protocol with the network service
|
|
||||||
pub fn new_light(config: Configuration, executor: TaskExecutor)
|
|
||||||
-> Result<Service<LightComponents<Factory>>, Error>
|
|
||||||
{
|
|
||||||
let service = service::Service::<LightComponents<Factory>>::new(config, executor.clone())?;
|
|
||||||
let api = Arc::new(RemotePolkadotApiWrapper(service.client()));
|
|
||||||
let pool = service.extrinsic_pool();
|
|
||||||
let events = service.client().import_notification_stream()
|
|
||||||
.for_each(move |notification| {
|
|
||||||
// re-verify all transactions without the sender.
|
|
||||||
pool.retry_verification(&BlockId::hash(notification.hash), None)
|
|
||||||
.map_err(|e| warn!("Error re-verifying transactions: {:?}", e))?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.then(|_| Ok(()));
|
|
||||||
executor.spawn(events);
|
|
||||||
Ok(Service {
|
|
||||||
client: service.client(),
|
|
||||||
network: service.network(),
|
|
||||||
api: api,
|
|
||||||
inner: service,
|
|
||||||
_consensus: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates full client and register protocol with the network service
|
|
||||||
pub fn new_full(config: Configuration, executor: TaskExecutor)
|
|
||||||
-> Result<Service<FullComponents<Factory>>, Error>
|
|
||||||
{
|
|
||||||
// open availability store.
|
|
||||||
let av_store = {
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
let mut path = PathBuf::from(config.database_path.clone());
|
|
||||||
path.push("availability");
|
|
||||||
|
|
||||||
::av_store::Store::new(::av_store::Config {
|
|
||||||
cache_size: None,
|
|
||||||
path,
|
|
||||||
})?
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_validator = (config.roles & Roles::AUTHORITY) == Roles::AUTHORITY;
|
|
||||||
let service = service::Service::<FullComponents<Factory>>::new(config, executor.clone())?;
|
|
||||||
let pool = service.extrinsic_pool();
|
|
||||||
let events = service.client().import_notification_stream()
|
|
||||||
.for_each(move |notification| {
|
|
||||||
// re-verify all transactions without the sender.
|
|
||||||
pool.retry_verification(&BlockId::hash(notification.hash), None)
|
|
||||||
.map_err(|e| warn!("Error re-verifying transactions: {:?}", e))?;
|
|
||||||
Ok(())
|
|
||||||
})
|
|
||||||
.then(|_| Ok(()));
|
|
||||||
executor.spawn(events);
|
|
||||||
// Spin consensus service if configured
|
|
||||||
let consensus = if is_validator {
|
|
||||||
// Load the first available key
|
|
||||||
let key = service.keystore().load(&service.keystore().contents()?[0], "")?;
|
|
||||||
info!("Using authority key {}", key.public());
|
info!("Using authority key {}", key.public());
|
||||||
|
let task = start_aura(
|
||||||
|
AuraConfig {
|
||||||
|
local_key: Some(Arc::new(key)),
|
||||||
|
slot_duration: AURA_SLOT_DURATION,
|
||||||
|
},
|
||||||
|
service.client(),
|
||||||
|
service.proposer(),
|
||||||
|
service.network(),
|
||||||
|
);
|
||||||
|
|
||||||
let client = service.client();
|
executor.spawn(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let consensus_net = ConsensusNetwork::new(service.network(), client.clone());
|
service
|
||||||
Some(consensus::Service::new(
|
|
||||||
client.clone(),
|
|
||||||
client.clone(),
|
|
||||||
consensus_net,
|
|
||||||
service.extrinsic_pool(),
|
|
||||||
executor,
|
|
||||||
::std::time::Duration::from_secs(4), // TODO: dynamic
|
|
||||||
key,
|
|
||||||
av_store.clone(),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
service.network().with_spec(|spec, _| spec.register_availability_store(av_store));
|
|
||||||
|
|
||||||
Ok(Service {
|
|
||||||
client: service.client(),
|
|
||||||
network: service.network(),
|
|
||||||
api: service.client(),
|
|
||||||
inner: service,
|
|
||||||
_consensus: consensus,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
},
|
||||||
/// Creates bare client without any networking.
|
AuthoritySetup = { |service, _, _| Ok(service) },
|
||||||
pub fn new_client(config: Configuration)
|
LightService = LightComponents<Self>
|
||||||
-> Result<Arc<service::ComponentClient<FullComponents<Factory>>>, Error>
|
{ |config, executor| <LightComponents<Factory>>::new(config, executor) },
|
||||||
{
|
FullImportQueue = AuraImportQueue<Self::Block, FullClient<Self>, NothingExtra>
|
||||||
service::new_client::<Factory>(config)
|
{ |config, client| Ok(import_queue(
|
||||||
}
|
AuraConfig {
|
||||||
|
local_key: None,
|
||||||
impl<C: Components> ::std::ops::Deref for Service<C> {
|
slot_duration: 5
|
||||||
type Target = service::Service<C>;
|
},
|
||||||
|
client,
|
||||||
fn deref(&self) -> &Self::Target {
|
NothingExtra,
|
||||||
&self.inner
|
))
|
||||||
|
},
|
||||||
|
LightImportQueue = AuraImportQueue<Self::Block, LightClient<Self>, NothingExtra>
|
||||||
|
{ |config, client| Ok(import_queue(
|
||||||
|
AuraConfig {
|
||||||
|
local_key: None,
|
||||||
|
slot_duration: 5
|
||||||
|
},
|
||||||
|
client,
|
||||||
|
NothingExtra,
|
||||||
|
))
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ extern crate futures;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate error_chain;
|
extern crate error_chain;
|
||||||
|
|
||||||
use cli::{ServiceComponents, Service, VersionInfo};
|
use cli::{PolkadotService, VersionInfo};
|
||||||
use futures::sync::oneshot;
|
use futures::sync::oneshot;
|
||||||
use futures::{future, Future};
|
use futures::{future, Future};
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ impl cli::IntoExit for Worker {
|
|||||||
|
|
||||||
impl cli::Worker for Worker {
|
impl cli::Worker for Worker {
|
||||||
type Work = <Self as cli::IntoExit>::Exit;
|
type Work = <Self as cli::IntoExit>::Exit;
|
||||||
fn work<C: ServiceComponents>(self, _service: &Service<C>) -> Self::Work {
|
fn work<S: PolkadotService>(self, _service: &S) -> Self::Work {
|
||||||
use cli::IntoExit;
|
use cli::IntoExit;
|
||||||
self.into_exit()
|
self.into_exit()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ version = "0.1.0"
|
|||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
authors = ["Parity Technologies <admin@parity.io>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate" }
|
parity-codec = "2.1"
|
||||||
parity-codec-derive = { git = "https://github.com/paritytech/substrate" }
|
parity-codec-derive = "2.1"
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
||||||
polkadot-primitives = { path = "../primitives" }
|
polkadot-primitives = { path = "../primitives" }
|
||||||
|
|||||||
@@ -6,5 +6,6 @@ description = "Test parachain which adds to a number as its state transition"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
polkadot-parachain = { path = "../../parachain/", default-features = false }
|
polkadot-parachain = { path = "../../parachain/", default-features = false }
|
||||||
parity-codec-derive = { git = "https://github.com/paritytech/substrate", default-features = false }
|
parity-codec = { version = "2.1", default-features = false }
|
||||||
|
parity-codec-derive = { version = "2.1", default-features = false }
|
||||||
tiny-keccak = "1.4"
|
tiny-keccak = "1.4"
|
||||||
|
|||||||
@@ -20,11 +20,11 @@
|
|||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate parity_codec_derive;
|
extern crate parity_codec_derive;
|
||||||
|
extern crate parity_codec;
|
||||||
extern crate polkadot_parachain as parachain;
|
extern crate polkadot_parachain as parachain;
|
||||||
extern crate tiny_keccak;
|
extern crate tiny_keccak;
|
||||||
|
|
||||||
use parachain::codec::{self, Encode};
|
use parity_codec::Encode;
|
||||||
|
|
||||||
/// Head data for this parachain.
|
/// Head data for this parachain.
|
||||||
#[derive(Default, Clone, Hash, Eq, PartialEq, Encode, Decode)]
|
#[derive(Default, Clone, Hash, Eq, PartialEq, Encode, Decode)]
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "polkadot-transaction-pool"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Parity Technologies <admin@parity.io>"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
log = "0.3.0"
|
|
||||||
error-chain = "0.12"
|
|
||||||
parking_lot = "0.4"
|
|
||||||
polkadot-api = { path = "../api" }
|
|
||||||
polkadot-primitives = { path = "../primitives" }
|
|
||||||
polkadot-runtime = { path = "../runtime" }
|
|
||||||
substrate-client = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
parity-codec = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
substrate-keyring = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
substrate-extrinsic-pool = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
substrate-primitives = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
sr-primitives = { git = "https://github.com/paritytech/substrate" }
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
= Polkadot Transactin pool
|
|
||||||
|
|
||||||
placeholder
|
|
||||||
//TODO Write content :)
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Polkadot.
|
|
||||||
|
|
||||||
// Polkadot 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.
|
|
||||||
|
|
||||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
use extrinsic_pool;
|
|
||||||
use polkadot_api;
|
|
||||||
use primitives::Hash;
|
|
||||||
use runtime::{Address, UncheckedExtrinsic};
|
|
||||||
|
|
||||||
error_chain! {
|
|
||||||
links {
|
|
||||||
Pool(extrinsic_pool::Error, extrinsic_pool::ErrorKind);
|
|
||||||
Api(polkadot_api::Error, polkadot_api::ErrorKind);
|
|
||||||
}
|
|
||||||
errors {
|
|
||||||
/// Unexpected extrinsic format submitted
|
|
||||||
InvalidExtrinsicFormat {
|
|
||||||
description("Invalid extrinsic format."),
|
|
||||||
display("Invalid extrinsic format."),
|
|
||||||
}
|
|
||||||
/// Attempted to queue an inherent transaction.
|
|
||||||
IsInherent(xt: UncheckedExtrinsic) {
|
|
||||||
description("Inherent transactions cannot be queued."),
|
|
||||||
display("Inherent transactions cannot be queued."),
|
|
||||||
}
|
|
||||||
/// Attempted to queue a transaction with bad signature.
|
|
||||||
BadSignature(e: &'static str) {
|
|
||||||
description("Transaction had bad signature."),
|
|
||||||
display("Transaction had bad signature: {}", e),
|
|
||||||
}
|
|
||||||
/// Attempted to queue a transaction that is already in the pool.
|
|
||||||
AlreadyImported(hash: Hash) {
|
|
||||||
description("Transaction is already in the pool."),
|
|
||||||
display("Transaction {:?} is already in the pool.", hash),
|
|
||||||
}
|
|
||||||
/// Import error.
|
|
||||||
Import(err: Box<::std::error::Error + Send>) {
|
|
||||||
description("Error importing transaction"),
|
|
||||||
display("Error importing transaction: {}", err.description()),
|
|
||||||
}
|
|
||||||
/// Runtime failure.
|
|
||||||
UnrecognisedAddress(who: Address) {
|
|
||||||
description("Unrecognised address in extrinsic"),
|
|
||||||
display("Unrecognised address in extrinsic: {}", who),
|
|
||||||
}
|
|
||||||
/// Extrinsic too large
|
|
||||||
TooLarge(got: usize, max: usize) {
|
|
||||||
description("Extrinsic too large"),
|
|
||||||
display("Extrinsic is too large ({} > {})", got, max),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl extrinsic_pool::IntoPoolError for Error {
|
|
||||||
fn into_pool_error(self) -> ::std::result::Result<extrinsic_pool::Error, Self> {
|
|
||||||
match self {
|
|
||||||
Error(ErrorKind::Pool(e), c) => Ok(extrinsic_pool::Error(e, c)),
|
|
||||||
e => Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,579 +0,0 @@
|
|||||||
// Copyright 2018 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Polkadot.
|
|
||||||
|
|
||||||
// Polkadot 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.
|
|
||||||
|
|
||||||
// Polkadot 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 Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
extern crate substrate_client as client;
|
|
||||||
extern crate parity_codec as codec;
|
|
||||||
extern crate substrate_extrinsic_pool as extrinsic_pool;
|
|
||||||
extern crate substrate_primitives;
|
|
||||||
extern crate sr_primitives;
|
|
||||||
extern crate polkadot_runtime as runtime;
|
|
||||||
extern crate polkadot_primitives as primitives;
|
|
||||||
extern crate polkadot_api;
|
|
||||||
extern crate parking_lot;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
extern crate substrate_keyring;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate error_chain;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log;
|
|
||||||
|
|
||||||
mod error;
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
cmp::Ordering,
|
|
||||||
collections::HashMap,
|
|
||||||
sync::Arc,
|
|
||||||
};
|
|
||||||
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use extrinsic_pool::{Readiness, scoring::{Change, Choice}, VerifiedFor, ExtrinsicFor};
|
|
||||||
use polkadot_api::PolkadotApi;
|
|
||||||
use primitives::{AccountId, BlockId, Block, Hash, Index};
|
|
||||||
use runtime::{Address, UncheckedExtrinsic};
|
|
||||||
use sr_primitives::traits::{Bounded, Checkable, Hash as HashT, BlakeTwo256};
|
|
||||||
|
|
||||||
pub use extrinsic_pool::{Options, Status, LightStatus, VerifiedTransaction as VerifiedTransactionOps};
|
|
||||||
pub use error::{Error, ErrorKind, Result};
|
|
||||||
|
|
||||||
/// Maximal size of a single encoded extrinsic.
|
|
||||||
///
|
|
||||||
/// See also polkadot-consensus::MAX_TRANSACTIONS_SIZE
|
|
||||||
const MAX_TRANSACTION_SIZE: usize = 4 * 1024 * 1024;
|
|
||||||
|
|
||||||
/// Type alias for convenience.
|
|
||||||
pub type CheckedExtrinsic = <UncheckedExtrinsic as Checkable<fn(Address) -> std::result::Result<AccountId, &'static str>>>::Checked;
|
|
||||||
|
|
||||||
/// Type alias for polkadot transaction pool.
|
|
||||||
pub type TransactionPool<A> = extrinsic_pool::Pool<ChainApi<A>>;
|
|
||||||
|
|
||||||
/// A verified transaction which should be includable and non-inherent.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct VerifiedTransaction {
|
|
||||||
inner: Option<CheckedExtrinsic>,
|
|
||||||
sender: Option<AccountId>,
|
|
||||||
hash: Hash,
|
|
||||||
encoded_size: usize,
|
|
||||||
index: Index,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VerifiedTransaction {
|
|
||||||
/// Consume the verified transaction, yielding the checked counterpart.
|
|
||||||
pub fn into_inner(self) -> Option<CheckedExtrinsic> {
|
|
||||||
self.inner
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the 256-bit hash of this transaction.
|
|
||||||
pub fn hash(&self) -> &Hash {
|
|
||||||
&self.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the account ID of the sender of this transaction.
|
|
||||||
pub fn sender(&self) -> Option<AccountId> {
|
|
||||||
self.sender
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the account ID of the sender of this transaction.
|
|
||||||
pub fn index(&self) -> Index {
|
|
||||||
self.index
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get encoded size of the transaction.
|
|
||||||
pub fn encoded_size(&self) -> usize {
|
|
||||||
self.encoded_size
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if the transaction is not yet fully verified.
|
|
||||||
pub fn is_fully_verified(&self) -> bool {
|
|
||||||
self.inner.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl extrinsic_pool::VerifiedTransaction for VerifiedTransaction {
|
|
||||||
type Hash = Hash;
|
|
||||||
type Sender = Option<AccountId>;
|
|
||||||
|
|
||||||
fn hash(&self) -> &Self::Hash {
|
|
||||||
&self.hash
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sender(&self) -> &Self::Sender {
|
|
||||||
&self.sender
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mem_usage(&self) -> usize {
|
|
||||||
self.encoded_size // TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The polkadot transaction pool logic.
|
|
||||||
pub struct ChainApi<A> {
|
|
||||||
api: Arc<A>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A> ChainApi<A> where
|
|
||||||
A: PolkadotApi,
|
|
||||||
{
|
|
||||||
const NO_ACCOUNT: &'static str = "Account not found.";
|
|
||||||
/// Create a new instance.
|
|
||||||
pub fn new(api: Arc<A>) -> Self {
|
|
||||||
ChainApi {
|
|
||||||
api,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn lookup(&self, at: &BlockId, address: Address) -> ::std::result::Result<AccountId, &'static str> {
|
|
||||||
// TODO [ToDr] Consider introducing a cache for this.
|
|
||||||
match self.api.lookup(at, address.clone()) {
|
|
||||||
Ok(Some(address)) => Ok(address),
|
|
||||||
Ok(None) => Err(Self::NO_ACCOUNT.into()),
|
|
||||||
Err(e) => {
|
|
||||||
error!("Error looking up address: {:?}: {:?}", address, e);
|
|
||||||
Err("API error.")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A> extrinsic_pool::ChainApi for ChainApi<A> where
|
|
||||||
A: PolkadotApi + Send + Sync,
|
|
||||||
{
|
|
||||||
type Block = Block;
|
|
||||||
type Hash = Hash;
|
|
||||||
type Sender = Option<AccountId>;
|
|
||||||
type VEx = VerifiedTransaction;
|
|
||||||
type Ready = HashMap<AccountId, u32>;
|
|
||||||
type Error = Error;
|
|
||||||
type Score = u64;
|
|
||||||
type Event = ();
|
|
||||||
|
|
||||||
fn verify_transaction(&self, at: &BlockId, xt: &ExtrinsicFor<Self>) -> Result<Self::VEx> {
|
|
||||||
let encoded = xt.encode();
|
|
||||||
let uxt = UncheckedExtrinsic::decode(&mut encoded.as_slice()).ok_or_else(|| ErrorKind::InvalidExtrinsicFormat)?;
|
|
||||||
if !uxt.is_signed() {
|
|
||||||
bail!(ErrorKind::IsInherent(uxt))
|
|
||||||
}
|
|
||||||
|
|
||||||
let (encoded_size, hash) = (encoded.len(), BlakeTwo256::hash(&encoded));
|
|
||||||
if encoded_size > MAX_TRANSACTION_SIZE {
|
|
||||||
bail!(ErrorKind::TooLarge(encoded_size, MAX_TRANSACTION_SIZE));
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!(target: "transaction-pool", "Transaction submitted: {}", ::substrate_primitives::hexdisplay::HexDisplay::from(&encoded));
|
|
||||||
let inner = match uxt.clone().check_with(|a| self.lookup(at, a)) {
|
|
||||||
Ok(xt) => Some(xt),
|
|
||||||
// keep the transaction around in the future pool and attempt to promote it later.
|
|
||||||
Err(Self::NO_ACCOUNT) => None,
|
|
||||||
Err(e) => bail!(e),
|
|
||||||
};
|
|
||||||
let sender = match inner.as_ref() {
|
|
||||||
Some(cxt) => match cxt.signed {
|
|
||||||
Some(ref sender) => Some(sender.clone()),
|
|
||||||
None => bail!(ErrorKind::IsInherent(uxt))
|
|
||||||
},
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if encoded_size < 1024 {
|
|
||||||
debug!(target: "transaction-pool", "Transaction verified: {} => {:?}", hash, uxt);
|
|
||||||
} else {
|
|
||||||
debug!(target: "transaction-pool", "Transaction verified: {} ({} bytes is too large to display)", hash, encoded_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(VerifiedTransaction {
|
|
||||||
index: uxt.index,
|
|
||||||
inner,
|
|
||||||
sender,
|
|
||||||
hash,
|
|
||||||
encoded_size,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ready(&self) -> Self::Ready {
|
|
||||||
HashMap::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_ready(&self, at: &BlockId, known_nonces: &mut Self::Ready, xt: &VerifiedFor<Self>) -> Readiness {
|
|
||||||
let sender = match xt.verified.sender() {
|
|
||||||
Some(sender) => sender,
|
|
||||||
None => return Readiness::Future
|
|
||||||
};
|
|
||||||
|
|
||||||
trace!(target: "transaction-pool", "Checking readiness of {} (from {})", xt.verified.hash, Hash::from(sender));
|
|
||||||
|
|
||||||
// TODO: find a way to handle index error properly -- will need changes to
|
|
||||||
// transaction-pool trait.
|
|
||||||
let api = &self.api;
|
|
||||||
let next_index = known_nonces.entry(sender)
|
|
||||||
.or_insert_with(|| api.index(at, sender).ok().unwrap_or_else(Bounded::max_value));
|
|
||||||
|
|
||||||
trace!(target: "transaction-pool", "Next index for sender is {}; xt index is {}", next_index, xt.verified.index);
|
|
||||||
|
|
||||||
let result = match xt.verified.index.cmp(&next_index) {
|
|
||||||
// TODO: this won't work perfectly since accounts can now be killed, returning the nonce
|
|
||||||
// to zero.
|
|
||||||
// We should detect if the index was reset and mark all transactions as `Stale` for cull to work correctly.
|
|
||||||
// Otherwise those transactions will keep occupying the queue.
|
|
||||||
// Perhaps we could mark as stale if `index - state_index` > X?
|
|
||||||
Ordering::Greater => Readiness::Future,
|
|
||||||
Ordering::Equal => Readiness::Ready,
|
|
||||||
// TODO [ToDr] Should mark transactions referencing too old blockhash as `Stale` as well.
|
|
||||||
Ordering::Less => Readiness::Stale,
|
|
||||||
};
|
|
||||||
|
|
||||||
// remember to increment `next_index`
|
|
||||||
*next_index = next_index.saturating_add(1);
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare(old: &VerifiedFor<Self>, other: &VerifiedFor<Self>) -> Ordering {
|
|
||||||
old.verified.index().cmp(&other.verified.index())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn choose(old: &VerifiedFor<Self>, new: &VerifiedFor<Self>) -> Choice {
|
|
||||||
if old.verified.is_fully_verified() {
|
|
||||||
assert!(new.verified.is_fully_verified(), "Scoring::choose called with transactions from different senders");
|
|
||||||
if old.verified.index() == new.verified.index() {
|
|
||||||
return Choice::ReplaceOld;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will keep both transactions, even though they have the same indices.
|
|
||||||
// It's fine for not fully verified transactions, we might also allow it for
|
|
||||||
// verified transactions but it would mean that only one of the two is actually valid
|
|
||||||
// (most likely the first to be included in the block).
|
|
||||||
Choice::InsertNew
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_scores(
|
|
||||||
xts: &[extrinsic_pool::Transaction<VerifiedFor<Self>>],
|
|
||||||
scores: &mut [Self::Score],
|
|
||||||
_change: Change<()>
|
|
||||||
) {
|
|
||||||
for i in 0..xts.len() {
|
|
||||||
if !xts[i].verified.is_fully_verified() {
|
|
||||||
scores[i] = 0;
|
|
||||||
} else {
|
|
||||||
// all the same score since there are no fees.
|
|
||||||
// TODO: prioritize things like misbehavior or fishermen reports
|
|
||||||
scores[i] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn should_replace(old: &VerifiedFor<Self>, _new: &VerifiedFor<Self>) -> Choice {
|
|
||||||
if old.verified.is_fully_verified() {
|
|
||||||
// Don't allow new transactions if we are reaching the limit.
|
|
||||||
Choice::RejectNew
|
|
||||||
} else {
|
|
||||||
// Always replace not fully verified transactions.
|
|
||||||
Choice::ReplaceOld
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use std::sync::{atomic::{self, AtomicBool}, Arc};
|
|
||||||
use substrate_keyring::Keyring::{self, *};
|
|
||||||
use codec::{Decode, Encode};
|
|
||||||
use polkadot_api::{PolkadotApi, BlockBuilder, Result};
|
|
||||||
use primitives::{AccountId, AccountIndex, Block, BlockId, Hash, Index, SessionKey,
|
|
||||||
UncheckedExtrinsic as FutureProofUncheckedExtrinsic};
|
|
||||||
use runtime::{RawAddress, Call, TimestampCall, UncheckedExtrinsic};
|
|
||||||
use primitives::parachain::{DutyRoster, Id as ParaId};
|
|
||||||
use sr_primitives::generic;
|
|
||||||
use extrinsic_pool::Pool;
|
|
||||||
use super::ChainApi;
|
|
||||||
|
|
||||||
struct TestBlockBuilder;
|
|
||||||
impl BlockBuilder for TestBlockBuilder {
|
|
||||||
fn push_extrinsic(&mut self, _extrinsic: FutureProofUncheckedExtrinsic) -> Result<()> { unimplemented!() }
|
|
||||||
fn bake(self) -> Result<Block> { unimplemented!() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn number_of(at: &BlockId) -> u32 {
|
|
||||||
match at {
|
|
||||||
generic::BlockId::Number(n) => *n as u32,
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default, Clone)]
|
|
||||||
struct TestPolkadotApi {
|
|
||||||
no_lookup: Arc<AtomicBool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestPolkadotApi {
|
|
||||||
fn without_lookup() -> Self {
|
|
||||||
TestPolkadotApi {
|
|
||||||
no_lookup: Arc::new(AtomicBool::new(true)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enable_lookup(&self) {
|
|
||||||
self.no_lookup.store(false, atomic::Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PolkadotApi for TestPolkadotApi {
|
|
||||||
type BlockBuilder = TestBlockBuilder;
|
|
||||||
|
|
||||||
fn session_keys(&self, _at: &BlockId) -> Result<Vec<SessionKey>> { unimplemented!() }
|
|
||||||
fn validators(&self, _at: &BlockId) -> Result<Vec<AccountId>> { unimplemented!() }
|
|
||||||
fn random_seed(&self, _at: &BlockId) -> Result<Hash> { unimplemented!() }
|
|
||||||
fn duty_roster(&self, _at: &BlockId) -> Result<DutyRoster> { unimplemented!() }
|
|
||||||
fn timestamp(&self, _at: &BlockId) -> Result<u64> { unimplemented!() }
|
|
||||||
fn evaluate_block(&self, _at: &BlockId, _block: Block) -> Result<bool> { unimplemented!() }
|
|
||||||
fn active_parachains(&self, _at: &BlockId) -> Result<Vec<ParaId>> { unimplemented!() }
|
|
||||||
fn parachain_code(&self, _at: &BlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> { unimplemented!() }
|
|
||||||
fn parachain_head(&self, _at: &BlockId, _parachain: ParaId) -> Result<Option<Vec<u8>>> { unimplemented!() }
|
|
||||||
fn build_block(&self, _at: &BlockId, _inherent: ::primitives::InherentData) -> Result<Self::BlockBuilder> { unimplemented!() }
|
|
||||||
fn inherent_extrinsics(&self, _at: &BlockId, _inherent: ::primitives::InherentData) -> Result<Vec<::primitives::UncheckedExtrinsic>> { unimplemented!() }
|
|
||||||
|
|
||||||
fn index(&self, _at: &BlockId, _account: AccountId) -> Result<Index> {
|
|
||||||
Ok((_account[0] as u32) + number_of(_at))
|
|
||||||
}
|
|
||||||
fn lookup(&self, _at: &BlockId, _address: RawAddress<AccountId, AccountIndex>) -> Result<Option<AccountId>> {
|
|
||||||
match _address {
|
|
||||||
RawAddress::Id(i) => Ok(Some(i)),
|
|
||||||
RawAddress::Index(_) if self.no_lookup.load(atomic::Ordering::SeqCst) => Ok(None),
|
|
||||||
RawAddress::Index(i) => Ok(match (i < 8, i + (number_of(_at) as u64) % 8) {
|
|
||||||
(false, _) => None,
|
|
||||||
(_, 0) => Some(Alice.to_raw_public().into()),
|
|
||||||
(_, 1) => Some(Bob.to_raw_public().into()),
|
|
||||||
(_, 2) => Some(Charlie.to_raw_public().into()),
|
|
||||||
(_, 3) => Some(Dave.to_raw_public().into()),
|
|
||||||
(_, 4) => Some(Eve.to_raw_public().into()),
|
|
||||||
(_, 5) => Some(Ferdie.to_raw_public().into()),
|
|
||||||
(_, 6) => Some(One.to_raw_public().into()),
|
|
||||||
(_, 7) => Some(Two.to_raw_public().into()),
|
|
||||||
_ => None,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn uxt(who: Keyring, nonce: Index, use_id: bool) -> FutureProofUncheckedExtrinsic {
|
|
||||||
let sxt = (nonce, Call::Timestamp(TimestampCall::set(0)));
|
|
||||||
let sig = sxt.using_encoded(|e| who.sign(e));
|
|
||||||
let signed = who.to_raw_public().into();
|
|
||||||
let sender = if use_id { RawAddress::Id(signed) } else { RawAddress::Index(
|
|
||||||
match who {
|
|
||||||
Alice => 0,
|
|
||||||
Bob => 1,
|
|
||||||
Charlie => 2,
|
|
||||||
Dave => 3,
|
|
||||||
Eve => 4,
|
|
||||||
Ferdie => 5,
|
|
||||||
One => 6,
|
|
||||||
Two => 7,
|
|
||||||
}
|
|
||||||
)};
|
|
||||||
UncheckedExtrinsic::new_signed(sxt.0, sxt.1, sender, sig.into())
|
|
||||||
.using_encoded(|e| FutureProofUncheckedExtrinsic::decode(&mut &e[..]))
|
|
||||||
.unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pool(api: &TestPolkadotApi) -> Pool<ChainApi<TestPolkadotApi>> {
|
|
||||||
Pool::new(Default::default(), ChainApi { api: Arc::new(api.clone()) })
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn id_submission_should_work() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, true)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn index_submission_should_work() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, false)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn multiple_id_submission_should_work() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, true)).unwrap();
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 210, true)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn multiple_index_submission_should_work() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, false)).unwrap();
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 210, false)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn id_based_early_nonce_should_be_culled() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 208, true)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn index_based_early_nonce_should_be_culled() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 208, false)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn id_based_late_nonce_should_be_queued() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 210, true)).unwrap();
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![]);
|
|
||||||
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, true)).unwrap();
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn index_based_late_nonce_should_be_queued() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 210, false)).unwrap();
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![]);
|
|
||||||
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, false)).unwrap();
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![(Some(Alice.to_raw_public().into()), 209), (Some(Alice.to_raw_public().into()), 210)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn index_then_id_submission_should_make_progress() {
|
|
||||||
let api = TestPolkadotApi::without_lookup();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, false)).unwrap();
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 210, true)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![]);
|
|
||||||
|
|
||||||
api.enable_lookup();
|
|
||||||
pool.retry_verification(&BlockId::number(0), None).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![
|
|
||||||
(Some(Alice.to_raw_public().into()), 209),
|
|
||||||
(Some(Alice.to_raw_public().into()), 210)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn retrying_verification_might_not_change_anything() {
|
|
||||||
let api = TestPolkadotApi::without_lookup();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, false)).unwrap();
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 210, true)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![]);
|
|
||||||
|
|
||||||
pool.retry_verification(&BlockId::number(1), None).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn id_then_index_submission_should_make_progress() {
|
|
||||||
let api = TestPolkadotApi::without_lookup();
|
|
||||||
let pool = pool(&api);
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 209, true)).unwrap();
|
|
||||||
pool.submit_one(&BlockId::number(0), uxt(Alice, 210, false)).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![
|
|
||||||
(Some(Alice.to_raw_public().into()), 209)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// when
|
|
||||||
api.enable_lookup();
|
|
||||||
pool.retry_verification(&BlockId::number(0), None).unwrap();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(0), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![
|
|
||||||
(Some(Alice.to_raw_public().into()), 209),
|
|
||||||
(Some(Alice.to_raw_public().into()), 210)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn index_change_should_result_in_second_tx_culled_or_future() {
|
|
||||||
let api = TestPolkadotApi::default();
|
|
||||||
let pool = pool(&api);
|
|
||||||
let block = BlockId::number(0);
|
|
||||||
pool.submit_one(&block, uxt(Alice, 209, false)).unwrap();
|
|
||||||
let hash = *pool.submit_one(&block, uxt(Alice, 210, false)).unwrap().verified.hash();
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&block, |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![
|
|
||||||
(Some(Alice.to_raw_public().into()), 209),
|
|
||||||
(Some(Alice.to_raw_public().into()), 210)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// first xt is mined, but that has a side-effect of switching index 0 from Alice to Bob.
|
|
||||||
// second xt now invalid signature, so it fails.
|
|
||||||
|
|
||||||
// there is no way of reporting this back to the queue right now (TODO). this should cause
|
|
||||||
// the queue to flush all information regarding the sender index/account.
|
|
||||||
|
|
||||||
// after this, a re-evaluation of the second's readiness should result in it being thrown
|
|
||||||
// out (or maybe placed in future queue).
|
|
||||||
let err = pool.reverify_transaction(&BlockId::number(1), hash).unwrap_err();
|
|
||||||
match *err.kind() {
|
|
||||||
::error::ErrorKind::Msg(ref m) if m == "bad signature in extrinsic" => {},
|
|
||||||
ref e => assert!(false, "The transaction should be rejected with BadSignature error, got: {:?}", e),
|
|
||||||
}
|
|
||||||
|
|
||||||
let pending: Vec<_> = pool.cull_and_get_pending(&BlockId::number(1), |p| p.map(|a| (a.verified.sender(), a.verified.index())).collect()).unwrap();
|
|
||||||
assert_eq!(pending, vec![]);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user