diff --git a/Cargo.lock b/Cargo.lock index 62e3721f8c60a..84dc99510197f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -967,6 +967,7 @@ dependencies = [ "polkadot-runtime 0.1.0", "substrate-client 0.1.0", "substrate-executor 0.1.0", + "substrate-keyring 0.1.0", "substrate-state-machine 0.1.0", ] @@ -1007,8 +1008,11 @@ name = "polkadot-consensus" version = "0.1.0" dependencies = [ "ed25519 0.1.0", + "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "polkadot-api 0.1.0", + "polkadot-collator 0.1.0", "polkadot-primitives 0.1.0", "polkadot-statement-table 0.1.0", "substrate-bft 0.1.0", diff --git a/polkadot/api/Cargo.toml b/polkadot/api/Cargo.toml index de3e082548a75..5e78af8c12068 100644 --- a/polkadot/api/Cargo.toml +++ b/polkadot/api/Cargo.toml @@ -11,3 +11,6 @@ polkadot-primitives = { path = "../primitives" } substrate-client = { path = "../../substrate/client" } substrate-executor = { path = "../../substrate/executor" } substrate-state-machine = { path = "../../substrate/state-machine" } + +[dev-dependencies] +substrate-keyring = { path = "../../substrate/keyring" } diff --git a/polkadot/api/src/lib.rs b/polkadot/api/src/lib.rs index efe0f45220099..0ffb55233acfe 100644 --- a/polkadot/api/src/lib.rs +++ b/polkadot/api/src/lib.rs @@ -18,7 +18,7 @@ //! runtime. extern crate polkadot_executor as polkadot_executor; -extern crate polkadot_runtime ; +extern crate polkadot_runtime; extern crate polkadot_primitives as primitives; extern crate substrate_client as client; extern crate substrate_executor as substrate_executor; @@ -27,13 +27,18 @@ extern crate substrate_state_machine as state_machine; #[macro_use] extern crate error_chain; +#[cfg(test)] +extern crate substrate_keyring as keyring; + use client::backend::Backend; use client::Client; use polkadot_runtime::runtime; use polkadot_executor::Executor as LocalDispatch; use substrate_executor::{NativeExecutionDispatch, NativeExecutor}; -use primitives::{AccountId, SessionKey}; -use primitives::block::Id as BlockId; +use state_machine::OverlayedChanges; +use primitives::{AccountId, SessionKey, Timestamp}; +use primitives::block::{Id as BlockId, Block, Header, Body}; +use primitives::transaction::UncheckedTransaction; use primitives::parachain::DutyRoster; error_chain! { @@ -43,10 +48,21 @@ error_chain! { description("Unknown runtime code") display("Unknown runtime code") } + /// Unknown block ID. UnknownBlock(b: BlockId) { description("Unknown block") display("Unknown block") } + /// Attempted to push an inherent transaction manually. + PushedInherentTransaction(tx: UncheckedTransaction) { + description("Attempted to push an inherent transaction to a block."), + display("Pushed inherent transaction to a block: {:?}", tx), + } + /// Badly-formed transaction. + BadlyFormedTransaction(tx: UncheckedTransaction) { + description("Attempted to push a badly-formed transaction to a block."), + display("Pushed badly-formed transaction to a block: {:?}", tx), + } /// Some other error. // TODO: allow to be specified as associated type of PolkadotApi Other(e: Box<::std::error::Error + Send>) { @@ -60,10 +76,29 @@ error_chain! { } } +impl From 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)), + other => Error::from_kind(ErrorKind::Other(Box::new(other) as Box<_>)), + } + } +} + +pub trait BlockBuilder: Sized { + /// Push a non-inherent transaction. + fn push_transaction(&mut self, transaction: UncheckedTransaction) -> Result<()>; + + /// Finalise the block. + fn bake(self) -> Block; +} + /// Trait encapsulating the Polkadot API. /// /// All calls should fail when the exact runtime is unknown. pub trait PolkadotApi { + type BlockBuilder: BlockBuilder; + /// Get session keys at a given block. fn session_keys(&self, at: &BlockId) -> Result>; @@ -72,24 +107,26 @@ pub trait PolkadotApi { /// Get the authority duty roster at a block. fn duty_roster(&self, at: &BlockId) -> Result; -} -fn convert_client_error(e: client::error::Error) -> Error { - match e { - client::error::Error(client::error::ErrorKind::UnknownBlock(b), _) => Error::from_kind(ErrorKind::UnknownBlock(b)), - other => Error::from_kind(ErrorKind::Other(Box::new(other) as Box<_>)), - } + /// Get the timestamp registered at a block. + fn timestamp(&self, at: &BlockId) -> Result; + + /// Evaluate a block and see if it gives an error. + fn evaluate_block(&self, at: &BlockId, block: Block) -> Result<()>; + + /// Create a block builder on top of the parent block. + fn build_block(&self, parent: &BlockId, timestamp: u64) -> Result; } // set up the necessary scaffolding to execute the runtime. macro_rules! with_runtime { ($client: ident, $at: expr, $exec: expr) => {{ // bail if the code is not the same as the natively linked. - if $client.code_at($at).map_err(convert_client_error)? != LocalDispatch::native_equivalent() { + if $client.code_at($at)? != LocalDispatch::native_equivalent() { bail!(ErrorKind::UnknownRuntime); } - $client.state_at($at).map_err(convert_client_error).and_then(|state| { + $client.state_at($at).map_err(Error::from).and_then(|state| { let mut changes = Default::default(); let mut ext = state_machine::Ext { overlay: &mut changes, @@ -104,6 +141,8 @@ macro_rules! with_runtime { impl PolkadotApi for Client> where ::client::error::Error: From<<::State as state_machine::backend::Backend>::Error> { + type BlockBuilder = ClientBlockBuilder; + fn session_keys(&self, at: &BlockId) -> Result> { with_runtime!(self, at, ::runtime::consensus::authorities) } @@ -115,4 +154,185 @@ impl PolkadotApi for Client> fn duty_roster(&self, at: &BlockId) -> Result { with_runtime!(self, at, ::runtime::parachains::calculate_duty_roster) } + + fn timestamp(&self, at: &BlockId) -> Result { + with_runtime!(self, at, ::runtime::timestamp::get) + } + + fn evaluate_block(&self, at: &BlockId, block: Block) -> Result<()> { + with_runtime!(self, at, || ::runtime::system::internal::execute_block(block)) + } + + fn build_block(&self, parent: &BlockId, timestamp: Timestamp) -> Result { + if self.code_at(parent)? != LocalDispatch::native_equivalent() { + bail!(ErrorKind::UnknownRuntime); + } + + let header = Header { + parent_hash: self.block_hash_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))?, + number: self.block_number_from_id(parent)?.ok_or(ErrorKind::UnknownBlock(*parent))? + 1, + state_root: Default::default(), + transaction_root: Default::default(), + digest: Default::default(), + }; + + let body = Body { + timestamp: timestamp, + transactions: Vec::new(), + }; + + let mut builder = ClientBlockBuilder { + parent: *parent, + changes: OverlayedChanges::default(), + state: self.state_at(parent)?, + header, + timestamp, + transactions: Vec::new(), + }; + + for inherent in body.inherent_transactions() { + builder.execute_transaction(inherent)?; + } + + Ok(builder) + } +} + +/// A polkadot block builder. +#[derive(Debug, Clone)] +pub struct ClientBlockBuilder { + parent: BlockId, + changes: OverlayedChanges, + state: S, + header: Header, + timestamp: Timestamp, + transactions: Vec, +} + +impl ClientBlockBuilder + where S::Error: Into +{ + // executes a transaction, inherent or otherwise, without appending to the list + fn execute_transaction(&mut self, transaction: UncheckedTransaction) -> Result<()> { + if !transaction.is_well_formed() { + bail!(ErrorKind::BadlyFormedTransaction(transaction)); + } + + let mut ext = state_machine::Ext { + overlay: &mut self.changes, + backend: &self.state, + }; + + // TODO: avoid clone + let header = self.header.clone(); + let result = ::substrate_executor::with_native_environment( + &mut ext, + move || runtime::system::internal::execute_transaction(transaction, header), + ).map_err(Into::into); + + match result { + Ok(header) => { + ext.overlay.commit_prospective(); + self.header = header; + Ok(()) + } + Err(e) => { + ext.overlay.discard_prospective(); + Err(e) + } + } + } +} + +impl BlockBuilder for ClientBlockBuilder + where S::Error: Into +{ + fn push_transaction(&mut self, transaction: UncheckedTransaction) -> Result<()> { + if transaction.transaction.function.is_inherent() { + bail!(ErrorKind::PushedInherentTransaction(transaction)); + } else { + self.execute_transaction(transaction.clone())?; + self.transactions.push(transaction); + Ok(()) + } + } + + fn bake(mut self) -> Block { + let mut ext = state_machine::Ext { + overlay: &mut self.changes, + backend: &self.state, + }; + + let old_header = self.header; + let final_header = ::substrate_executor::with_native_environment( + &mut ext, + move || runtime::system::internal::finalise_block(old_header) + ).expect("all inherent transactions pushed; all other transactions executed correctly; qed"); + + Block { + header: final_header, + body: Body { + timestamp: self.timestamp, + transactions: self.transactions, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use client::in_mem::Backend as InMemory; + use polkadot_runtime::genesismap::{additional_storage_with_genesis, GenesisConfig}; + use substrate_executor::NativeExecutionDispatch; + use keyring::Keyring; + + fn validators() -> Vec { + vec![ + Keyring::One.to_raw_public(), + Keyring::Two.to_raw_public(), + ] + } + + fn client() -> Client> { + ::client::new_in_mem( + LocalDispatch::new(), + || { + let config = GenesisConfig::new_simple(validators(), 100); + + // override code entry. + let mut storage = config.genesis_map(); + storage.insert(b":code".to_vec(), LocalDispatch::native_equivalent().to_vec()); + + let block = ::client::genesis::construct_genesis_block( + &config.genesis_map() + ); + storage.extend(additional_storage_with_genesis(&block)); + (block.header, storage.into_iter().collect()) + } + ).unwrap() + } + + #[test] + fn gets_session_and_validator_keys() { + let client = client(); + assert_eq!(client.session_keys(&BlockId::Number(0)).unwrap(), validators()); + assert_eq!(client.validators(&BlockId::Number(0)).unwrap(), validators()); + } + + #[test] + fn build_block() { + let client = client(); + + let block_builder = client.build_block(&BlockId::Number(0), 1_000_000).unwrap(); + let block = block_builder.bake(); + + assert_eq!(block.header.number, 1); + } + + #[test] + fn cannot_build_block_on_unknown_parent() { + let client = client(); + assert!(client.build_block(&BlockId::Number(100), 1_000_000).is_err()); + } } diff --git a/polkadot/collator/src/lib.rs b/polkadot/collator/src/lib.rs index b520b269fc0e1..14b430ebbad17 100644 --- a/polkadot/collator/src/lib.rs +++ b/polkadot/collator/src/lib.rs @@ -61,7 +61,7 @@ pub trait ParachainContext { fn produce_candidate>( &self, ingress: I, - ) -> (parachain::BlockData, polkadot_primitives::Signature); + ) -> (parachain::BlockData, polkadot_primitives::AccountId, polkadot_primitives::Signature); } /// Relay chain context needed to collate. @@ -131,7 +131,7 @@ pub fn collate<'a, R, P>(local_id: ParaId, relay_context: R, para_context: P) P: ParachainContext + 'a, { Box::new(collate_ingress(relay_context).map(move |ingress| { - let (block_data, signature) = para_context.produce_candidate( + let (block_data, _, signature) = para_context.produce_candidate( ingress.0.iter().flat_map(|&(id, ref msgs)| msgs.iter().cloned().map(move |msg| (id, msg))) ); diff --git a/polkadot/consensus/Cargo.toml b/polkadot/consensus/Cargo.toml index aeda287f80cac..298d6478700ee 100644 --- a/polkadot/consensus/Cargo.toml +++ b/polkadot/consensus/Cargo.toml @@ -8,6 +8,9 @@ futures = "0.1.17" parking_lot = "0.4" tokio-timer = "0.1.2" ed25519 = { path = "../../substrate/ed25519" } +error-chain = "0.11" +polkadot-api = { path = "../api" } +polkadot-collator = { path = "../collator" } polkadot-primitives = { path = "../primitives" } polkadot-statement-table = { path = "../statement-table" } substrate-bft = { path = "../../substrate/bft" } diff --git a/polkadot/consensus/src/error.rs b/polkadot/consensus/src/error.rs new file mode 100644 index 0000000000000..648072ef0b7b3 --- /dev/null +++ b/polkadot/consensus/src/error.rs @@ -0,0 +1,51 @@ +// 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 . + +//! Errors that can occur during the consensus process. + +use primitives::block::HeaderHash; + +error_chain! { + links { + PolkadotApi(::polkadot_api::Error, ::polkadot_api::ErrorKind); + Bft(::bft::Error, ::bft::ErrorKind); + } + + errors { + InvalidDutyRosterLength(expected: usize, got: usize) { + description("Duty Roster had invalid length"), + display("Invalid duty roster length: expected {}, got {}", expected, got), + } + ProposalNotForPolkadot { + description("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."), + } + WrongParentHash(expected: HeaderHash, got: HeaderHash) { + description("Proposal had wrong parent hash."), + display("Proposal had wrong parent hash. Expected {:?}, got {:?}", expected, got), + } + } +} + +impl From<::bft::InputStreamConcluded> for Error { + fn from(err: ::bft::InputStreamConcluded) -> Self { + ::bft::Error::from(err).into() + } +} diff --git a/polkadot/consensus/src/lib.rs b/polkadot/consensus/src/lib.rs index f3e62ba1d56d2..ef00318209757 100644 --- a/polkadot/consensus/src/lib.rs +++ b/polkadot/consensus/src/lib.rs @@ -29,31 +29,73 @@ //! //! Groups themselves may be compromised by malicious authorities. -use std::collections::{HashMap, HashSet}; -use std::sync::Arc; - -use codec::Slicable; -use table::Table; -use table::generic::Statement as GenericStatement; -use polkadot_primitives::Hash; -use polkadot_primitives::parachain::{Id as ParaId, CandidateReceipt}; -use primitives::block::Block as SubstrateBlock; -use primitives::AuthorityId; - -use parking_lot::Mutex; - extern crate futures; extern crate ed25519; extern crate parking_lot; extern crate tokio_timer; +extern crate polkadot_api; +extern crate polkadot_collator as collator; extern crate polkadot_statement_table as table; extern crate polkadot_primitives; extern crate substrate_bft as bft; extern crate substrate_codec as codec; extern crate substrate_primitives as primitives; +#[macro_use] +extern crate error_chain; + +use std::collections::{HashMap, HashSet}; +use std::sync::Arc; + +use codec::Slicable; +use table::{Table, Context as TableContextTrait}; +use table::generic::Statement as GenericStatement; +use polkadot_api::{PolkadotApi, BlockBuilder}; +use polkadot_primitives::{Hash, Timestamp}; +use polkadot_primitives::block::Block as PolkadotBlock; +use polkadot_primitives::parachain::{Id as ParaId, DutyRoster, BlockData, Extrinsic, CandidateReceipt}; +use primitives::block::{Block as SubstrateBlock, Header as SubstrateHeader, HeaderHash, Id as BlockId}; +use primitives::AuthorityId; + +use futures::prelude::*; +use futures::future; +use parking_lot::Mutex; + +pub use self::error::{ErrorKind, Error}; + +mod error; + +/// A handle to a statement table router. +pub trait TableRouter { + /// Errors when fetching data from the network. + type Error; + /// Future that resolves when candidate data is fetched. + type FetchCandidate: IntoFuture; + /// Future that resolves when extrinsic candidate data is fetched. + type FetchExtrinsic: IntoFuture; + + /// Note local candidate data. + fn local_candidate_data(&self, block_data: BlockData, extrinsic: Extrinsic); + + /// Fetch block data for a specific candidate. + fn fetch_block_data(&self, candidate: &CandidateReceipt) -> Self::FetchCandidate; + + /// Fetch extrinsic data for a specific candidate. + fn fetch_extrinsic_data(&self, candidate: &CandidateReceipt) -> Self::FetchExtrinsic; +} + +/// A long-lived network which can create statement table routing instances. +pub trait Network { + /// The table router type. This should handle importing of any statements, + /// routing statements to peers, and driving completion of any `StatementProducers`. + type TableRouter: TableRouter; + + /// Instantiate a table router using the given shared table. + fn table_router(&self, table: Arc) -> Self::TableRouter; +} + /// Information about a specific group. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct GroupInfo { /// Authorities meant to check validity of candidates. pub validity_guarantors: HashSet, @@ -89,6 +131,10 @@ impl table::Context for TableContext { } impl TableContext { + fn local_id(&self) -> AuthorityId { + self.key.public().0 + } + fn sign_statement(&self, statement: table::Statement) -> table::SignedStatement { let signature = sign_table_statement(&statement, &self.key, &self.parent_hash); let local_id = self.key.public().0; @@ -124,16 +170,120 @@ pub fn sign_table_statement(statement: &table::Statement, key: &ed25519::Pair, p struct SharedTableInner { table: Table, proposed_digest: Option, + checked_validity: HashSet, + checked_availability: HashSet, } impl SharedTableInner { - fn import_statement( + // Import a single statement. Provide a handle to a table router. + fn import_statement( &mut self, context: &TableContext, - statement: ::table::SignedStatement, + router: &R, + statement: table::SignedStatement, received_from: Option, - ) -> Option { - self.table.import_statement(context, statement, received_from) + ) -> StatementProducer<::Future, ::Future> { + let mut producer = StatementProducer { + fetch_block_data: None, + fetch_extrinsic: None, + produced_statements: Default::default(), + _key: context.key.clone(), + }; + + let summary = match self.table.import_statement(context, statement, received_from) { + Some(summary) => summary, + None => return producer, + }; + + let local_id = context.local_id(); + let is_validity_member = context.is_member_of(&local_id, &summary.group_id); + let is_availability_member = + context.is_availability_guarantor_of(&local_id, &summary.group_id); + + let digest = &summary.candidate; + + // TODO: consider a strategy based on the number of candidate votes as well. + // only check validity if this wasn't locally proposed. + let checking_validity = is_validity_member + && self.proposed_digest.as_ref().map_or(true, |d| d != digest) + && self.checked_validity.insert(digest.clone()); + + let checking_availability = is_availability_member && self.checked_availability.insert(digest.clone()); + + if checking_validity || checking_availability { + match self.table.get_candidate(&digest) { + None => {} // TODO: handle table inconsistency somehow? + Some(candidate) => { + if checking_validity { + producer.fetch_block_data = Some(router.fetch_block_data(candidate).into_future().fuse()); + } + + if checking_availability { + producer.fetch_extrinsic = Some(router.fetch_extrinsic_data(candidate).into_future().fuse()); + } + } + } + } + + producer + } +} + +/// Produced statements about a specific candidate. +/// Both may be `None`. +#[derive(Default)] +pub struct ProducedStatements { + /// A statement about the validity of the candidate. + pub validity: Option, + /// A statement about the availability of the candidate. + pub availability: Option, +} + +/// Future that produces statements about a specific candidate. +pub struct StatementProducer { + fetch_block_data: Option>, + fetch_extrinsic: Option>, + produced_statements: ProducedStatements, + _key: Arc, +} + +impl Future for StatementProducer + where + D: Future, + E: Future, +{ + type Item = ProducedStatements; + type Error = Err; + + fn poll(&mut self) -> Poll { + let mut done = true; + if let Some(ref mut fetch_block_data) = self.fetch_block_data { + match fetch_block_data.poll()? { + Async::Ready(_block_data) => { + // TODO [PoC-2] : validate block data here and make statement. + }, + Async::NotReady => { + done = false; + } + } + } + + if let Some(ref mut fetch_extrinsic) = self.fetch_extrinsic { + match fetch_extrinsic.poll()? { + Async::Ready(_extrinsic) => { + // TODO [PoC-2]: guarantee availability of data and make statment. + } + Async::NotReady => { + done = false; + } + } + } + + if done { + Ok(Async::Ready(::std::mem::replace(&mut self.produced_statements, Default::default()))) + } else { + Ok(Async::NotReady) + } } } @@ -147,7 +297,7 @@ impl Clone for SharedTable { fn clone(&self) -> Self { SharedTable { context: self.context.clone(), - inner: self.inner.clone() + inner: self.inner.clone(), } } } @@ -163,24 +313,34 @@ impl SharedTable { inner: Arc::new(Mutex::new(SharedTableInner { table: Table::default(), proposed_digest: None, + checked_validity: HashSet::new(), + checked_availability: HashSet::new(), })) } } - /// Import a single statement. - pub fn import_statement( + /// Get group info. + pub fn group_info(&self) -> &HashMap { + &self.context.groups + } + + /// Import a single statement. Provide a handle to a table router + /// for dispatching any other requests which come up. + pub fn import_statement( &self, + router: &R, statement: table::SignedStatement, received_from: Option, - ) -> Option { - self.inner.lock().import_statement(&*self.context, statement, received_from) + ) -> StatementProducer<::Future, ::Future> { + self.inner.lock().import_statement(&*self.context, router, statement, received_from) } /// Sign and import a local statement. - pub fn sign_and_import( + pub fn sign_and_import( &self, + router: &R, statement: table::Statement, - ) -> Option { + ) -> StatementProducer<::Future, ::Future> { let proposed_digest = match statement { GenericStatement::Candidate(ref c) => Some(c.hash()), _ => None, @@ -193,21 +353,25 @@ impl SharedTable { inner.proposed_digest = proposed_digest; } - inner.import_statement(&*self.context, signed_statement, None) + inner.import_statement(&*self.context, router, signed_statement, None) } /// Import many statements at once. /// /// Provide an iterator yielding pairs of (statement, received_from). - pub fn import_statements(&self, iterable: I) -> U + pub fn import_statements(&self, router: &R, iterable: I) -> U where + R: TableRouter, I: IntoIterator)>, - U: ::std::iter::FromIterator, + U: ::std::iter::FromIterator::Future, + ::Future> + >, { let mut inner = self.inner.lock(); - iterable.into_iter().filter_map(move |(statement, received_from)| { - inner.import_statement(&*self.context, statement, received_from) + iterable.into_iter().map(move |(statement, received_from)| { + inner.import_statement(&*self.context, router, statement, received_from) }).collect() } @@ -241,3 +405,152 @@ impl SharedTable { self.inner.lock().proposed_digest.clone() } } + +fn make_group_info(roster: DutyRoster, authorities: &[AuthorityId]) -> Result, Error> { + if roster.validator_duty.len() != authorities.len() { + bail!(ErrorKind::InvalidDutyRosterLength(authorities.len(), roster.validator_duty.len())) + } + + if roster.guarantor_duty.len() != authorities.len() { + bail!(ErrorKind::InvalidDutyRosterLength(authorities.len(), roster.guarantor_duty.len())) + } + + let mut map = HashMap::new(); + + let duty_iter = authorities.iter().zip(&roster.validator_duty).zip(&roster.guarantor_duty); + for ((authority, v_duty), a_duty) in duty_iter { + use polkadot_primitives::parachain::Chain; + + match *v_duty { + Chain::Relay => {}, // does nothing for now. + Chain::Parachain(ref id) => { + map.entry(id.clone()).or_insert_with(GroupInfo::default) + .validity_guarantors + .insert(authority.clone()); + } + } + + match *a_duty { + Chain::Relay => {}, // does nothing for now. + Chain::Parachain(ref id) => { + map.entry(id.clone()).or_insert_with(GroupInfo::default) + .availability_guarantors + .insert(authority.clone()); + } + } + } + + for live_group in map.values_mut() { + let validity_len = live_group.validity_guarantors.len(); + let availability_len = live_group.availability_guarantors.len(); + + live_group.needed_validity = validity_len / 2 + validity_len % 2; + live_group.needed_availability = availability_len / 2 + availability_len % 2; + } + + Ok(map) +} + +/// Polkadot proposer factory. +pub struct ProposerFactory { + /// The client instance. + pub client: Arc, + /// The backing network handle. + pub network: N, +} + +impl bft::ProposerFactory for ProposerFactory { + type Proposer = Proposer; + type Error = Error; + + fn init(&self, parent_header: &SubstrateHeader, authorities: &[AuthorityId], sign_with: Arc) -> Result { + let parent_hash = parent_header.hash(); + let duty_roster = self.client.duty_roster(&BlockId::Hash(parent_hash))?; + + let group_info = make_group_info(duty_roster, authorities)?; + let table = Arc::new(SharedTable::new(group_info, sign_with, parent_hash)); + let router = self.network.table_router(table.clone()); + + // TODO [PoC-2]: kick off collation process. + Ok(Proposer { + parent_hash, + _table: table, + _router: router, + client: self.client.clone(), + }) + } +} + +fn current_timestamp() -> Timestamp { + use std::time; + + time::SystemTime::now().duration_since(time::UNIX_EPOCH) + .expect("now always later than unix epoch; qed") + .as_secs() +} + +/// The Polkadot proposer logic. +pub struct Proposer { + parent_hash: HeaderHash, + client: Arc, + _table: Arc, + _router: R, +} + +impl bft::Proposer for Proposer { + type Error = Error; + type Create = Result; + type Evaluate = Result; + + fn propose(&self) -> Result { + // TODO: handle case when current timestamp behind that in state. + let polkadot_block = self.client.build_block( + &BlockId::Hash(self.parent_hash), + current_timestamp() + )?.bake(); + + // TODO: integrate transaction queue and `push_transaction`s. + + let substrate_block = Slicable::decode(&mut polkadot_block.encode().as_slice()) + .expect("polkadot blocks defined to serialize to substrate blocks correctly; qed"); + + Ok(substrate_block) + } + + // TODO: certain kinds of errors here should lead to a misbehavior report. + fn evaluate(&self, proposal: &SubstrateBlock) -> Result { + evaluate_proposal(proposal, &*self.client, current_timestamp(), &self.parent_hash) + } +} + +fn evaluate_proposal( + proposal: &SubstrateBlock, + client: &C, + now: Timestamp, + parent_hash: &HeaderHash, +) -> Result { + const MAX_TIMESTAMP_DRIFT: Timestamp = 4; + + let encoded = Slicable::encode(proposal); + let proposal = PolkadotBlock::decode(&mut &encoded[..]) + .ok_or_else(|| ErrorKind::ProposalNotForPolkadot)?; + + if proposal.header.parent_hash != *parent_hash { + bail!(ErrorKind::WrongParentHash(*parent_hash, proposal.header.parent_hash)); + } + + // no need to check number because + // a) we assume the parent is valid. + // b) the runtime checks that `proposal.parent_hash` == `block_hash(proposal.number - 1)` + + let block_timestamp = proposal.body.timestamp; + + // TODO: just defer using `tokio_timer` to delay prepare vote. + if block_timestamp > now + MAX_TIMESTAMP_DRIFT { + bail!(ErrorKind::TimestampInFuture) + } + + // execute the block. + client.evaluate_block(&BlockId::Hash(*parent_hash), proposal)?; + Ok(true) +} diff --git a/polkadot/executor/src/lib.rs b/polkadot/executor/src/lib.rs index f7ded050250b9..a1f472a859ec5 100644 --- a/polkadot/executor/src/lib.rs +++ b/polkadot/executor/src/lib.rs @@ -44,8 +44,10 @@ mod tests { use polkadot_runtime::runtime::staking::balance; use state_machine::{CodeExecutor, TestExternalities}; use primitives::twox_128; - use polkadot_primitives::{Hash, Header, BlockNumber, Block, Digest, Transaction, - UncheckedTransaction, Function}; + use polkadot_primitives::{ + Hash, Header, Body, BlockNumber, Block, Digest, Transaction, + UncheckedTransaction, Function, InherentFunction, + }; use ed25519::{Public, Pair}; const BLOATY_CODE: &[u8] = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm"); @@ -58,76 +60,6 @@ mod tests { ) } - fn tx() -> UncheckedTransaction { - let transaction = Transaction { - signed: Keyring::One.to_raw_public(), - nonce: 0, - function: Function::StakingTransfer(Keyring::Two.to_raw_public(), 69), - }; - let signature = Keyring::from_raw_public(transaction.signed).unwrap() - .sign(&transaction.encode()); - - UncheckedTransaction { transaction, signature } - } - - #[test] - fn panic_execution_with_foreign_code_gives_error() { - let one = Keyring::One.to_raw_public(); - let mut t: TestExternalities = map![ - twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] - ]; - - let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); - assert!(r.is_err()); - } - - #[test] - fn panic_execution_with_native_equivalent_code_gives_error() { - let one = Keyring::One.to_raw_public(); - let mut t: TestExternalities = map![ - twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] - ]; - - let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); - assert!(r.is_err()); - } - - #[test] - fn successful_execution_with_native_equivalent_code_gives_ok() { - let one = Keyring::One.to_raw_public(); - let two = Keyring::Two.to_raw_public(); - - let mut t: TestExternalities = map![ - twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ]; - - let r = Executor::new().call(&mut t, COMPACT_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); - assert!(r.is_ok()); - - runtime_io::with_externalities(&mut t, || { - assert_eq!(balance(&one), 42); - assert_eq!(balance(&two), 69); - }); - } - - #[test] - fn successful_execution_with_foreign_code_gives_ok() { - let one = Keyring::One.to_raw_public(); - let two = Keyring::Two.to_raw_public(); - - let mut t: TestExternalities = map![ - twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] - ]; - - let r = Executor::new().call(&mut t, BLOATY_CODE, "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); - assert!(r.is_ok()); - - runtime_io::with_externalities(&mut t, || { - assert_eq!(balance(&one), 42); - assert_eq!(balance(&two), 69); - }); - } - fn new_test_ext() -> TestExternalities { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); @@ -152,40 +84,77 @@ mod tests { ] } - fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, txs: Vec) -> (Vec, Hash) { + fn set_timestamp(timestamp: u64) -> UncheckedTransaction { + UncheckedTransaction::inherent(InherentFunction::TimestampSet(timestamp)) + } + + fn tx() -> UncheckedTransaction { + let transaction = Transaction { + signed: Keyring::One.to_raw_public(), + nonce: 0, + function: Function::StakingTransfer(Keyring::Two.to_raw_public(), 69), + }; + let signature = Keyring::from_raw_public(transaction.signed).unwrap() + .sign(&transaction.encode()); + + UncheckedTransaction { transaction, signature } + } + + fn execute_tx_on(executor: C, ext: &mut TestExternalities, code: &[u8], tx: UncheckedTransaction, header: Header) + -> Result, C::Error> + where C: CodeExecutor + { + let next_header = executor.call(ext, code, "execute_transaction", &vec![].and(&header).and(&set_timestamp(100_000))).unwrap(); + let next_input = next_header.and(&tx); + + executor.call(ext, code, "execute_transaction", &next_input[..]) + } + + fn construct_block(number: BlockNumber, parent_hash: Hash, state_root: Hash, timestamp: u64, txs: Vec) -> (Vec, Hash) { use triehash::ordered_trie_root; + let transactions = txs.into_iter().map(|transaction| { let signature = Pair::from(Keyring::from_public(Public::from_raw(transaction.signed)).unwrap()) .sign(&transaction.encode()); UncheckedTransaction { transaction, signature } - }).collect::>(); - - let transaction_root = ordered_trie_root(transactions.iter().map(Slicable::encode)).0.into(); + }).collect(); let header = Header { parent_hash, number, state_root, - transaction_root, + transaction_root: Default::default(), digest: Digest { logs: vec![], }, }; - let hash = header.blake2_256(); - (Block { header, transactions }.encode(), hash.into()) + let mut block = Block { + header, + body: Body { timestamp, transactions }, + }; + + let transaction_root = ordered_trie_root(block.all_transactions().map(|tx| Slicable::encode(&tx))).0.into(); + block.header.transaction_root = transaction_root; + + let hash = block.header.blake2_256(); + + (block.encode(), hash.into()) } fn block1() -> (Vec, Hash) { construct_block( 1, [69u8; 32].into(), - hex!("2481853da20b9f4322f34650fea5f240dcbfb266d02db94bfa0153c31f4a29db").into(), - vec![Transaction { - signed: Keyring::One.to_raw_public(), - nonce: 0, - function: Function::StakingTransfer(Keyring::Two.to_raw_public(), 69), - }] + hex!("3df569d47a0d7f4a448486f04fba4eea3e9dfca001319c609f88b3a67b0dd1ea").into(), + 100_000, + vec![ + Transaction { + signed: Keyring::One.to_raw_public(), + nonce: 0, + function: Function::StakingTransfer(Keyring::Two.to_raw_public(), 69), + } + ] ) } @@ -193,7 +162,8 @@ mod tests { construct_block( 2, block1().1, - hex!("1feb4d3a2e587079e6ce1685fa79994efd995e33cb289d39cded67aac1bb46a9").into(), + hex!("c8776c92e8012bf6b3f206448eda3f00bca26d77f220f4714c81cbc92a30e1e2").into(), + 200_000, vec![ Transaction { signed: Keyring::Two.to_raw_public(), @@ -209,6 +179,64 @@ mod tests { ) } + #[test] + fn panic_execution_with_foreign_code_gives_error() { + let one = Keyring::One.to_raw_public(); + let mut t: TestExternalities = map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] + ]; + + let r = execute_tx_on(Executor::new(), &mut t, BLOATY_CODE, tx(), Header::from_block_number(1)); + assert!(r.is_err()); + } + + #[test] + fn panic_execution_with_native_equivalent_code_gives_error() { + let one = Keyring::One.to_raw_public(); + let mut t: TestExternalities = map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![68u8, 0, 0, 0, 0, 0, 0, 0] + ]; + + let r = execute_tx_on(Executor::new(), &mut t, COMPACT_CODE, tx(), Header::from_block_number(1)); + assert!(r.is_err()); + } + + #[test] + fn successful_execution_with_native_equivalent_code_gives_ok() { + let one = Keyring::One.to_raw_public(); + let two = Keyring::Two.to_raw_public(); + + let mut t: TestExternalities = map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ]; + + let r = execute_tx_on(Executor::new(), &mut t, COMPACT_CODE, tx(), Header::from_block_number(1)); + assert!(r.is_ok()); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 69); + }); + } + + #[test] + fn successful_execution_with_foreign_code_gives_ok() { + let one = Keyring::One.to_raw_public(); + let two = Keyring::Two.to_raw_public(); + + let mut t: TestExternalities = map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ]; + + let r = execute_tx_on(Executor::new(), &mut t, BLOATY_CODE, tx(), Header::from_block_number(1)); + assert!(r.is_ok()); + + runtime_io::with_externalities(&mut t, || { + assert_eq!(balance(&one), 42); + assert_eq!(balance(&two), 69); + }); + } + #[test] fn full_native_block_import_works() { let mut t = new_test_ext(); @@ -255,7 +283,7 @@ mod tests { ]; let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.wasm"); - let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); + let r = execute_tx_on(WasmExecutor, &mut t, &foreign_code[..], tx(), Header::from_block_number(1)); assert!(r.is_err()); } @@ -269,7 +297,7 @@ mod tests { ]; let foreign_code = include_bytes!("../../runtime/wasm/target/wasm32-unknown-unknown/release/polkadot_runtime.compact.wasm"); - let r = WasmExecutor.call(&mut t, &foreign_code[..], "execute_transaction", &vec![].and(&Header::from_block_number(1u64)).and(&tx())); + let r = execute_tx_on(WasmExecutor, &mut t, &foreign_code[..], tx(), Header::from_block_number(1)); assert!(r.is_ok()); runtime_io::with_externalities(&mut t, || { diff --git a/polkadot/primitives/src/block.rs b/polkadot/primitives/src/block.rs index 987744baa61bc..b2d90a19c09d2 100644 --- a/polkadot/primitives/src/block.rs +++ b/polkadot/primitives/src/block.rs @@ -21,7 +21,7 @@ use primitives::bytes; use primitives::H256; use rstd::vec::Vec; use codec::{Input, Slicable}; -use transaction::UncheckedTransaction; +use transaction::{UncheckedTransaction, Function, InherentFunction}; pub use primitives::block::Id; @@ -69,8 +69,65 @@ impl Slicable for Digest { } } -/// The block "body": A bunch of transactions. -pub type Body = Vec; +/// Iterator over all inherent transactions. +pub struct InherentTransactions<'a> { + number: u64, + body: &'a Body, +} + +impl<'a> Iterator for InherentTransactions<'a> { + type Item = UncheckedTransaction; + + fn next(&mut self) -> Option { + if self.number == InherentFunction::count() { + return None + } + + self.number += 1; + + let function = match self.number { + 1 => Some(InherentFunction::TimestampSet(self.body.timestamp)), + _ => None, + }; + + function.map(UncheckedTransaction::inherent) + } +} + +/// Type alias for an iterator over all transactions in a block. +pub type AllTransactions<'a> = ::rstd::iter::Chain< + InherentTransactions<'a>, + ::rstd::iter::Cloned<::rstd::slice::Iter<'a, UncheckedTransaction>>, +>; + +/// The block body. Contains timestamp and transactions. +// TODO: add candidates update as well. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +pub struct Body { + /// The timestamp of the block. + pub timestamp: u64, + /// The transactions in the block. + pub transactions: Vec, +} + +impl Body { + /// Get an iterator over all inherent transactions of the body. + pub fn inherent_transactions(&self) -> InherentTransactions { + InherentTransactions { + number: 0, + body: self, + } + } + + /// Get an iterator over all transactions in a block. + pub fn all_transactions(&self) -> AllTransactions { + self.inherent_transactions().chain(self.transactions.iter().cloned()) + } +} + /// A Polkadot relay chain block. #[derive(PartialEq, Eq, Clone)] @@ -78,21 +135,65 @@ pub type Body = Vec; pub struct Block { /// The block header. pub header: Header, - /// All relay-chain transactions. - pub transactions: Body, + /// The block body. + pub body: Body, +} + +impl Block { + /// Get an iterator over all inherent transactions of the body. + pub fn inherent_transactions(&self) -> InherentTransactions { + self.body.inherent_transactions() + } + + /// Get an iterator over all transactions in a block. + pub fn all_transactions(&self) -> AllTransactions { + self.body.all_transactions() + } } impl Slicable for Block { fn decode(input: &mut I) -> Option { - let (header, transactions) = try_opt!(Slicable::decode(input)); - Some(Block { header, transactions }) + let header = try_opt!(Slicable::decode(input)); + + let transactions_len: u32 = try_opt!(Slicable::decode(input)); + let regular_transactions_len = try_opt!(transactions_len.checked_sub(InherentFunction::count() as u32)); + + let timestamp_tx = try_opt!(UncheckedTransaction::decode(input)); + let timestamp = match timestamp_tx.transaction.function { + Function::Inherent(InherentFunction::TimestampSet(ref t)) if timestamp_tx.is_well_formed() => { t.clone() } + _ => return None, + }; + + let transactions: Option> = (0..regular_transactions_len) + .map(|_| UncheckedTransaction::decode(input)) + .filter(|tx| tx.as_ref().map_or(true, |tx| tx.is_well_formed())) + .collect(); + + let body = Body { + timestamp, + transactions: try_opt!(transactions), + }; + + Some(Block { header, body }) } fn encode(&self) -> Vec { let mut v = Vec::new(); v.extend(self.header.encode()); - v.extend(self.transactions.encode()); + + // encode inherent transactions before non-inherent. + let transactions_len = self.body.transactions.len() as u64 + InherentFunction::count(); + (transactions_len as u32).using_encoded(|s| v.extend(s)); + + let timestamp_set_tx = UncheckedTransaction::inherent( + InherentFunction::TimestampSet(self.body.timestamp) + ); + + v.extend(timestamp_set_tx.encode()); + for non_inherent_transaction in &self.body.transactions { + v.extend(non_inherent_transaction.encode()); + } v } @@ -186,4 +287,89 @@ mod tests { let v = header.encode(); assert_eq!(Header::decode(&mut &v[..]).unwrap(), header); } + + #[test] + fn block_encoding_round_trip() { + let mut block = Block { + header: Header::from_block_number(1), + body: Body { + timestamp: 100_000_000, + transactions: Vec::new(), + } + }; + + let raw = block.encode(); + let decoded = Block::decode(&mut &raw[..]).unwrap(); + + assert_eq!(block, decoded); + + block.body.transactions.push(UncheckedTransaction { + transaction: ::transaction::Transaction { + function: Function::StakingStake, + signed: Default::default(), + nonce: 10101, + }, + signature: Default::default(), + }); + + 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::from_block_number(1), + body: Body { + timestamp: 100_000_000, + transactions: Vec::new(), + } + }; + + block.body.transactions.push(UncheckedTransaction { + transaction: ::transaction::Transaction { + function: Function::StakingStake, + signed: Default::default(), + nonce: 10101, + }, + signature: Default::default(), + }); + + let raw = block.encode(); + let decoded_substrate = ::primitives::block::Block::decode(&mut &raw[..]).unwrap(); + let encoded_substrate = decoded_substrate.encode(); + let decoded = Block::decode(&mut &encoded_substrate[..]).unwrap(); + + assert_eq!(block, decoded); + } + + #[test] + fn decode_body_without_inherents_fails() { + let substrate_blank = ::primitives::block::Block { + header: ::primitives::block::Header::from_block_number(1), + transactions: Vec::new(), + }; + + let encoded_substrate = substrate_blank.encode(); + assert!(Block::decode(&mut &encoded_substrate[..]).is_none()); + } + + #[test] + fn inherent_transactions_iter_contains_all_inherent() { + let block = Block { + header: Header::from_block_number(1), + body: Body { + timestamp: 10101, + transactions: Vec::new(), + } + }; + + let mut iter = block.inherent_transactions(); + + assert_eq!(InherentFunction::count(), 1); // following depends on this assertion. + assert_eq!(iter.next().unwrap(), UncheckedTransaction::inherent(InherentFunction::TimestampSet(10101))); + assert!(iter.next().is_none()); + } } diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs index 14b8e78d30f40..d65291476ab71 100644 --- a/polkadot/primitives/src/lib.rs +++ b/polkadot/primitives/src/lib.rs @@ -49,9 +49,9 @@ pub mod validator; pub mod block; pub mod transaction; -pub use self::block::{Header, Block, Log, Digest}; +pub use self::block::{Header, Body, Block, Log, Digest}; pub use self::block::Number as BlockNumber; -pub use self::transaction::{Transaction, UncheckedTransaction, Function, Proposal}; +pub use self::transaction::{Transaction, UncheckedTransaction, Function, InherentFunction, Proposal}; /// Virtual account ID that represents the idea of a dispatch/statement being signed by everybody /// (who matters). Essentially this means that a majority of validators have decided it is @@ -75,5 +75,8 @@ pub type TxOrder = u64; /// A hash of some data used by the relay chain. pub type Hash = primitives::H256; -/// Alias to 520-bit hash when used in the context of a signature on the relay chain. +/// Alias to 512-bit hash when used in the context of a signature on the relay chain. pub type Signature = primitives::hash::H512; + +/// A timestamp: seconds since the unix epoch. +pub type Timestamp = u64; diff --git a/polkadot/primitives/src/parachain.rs b/polkadot/primitives/src/parachain.rs index a39904f44ec53..34a8b34733251 100644 --- a/polkadot/primitives/src/parachain.rs +++ b/polkadot/primitives/src/parachain.rs @@ -121,6 +121,13 @@ impl Slicable for DutyRoster { } } +/// Extrinsic data for a parachain. +#[derive(PartialEq, Eq, Clone)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +#[cfg_attr(feature = "std", serde(rename_all = "camelCase"))] +#[cfg_attr(feature = "std", serde(deny_unknown_fields))] +pub struct Extrinsic; + /// Candidate parachain block. /// /// https://github.com/w3f/polkadot-spec/blob/master/spec.md#candidate-para-chain-block diff --git a/polkadot/primitives/src/transaction.rs b/polkadot/primitives/src/transaction.rs index 6279a2d0aa078..d0b5ab9557f4f 100644 --- a/polkadot/primitives/src/transaction.rs +++ b/polkadot/primitives/src/transaction.rs @@ -18,6 +18,7 @@ use rstd::vec::Vec; use codec::{Input, Slicable}; +use ::Signature; #[cfg(feature = "std")] use std::fmt; @@ -184,12 +185,35 @@ impl FunctionId { } } -/// Functions on the runtime. +/// Inherent functions on the runtime. +/// These must be called each block by the `EVERYBODY` account. #[derive(Clone, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] -pub enum Function { +pub enum InherentFunction { /// Set the timestamp. TimestampSet(u64), +} + +impl InherentFunction { + /// Get the number of inherent functions. + pub fn count() -> u64 { + 1 + } + + /// Get the index. + pub fn index(&self) -> u64 { + match *self { + InherentFunction::TimestampSet(_) => 0, + } + } +} + +/// Functions on the runtime. +#[derive(Clone, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug))] +pub enum Function { + /// An inherent function. + Inherent(InherentFunction), /// Set temporary session key as a validator. SessionSetKey(::SessionKey), /// Staking subsystem: begin staking. @@ -204,12 +228,34 @@ pub enum Function { GovernanceApprove(BlockNumber), } +impl Function { + /// The number of inherent functions. + pub fn inherent_functions() -> u64 { InherentFunction::count() } + + /// Whether this function is "inherent": that it must be part of every + /// block at the given index and no other. + /// + /// Transactions containing inherent functions should not be signed. + pub fn is_inherent(&self) -> bool { + self.inherent_index().is_some() + } + + /// If this function is inherent, returns the index it should occupy + /// in the block. Otherwise returns `None`. + pub fn inherent_index(&self) -> Option { + match *self { + Function::Inherent(ref inner) => Some(inner.index()), + _ => None, + } + } +} + impl Slicable for Function { fn decode(input: &mut I) -> Option { let id = try_opt!(u8::decode(input).and_then(FunctionId::from_u8)); Some(match id { FunctionId::TimestampSet => - Function::TimestampSet(try_opt!(Slicable::decode(input))), + Function::Inherent(InherentFunction::TimestampSet(try_opt!(Slicable::decode(input)))), FunctionId::SessionSetKey => Function::SessionSetKey(try_opt!(Slicable::decode(input))), FunctionId::StakingStake => Function::StakingStake, @@ -230,7 +276,7 @@ impl Slicable for Function { fn encode(&self) -> Vec { let mut v = Vec::new(); match *self { - Function::TimestampSet(ref data) => { + Function::Inherent(InherentFunction::TimestampSet(ref data)) => { (FunctionId::TimestampSet as u8).using_encoded(|s| v.extend(s)); data.using_encoded(|s| v.extend(s)); } @@ -308,7 +354,33 @@ pub struct UncheckedTransaction { /// The actual transaction information. pub transaction: Transaction, /// The signature; should be an Ed25519 signature applied to the serialised `transaction` field. - pub signature: super::Signature, + pub signature: Signature, +} + +impl UncheckedTransaction { + /// Whether the transaction is well-formed. In particular checks that + /// inherent transactions have the correct signed and signature fields. + /// + /// Does not check signatures on other transactions. + pub fn is_well_formed(&self) -> bool { + if self.transaction.function.is_inherent() { + self.transaction.signed == ::EVERYBODY && self.signature == Signature::zero() + } else { + true + } + } + + /// Create a new inherent-style transaction from the given function. + pub fn inherent(function: InherentFunction) -> Self { + UncheckedTransaction { + transaction: Transaction { + function: Function::Inherent(function), + nonce: 0, + signed: ::EVERYBODY + }, + signature: Signature::zero(), + } + } } impl Slicable for UncheckedTransaction { @@ -372,7 +444,7 @@ mod tests { transaction: Transaction { signed: [1; 32], nonce: 999u64, - function: Function::TimestampSet(135135), + function: Function::Inherent(InherentFunction::TimestampSet(135135)), }, signature: primitives::hash::H512([0; 64]), }; diff --git a/polkadot/runtime/src/api.rs b/polkadot/runtime/src/api.rs index 3f9ff71473dac..b8371a5da8531 100644 --- a/polkadot/runtime/src/api.rs +++ b/polkadot/runtime/src/api.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . -use runtime::{system, parachains, consensus, session}; +use runtime::{system, parachains, consensus, session, timestamp}; impl_stubs!( execute_block => |block| system::internal::execute_block(block), @@ -23,5 +23,6 @@ impl_stubs!( validator_count => |()| session::validator_count(), validators => |()| session::validators(), authorities => |()| consensus::authorities(), - duty_roster => |()| parachains::calculate_duty_roster() + duty_roster => |()| parachains::calculate_duty_roster(), + get_timestamp => |()| timestamp::get() ); diff --git a/polkadot/runtime/src/environment.rs b/polkadot/runtime/src/environment.rs index d7651c575aa36..5b311ce1f98b9 100644 --- a/polkadot/runtime/src/environment.rs +++ b/polkadot/runtime/src/environment.rs @@ -17,7 +17,6 @@ //! Environment API: Allows certain information to be accessed throughout the runtime. use rstd::boxed::Box; -use rstd::mem; use rstd::cell::RefCell; use rstd::rc::Rc; @@ -32,6 +31,8 @@ pub struct Environment { pub parent_hash: Hash, /// The current block digest. pub digest: Digest, + /// The current transaction index + pub transaction_index: u64, } /// Do something with the environment and return its value. Keep the function short. @@ -52,7 +53,7 @@ fn env() -> Rc> { let singleton: Rc> = Rc::new(RefCell::new(Default::default())); // Put it in the heap so it can outlive this call - SINGLETON = mem::transmute(Box::new(singleton)); + SINGLETON = Box::into_raw(Box::new(singleton)) as *const _; } // Now we give out a copy of the data that is safe to use concurrently. @@ -73,7 +74,7 @@ fn env() -> Rc> { let singleton: Rc> = Rc::new(RefCell::new(Default::default())); // Put it in the heap so it can outlive this call - *s.borrow_mut() = mem::transmute(Box::new(singleton)); + *s.borrow_mut() = Box::into_raw(Box::new(singleton)) as *const _; } // Now we give out a copy of the data that is safe to use concurrently. diff --git a/polkadot/runtime/src/lib.rs b/polkadot/runtime/src/lib.rs index 99aebd5990267..4059a16551632 100644 --- a/polkadot/runtime/src/lib.rs +++ b/polkadot/runtime/src/lib.rs @@ -41,7 +41,7 @@ pub mod api; pub mod transaction { use rstd::ops; use polkadot_primitives::Signature; - pub use polkadot_primitives::{Transaction, UncheckedTransaction}; + pub use polkadot_primitives::{Transaction, Function, UncheckedTransaction}; /// A type-safe indicator that a transaction has been checked. #[derive(PartialEq, Eq, Clone)] @@ -63,10 +63,20 @@ pub mod transaction { } } - /// Check the signature on a transaction. - /// - /// On failure, return the transaction back. - pub fn check(tx: UncheckedTransaction) -> Result { + /// Check the validity of a transaction: whether it can appear at the given index + /// and whether it is correctly authenticated. + pub fn check(tx: UncheckedTransaction, index: u64) -> Result { + match tx.transaction.function.inherent_index() { + Some(correct_index) => { + if index != correct_index || !tx.is_well_formed() { return Err(tx) } + return Ok(CheckedTransaction(tx)); + } + None => { + // non-inherent functions must appear after inherent. + if index < Function::inherent_functions() { return Err(tx) } + } + } + let msg = ::codec::Slicable::encode(&tx.transaction); if ::runtime_io::ed25519_verify(&tx.signature.0, &msg, &tx.transaction.signed) { Ok(CheckedTransaction(tx)) diff --git a/polkadot/runtime/src/runtime/system.rs b/polkadot/runtime/src/runtime/system.rs index 7d5e8835ed05c..b9d28cf18e5ff 100644 --- a/polkadot/runtime/src/runtime/system.rs +++ b/polkadot/runtime/src/runtime/system.rs @@ -24,12 +24,12 @@ use codec::{KeyedVec, Slicable}; use runtime_support::{Hashable, storage}; use environment::with_env; use polkadot_primitives::{AccountId, Hash, TxOrder, BlockNumber, Block, Header, - UncheckedTransaction, Function, Log}; + UncheckedTransaction, Function, InherentFunction, Log}; use runtime::{staking, session}; const NONCE_OF: &[u8] = b"sys:non:"; const BLOCK_HASH_AT: &[u8] = b"sys:old:"; -const CODE: &[u8] = b"sys:cod"; +const TEMP_TRANSACTION_NUMBER: &[u8] = b"temp:txcount:"; /// The current block number being processed. Set by `execute_block`. pub fn block_number() -> BlockNumber { @@ -71,8 +71,10 @@ pub mod internal { // any initial checks initial_checks(&block); - // execute transactions - block.transactions.iter().cloned().for_each(super::execute_transaction); + // execute all transactions, inherent or otherwise. + for (tx_num, tx) in block.all_transactions().enumerate() { + super::execute_transaction(tx, tx_num as u64); + } // post-transactional book-keeping. staking::internal::check_new_era(); @@ -87,6 +89,8 @@ pub mod internal { /// Execute a transaction outside of the block execution function. /// This doesn't attempt to validate anything regarding the block. + /// Note that when building a block transaction by transaction, the + /// inherent methods must be called manually. pub fn execute_transaction(utx: UncheckedTransaction, mut header: Header) -> Header { // populate environment from header. with_env(|e| { @@ -95,11 +99,16 @@ pub mod internal { mem::swap(&mut header.digest, &mut e.digest); }); - super::execute_transaction(utx); + let tx_num: u64 = storage::get_or_default(TEMP_TRANSACTION_NUMBER); + + super::execute_transaction(utx, tx_num); with_env(|e| { mem::swap(&mut header.digest, &mut e.digest); }); + + storage::put(TEMP_TRANSACTION_NUMBER, &(tx_num + 1)); + header } @@ -113,6 +122,11 @@ pub mod internal { mem::swap(&mut header.digest, &mut e.digest); }); + let tx_count: u64 = storage::take_or_default(TEMP_TRANSACTION_NUMBER); + if tx_count < Function::inherent_functions() { + panic!("Not enough transactions provided to fulfill all inherent functions."); + } + staking::internal::check_new_era(); session::internal::check_rotate_session(); @@ -125,54 +139,61 @@ pub mod internal { header } +} - /// Dispatch a function. - pub fn dispatch_function(function: &Function, transactor: &AccountId) { - match *function { - Function::StakingStake => { - ::runtime::staking::public::stake(transactor); - } - Function::StakingUnstake => { - ::runtime::staking::public::unstake(transactor); - } - Function::StakingTransfer(dest, value) => { - ::runtime::staking::public::transfer(transactor, &dest, value); - } - Function::SessionSetKey(session) => { - ::runtime::session::public::set_key(transactor, &session); - } - Function::TimestampSet(t) => { - ::runtime::timestamp::public::set(t); - } - Function::GovernancePropose(ref proposal) => { - ::runtime::governance::public::propose(transactor, proposal); - } - Function::GovernanceApprove(era_index) => { - ::runtime::governance::public::approve(transactor, era_index); - } +/// Dispatch a function. +fn dispatch_function(function: &Function, transactor: &AccountId) { + match *function { + Function::Inherent(InherentFunction::TimestampSet(t)) => { + ::runtime::timestamp::public::set(t); + } + Function::StakingStake => { + ::runtime::staking::public::stake(transactor); + } + Function::StakingUnstake => { + ::runtime::staking::public::unstake(transactor); + } + Function::StakingTransfer(dest, value) => { + ::runtime::staking::public::transfer(transactor, &dest, value); + } + Function::SessionSetKey(session) => { + ::runtime::session::public::set_key(transactor, &session); + } + Function::GovernancePropose(ref proposal) => { + ::runtime::governance::public::propose(transactor, proposal); + } + Function::GovernanceApprove(era_index) => { + ::runtime::governance::public::approve(transactor, era_index); } } } -fn execute_transaction(utx: UncheckedTransaction) { +fn execute_transaction(utx: UncheckedTransaction, tx_num: u64) { use ::transaction; - // Verify the signature is good. - let tx = match transaction::check(utx) { + // Verify the transaction is authenticated at its position. + let tx = match transaction::check(utx, tx_num) { Ok(tx) => tx, - Err(_) => panic!("All transactions should be properly signed"), + Err(_) => panic!("Transaction at index {} not properly authenticated", tx_num), }; // check nonce let nonce_key = tx.signed.to_keyed_vec(NONCE_OF); - let expected_nonce: TxOrder = storage::get_or(&nonce_key, 0); + let (expected_nonce, increment_nonce) = if !tx.function.is_inherent() { + (storage::get_or(&nonce_key, 0), true) + } else { + (0, false) + }; + assert!(tx.nonce == expected_nonce, "All transactions should have the correct nonce"); - // increment nonce in storage - storage::put(&nonce_key, &(expected_nonce + 1)); + // increment nonce in storage, unless it's the EVERYBODY account. + if increment_nonce { + storage::put(&nonce_key, &(expected_nonce + 1)); + } // decode parameters and dispatch - internal::dispatch_function(&tx.function, &tx.signed); + dispatch_function(&tx.function, &tx.signed); } fn initial_checks(block: &Block) { @@ -185,7 +206,7 @@ fn initial_checks(block: &Block) { ); // check transaction trie root represents the transactions. - let txs = block.transactions.iter().map(Slicable::encode).collect::>(); + let txs = block.all_transactions().map(|tx| Slicable::encode(&tx)).collect::>(); let txs = txs.iter().map(Vec::as_slice).collect::>(); let txs_root = enumerated_trie_root(&txs).into(); info_expect_equal_hash(&header.transaction_root, &txs_root); @@ -239,11 +260,16 @@ mod tests { use keyring::Keyring; use environment::with_env; use primitives::hexdisplay::HexDisplay; - use polkadot_primitives::{Header, Digest, UncheckedTransaction, Transaction, Function}; + use polkadot_primitives::{Header, Body, Digest, UncheckedTransaction, Transaction, Function, InherentFunction}; use runtime::staking; + fn set_timestamp() -> UncheckedTransaction { + UncheckedTransaction::inherent(InherentFunction::TimestampSet(100_000)) + } + #[test] - fn staking_balance_transfer_dispatch_works() { + #[should_panic] + fn fails_if_first_not_timestamp_set() { let one = Keyring::One.to_raw_public(); let two = Keyring::Two.to_raw_public(); @@ -262,6 +288,31 @@ mod tests { with_externalities(&mut t, || { internal::execute_transaction(tx, Header::from_block_number(1)); + }); + } + + #[test] + fn staking_balance_transfer_dispatch_works() { + let one = Keyring::One.to_raw_public(); + let two = Keyring::Two.to_raw_public(); + + let mut t: TestExternalities = map![ + twox_128(&one.to_keyed_vec(b"sta:bal:")).to_vec() => vec![111u8, 0, 0, 0, 0, 0, 0, 0] + ]; + + let tx = UncheckedTransaction { + transaction: Transaction { + signed: one.clone(), + nonce: 0, + function: Function::StakingTransfer(two, 69), + }, + signature: hex!("5f9832c5a4a39e2dd4a3a0c5b400e9836beb362cb8f7d845a8291a2ae6fe366612e080e4acd0b5a75c3d0b6ee69614a68fb63698c1e76bf1f2dcd8fa617ddf05").into(), + }; + + with_externalities(&mut t, || { + let header = Header::from_block_number(1); + let header = internal::execute_transaction(set_timestamp(), header); + internal::execute_transaction(tx, header); assert_eq!(staking::balance(&one), 42); assert_eq!(staking::balance(&two), 69); }); @@ -293,22 +344,22 @@ mod tests { #[test] fn block_import_works() { - let one = Keyring::One.to_raw_public(); - let two = Keyring::Two.to_raw_public(); - let mut t = new_test_ext(); let h = Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("1ab2dbb7d4868a670b181327b0b6a58dc64b10cfb9876f737a5aa014b8da31e0").into(), - transaction_root: hex!("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").into(), + state_root: hex!("aa4fbcdc09b21e4366aebccd9b9ec0831a8a2765c712d3397f121ff8e60e21e2").into(), + transaction_root: hex!("328ae80be3adf358d2a2e188cbe1bfd3f8cd5b15a2e7666e2b4eccf7450efc32").into(), digest: Digest { logs: vec![], }, }; let b = Block { header: h, - transactions: vec![], + body: Body { + timestamp: 100_000, + transactions: vec![] + } }; with_externalities(&mut t, || { @@ -334,7 +385,10 @@ mod tests { let b = Block { header: h, - transactions: vec![], + body: Body { + timestamp: 100_000, + transactions: vec![], + } }; with_externalities(&mut t, || { @@ -360,7 +414,10 @@ mod tests { let b = Block { header: h, - transactions: vec![], + body: Body { + timestamp: 100_000, + transactions: vec![], + } }; with_externalities(&mut t, || { diff --git a/polkadot/runtime/src/runtime/timestamp.rs b/polkadot/runtime/src/runtime/timestamp.rs index 0b96f39bd4a8c..8252f372254a8 100644 --- a/polkadot/runtime/src/runtime/timestamp.rs +++ b/polkadot/runtime/src/runtime/timestamp.rs @@ -17,8 +17,7 @@ //! Timestamp manager: just handles the current timestamp. use runtime_support::storage; - -pub type Timestamp = u64; +use polkadot_primitives::Timestamp; const CURRENT_TIMESTAMP: &[u8] = b"tim:val"; @@ -32,6 +31,10 @@ pub mod public { /// Set the current time. pub fn set(now: Timestamp) { + if super::get() > now { + panic!("last timestamp less than now"); + } + storage::put(CURRENT_TIMESTAMP, &now); } } 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 a1f28badf2e10..2960433376153 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 160550b5c786f..e7a92e3450d71 100644 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/substrate/bft/src/error.rs b/substrate/bft/src/error.rs index 38f560c2cc78d..d248b217ff556 100644 --- a/substrate/bft/src/error.rs +++ b/substrate/bft/src/error.rs @@ -47,6 +47,12 @@ error_chain! { description("Unable to dispatch agreement future"), display("Unable to dispatch agreement future: {:?}", e), } + + /// Some other error. + Other(e: Box<::std::error::Error + Send>) { + description("Other error") + display("Other error: {}", e.description()) + } } } diff --git a/substrate/bft/src/generic/mod.rs b/substrate/bft/src/generic/mod.rs index f5cd34b8b69fd..4ebe508e36b0b 100644 --- a/substrate/bft/src/generic/mod.rs +++ b/substrate/bft/src/generic/mod.rs @@ -74,6 +74,8 @@ pub struct LocalizedMessage { /// Provides necessary types for protocol messages, and functions necessary for a /// participant to evaluate and create those messages. pub trait Context { + /// Errors which can occur from the futures in this context. + type Error: From; /// Candidate proposed. type Candidate: Debug + Eq + Clone; /// Candidate digest. @@ -83,9 +85,11 @@ pub trait Context { /// Signature. type Signature: Debug + Eq + Clone; /// A future that resolves when a round timeout is concluded. - type RoundTimeout: Future; + type RoundTimeout: Future; /// A future that resolves when a proposal is ready. - type CreateProposal: Future; + type CreateProposal: Future; + /// A future that resolves when a proposal has been evaluated. + type EvaluateProposal: Future; /// Get the local authority ID. fn local_id(&self) -> Self::AuthorityId; @@ -103,8 +107,8 @@ pub trait Context { /// Get the proposer for a given round of consensus. fn round_proposer(&self, round: usize) -> Self::AuthorityId; - /// Whether the candidate is valid. - fn candidate_valid(&self, candidate: &Self::Candidate) -> bool; + /// Whether the proposal is valid. + fn proposal_valid(&self, proposal: &Self::Candidate) -> Self::EvaluateProposal; /// Create a round timeout. The context will determine the correct timeout /// length, and create a future that will resolve when the timeout is @@ -246,6 +250,7 @@ struct Strategy { nodes: usize, max_faulty: usize, fetching_proposal: Option, + evaluating_proposal: Option, round_timeout: future::Fuse, local_state: LocalState, locked: Option>, @@ -278,6 +283,7 @@ impl Strategy { current_accumulator, future_accumulator, fetching_proposal: None, + evaluating_proposal: None, local_state: LocalState::Start, locked: None, notable_candidates: HashMap::new(), @@ -324,15 +330,12 @@ impl Strategy { // rounds if necessary. // // only call within the context of a `Task`. - fn poll( + fn poll( &mut self, context: &C, sending: &mut Sending<::Communication> ) - -> Poll, E> - where - C::RoundTimeout: Future, - C::CreateProposal: Future, + -> Poll, C::Error> { let mut last_watermark = ( self.current_accumulator.round_number(), @@ -361,18 +364,15 @@ impl Strategy { // perform one round of polling: attempt to broadcast messages and change the state. // if the round or internal round-state changes, this should be called again. - fn poll_once( + fn poll_once( &mut self, context: &C, sending: &mut Sending<::Communication> ) - -> Poll, E> - where - C::RoundTimeout: Future, - C::CreateProposal: Future, + -> Poll, C::Error> { self.propose(context, sending)?; - self.prepare(context, sending); + self.prepare(context, sending)?; self.commit(context, sending); self.vote_advance(context, sending)?; @@ -423,7 +423,7 @@ impl Strategy { context: &C, sending: &mut Sending<::Communication> ) - -> Result<(), ::Error> + -> Result<(), C::Error> { if let LocalState::Start = self.local_state { let mut propose = false; @@ -486,11 +486,13 @@ impl Strategy { &mut self, context: &C, sending: &mut Sending<::Communication> - ) { + ) + -> Result<(), C::Error> + { // prepare only upon start or having proposed. match self.local_state { LocalState::Start | LocalState::Proposed => {}, - _ => return + _ => return Ok(()) }; let mut prepare_for = None; @@ -508,8 +510,17 @@ impl Strategy { // this is necessary to preserve the liveness property. prepare_for = Some(digest); } - None => if context.candidate_valid(candidate) { - prepare_for = Some(digest); + None => { + let res = self.evaluating_proposal + .get_or_insert_with(|| context.proposal_valid(candidate)) + .poll()?; + + if let Async::Ready(valid) = res { + self.evaluating_proposal = None; + if valid { + prepare_for = Some(digest); + } + } } } } @@ -523,6 +534,8 @@ impl Strategy { self.import_and_send_message(message, context, sending); self.local_state = LocalState::Prepared; } + + Ok(()) } fn commit( @@ -561,7 +574,7 @@ impl Strategy { context: &C, sending: &mut Sending<::Communication> ) - -> Result<(), ::Error> + -> Result<(), C::Error> { // we can vote for advancement under all circumstances unless we have already. if let LocalState::VoteAdvance = self.local_state { return Ok(()) } @@ -592,6 +605,7 @@ impl Strategy { let threshold = self.nodes - self.max_faulty; self.fetching_proposal = None; + self.evaluating_proposal = None; self.round_timeout = context.begin_round_timeout(round).fuse(); self.local_state = LocalState::Start; @@ -647,17 +661,14 @@ pub struct Agreement { strategy: Strategy, } -impl Future for Agreement +impl Future for Agreement where C: Context, - C::RoundTimeout: Future, - C::CreateProposal: Future, - I: Stream::Communication,Error=E>, - O: Sink::Communication,SinkError=E>, - E: From, + I: Stream::Communication,Error=C::Error>, + O: Sink::Communication,SinkError=C::Error>, { type Item = Committed; - type Error = E; + type Error = C::Error; fn poll(&mut self) -> Poll { // even if we've observed the conclusion, wait until all @@ -721,15 +732,12 @@ impl Future for Agreement /// conclude without having witnessed the conclusion. /// In general, this future should be pre-empted by the import of a justification /// set for this block height. -pub fn agree(context: C, nodes: usize, max_faulty: usize, input: I, output: O) +pub fn agree(context: C, nodes: usize, max_faulty: usize, input: I, output: O) -> Agreement where C: Context, - C::RoundTimeout: Future, - C::CreateProposal: Future, - I: Stream::Communication,Error=E>, - O: Sink::Communication,SinkError=E>, - E: From, + I: Stream::Communication,Error=C::Error>, + O: Sink::Communication,SinkError=C::Error>, { let strategy = Strategy::create(&context, nodes, max_faulty); Agreement { diff --git a/substrate/bft/src/generic/tests.rs b/substrate/bft/src/generic/tests.rs index 41d79dcca3782..c64907cbeef77 100644 --- a/substrate/bft/src/generic/tests.rs +++ b/substrate/bft/src/generic/tests.rs @@ -159,12 +159,14 @@ struct TestContext { } impl Context for TestContext { + type Error = Error; type Candidate = Candidate; type Digest = Digest; type AuthorityId = AuthorityId; type Signature = Signature; type RoundTimeout = Box>; type CreateProposal = FutureResult; + type EvaluateProposal = FutureResult; fn local_id(&self) -> AuthorityId { self.local_id.clone() @@ -200,8 +202,8 @@ impl Context for TestContext { self.shared.lock().unwrap().round_proposer(round) } - fn candidate_valid(&self, candidate: &Candidate) -> bool { - candidate.0 % 3 != 0 + fn proposal_valid(&self, proposal: &Candidate) -> FutureResult { + Ok(proposal.0 % 3 != 0).into_future() } fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout { diff --git a/substrate/bft/src/lib.rs b/substrate/bft/src/lib.rs index 1f81b23cac0e5..977245e26f4ee 100644 --- a/substrate/bft/src/lib.rs +++ b/substrate/bft/src/lib.rs @@ -100,22 +100,35 @@ pub type Committed = generic::Committed; /// Communication between BFT participants. pub type Communication = generic::Communication; +/// Proposer factory. Can be used to create a proposer instance. +pub trait ProposerFactory { + /// The proposer type this creates. + type Proposer: Proposer; + /// Error which can occur upon creation. + type Error: From; + + /// Initialize the proposal logic on top of a specific header. + // TODO: provide state context explicitly? + fn init(&self, parent_header: &Header, authorities: &[AuthorityId], sign_with: Arc) -> Result; +} + /// Logic for a proposer. /// /// This will encapsulate creation and evaluation of proposals at a specific /// block. -pub trait Proposer: Sized { - type CreateProposal: IntoFuture; - - /// Initialize the proposal logic on top of a specific header. - // TODO: provide state context explicitly? - fn init(parent_header: &Header, sign_with: Arc) -> Self; - - /// Create a proposal. - fn propose(&self) -> Self::CreateProposal; - /// Evaluate proposal. True means valid. +pub trait Proposer { + /// Error type which can occur when proposing or evaluating. + type Error: From + From + 'static; + /// Future that resolves to a created proposal. + type Create: IntoFuture; + /// Future that resolves when a proposal is evaluated. + type Evaluate: IntoFuture; + + /// Create a proposal. + fn propose(&self) -> Self::Create; + /// Evaluate proposal. True means valid. // TODO: change this to a future. - fn evaluate(&self, proposal: &Block) -> bool; + fn evaluate(&self, proposal: &Block) -> Self::Evaluate; } /// Block import trait. @@ -141,12 +154,14 @@ struct BftInstance

