diff --git a/Cargo.lock b/Cargo.lock index 4bc5b446e5ad6..686376a0cac92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,3 +1,26 @@ +[[package]] +name = "adder" +version = "0.1.0" +dependencies = [ + "polkadot-parachain 0.1.0", + "tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "adder-collator" +version = "0.1.0" +dependencies = [ + "adder 0.1.0", + "ctrlc 1.1.1 (git+https://github.com/paritytech/rust-ctrlc.git)", + "ed25519 0.1.0", + "exit-future 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-collator 0.1.0", + "polkadot-parachain 0.1.0", + "polkadot-primitives 0.1.0", +] + [[package]] name = "aho-corasick" version = "0.6.4" diff --git a/Cargo.toml b/Cargo.toml index 6b5271641edec..a7630a11ce9a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,9 @@ members = [ "polkadot/transaction-pool", "polkadot/service", + "polkadot/test-parachains/adder", + "polkadot/test-parachains/adder/collator", + "substrate/bft", "substrate/cli", "substrate/client", @@ -77,6 +80,7 @@ members = [ ] exclude = [ "polkadot/runtime/wasm", + "polkadot/test-parachains/adder/wasm", "demo/runtime/wasm", "substrate/executor/wasm", "substrate/pwasm-alloc", @@ -92,4 +96,4 @@ is-it-maintained-open-issues = { repository = "paritytech/polkadot" } [profile.release] # Substrate runtime requires unwinding. -panic = "unwind" +panic = "unwind" diff --git a/common.sh b/common.sh index 847aa23820439..254a4260e4bd6 100644 --- a/common.sh +++ b/common.sh @@ -8,7 +8,7 @@ SRCS=( "substrate/executor/wasm" "demo/runtime/wasm" "substrate/test-runtime/wasm" - "polkadot/parachain/test-chains/basic_add" + "polkadot/test-parachains/" ) # Make pushd/popd silent. diff --git a/demo/runtime/wasm/Cargo.lock b/demo/runtime/wasm/Cargo.lock index d5eac31f66037..e3fda5b1f0b2b 100644 --- a/demo/runtime/wasm/Cargo.lock +++ b/demo/runtime/wasm/Cargo.lock @@ -730,7 +730,7 @@ dependencies = [ "substrate-runtime-std 0.1.0", "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)", - "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -846,7 +846,7 @@ dependencies = [ "substrate-primitives 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-std 0.1.0", - "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1107,7 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasmi" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1231,7 +1231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4a6d379e9332b1b1f52c5a87f2481c85c7c931d8ec411963dfb8f26b1ec1e3" +"checksum wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "522fe3fdd44a56f25cd5ddcd8ccdb1cf2e982ceb28fcb00f41d8a018ae5245a8" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm index b51ad28c97b97..e202d418ffb6f 100644 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.compact.wasm differ diff --git a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm index 3b0e6e0b0f864..b684d5b09713a 100755 Binary files a/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm and b/demo/runtime/wasm/target/wasm32-unknown-unknown/release/demo_runtime.wasm differ diff --git a/polkadot/collator/src/lib.rs b/polkadot/collator/src/lib.rs index 37428495d9d38..60b46d47f0b89 100644 --- a/polkadot/collator/src/lib.rs +++ b/polkadot/collator/src/lib.rs @@ -60,6 +60,7 @@ extern crate polkadot_primitives; extern crate log; use std::collections::{BTreeSet, BTreeMap, HashSet}; +use std::fmt; use std::sync::Arc; use std::time::{Duration, Instant}; @@ -68,12 +69,36 @@ use client::BlockchainEvents; use polkadot_api::PolkadotApi; use polkadot_primitives::{AccountId, BlockId, SessionKey}; use polkadot_primitives::parachain::{self, BlockData, DutyRoster, HeadData, ConsolidatedIngress, Message, Id as ParaId}; -use polkadot_cli::{ServiceComponents, Service, CustomConfiguration, VersionInfo}; +use polkadot_cli::{ServiceComponents, Service, CustomConfiguration}; use polkadot_cli::{Worker, IntoExit}; use tokio::timer::Deadline; +pub use polkadot_cli::VersionInfo; + const COLLATION_TIMEOUT: Duration = Duration::from_secs(30); +/// Error to return when the head data was invalid. +#[derive(Clone, Copy, Debug)] +pub struct InvalidHead; + +/// Collation errors. +#[derive(Debug)] +pub enum Error { + /// Error on the relay-chain side of things. + Polkadot(R), + /// Error on the collator side of things. + Collator(InvalidHead), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::Polkadot(ref err) => write!(f, "Polkadot node error: {}", err), + Error::Collator(_) => write!(f, "Collator node error: Invalid head data"), + } + } +} + /// Parachain context needed for collation. /// /// This can be implemented through an externally attached service or a stub. @@ -84,7 +109,7 @@ pub trait ParachainContext: Clone { &self, last_head: HeadData, ingress: I, - ) -> (BlockData, HeadData); + ) -> Result<(BlockData, HeadData), InvalidHead>; } /// Relay chain context needed to collate. @@ -154,18 +179,18 @@ pub fn collate<'a, R, P>( para_context: P, key: Arc, ) - -> impl Future + 'a + -> impl Future> + 'a where R: RelayChainContext + 'a, R::Error: 'a, R::FutureEgress: 'a, P: ParachainContext + 'a, { - collate_ingress(relay_context).map(move |ingress| { + collate_ingress(relay_context).map_err(Error::Polkadot).and_then(move |ingress| { let (block_data, head_data) = para_context.produce_candidate( last_head, ingress.0.iter().flat_map(|&(id, ref msgs)| msgs.iter().cloned().map(move |msg| (id, msg))) - ); + ).map_err(Error::Collator)?; let block_data_hash = block_data.hash(); let signature = key.sign(&block_data_hash.0[..]).into(); @@ -181,10 +206,10 @@ pub fn collate<'a, R, P>( block_data_hash, }; - parachain::Collation { + Ok(parachain::Collation { receipt, block_data, - } + }) }) } @@ -248,7 +273,7 @@ impl Worker for CollationNode where ($e:expr) => { match $e { Ok(x) => x, - Err(e) => return future::Either::A(future::err(e)), + Err(e) => return future::Either::A(future::err(Error::Polkadot(e))), } } } @@ -323,17 +348,19 @@ fn compute_targets(para_id: ParaId, session_keys: &[SessionKey], roster: DutyRos /// /// Provide a future which resolves when the node should exit. /// This function blocks until done. -pub fn run_collator( +pub fn run_collator( parachain_context: P, para_id: ParaId, exit: E, key: Arc, - args: Vec<::std::ffi::OsString>, + args: I, version: VersionInfo, ) -> polkadot_cli::error::Result<()> where P: ParachainContext + Send + 'static, E: IntoFuture, E::Future: Send + Clone + 'static, + I: IntoIterator, + ArgT: Into + Clone, { let node_logic = CollationNode { parachain_context, exit: exit.into_future(), para_id, key }; polkadot_cli::run(args, node_logic, version) diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index 4aee785def5b2..616f821b5b3df 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -273,8 +273,13 @@ impl bft::Environment for ProposerFactory sign_with.public().into(), )?; + info!("Starting consensus session on top of parent {:?}. Local parachain duty is {:?}", + parent_hash, local_duty.validation); + let active_parachains = self.client.active_parachains(&id)?; + debug!(target: "consensus", "Active parachains: {:?}", active_parachains); + let n_parachains = active_parachains.len(); let table = Arc::new(SharedTable::new(group_info, sign_with.clone(), parent_hash)); let (router, input, output) = self.network.communication_for( diff --git a/polkadot/consensus/src/shared_table/mod.rs b/polkadot/consensus/src/shared_table/mod.rs index 21919dd4a4869..825c62428265d 100644 --- a/polkadot/consensus/src/shared_table/mod.rs +++ b/polkadot/consensus/src/shared_table/mod.rs @@ -143,6 +143,7 @@ impl SharedTableInner { fetch_block_data, fetch_extrinsic, evaluate: checking_validity, + ensure_available: checking_availability, }) } } @@ -206,6 +207,7 @@ struct Work { fetch_block_data: future::Fuse, fetch_extrinsic: Option>, evaluate: bool, + ensure_available: bool, } /// Primed statement producer. @@ -235,31 +237,35 @@ impl Future for PrimedStatementProducer }); let hash = work.candidate_receipt.hash(); + + debug!(target: "consensus", "Making validity statement about candidate {}: is_good? {:?}", hash, is_good); self.inner.produced_statements.validity = match is_good { Some(true) => Some(GenericStatement::Valid(hash)), Some(false) => Some(GenericStatement::Invalid(hash)), None => None, }; - } - } - if let Some(ref mut fetch_extrinsic) = work.fetch_extrinsic { - if let Async::Ready(extrinsic) = fetch_extrinsic.poll()? { - self.inner.produced_statements.extrinsic = Some(extrinsic); + work.evaluate = false; } } - let done = self.inner.produced_statements.block_data.is_some() && { - if work.evaluate { - true - } else if self.inner.produced_statements.extrinsic.is_some() { + if let Async::Ready(Some(extrinsic)) = work.fetch_extrinsic.poll()? { + if work.ensure_available { + let hash = work.candidate_receipt.hash(); + debug!(target: "consensus", "Claiming candidate {} available.", hash); + + // TODO: actually wait for block data and then ensure availability. + self.inner.produced_statements.extrinsic = Some(extrinsic); self.inner.produced_statements.availability = - Some(GenericStatement::Available(work.candidate_receipt.hash())); + Some(GenericStatement::Available(hash)); - true - } else { - false + work.ensure_available = false; } + } + + let done = match (work.evaluate, work.ensure_available) { + (false, false) => true, + _ => false, }; if done { @@ -356,10 +362,25 @@ impl SharedTable { } /// Sign and import a local statement. - pub fn sign_and_import(&self, statement: table::Statement) -> SignedStatement { - let proposed_digest = match statement { - GenericStatement::Candidate(ref c) => Some(c.hash()), - _ => None, + /// + /// For candidate statements, this may also produce a second signed statement + /// concerning the availability of the candidate data. + pub fn sign_and_import(&self, statement: table::Statement) + -> (SignedStatement, Option) + { + let (proposed_digest, availability) = match statement { + GenericStatement::Candidate(ref c) => { + let mut availability = None; + let hash = c.hash(); + + // TODO: actually store the data in an availability store of some kind. + if self.context.is_availability_guarantor_of(&self.context.local_id(), &c.parachain_index) { + availability = Some(self.context.sign_statement(GenericStatement::Available(hash))); + } + + (Some(hash), availability) + } + _ => (None, None), }; let signed_statement = self.context.sign_statement(statement); @@ -370,7 +391,13 @@ impl SharedTable { } inner.table.import_statement(&*self.context, signed_statement.clone()); - signed_statement + + // ensure the availability statement is imported after the candidate. + if let Some(a) = availability.clone() { + inner.table.import_statement(&*self.context, a); + } + + (signed_statement, availability) } /// Execute a closure using a specific candidate. @@ -543,5 +570,6 @@ mod tests { assert!(producer.work.fetch_extrinsic.is_some(), "should fetch extrinsic when guaranteeing availability"); assert!(!producer.work.evaluate, "should not evaluate validity"); + assert!(producer.work.ensure_available); } } diff --git a/polkadot/network/src/consensus.rs b/polkadot/network/src/consensus.rs index 4f29899100350..e3a1c592e787d 100644 --- a/polkadot/network/src/consensus.rs +++ b/polkadot/network/src/consensus.rs @@ -176,6 +176,7 @@ impl MessageProcessTask

{ } } ConsensusMessage::ChainSpecific(msg, _) => { + debug!(target: "consensus", "Processing consensus statement for live consensus"); if let Some(Message::Statement(parent_hash, statement)) = Decode::decode(&mut msg.as_slice()) { if ::polkadot_consensus::check_statement(&statement.statement, &statement.signature, statement.sender, &parent_hash) { self.table_router.import_statement(statement); diff --git a/polkadot/network/src/lib.rs b/polkadot/network/src/lib.rs index 8fbc6dba3a7b5..dfbaa2ea173af 100644 --- a/polkadot/network/src/lib.rs +++ b/polkadot/network/src/lib.rs @@ -111,10 +111,36 @@ struct BlockDataRequest { sender: oneshot::Sender, } +// ensures collator-protocol messages are sent in correct order. +// session key must be sent before collator role. +enum CollatorState { + Fresh, + RolePending(Role), + Primed, +} + +impl CollatorState { + fn send_key(&mut self, key: SessionKey, mut f: F) { + f(Message::SessionKey(key)); + if let CollatorState::RolePending(role) = ::std::mem::replace(self, CollatorState::Primed) { + f(Message::CollatorRole(role)); + } + } + + fn set_role(&mut self, role: Role, mut f: F) { + if let CollatorState::Primed = *self { + f(Message::CollatorRole(role)); + } else { + *self = CollatorState::RolePending(role); + } + } +} + struct PeerInfo { collating_for: Option<(AccountId, ParaId)>, validator_key: Option, claimed_validator: bool, + collator_state: CollatorState, } #[derive(Default)] @@ -281,8 +307,8 @@ impl PolkadotProtocol { } } - /// Send a statement to a validator. - fn send_statement(&mut self, ctx: &mut Context, _val: SessionKey, parent_hash: Hash, statement: SignedStatement) { + /// Gossip a consensus statement. + fn gossip_statement(&mut self, ctx: &mut Context, 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); @@ -309,14 +335,14 @@ impl PolkadotProtocol { let old_data = self.live_consensus.as_ref().map(|c| (c.parent_hash, c.local_session_key)); if Some(&consensus.local_session_key) != old_data.as_ref().map(|&(_, ref key)| key) { - for (id, _) in self.peers.iter() + for (id, peer_data) in self.peers.iter_mut() .filter(|&(_, ref info)| info.claimed_validator || info.collating_for.is_some()) { - send_polkadot_message( + peer_data.collator_state.send_key(consensus.local_session_key, |msg| send_polkadot_message( ctx, *id, - Message::SessionKey(consensus.local_session_key) - ); + msg + )); } } @@ -452,12 +478,15 @@ impl PolkadotProtocol { } }; + debug!(target: "p_net", "New collator role {:?} from {}", role, who); + match info.validator_key { None => ctx.report_peer( who, Severity::Bad("Sent collator role without registering first as validator"), ), Some(key) => for (relay_parent, collation) in self.local_collations.note_validator_role(key, role) { + debug!(target: "p_net", "Broadcasting collation on relay parent {:?}", relay_parent); send_polkadot_message( ctx, who, @@ -481,38 +510,41 @@ impl Specialization for PolkadotProtocol { } }; + let validator = status.roles.contains(substrate_network::Roles::AUTHORITY); + let send_key = validator || local_status.collating_for.is_some(); + + let mut peer_info = PeerInfo { + collating_for: local_status.collating_for, + validator_key: None, + claimed_validator: validator, + collator_state: CollatorState::Fresh, + }; + if let Some((ref acc_id, ref para_id)) = local_status.collating_for { - if self.collator_peer_id(acc_id.clone()).is_some() { + if self.collator_peer(acc_id.clone()).is_some() { ctx.report_peer(who, Severity::Useless("Unknown Polkadot-specific reason")); return } let collator_role = self.collators.on_new_collator(acc_id.clone(), para_id.clone()); - send_polkadot_message( + + peer_info.collator_state.set_role(collator_role, |msg| send_polkadot_message( ctx, who, - Message::CollatorRole(collator_role), - ); + msg, + )); } - let validator = status.roles.contains(substrate_network::Roles::AUTHORITY); - let send_key = validator || local_status.collating_for.is_some(); - - self.peers.insert(who, PeerInfo { - collating_for: local_status.collating_for, - validator_key: None, - claimed_validator: validator, - }); - - self.consensus_gossip.new_peer(ctx, who, status.roles); if let (true, &Some(ref consensus)) = (send_key, &self.live_consensus) { - send_polkadot_message( + peer_info.collator_state.send_key(consensus.local_session_key, |msg| send_polkadot_message( ctx, who, - Message::SessionKey(consensus.local_session_key) - ); + msg, + )); } + self.peers.insert(who, peer_info); + self.consensus_gossip.new_peer(ctx, who, status.roles); self.dispatch_pending_requests(ctx); } @@ -520,14 +552,14 @@ impl Specialization for PolkadotProtocol { if let Some(info) = self.peers.remove(&who) { if let Some((acc_id, _)) = info.collating_for { let new_primary = self.collators.on_disconnect(acc_id) - .and_then(|new_primary| self.collator_peer_id(new_primary)); + .and_then(|new_primary| self.collator_peer(new_primary)); - if let Some(new_primary) = new_primary { - send_polkadot_message( + if let Some((new_primary, primary_info)) = new_primary { + primary_info.collator_state.set_role(Role::Primary, |msg| send_polkadot_message( ctx, new_primary, - Message::CollatorRole(Role::Primary), - ) + msg, + )); } } @@ -592,12 +624,12 @@ impl Specialization for PolkadotProtocol { for collator_action in self.collators.maintain_peers() { match collator_action { Action::Disconnect(collator) => self.disconnect_bad_collator(ctx, collator), - Action::NewRole(account_id, role) => if let Some(collator) = self.collator_peer_id(account_id) { - send_polkadot_message( + Action::NewRole(account_id, role) => if let Some((collator, info)) = self.collator_peer(account_id) { + info.collator_state.set_role(role, |msg| send_polkadot_message( ctx, collator, - Message::CollatorRole(role), - ) + msg, + )) }, } } @@ -622,6 +654,7 @@ impl PolkadotProtocol { Some((ref acc_id, ref para_id)) => { let structurally_valid = para_id == &collation_para && acc_id == &collated_acc; if structurally_valid && collation.receipt.check_signature().is_ok() { + debug!(target: "p_net", "Received collation for parachain {:?} from peer {}", para_id, from); self.collators.on_collation(acc_id.clone(), relay_parent, collation) } else { ctx.report_peer(from, Severity::Bad("Sent malformed collation")) @@ -633,27 +666,28 @@ impl PolkadotProtocol { fn await_collation(&mut self, relay_parent: Hash, para_id: ParaId) -> oneshot::Receiver { let (tx, rx) = oneshot::channel(); + debug!(target: "p_net", "Attempting to get collation for parachain {:?} on relay parent {:?}", para_id, relay_parent); self.collators.await_collation(relay_parent, para_id, tx); rx } // get connected peer with given account ID for collation. - fn collator_peer_id(&self, account_id: AccountId) -> Option { + fn collator_peer(&mut self, account_id: AccountId) -> Option<(NodeIndex, &mut PeerInfo)> { let check_info = |info: &PeerInfo| info .collating_for .as_ref() .map_or(false, |&(ref acc_id, _)| acc_id == &account_id); self.peers - .iter() - .filter(|&(_, info)| check_info(info)) - .map(|(who, _)| *who) + .iter_mut() + .filter(|&(_, ref info)| check_info(&**info)) + .map(|(who, info)| (*who, info)) .next() } // disconnect a collator by account-id. - fn disconnect_bad_collator(&self, ctx: &mut Context, account_id: AccountId) { - if let Some(who) = self.collator_peer_id(account_id) { + fn disconnect_bad_collator(&mut self, ctx: &mut Context, account_id: AccountId) { + if let Some((who, _)) = self.collator_peer(account_id) { ctx.report_peer(who, Severity::Bad("Consensus layer determined the given collator misbehaved")) } } @@ -668,13 +702,19 @@ impl PolkadotProtocol { targets: HashSet, collation: Collation, ) { + debug!(target: "p_net", "Importing local collation on relay parent {:?} and parachain {:?}", + relay_parent, collation.receipt.parachain_index); + for (primary, cloned_collation) in self.local_collations.add_collation(relay_parent, targets, collation.clone()) { match self.validators.get(&primary) { - Some(who) => send_polkadot_message( - ctx, - *who, - Message::Collation(relay_parent, cloned_collation), - ), + Some(who) => { + debug!(target: "p_net", "Sending local collation to {:?}", primary); + send_polkadot_message( + ctx, + *who, + Message::Collation(relay_parent, cloned_collation), + ) + }, None => warn!(target: "polkadot_network", "Encountered tracked but disconnected validator {:?}", primary), } diff --git a/polkadot/network/src/router.rs b/polkadot/network/src/router.rs index ff90502a31a24..8d6bc18540bea 100644 --- a/polkadot/network/src/router.rs +++ b/polkadot/network/src/router.rs @@ -25,7 +25,7 @@ use polkadot_api::{PolkadotApi, LocalPolkadotApi}; use polkadot_consensus::{SharedTable, TableRouter, SignedStatement, GenericStatement, StatementProducer}; use polkadot_primitives::{Hash, BlockId, SessionKey}; -use polkadot_primitives::parachain::{BlockData, Extrinsic, CandidateReceipt, Id as ParaId}; +use polkadot_primitives::parachain::{BlockData, Extrinsic, CandidateReceipt}; use futures::prelude::*; use tokio::runtime::TaskExecutor; @@ -89,14 +89,16 @@ impl Clone for Router

{ impl Router

{ /// Import a statement whose signature has been checked already. pub(crate) fn import_statement(&self, statement: SignedStatement) { + trace!(target: "p_net", "importing consensus statement {:?}", statement.statement); + // defer any statements for which we haven't imported the candidate yet - let (c_hash, parachain_index) = { + let c_hash = { let candidate_data = match statement.statement { - GenericStatement::Candidate(ref c) => Some((c.hash(), c.parachain_index)), + GenericStatement::Candidate(ref c) => Some(c.hash()), GenericStatement::Valid(ref hash) | GenericStatement::Invalid(ref hash) | GenericStatement::Available(ref hash) - => self.table.with_candidate(hash, |c| c.map(|c| (*hash, c.parachain_index))), + => self.table.with_candidate(hash, |c| c.map(|_| *hash)), }; match candidate_data { Some(x) => x, @@ -115,6 +117,7 @@ impl Router

{ }; // prepend the candidate statement. + debug!(target: "consensus", "Importing statements about candidate {:?}", c_hash); statements.insert(0, statement); let producers: Vec<_> = self.table.import_remote_statements( self, @@ -122,17 +125,16 @@ impl Router

{ ); // dispatch future work as necessary. for (producer, statement) in producers.into_iter().zip(statements) { - let producer = match producer { - Some(p) => p, - None => continue, // statement redundant - }; - self.knowledge.lock().note_statement(statement.sender, &statement.statement); - self.dispatch_work(c_hash, producer, parachain_index); + + if let Some(producer) = producer { + trace!(target: "consensus", "driving statement work to completion"); + self.dispatch_work(c_hash, producer); + } } } - fn dispatch_work(&self, candidate_hash: Hash, producer: StatementProducer, parachain: ParaId) where + fn dispatch_work(&self, candidate_hash: Hash, producer: StatementProducer) where D: Future + Send + 'static, E: Future + Send + 'static, { @@ -160,13 +162,13 @@ impl Router

{ // propagate the statements if let Some(validity) = produced.validity { - let signed = table.sign_and_import(validity.clone()); - route_statement(&*network, &*table, parachain, parent_hash, signed); + let signed = table.sign_and_import(validity.clone()).0; + network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed)); } if let Some(availability) = produced.availability { - let signed = table.sign_and_import(availability); - route_statement(&*network, &*table, parachain, parent_hash, signed); + let signed = table.sign_and_import(availability).0; + network.with_spec(|spec, ctx| spec.gossip_statement(ctx, parent_hash, signed)); } }); @@ -182,11 +184,15 @@ impl TableRouter for Router

{ fn local_candidate(&self, receipt: CandidateReceipt, block_data: BlockData, extrinsic: Extrinsic) { // give to network to make available. let hash = receipt.hash(); - let para_id = receipt.parachain_index; - let signed = 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)); - route_statement(&*self.network, &*self.table, para_id, self.parent_hash, signed); + self.network.with_spec(|spec, ctx| { + spec.gossip_statement(ctx, self.parent_hash, candidate); + if let Some(availability) = availability { + spec.gossip_statement(ctx, self.parent_hash, availability); + } + }); } fn fetch_block_data(&self, candidate: &CandidateReceipt) -> BlockDataReceiver { @@ -217,32 +223,6 @@ impl Future for BlockDataReceiver { } } -// get statement to relevant validators. -fn route_statement(network: &NetworkService, table: &SharedTable, para_id: ParaId, parent_hash: Hash, statement: SignedStatement) { - let broadcast = |i: &mut Iterator| { - let local_key = table.session_key(); - network.with_spec(|spec, ctx| { - for val in i.filter(|&x| x != &local_key) { - spec.send_statement(ctx, *val, parent_hash, statement.clone()); - } - }); - }; - - let g_info = table - .group_info() - .get(¶_id) - .expect("statements only produced about groups which exist"); - - match statement.statement { - GenericStatement::Candidate(_) => - broadcast(&mut g_info.validity_guarantors.iter().chain(g_info.availability_guarantors.iter())), - GenericStatement::Valid(_) | GenericStatement::Invalid(_) => - broadcast(&mut g_info.validity_guarantors.iter()), - GenericStatement::Available(_) => - broadcast(&mut g_info.availability_guarantors.iter()), - } -} - // A unique trace for valid statements issued by a validator. #[derive(Hash, PartialEq, Eq, Clone, Debug)] enum StatementTrace { diff --git a/polkadot/parachain/test-chains/basic_add/Cargo.toml b/polkadot/parachain/test-chains/basic_add/Cargo.toml deleted file mode 100644 index f799cf6b6b8c9..0000000000000 --- a/polkadot/parachain/test-chains/basic_add/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "basic_add" -version = "0.1.0" -authors = ["Parity Technologies "] -description = "Test parachain which adds to a number as its state transition" - -[lib] -crate-type = ["cdylib"] - -[dependencies] -polkadot-parachain = { path = "../../", default-features = false } -wee_alloc = "0.4.1" -tiny-keccak = "1.4" -pwasm-libc = "0.2" - -[features] -default = ["std"] -std = ["polkadot-parachain/std"] - -[profile.release] -panic = "abort" -lto = true - -[workspace] -members = [] diff --git a/polkadot/parachain/test-chains/basic_add/build.sh b/polkadot/parachain/test-chains/basic_add/build.sh deleted file mode 100755 index f25f775d43503..0000000000000 --- a/polkadot/parachain/test-chains/basic_add/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash -set -e - -# Make LLD produce a binary that imports memory from the outside environment. -export RUSTFLAGS="-C link-arg=--import-memory" - -cargo +nightly build --target=wasm32-unknown-unknown --release --no-default-features - -for i in basic_add -do - wasm-gc target/wasm32-unknown-unknown/release/$i.wasm target/wasm32-unknown-unknown/release/$i.compact.wasm -done diff --git a/polkadot/parachain/test-chains/basic_add/src/lib.rs b/polkadot/parachain/test-chains/basic_add/src/lib.rs deleted file mode 100644 index ff9006c8c2173..0000000000000 --- a/polkadot/parachain/test-chains/basic_add/src/lib.rs +++ /dev/null @@ -1,142 +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 . - -//! Basic parachain that adds a number as part of its state. - -#![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr( - not(feature = "std"), - feature( - alloc, core_intrinsics, lang_items, panic_implementation, core_panic_info, - alloc_error_handler - ) -)] - -#[cfg(not(feature = "std"))] -extern crate alloc; - -extern crate polkadot_parachain as parachain; -extern crate wee_alloc; -extern crate tiny_keccak; -extern crate pwasm_libc; - -use parachain::codec::{Decode, Encode, Input, Output}; - -#[cfg(not(feature = "std"))] -mod wasm; - -#[cfg(not(feature = "std"))] -pub use wasm::*; - -// Define global allocator. -#[cfg(not(feature = "std"))] -#[global_allocator] -static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; - -// Head data for this parachain. -#[derive(Default, Clone)] -struct HeadData { - // Block number - number: u64, - // parent block keccak256 - parent_hash: [u8; 32], - // hash of post-execution state. - post_state: [u8; 32], -} - -impl Encode for HeadData { - fn encode_to(&self, dest: &mut T) { - dest.push(&self.number); - dest.push(&self.parent_hash); - dest.push(&self.post_state); - } -} - -impl Decode for HeadData { - fn decode(input: &mut I) -> Option { - Some(HeadData { - number: Decode::decode(input)?, - parent_hash: Decode::decode(input)?, - post_state: Decode::decode(input)?, - }) - } -} - -// Block data for this parachain. -#[derive(Default, Clone)] -struct BlockData { - // State to begin from. - state: u64, - // Amount to add (overflowing) - add: u64, -} - -impl Encode for BlockData { - fn encode_to(&self, dest: &mut T) { - dest.push(&self.state); - dest.push(&self.add); - } -} - -impl Decode for BlockData { - fn decode(input: &mut I) -> Option { - Some(BlockData { - state: Decode::decode(input)?, - add: Decode::decode(input)?, - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use parachain::ValidationParams; - - const TEST_CODE: &[u8] = include_bytes!("../wasm/test.wasm"); - - fn hash_state(state: u64) -> [u8; 32] { - ::tiny_keccak::keccak256(state.encode().as_slice()) - } - - fn hash_head(head: &HeadData) -> [u8; 32] { - ::tiny_keccak::keccak256(head.encode().as_slice()) - } - - #[test] - fn execute_good_on_parent() { - let parent_head = HeadData { - number: 0, - parent_hash: [0; 32], - post_state: hash_state(0), - }; - - let block_data = BlockData { - state: 0, - add: 512, - }; - - let ret = parachain::wasm::validate_candidate(TEST_CODE, ValidationParams { - parent_head: parent_head.encode(), - block_data: block_data.encode(), - }).unwrap(); - - let new_head = HeadData::decode(&mut &ret.head_data[..]).unwrap(); - - assert_eq!(new_head.number, 1); - assert_eq!(new_head.parent_hash, hash_head(&parent_head)); - assert_eq!(new_head.post_state, hash_state(512)); - } -} diff --git a/polkadot/parachain/tests/basic_add.rs b/polkadot/parachain/tests/adder.rs similarity index 98% rename from polkadot/parachain/tests/basic_add.rs rename to polkadot/parachain/tests/adder.rs index 566a6efc3f2ad..d93b2d46a3243 100644 --- a/polkadot/parachain/tests/basic_add.rs +++ b/polkadot/parachain/tests/adder.rs @@ -76,7 +76,7 @@ impl Decode for BlockData { } } -const TEST_CODE: &[u8] = include_bytes!("res/basic_add.wasm"); +const TEST_CODE: &[u8] = include_bytes!("res/adder.wasm"); fn hash_state(state: u64) -> [u8; 32] { ::tiny_keccak::keccak256(state.encode().as_slice()) diff --git a/polkadot/parachain/tests/res/adder.wasm b/polkadot/parachain/tests/res/adder.wasm new file mode 100644 index 0000000000000..93f354400f5e8 Binary files /dev/null and b/polkadot/parachain/tests/res/adder.wasm differ diff --git a/polkadot/parachain/tests/res/basic_add.wasm b/polkadot/parachain/tests/res/basic_add.wasm deleted file mode 100755 index c32910f0adf97..0000000000000 Binary files a/polkadot/parachain/tests/res/basic_add.wasm and /dev/null differ diff --git a/polkadot/runtime/src/parachains.rs b/polkadot/runtime/src/parachains.rs index 4ac5f3f2a1089..5629f440b5431 100644 --- a/polkadot/runtime/src/parachains.rs +++ b/polkadot/runtime/src/parachains.rs @@ -194,8 +194,8 @@ impl Executable for Module { #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] pub struct GenesisConfig { - /// The initial parachains, mapped to code. - pub parachains: Vec<(Id, Vec)>, + /// The initial parachains, mapped to code and initial head data + pub parachains: Vec<(Id, Vec, Vec)>, /// Phantom data. #[serde(skip)] pub phantom: PhantomData, @@ -218,18 +218,21 @@ impl runtime_primitives::BuildStorage for GenesisConfig use std::collections::HashMap; use codec::Encode; - self.parachains.sort_unstable_by_key(|&(ref id, _)| id.clone()); - self.parachains.dedup_by_key(|&mut (ref id, _)| id.clone()); + 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 only_ids: Vec<_> = self.parachains.iter().map(|&(ref id, _, _)| id).cloned().collect(); let mut map: HashMap<_, _> = map![ Self::hash(>::key()).to_vec() => only_ids.encode() ]; - for (id, code) in self.parachains { - let key = Self::hash(&>::key_for(&id)).to_vec(); - map.insert(key, code.encode()); + for (id, code, genesis) in self.parachains { + let code_key = Self::hash(&>::key_for(&id)).to_vec(); + let head_key = Self::hash(&>::key_for(&id)).to_vec(); + + map.insert(code_key, code.encode()); + map.insert(head_key, genesis.encode()); } Ok(map.into()) @@ -280,7 +283,7 @@ mod tests { type Parachains = Module; - fn new_test_ext(parachains: Vec<(Id, Vec)>) -> runtime_io::TestExternalities { + fn new_test_ext(parachains: Vec<(Id, Vec, Vec)>) -> runtime_io::TestExternalities { let mut t = system::GenesisConfig::::default().build_storage().unwrap(); t.extend(consensus::GenesisConfig::{ code: vec![], @@ -301,8 +304,8 @@ mod tests { #[test] fn active_parachains_should_work() { let parachains = vec![ - (5u32.into(), vec![1,2,3]), - (100u32.into(), vec![4,5,6]), + (5u32.into(), vec![1,2,3], vec![1]), + (100u32.into(), vec![4,5,6], vec![2]), ]; with_externalities(&mut new_test_ext(parachains), || { @@ -315,8 +318,8 @@ mod tests { #[test] fn register_deregister() { let parachains = vec![ - (5u32.into(), vec![1,2,3]), - (100u32.into(), vec![4,5,6]), + (5u32.into(), vec![1,2,3], vec![1]), + (100u32.into(), vec![4,5,6], vec![2,]), ]; with_externalities(&mut new_test_ext(parachains), || { @@ -340,8 +343,8 @@ mod tests { #[test] fn duty_roster_works() { let parachains = vec![ - (0u32.into(), vec![]), - (1u32.into(), vec![]), + (0u32.into(), vec![], vec![]), + (1u32.into(), vec![], vec![]), ]; with_externalities(&mut new_test_ext(parachains), || { diff --git a/polkadot/runtime/wasm/Cargo.lock b/polkadot/runtime/wasm/Cargo.lock index b90d8e14e89d8..a2c3b0413a358 100644 --- a/polkadot/runtime/wasm/Cargo.lock +++ b/polkadot/runtime/wasm/Cargo.lock @@ -730,7 +730,7 @@ dependencies = [ "substrate-runtime-std 0.1.0", "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)", - "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -846,7 +846,7 @@ dependencies = [ "substrate-primitives 0.1.0", "substrate-runtime-io 0.1.0", "substrate-runtime-std 0.1.0", - "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1107,7 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasmi" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1231,7 +1231,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4a6d379e9332b1b1f52c5a87f2481c85c7c931d8ec411963dfb8f26b1ec1e3" +"checksum wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "522fe3fdd44a56f25cd5ddcd8ccdb1cf2e982ceb28fcb00f41d8a018ae5245a8" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm index b04b146ea1c7b..74088d063a466 100644 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm differ diff --git a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm index fa48cfe95fb57..c36c36e1bb808 100755 Binary files a/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm and b/polkadot/runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm differ diff --git a/polkadot/service/src/lib.rs b/polkadot/service/src/lib.rs index 6d2c4aee157b8..10349d94dd8cd 100644 --- a/polkadot/service/src/lib.rs +++ b/polkadot/service/src/lib.rs @@ -212,7 +212,7 @@ pub fn new_full(config: Configuration, executor: TaskExecutor) consensus_net, service.extrinsic_pool(), executor, - ::std::time::Duration::from_millis(4000), // TODO: dynamic + ::std::time::Duration::from_secs(4), // TODO: dynamic key, )) } else { @@ -302,13 +302,13 @@ impl network::TransactionPool for TransactionPoolAdapter match *e.kind() { transaction_pool::ErrorKind::AlreadyImported(hash) => Some(hash[..].into()), _ => { - debug!("Error adding transaction to the pool: {:?}", e); + debug!(target: "txpool", "Error adding transaction to the pool: {:?}", e); None }, } } } else { - debug!("Error decoding transaction"); + debug!(target: "txpool", "Error decoding transaction"); None } } diff --git a/polkadot/parachain/test-chains/.gitignore b/polkadot/test-parachains/.gitignore similarity index 100% rename from polkadot/parachain/test-chains/.gitignore rename to polkadot/test-parachains/.gitignore diff --git a/polkadot/test-parachains/README.md b/polkadot/test-parachains/README.md new file mode 100644 index 0000000000000..4661007f76360 --- /dev/null +++ b/polkadot/test-parachains/README.md @@ -0,0 +1,5 @@ +# Test Parachains + +Each parachain consists of three parts: a `#![no_std]` library with the main execution logic, a WASM crate which wraps this logic, and a collator node. + +Run `build.sh` in this directory to build all registered test parachains and copy the generated WASM to the `parachain/tests/res` folder. diff --git a/polkadot/test-parachains/adder/Cargo.toml b/polkadot/test-parachains/adder/Cargo.toml new file mode 100644 index 0000000000000..1ff06689dd0cf --- /dev/null +++ b/polkadot/test-parachains/adder/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "adder" +version = "0.1.0" +authors = ["Parity Technologies "] +description = "Test parachain which adds to a number as its state transition" + +[dependencies] +polkadot-parachain = { path = "../../parachain/", default-features = false } +tiny-keccak = "1.4" diff --git a/polkadot/test-parachains/adder/collator/Cargo.toml b/polkadot/test-parachains/adder/collator/Cargo.toml new file mode 100644 index 0000000000000..97ae48cd25c22 --- /dev/null +++ b/polkadot/test-parachains/adder/collator/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "adder-collator" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +adder = { path = ".." } +polkadot-parachain = { path = "../../../parachain" } +polkadot-collator = { path = "../../../collator" } +polkadot-primitives = { path = "../../../primitives" } +ed25519 = { path = "../../../../substrate/ed25519" } +parking_lot = "0.4" +ctrlc = { git = "https://github.com/paritytech/rust-ctrlc.git" } +futures = "0.1" +exit-future = "0.1.2" diff --git a/polkadot/test-parachains/adder/collator/src/main.rs b/polkadot/test-parachains/adder/collator/src/main.rs new file mode 100644 index 0000000000000..a20e64b23046c --- /dev/null +++ b/polkadot/test-parachains/adder/collator/src/main.rs @@ -0,0 +1,145 @@ +// 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 . + +//! Collator for polkadot + +extern crate adder; +extern crate polkadot_parachain as parachain; +extern crate polkadot_primitives as primitives; +extern crate polkadot_collator as collator; +extern crate ed25519; +extern crate parking_lot; +extern crate ctrlc; +extern crate futures; +extern crate exit_future; + +use std::cell::RefCell; +use std::collections::HashMap; +use std::sync::Arc; + +use adder::{HeadData as AdderHead, BlockData as AdderBody}; +use ed25519::Pair; +use parachain::codec::{Encode, Decode}; +use primitives::parachain::{HeadData, BlockData, Id as ParaId, Message}; +use collator::{InvalidHead, ParachainContext, VersionInfo}; +use parking_lot::Mutex; + +const GENESIS: AdderHead = AdderHead { + number: 0, + parent_hash: [0; 32], + post_state: [1, 27, 77, 3, 221, 140, 1, 241, 4, 145, 67, 207, 156, 76, 129, 126, 75, 22, 127, 29, 27, 131, 229, 198, 240, 241, 13, 137, 186, 30, 123, 206], +}; + +const GENESIS_BODY: AdderBody = AdderBody { + state: 0, + add: 0, +}; + +#[derive(Clone)] +struct AdderContext { + db: Arc>>, +} + +/// The parachain context. +impl ParachainContext for AdderContext { + fn produce_candidate>( + &self, + last_head: HeadData, + _ingress: I, + ) -> Result<(BlockData, HeadData), InvalidHead> + { + let adder_head = AdderHead::decode(&mut &last_head.0[..]) + .ok_or(InvalidHead)?; + + let mut db = self.db.lock(); + + let last_body = if adder_head == GENESIS { + GENESIS_BODY + } else { + db.get(&adder_head) + .expect("All past bodies stored since this is the only collator") + .clone() + }; + + let next_body = AdderBody { + state: last_body.state.overflowing_add(last_body.add).0, + add: adder_head.number % 100, + }; + + let next_head = ::adder::execute(adder_head.hash(), adder_head, &next_body) + .expect("good execution params; qed"); + + let encoded_head = HeadData(next_head.encode()); + let encoded_body = BlockData(next_body.encode()); + + println!("Created collation for #{}, post-state={}", + next_head.number, next_body.state.overflowing_add(next_body.add).0); + + db.insert(next_head.clone(), next_body); + Ok((encoded_body, encoded_head)) + } +} + +fn main() { + let key = Arc::new(Pair::from_seed(&[1; 32])); + let id: ParaId = 100.into(); + + println!("Starting adder collator with genesis: "); + + { + let encoded = GENESIS.encode(); + println!("Dec: {:?}", encoded); + print!("Hex: 0x"); + for byte in encoded { + print!("{:02x}", byte); + } + + println!(); + } + + // can't use signal directly here because CtrlC takes only `Fn`. + let (exit_send, exit) = exit_future::signal(); + + let exit_send_cell = RefCell::new(Some(exit_send)); + ctrlc::CtrlC::set_handler(move || { + if let Some(exit_send) = exit_send_cell.try_borrow_mut().expect("signal handler not reentrant; qed").take() { + exit_send.fire(); + } + }); + + let context = AdderContext { + db: Arc::new(Mutex::new(HashMap::new())), + }; + + let res = ::collator::run_collator( + context, + id, + exit, + key, + ::std::env::args(), + VersionInfo { + version: "", + commit: "", + executable_name: "adder-collator", + description: "collator for adder parachain", + author: "parity technologies", + } + ); + + if let Err(e) = res { + println!("{}", e); + } +} diff --git a/polkadot/test-parachains/adder/src/lib.rs b/polkadot/test-parachains/adder/src/lib.rs new file mode 100644 index 0000000000000..aa23dea7772cf --- /dev/null +++ b/polkadot/test-parachains/adder/src/lib.rs @@ -0,0 +1,110 @@ +// 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 . + +//! Basic parachain that adds a number as part of its state. + +#![no_std] + +extern crate polkadot_parachain as parachain; +extern crate tiny_keccak; + +use parachain::codec::{Decode, Encode, Input, Output}; + +/// Head data for this parachain. +#[derive(Default, Clone, Hash, Eq, PartialEq)] +pub struct HeadData { + /// Block number + pub number: u64, + /// parent block keccak256 + pub parent_hash: [u8; 32], + /// hash of post-execution state. + pub post_state: [u8; 32], +} + +impl HeadData { + pub fn hash(&self) -> [u8; 32] { + ::tiny_keccak::keccak256(&self.encode()) + } +} + +impl Encode for HeadData { + fn encode_to(&self, dest: &mut T) { + dest.push(&self.number); + dest.push(&self.parent_hash); + dest.push(&self.post_state); + } +} + +impl Decode for HeadData { + fn decode(input: &mut I) -> Option { + Some(HeadData { + number: Decode::decode(input)?, + parent_hash: Decode::decode(input)?, + post_state: Decode::decode(input)?, + }) + } +} + +/// Block data for this parachain. +#[derive(Default, Clone)] +pub struct BlockData { + /// State to begin from. + pub state: u64, + /// Amount to add (overflowing) + pub add: u64, +} + +impl Encode for BlockData { + fn encode_to(&self, dest: &mut T) { + dest.push(&self.state); + dest.push(&self.add); + } +} + +impl Decode for BlockData { + fn decode(input: &mut I) -> Option { + Some(BlockData { + state: Decode::decode(input)?, + add: Decode::decode(input)?, + }) + } +} + +pub fn hash_state(state: u64) -> [u8; 32] { + ::tiny_keccak::keccak256(state.encode().as_slice()) +} + +/// Start state mismatched with parent header's state hash. +#[derive(Debug)] +pub struct StateMismatch; + +/// Execute a block body on top of given parent head, producing new parent head +/// if valid. +pub fn execute(parent_hash: [u8; 32], parent_head: HeadData, block_data: &BlockData) -> Result { + debug_assert_eq!(parent_hash, parent_head.hash()); + + if hash_state(block_data.state) != parent_head.post_state { + return Err(StateMismatch); + } + + let new_state = block_data.state.overflowing_add(block_data.add).0; + + Ok(HeadData { + number: parent_head.number + 1, + parent_hash, + post_state: hash_state(new_state), + }) +} diff --git a/polkadot/parachain/test-chains/basic_add/src/src b/polkadot/test-parachains/adder/src/src similarity index 100% rename from polkadot/parachain/test-chains/basic_add/src/src rename to polkadot/test-parachains/adder/src/src diff --git a/polkadot/test-parachains/adder/wasm/Cargo.toml b/polkadot/test-parachains/adder/wasm/Cargo.toml new file mode 100644 index 0000000000000..087b937b646d0 --- /dev/null +++ b/polkadot/test-parachains/adder/wasm/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "adder-wasm" +version = "0.1.0" +authors = ["Parity Technologies "] + +[dependencies] +adder = { path = ".." } +polkadot-parachain = { path = "../../../parachain", default-features = false } +wee_alloc = { version = "0.4.1" } +pwasm-libc = { version = "0.2" } +tiny-keccak = "1.4" + +[lib] +crate-type = ["cdylib"] + +[target.release] +panic = "abort" +lto = true + +[workspace] diff --git a/polkadot/parachain/test-chains/basic_add/src/wasm.rs b/polkadot/test-parachains/adder/wasm/src/lib.rs similarity index 60% rename from polkadot/parachain/test-chains/basic_add/src/wasm.rs rename to polkadot/test-parachains/adder/wasm/src/lib.rs index 10071131886fb..0e16142647ee4 100644 --- a/polkadot/parachain/test-chains/basic_add/src/wasm.rs +++ b/polkadot/test-parachains/adder/wasm/src/lib.rs @@ -14,12 +14,30 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -//! Defines WASM module logic. +//! WASM validation for adder parachain. -use core::{intrinsics, panic, alloc}; -use parachain::{self, ValidationResult}; +#![no_std] + +#![feature( + alloc, core_intrinsics, lang_items, panic_implementation, core_panic_info, + alloc_error_handler +)] + +extern crate alloc; +extern crate wee_alloc; +extern crate pwasm_libc; +extern crate adder; +extern crate polkadot_parachain as parachain; +extern crate tiny_keccak; + +// Define global allocator. +#[global_allocator] +static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; + +use core::{intrinsics, panic}; +use parachain::ValidationResult; use parachain::codec::{Encode, Decode}; -use super::{HeadData, BlockData}; +use adder::{HeadData, BlockData}; #[panic_implementation] #[no_mangle] @@ -31,7 +49,7 @@ pub fn panic(_info: &panic::PanicInfo) -> ! { #[alloc_error_handler] #[no_mangle] -pub fn oom(_: alloc::Layout) -> ! { +pub fn oom(_: ::core::alloc::Layout) -> ! { unsafe { intrinsics::abort(); } @@ -39,8 +57,6 @@ pub fn oom(_: alloc::Layout) -> ! { #[no_mangle] pub extern fn validate(offset: usize, len: usize) -> usize { - let hash_state = |state: u64| ::tiny_keccak::keccak256(state.encode().as_slice()); - let params = unsafe { ::parachain::load_params(offset, len) }; let parent_head = HeadData::decode(&mut ¶ms.parent_head[..]) .expect("invalid parent head format."); @@ -48,14 +64,10 @@ pub extern fn validate(offset: usize, len: usize) -> usize { let block_data = BlockData::decode(&mut ¶ms.block_data[..]) .expect("invalid block data format."); - assert_eq!(hash_state(block_data.state), parent_head.post_state, "wrong post-state proof"); - let new_state = block_data.state.saturating_add(block_data.add); - - let new_head = HeadData { - number: parent_head.number + 1, - parent_hash: ::tiny_keccak::keccak256(¶ms.parent_head[..]), - post_state: hash_state(new_state), - }; + let parent_hash = ::tiny_keccak::keccak256(¶ms.parent_head[..]); - parachain::write_result(ValidationResult { head_data: new_head.encode() }) + match ::adder::execute(parent_hash, parent_head, &block_data) { + Ok(new_head) => parachain::write_result(ValidationResult { head_data: new_head.encode() }), + Err(_) => panic!("execution failure"), + } } diff --git a/polkadot/test-parachains/build.sh b/polkadot/test-parachains/build.sh new file mode 100755 index 0000000000000..d53869f2d4774 --- /dev/null +++ b/polkadot/test-parachains/build.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +set -e + +# Make LLD produce a binary that imports memory from the outside environment. +export RUSTFLAGS="-C link-arg=--import-memory -C lto=fat -C panic=abort" + +for i in adder +do + cd $i/wasm + cargo +nightly build --target=wasm32-unknown-unknown --release --no-default-features --target-dir target + wasm-gc target/wasm32-unknown-unknown/release/$i'_'wasm.wasm target/wasm32-unknown-unknown/release/$i.wasm + cp target/wasm32-unknown-unknown/release/$i.wasm ../../../parachain/tests/res/ + rm -rf target + cd ../.. +done diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm index bd930a1e4cc7a..a4a5c76018e4a 100644 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.compact.wasm differ diff --git a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm index 15312bde3b2a0..7e4a37936f037 100755 Binary files a/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm and b/substrate/executor/wasm/target/wasm32-unknown-unknown/release/runtime_test.wasm differ diff --git a/substrate/network/src/consensus_gossip.rs b/substrate/network/src/consensus_gossip.rs index 8e9065b3a7a46..8a9f5db1f70ac 100644 --- a/substrate/network/src/consensus_gossip.rs +++ b/substrate/network/src/consensus_gossip.rs @@ -124,7 +124,9 @@ impl ConsensusGossip where B::Header: HeaderT { /// Handles incoming chain-specific message and repropagates pub fn on_chain_specific(&mut self, protocol: &mut Context, who: NodeIndex, message: Vec, parent_hash: B::Hash) { + debug!(target: "gossip", "received chain-specific gossip message"); if let Some((hash, message)) = self.handle_incoming(protocol, who, ConsensusMessage::ChainSpecific(message, parent_hash)) { + debug!(target: "gossip", "handled incoming chain-specific message"); // propagate to other peers. self.multicast(protocol, message, Some(hash)); } @@ -245,6 +247,7 @@ impl ConsensusGossip where B::Header: HeaderT { peer.known_messages.insert(hash); if let Some((sink, parent_hash)) = self.message_sink.take() { if parent == parent_hash { + debug!(target: "gossip", "Pushing relevant consensus message to sink."); if let Err(e) = sink.unbounded_send(message.clone()) { trace!(target:"gossip", "Error broadcasting message notification: {:?}", e); } diff --git a/substrate/test-runtime/wasm/Cargo.lock b/substrate/test-runtime/wasm/Cargo.lock index e78284183ae39..428dd7a76bf2d 100644 --- a/substrate/test-runtime/wasm/Cargo.lock +++ b/substrate/test-runtime/wasm/Cargo.lock @@ -678,7 +678,7 @@ dependencies = [ "substrate-runtime-std 0.1.0", "twox-hash 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "uint 0.1.2 (git+https://github.com/rphmeier/primitives.git?branch=compile-for-wasm)", - "wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -910,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasmi" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1034,7 +1034,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae" "checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum wasmi 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b4a6d379e9332b1b1f52c5a87f2481c85c7c931d8ec411963dfb8f26b1ec1e3" +"checksum wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "522fe3fdd44a56f25cd5ddcd8ccdb1cf2e982ceb28fcb00f41d8a018ae5245a8" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index d449d647e20ce..a8308fbcaacfb 100644 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm differ diff --git a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm index 2fe9433d00c92..8aa38b3dcebdd 100755 Binary files a/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm and b/substrate/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.wasm differ