diff --git a/substrate/substrate/client/src/block_builder.rs b/substrate/substrate/client/src/block_builder.rs index ebd8ec6c47..8c2d0c0628 100644 --- a/substrate/substrate/client/src/block_builder.rs +++ b/substrate/substrate/client/src/block_builder.rs @@ -42,24 +42,29 @@ impl BlockBuilder where E: CodeExecutor + Clone, error::Error: From<<::State as state_machine::backend::Backend>::Error>, { - /// Create a new instance of builder from the given client. + /// Create a new instance of builder from the given client, building on the latest block. 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); + Self::at_block(&BlockId::Number(best), client) + } + /// Create a new instance of builder from the given client using a particular block's ID to + /// build upon. + pub fn at_block(block_id: &BlockId, client: &Client) -> error::Result { Ok(BlockBuilder { header: Header { - number: best + 1, - parent_hash: client.block_hash(best)?.expect("We already ascertained this is InChain before; qed"), + number: client.block_number_from_id(block_id)?.ok_or(error::ErrorKind::UnknownBlock(*block_id))? + 1, + parent_hash: client.block_hash_from_id(block_id)?.ok_or(error::ErrorKind::UnknownBlock(*block_id))?, 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))?, + state: client.state_at(block_id)?, changes: Default::default(), }) } diff --git a/substrate/substrate/client/src/lib.rs b/substrate/substrate/client/src/lib.rs index 1a7d4ceb62..f347dda3cb 100644 --- a/substrate/substrate/client/src/lib.rs +++ b/substrate/substrate/client/src/lib.rs @@ -43,7 +43,6 @@ 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}; @@ -202,8 +201,13 @@ impl Client where } /// Create a new block, built on the head of the chain. - pub fn new_block(&self) -> error::Result> where E: Clone { - BlockBuilder::new(self) + pub fn new_block(&self) -> error::Result> where E: Clone { + block_builder::BlockBuilder::new(self) + } + + /// Create a new block, built on top of `parent`. + pub fn new_block_at(&self, parent: &BlockId) -> error::Result> where E: Clone { + block_builder::BlockBuilder::at_block(parent, &self) } /// Queue a block for import. @@ -249,6 +253,22 @@ impl Client where self.backend.blockchain().hash(block_number) } + /// Convert an arbitrary block ID into a block hash. + pub fn block_hash_from_id(&self, id: &BlockId) -> error::Result> { + match *id { + BlockId::Hash(h) => Ok(Some(h)), + BlockId::Number(n) => self.block_hash(n), + } + } + + /// Convert an arbitrary block ID into a block hash. + pub fn block_number_from_id(&self, id: &BlockId) -> error::Result> { + match *id { + BlockId::Hash(_) => Ok(self.header(id)?.map(|h| h.number)), + BlockId::Number(n) => Ok(Some(n)), + } + } + /// Get block header by id. pub fn header(&self, id: &BlockId) -> error::Result> { self.backend.blockchain().header(*id)