{ } impl generic::Context for BftInstance

{ + type Error = P::Error; type AuthorityId = AuthorityId; type Digest = HeaderHash; type Signature = LocalizedSignature; type Candidate = Block; - type RoundTimeout = Box + Send>; - type CreateProposal = ::Future; + type RoundTimeout = Box + Send>; + type CreateProposal = ::Future; + type EvaluateProposal = ::Future; fn local_id(&self) -> AuthorityId { self.key.public().0 @@ -181,8 +196,8 @@ impl generic::Context for BftInstance

{ self.authorities[(index as usize) % self.authorities.len()] } - fn candidate_valid(&self, proposal: &Block) -> bool { - self.proposer.evaluate(proposal) + fn proposal_valid(&self, proposal: &Block) -> Self::EvaluateProposal { + self.proposer.evaluate(proposal).into_future() } fn begin_round_timeout(&self, round: usize) -> Self::RoundTimeout { @@ -194,24 +209,25 @@ impl generic::Context for BftInstance

{ .saturating_mul(self.round_timeout_multiplier); Box::new(self.timer.sleep(Duration::from_secs(timeout)) - .map_err(|_| ErrorKind::FaultyTimer.into())) + .map_err(|_| Error::from(ErrorKind::FaultyTimer)) + .map_err(Into::into)) } } -type Input = stream::Empty; +type Input = stream::Empty; // "black hole" output sink. -struct Output; +struct Output(::std::marker::PhantomData); -impl Sink for Output { +impl Sink for Output { type SinkItem = Communication; - type SinkError = Error; + type SinkError = E; - fn start_send(&mut self, _item: Communication) -> ::futures::StartSend { + fn start_send(&mut self, _item: Communication) -> ::futures::StartSend { Ok(::futures::AsyncSink::Ready) } - fn poll_complete(&mut self) -> ::futures::Poll<(), Error> { + fn poll_complete(&mut self) -> ::futures::Poll<(), E> { Ok(Async::Ready(())) } } @@ -219,7 +235,7 @@ impl Sink for Output { /// A future that resolves either when canceled (witnessing a block from the network at same height) /// or when agreement completes. pub struct BftFuture { - inner: generic::Agreement, Input, Output>, + inner: generic::Agreement, Input, Output>, cancel: Arc, send_task: Option>, import: Arc, @@ -282,30 +298,38 @@ pub struct BftService { timer: Timer, round_timeout_multiplier: u64, key: Arc, // TODO: key changing over time. - _marker: ::std::marker::PhantomData

, + factory: P, } impl BftService where - P: Proposer, - E: Executor>, + P: ProposerFactory, + E: Executor>, I: BlockImport + Authorities, { /// Signal that a valid block with the given header has been imported. /// - /// This will begin the consensus process to build a block on top of it. - /// If the executor fails to run the future, an error will be returned. - pub fn build_upon(&self, header: &Header) -> Result<(), Error> { + /// If the local signing key is an authority, this will begin the consensus process to build a + /// block on top of it. If the executor fails to run the future, an error will be returned. + pub fn build_upon(&self, header: &Header) -> Result<(), P::Error> { let hash = header.hash(); - let mut _preempted_consensus = None; + let mut _preempted_consensus = None; // defers drop of live to the end. - let proposer = P::init(header, self.key.clone()); + let authorities = self.client.authorities(&BlockId::Hash(hash))?; // TODO: check key is one of the authorities. - let authorities = self.client.authorities(&BlockId::Hash(hash))?; let n = authorities.len(); let max_faulty = max_faulty_of(n); + let local_id = self.key.public().0; + + if !authorities.contains(&local_id) { + self.live_agreements.lock().remove(&header.parent_hash); + return Ok(()) + } + + let proposer = self.factory.init(header, &authorities, self.key.clone())?; + let bft_instance = BftInstance { proposer, parent_hash: hash, @@ -320,7 +344,7 @@ impl BftService n, max_faulty, stream::empty(), - Output, + Output(Default::default()), ); let cancel = Arc::new(AtomicBool::new(false)); @@ -331,7 +355,7 @@ impl BftService cancel: cancel.clone(), send_task: Some(tx), import: self.client.clone(), - }).map_err(|e| e.kind()).map_err(ErrorKind::Executor)?; + }).map_err(|e| e.kind()).map_err(ErrorKind::Executor).map_err(Error::from)?; { let mut live = self.live_agreements.lock(); @@ -455,29 +479,37 @@ mod tests { } } + struct DummyFactory; struct DummyProposer(block::Number); - impl Proposer for DummyProposer { - type CreateProposal = Result; + impl ProposerFactory for DummyFactory { + type Proposer = DummyProposer; + type Error = Error; - fn init(parent_header: &Header, _sign_with: Arc) -> Self { - DummyProposer(parent_header.number + 1) + fn init(&self, parent_header: &Header, _authorities: &[AuthorityId], _sign_with: Arc) -> Result { + Ok(DummyProposer(parent_header.number + 1)) } + } + + impl Proposer for DummyProposer { + type Error = Error; + type Create = Result; + type Evaluate = Result; - fn propose(&self) -> Result { + fn propose(&self) -> Result { Ok(Block { header: Header::from_block_number(self.0), transactions: Default::default() }) } - fn evaluate(&self, proposal: &Block) -> bool { - proposal.header.number == self.0 + fn evaluate(&self, proposal: &Block) -> Result { + Ok(proposal.header.number == self.0) } } fn make_service(client: FakeClient, handle: Handle) - -> BftService + -> BftService { BftService { client: Arc::new(client), @@ -486,7 +518,7 @@ mod tests { timer: Timer::default(), round_timeout_multiplier: 4, key: Arc::new(Keyring::One.into()), - _marker: Default::default(), + factory: DummyFactory } } 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 cbc81694c0999..8e3669ced5d9d 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 ab23e9b4f7a2a..c55856204bcac 100644 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/primitives/src/block.rs b/substrate/primitives/src/block.rs index 36640873ba3bf..f41f0ec8480f2 100644 --- a/substrate/primitives/src/block.rs +++ b/substrate/primitives/src/block.rs @@ -179,7 +179,7 @@ impl Slicable for Header { } /// Block indentification. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "std", derive(Debug))] pub enum Id { /// Identify by block header hash. diff --git a/substrate/runtime-support/src/storage.rs b/substrate/runtime-support/src/storage.rs index 7dbfd8013b35e..0960ecb0ba284 100644 --- a/substrate/runtime-support/src/storage.rs +++ b/substrate/runtime-support/src/storage.rs @@ -66,16 +66,11 @@ pub fn get_or_else T>(key: &[u8], default_va get(key).unwrap_or_else(default_value) } -/// Please `value` in storage under `key`. +/// Put `value` in storage under `key`. pub fn put(key: &[u8], value: &T) { value.using_encoded(|slice| runtime_io::set_storage(&twox_128(key)[..], slice)); } -/// Please `value` in storage under `key`. -pub fn place(key: &[u8], value: T) { - value.using_encoded(|slice| runtime_io::set_storage(&twox_128(key)[..], slice)); -} - /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. pub fn take(key: &[u8]) -> Option { let r = get(key); @@ -199,16 +194,11 @@ pub mod unhashed { get(key).unwrap_or_else(default_value) } - /// Please `value` in storage under `key`. + /// Put `value` in storage under `key`. pub fn put(key: &[u8], value: &T) { value.using_encoded(|slice| runtime_io::set_storage(key, slice)); } - /// Please `value` in storage under `key`. - pub fn place(key: &[u8], value: T) { - value.using_encoded(|slice| runtime_io::set_storage(key, slice)); - } - /// Remove `key` from storage, returning its value if it had an explicit entry or `None` otherwise. pub fn take(key: &[u8]) -> Option { let r = get(key); diff --git a/substrate/state-machine/src/lib.rs b/substrate/state-machine/src/lib.rs index 363f29383039a..3da401d0dd73e 100644 --- a/substrate/state-machine/src/lib.rs +++ b/substrate/state-machine/src/lib.rs @@ -47,7 +47,7 @@ pub use backend::Backend; /// /// A transaction shares all prospective changes within an inner overlay /// that can be cleared. -#[derive(Default)] +#[derive(Debug, Default, Clone)] pub struct OverlayedChanges { prospective: HashMap, Option>>, committed: HashMap, Option>>, 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 5007dd36a299a..e60f92cc39843 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 a57d2f1fd4ead..b03b591cc0e78 100644 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