Block builder (substrate)

This commit is contained in:
Gav
2018-02-12 18:14:13 +01:00
parent f344e15bf8
commit 12c62381c1
4 changed files with 111 additions and 10 deletions
@@ -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 <http://www.gnu.org/licenses/>.
//! 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<B, E> where
B: backend::Backend,
E: CodeExecutor + Clone,
error::Error: From<<<B as backend::Backend>::State as state_machine::backend::Backend>::Error>,
{
header: Header,
transactions: Vec<Transaction>,
executor: E,
state: B::State,
changes: state_machine::OverlayedChanges,
}
impl<B, E> BlockBuilder<B, E> where
B: backend::Backend,
E: CodeExecutor + Clone,
error::Error: From<<<B as backend::Backend>::State as state_machine::backend::Backend>::Error>,
{
/// Create a new instance of builder from the given client.
pub fn new(client: &Client<B, E>) -> error::Result<Self> {
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<Block> {
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,
})
}
}
+14 -3
View File
@@ -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<B, E> 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<B, E> Client<B, E> 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<Vec<AuthorityId>> {
let state = self.state_at(id)?;
@@ -183,10 +191,8 @@ impl<B, E> Client<B, E> where
/// No changes are made.
pub fn call(&self, id: &BlockId, method: &str, call_data: &[u8]) -> error::Result<CallResult> {
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<B, E> Client<B, E> where
Ok(CallResult { return_data, changes })
}
/// Create a new block, built on the head of the chain.
pub fn new_block(&self) -> error::Result<BlockBuilder<B, E>> where E: Clone {
BlockBuilder::new(self)
}
/// Queue a block for import.
pub fn import_block(&self, header: block::Header, body: Option<block::Body>) -> error::Result<ImportResult> {
// TODO: import lock
+5 -6
View File
@@ -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,
}
+3 -1
View File
@@ -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];