diff --git a/substrate/substrate/client/src/block_builder.rs b/substrate/substrate/client/src/block_builder.rs new file mode 100644 index 0000000000..ebd8ec6c47 --- /dev/null +++ b/substrate/substrate/client/src/block_builder.rs @@ -0,0 +1,89 @@ +// Copyright 2017 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Utility struct to build a block. + +use std::vec::Vec; +use codec::{Joiner, Slicable}; +use state_machine::{self, CodeExecutor}; +use primitives::{Header, Block}; +use primitives::block::Transaction; +use {backend, error, BlockId, BlockStatus, Client}; +use triehash::ordered_trie_root; + +/// Utility for building new (valid) blocks from a stream of transactions. +pub struct BlockBuilder where + B: backend::Backend, + E: CodeExecutor + Clone, + error::Error: From<<::State as state_machine::backend::Backend>::Error>, +{ + header: Header, + transactions: Vec, + executor: E, + state: B::State, + changes: state_machine::OverlayedChanges, +} + +impl BlockBuilder where + B: backend::Backend, + E: CodeExecutor + Clone, + error::Error: From<<::State as state_machine::backend::Backend>::Error>, +{ + /// Create a new instance of builder from the given client. + pub fn new(client: &Client) -> error::Result { + let best = (client.info().map(|i| i.chain.best_number)?..1) + .find(|&n| if let Ok(BlockStatus::InChain) = client.block_status(&BlockId::Number(n)) + { true } else { false }) + .unwrap_or(0); + + Ok(BlockBuilder { + header: Header { + number: best + 1, + parent_hash: client.block_hash(best)?.expect("We already ascertained this is InChain before; qed"), + state_root: Default::default(), + transaction_root: Default::default(), + digest: Default::default(), + }, + transactions: Default::default(), + executor: client.clone_executor(), + state: client.state_at(&BlockId::Number(best))?, + changes: Default::default(), + }) + } + + /// Push a transaction onto the block's list of transactions. This will ensure the transaction + /// can be validly executed (by executing it); if it is invalid, it'll be returned along with + /// the error. Otherwise, it will return a mutable reference to self (in order to chain). + pub fn push(&mut self, tx: Transaction) -> error::Result<()> { + let output = state_machine::execute(&self.state, &mut self.changes, &self.executor, "execute_transaction", + &vec![].and(&self.header).and(&tx))?; + self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime do must be valid"); + self.transactions.push(tx); + Ok(()) + } + + /// Consume the builder to return a valid `Block` containing all pushed transactions. + pub fn bake(mut self) -> error::Result { + self.header.transaction_root = ordered_trie_root(self.transactions.iter().map(Slicable::encode)).0.into(); + let output = state_machine::execute(&self.state, &mut self.changes, &self.executor, "finalise_block", + &self.header.encode())?; + self.header = Header::decode(&mut &output[..]).expect("Header came straight out of runtime do must be valid"); + Ok(Block { + header: self.header, + transactions: self.transactions, + }) + } +} diff --git a/substrate/substrate/client/src/lib.rs b/substrate/substrate/client/src/lib.rs index 530288ca29..1a7d4ceb62 100644 --- a/substrate/substrate/client/src/lib.rs +++ b/substrate/substrate/client/src/lib.rs @@ -39,9 +39,11 @@ pub mod blockchain; pub mod backend; pub mod in_mem; pub mod genesis; +pub mod block_builder; pub use blockchain::Info as ChainInfo; pub use blockchain::BlockId; +pub use block_builder::BlockBuilder; use primitives::{block, AuthorityId}; use primitives::storage::{StorageKey, StorageData}; @@ -59,6 +61,7 @@ pub struct Client where B: backend::Backend { } /// Client info +// TODO: split queue info from chain info and amalgamate into single struct. #[derive(Debug)] pub struct ClientInfo { /// Best block hash. @@ -167,6 +170,11 @@ impl Client where self.storage(id, &StorageKey(b":code".to_vec())).map(|data| data.0) } + /// Clone a new instance of Executor. + pub fn clone_executor(&self) -> E where E: Clone { + self.executor.clone() + } + /// Get the current set of authorities from storage. pub fn authorities_at(&self, id: &BlockId) -> error::Result> { let state = self.state_at(id)?; @@ -183,10 +191,8 @@ impl Client where /// No changes are made. pub fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result { let mut changes = state_machine::OverlayedChanges::default(); - let state = self.state_at(id)?; - let return_data = state_machine::execute( - &state, + &self.state_at(id)?, &mut changes, &self.executor, method, @@ -195,6 +201,11 @@ impl Client where Ok(CallResult { return_data, changes }) } + /// Create a new block, built on the head of the chain. + pub fn new_block(&self) -> error::Result> where E: Clone { + BlockBuilder::new(self) + } + /// Queue a block for import. pub fn import_block(&self, header: block::Header, body: Option) -> error::Result { // TODO: import lock diff --git a/substrate/substrate/primitives/src/block.rs b/substrate/substrate/primitives/src/block.rs index ba34dd085f..90882f7fc3 100644 --- a/substrate/substrate/primitives/src/block.rs +++ b/substrate/substrate/primitives/src/block.rs @@ -17,19 +17,18 @@ //! Block and header type definitions. #[cfg(feature = "std")] -use bytes; use rstd::vec::Vec; +use {bytes, Hash}; use codec::{Input, Slicable}; -use hash::H256; /// Used to refer to a block number. pub type Number = u64; /// Hash used to refer to a block hash. -pub type HeaderHash = H256; +pub type HeaderHash = Hash; /// Hash used to refer to a transaction hash. -pub type TransactionHash = H256; +pub type TransactionHash = Hash; /// Simple generic transaction type. #[derive(PartialEq, Eq, Clone)] @@ -127,9 +126,9 @@ pub struct Header { /// Block number. pub number: Number, /// State root after this transition. - pub state_root: H256, + pub state_root: Hash, /// The root of the trie that represents this block's transactions, indexed by a 32-byte integer. - pub transaction_root: H256, + pub transaction_root: Hash, /// The digest of activity on the block. pub digest: Digest, } diff --git a/substrate/substrate/primitives/src/lib.rs b/substrate/substrate/primitives/src/lib.rs index 1c282fe42e..211bfce8f5 100644 --- a/substrate/substrate/primitives/src/lib.rs +++ b/substrate/substrate/primitives/src/lib.rs @@ -90,8 +90,10 @@ mod tests; pub use self::hash::{H160, H256}; pub use self::uint::{U256, U512}; - pub use block::{Block, Header}; +/// General hash type. +pub type Hash = H256; + /// An identifier for an authority in the consensus algorithm. The same as ed25519::Public. pub type AuthorityId = [u8; 32];