From 5fe79b6bd3224230ebcfeff472981ce28b00755f Mon Sep 17 00:00:00 2001 From: Gav Date: Fri, 16 Feb 2018 20:16:02 +0100 Subject: [PATCH] Remerge in changes to client. --- substrate/substrate/client/src/client.rs | 120 ++++++++++++++++++++--- 1 file changed, 105 insertions(+), 15 deletions(-) diff --git a/substrate/substrate/client/src/client.rs b/substrate/substrate/client/src/client.rs index 57a607706a..8efc326310 100644 --- a/substrate/substrate/client/src/client.rs +++ b/substrate/substrate/client/src/client.rs @@ -17,6 +17,7 @@ //! Substrate Client use primitives::{block, AuthorityId}; +use primitives::block::Id as BlockId; use primitives::storage::{StorageKey, StorageData}; use runtime_support::Hashable; use codec::{KeyedVec, Slicable}; @@ -81,6 +82,20 @@ pub enum BlockStatus { Unknown, } +/// A header paired with a justification which has already been checked. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct JustifiedHeader { + header: block::Header, + justification: bft::Justification, +} + +impl JustifiedHeader { + /// Deconstruct the justified header into parts. + pub fn into_inner(self) -> (block::Header, bft::Justification) { + (self.header, self.justification) + } +} + /// Create an instance of in-memory client. pub fn new_in_mem( executor: E, @@ -112,7 +127,7 @@ impl Client where let (genesis_header, genesis_store) = build_genesis(); let mut op = backend.begin_operation(BlockId::Hash(block::HeaderHash::default()))?; op.reset_storage(genesis_store.into_iter())?; - op.set_block_data(genesis_header, Some(vec![]), true)?; + op.set_block_data(genesis_header, Some(vec![]), None, true)?; backend.commit_operation(op)?; } Ok(Client { @@ -203,15 +218,36 @@ impl Client where block_builder::BlockBuilder::at_block(parent, &self) } + /// Check a header's justification. + pub fn check_justification( + &self, + header: block::Header, + justification: bft::UncheckedJustification, + ) -> error::Result { + let authorities = self.authorities_at(&BlockId::Hash(header.parent_hash))?; + let just = bft::check_justification(&authorities[..], header.parent_hash, justification) + .map_err(|_| error::ErrorKind::BadJustification(BlockId::Hash(header.hash())))?; + Ok(JustifiedHeader { + header, + justification: just, + }) + } + /// Author a new block, filling it with valid transactions from our transaction pool. pub fn propose_block_at(&self, parent: &BlockId) -> block::Block { unimplemented!() } /// Queue a block for import. - pub fn import_block(&self, header: block::Header, body: Option) -> error::Result { + pub fn import_block( + &self, + header: JustifiedHeader, + body: Option, + ) -> error::Result { // TODO: import lock // TODO: validate block + // TODO: import justification. + let (header, justification) = header.into_inner(); match self.backend.blockchain().status(BlockId::Hash(header.parent_hash))? { blockchain::BlockStatus::InChain => (), blockchain::BlockStatus::Unknown => return Ok(ImportResult::UnknownParent), @@ -230,7 +266,7 @@ impl Client where let is_new_best = header.number == self.backend.blockchain().info()?.best_number + 1; trace!("Imported {}, (#{}), best={}", block::HeaderHash::from(header.blake2_256()), header.number, is_new_best); - transaction.set_block_data(header, body, is_new_best)?; + transaction.set_block_data(header, body, Some(justification.uncheck().into()), is_new_best)?; transaction.set_storage(overlay.drain())?; self.backend.commit_operation(transaction)?; Ok(ImportResult::Queued) @@ -285,6 +321,38 @@ impl Client where pub fn body(&self, id: &BlockId) -> error::Result> { self.backend.blockchain().body(*id) } + + /// Get block justification set by id. + pub fn justification(&self, id: &BlockId) -> error::Result> { + self.backend.blockchain().justification(*id) + } +} + +impl bft::BlockImport for Client + where + B: backend::Backend, + E: state_machine::CodeExecutor, + error::Error: From<::Error> +{ + fn import_block(&self, block: block::Block, justification: bft::Justification) { + let justified_header = JustifiedHeader { + header: block.header, + justification, + }; + + let _ = self.import_block(justified_header, Some(block.transactions)); + } +} + +impl bft::Authorities for Client + where + B: backend::Backend, + E: state_machine::CodeExecutor, + error::Error: From<::Error> +{ + fn authorities(&self, at: &BlockId) -> Result, bft::Error> { + self.authorities_at(at).map_err(|_| bft::ErrorKind::StateUnavailable(*at).into()) + } } #[cfg(test)] @@ -315,6 +383,31 @@ mod tests { (primitives::block::Header::decode(&mut block.header.encode().as_ref()).expect("to_vec() always gives a valid serialisation; qed"), storage.into_iter().collect()) } + // since we are in the client module we can create falsely justified + // headers. + // TODO: remove this in favor of custom verification pipelines for the + // client + fn justify(header: &block::Header) -> bft::UncheckedJustification { + let hash = header.hash(); + let authorities = vec![ + Keyring::Alice.into(), + Keyring::Bob.into(), + Keyring::Charlie.into(), + ]; + + bft::UncheckedJustification { + digest: hash, + signatures: authorities.iter().map(|key| { + bft::sign_message( + bft::generic::Message::Commit(1, hash), + key, + header.parent_hash + ).signature + }).collect(), + round_number: 1, + } + } + #[test] fn client_initialises_from_genesis_ok() { let client = new_in_mem(Executor::new(), prepare_genesis).unwrap(); @@ -327,11 +420,7 @@ mod tests { #[test] fn authorities_call_works() { - let genesis_config = GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ], 1000); + let genesis_config = genesis_config(); let prepare_genesis = || { let mut storage = genesis_config.genesis_map(); @@ -351,11 +440,7 @@ mod tests { #[test] fn block_builder_works_with_no_transactions() { - let genesis_config = GenesisConfig::new_simple(vec![ - Keyring::Alice.to_raw_public(), - Keyring::Bob.to_raw_public(), - Keyring::Charlie.to_raw_public() - ], 1000); + let genesis_config = genesis_config(); let prepare_genesis = || { let mut storage = genesis_config.genesis_map(); @@ -368,7 +453,9 @@ mod tests { let builder = client.new_block().unwrap(); let block = builder.bake().unwrap(); - client.import_block(block.header, Some(block.transactions)).unwrap(); + let justification = justify(&block.header); + let justified = client.check_justification(block.header, justification).unwrap(); + client.import_block(justified, Some(block.transactions)).unwrap(); assert_eq!(client.info().unwrap().chain.best_number, 1); assert_eq!(client.using_environment(|| test_runtime::system::latest_block_hash()).unwrap(), client.block_hash(1).unwrap().unwrap()); @@ -409,7 +496,10 @@ mod tests { nonce: 0 }.signed()).unwrap(); let block = builder.bake().unwrap(); - client.import_block(block.header, Some(block.transactions)).unwrap(); + + let justification = justify(&block.header); + let justified = client.check_justification(block.header, justification).unwrap(); + client.import_block(justified, Some(block.transactions)).unwrap(); assert_eq!(client.info().unwrap().chain.best_number, 1); assert!(client.state_at(&BlockId::Number(1)).unwrap() != client.state_at(&BlockId::Number(0)).unwrap());