diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 9b7c07e28d76712cd3091e4888304c4613f2bfab..91c602167c5a313d7519b2305c14f1594d227a53 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -4698,6 +4698,7 @@ dependencies = [ "sr-primitives 2.0.0", "structopt 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", "substrate-client 2.0.0", + "substrate-header-metadata 2.0.0", "substrate-keyring 2.0.0", "substrate-network 2.0.0", "substrate-panic-handler 2.0.0", @@ -4732,6 +4733,7 @@ dependencies = [ "sr-version 2.0.0", "substrate-consensus-common 2.0.0", "substrate-executor 2.0.0", + "substrate-header-metadata 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", "substrate-panic-handler 2.0.0", @@ -4760,6 +4762,7 @@ dependencies = [ "substrate-client 2.0.0", "substrate-consensus-common 2.0.0", "substrate-executor 2.0.0", + "substrate-header-metadata 2.0.0", "substrate-keyring 2.0.0", "substrate-primitives 2.0.0", "substrate-state-db 2.0.0", @@ -4845,6 +4848,7 @@ dependencies = [ "substrate-consensus-slots 2.0.0", "substrate-consensus-uncles 2.0.0", "substrate-executor 2.0.0", + "substrate-header-metadata 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", "substrate-keystore 2.0.0", @@ -5019,6 +5023,7 @@ dependencies = [ "substrate-consensus-babe-primitives 2.0.0", "substrate-consensus-common 2.0.0", "substrate-finality-grandpa-primitives 2.0.0", + "substrate-header-metadata 2.0.0", "substrate-inherents 2.0.0", "substrate-keyring 2.0.0", "substrate-keystore 2.0.0", @@ -5044,6 +5049,15 @@ dependencies = [ "substrate-client 2.0.0", ] +[[package]] +name = "substrate-header-metadata" +version = "2.0.0" +dependencies = [ + "lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-primitives 2.0.0", +] + [[package]] name = "substrate-inherents" version = "2.0.0" @@ -5114,6 +5128,7 @@ dependencies = [ "substrate-client 2.0.0", "substrate-consensus-babe-primitives 2.0.0", "substrate-consensus-common 2.0.0", + "substrate-header-metadata 2.0.0", "substrate-keyring 2.0.0", "substrate-peerset 2.0.0", "substrate-primitives 2.0.0", diff --git a/substrate/Cargo.toml b/substrate/Cargo.toml index 75f86e7761776dfa4b892735b0e72c78a97a104f..c1bac20a7c4a13964ef8786a7a39b13d8e9c4984 100644 --- a/substrate/Cargo.toml +++ b/substrate/Cargo.toml @@ -26,6 +26,7 @@ members = [ "core/cli", "core/client", "core/client/db", + "core/client/header-metadata", "core/consensus/aura", "core/consensus/babe", "core/consensus/common", diff --git a/substrate/core/cli/Cargo.toml b/substrate/core/cli/Cargo.toml index b33c48fbd1e61446e0a98779cc3d179f02e886d1..ba20d1169935ad2e82e934ac9cbd3537535b9a91 100644 --- a/substrate/core/cli/Cargo.toml +++ b/substrate/core/cli/Cargo.toml @@ -24,6 +24,7 @@ exit-future = "0.1" serde_json = "1.0" panic-handler = { package = "substrate-panic-handler", path = "../../core/panic-handler" } client = { package = "substrate-client", path = "../../core/client" } +header-metadata = { package = "substrate-header-metadata", path = "../../core/client/header-metadata" } network = { package = "substrate-network", path = "../../core/network" } sr-primitives = { path = "../../core/sr-primitives" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } diff --git a/substrate/core/cli/src/informant.rs b/substrate/core/cli/src/informant.rs index a5fe52c09af4046b398bfd841bb6fbf9362e9233..9d7f04b8f6d4f5162204107d5d4ee96f0e2971d8 100644 --- a/substrate/core/cli/src/informant.rs +++ b/substrate/core/cli/src/informant.rs @@ -20,7 +20,7 @@ use client::BlockchainEvents; use futures::{Future, Stream}; use futures03::{StreamExt as _, TryStreamExt as _}; use log::{info, warn}; -use sr_primitives::{generic::BlockId, traits::Header}; +use sr_primitives::traits::Header; use service::AbstractService; mod display; @@ -47,19 +47,18 @@ pub fn build(service: &impl AbstractService) -> impl Future<Item = (), Error = ( // detect and log reorganizations. if let Some((ref last_num, ref last_hash)) = last_best { if n.header.parent_hash() != last_hash && n.is_new_best { - let tree_route = ::client::blockchain::tree_route( - |id| client.header(&id)?.ok_or_else( - || client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(last_hash.clone()), - BlockId::Hash(n.hash), + let maybe_ancestor = header_metadata::lowest_common_ancestor( + &*client, + last_hash.clone(), + n.hash, ); - match tree_route { - Ok(ref t) if !t.retracted().is_empty() => info!( + match maybe_ancestor { + Ok(ref ancestor) if ancestor.hash != *last_hash => info!( "Reorg from #{},{} to #{},{}, common ancestor #{},{}", last_num, last_hash, n.header.number(), n.hash, - t.common_block().number, t.common_block().hash, + ancestor.number, ancestor.hash, ), Ok(_) => {}, Err(e) => warn!("Error computing tree route: {}", e), diff --git a/substrate/core/client/Cargo.toml b/substrate/core/client/Cargo.toml index 7fcd291f505a167c843af53e6d4260aac01ee71d..58de8233bbf43a830236c56cda4e7a5522bb8c2b 100644 --- a/substrate/core/client/Cargo.toml +++ b/substrate/core/client/Cargo.toml @@ -27,6 +27,7 @@ runtime-version = { package = "sr-version", path = "../sr-version", default-feat rstd = { package = "sr-std", path = "../sr-std", default-features = false } inherents = { package = "substrate-inherents", path = "../inherents", default-features = false } sr-api-macros = { path = "../sr-api-macros" } +header-metadata = { package = "substrate-header-metadata", path = "header-metadata", optional = true } [dev-dependencies] env_logger = "0.6" @@ -45,6 +46,7 @@ std = [ "sr-primitives/std", "runtime-version/std", "hash-db/std", + "header-metadata", "consensus", "parking_lot", "derive_more", diff --git a/substrate/core/client/db/Cargo.toml b/substrate/core/client/db/Cargo.toml index 89a1dcc0cbc8ae43da1ef07654410c77676f7790..594bda744c83c94d073c514ed0b417f76e7be897 100644 --- a/substrate/core/client/db/Cargo.toml +++ b/substrate/core/client/db/Cargo.toml @@ -22,6 +22,7 @@ executor = { package = "substrate-executor", path = "../../executor" } state_db = { package = "substrate-state-db", path = "../../state-db" } trie = { package = "substrate-trie", path = "../../trie" } consensus_common = { package = "substrate-consensus-common", path = "../../consensus/common" } +header_metadata = { package = "substrate-header-metadata", path = "../header-metadata" } [dev-dependencies] substrate-keyring = { path = "../../keyring" } diff --git a/substrate/core/client/db/src/lib.rs b/substrate/core/client/db/src/lib.rs index 728858e2d68e2b5fd59fe52a3d7695989789b268..126622364a76a25a2bcb4f9d1ddbab5d40a0e615 100644 --- a/substrate/core/client/db/src/lib.rs +++ b/substrate/core/client/db/src/lib.rs @@ -42,6 +42,7 @@ use client::backend::NewBlockState; use client::blockchain::{well_known_cache_keys, HeaderBackend}; use client::{ForkBlocks, ExecutionStrategies}; use client::backend::{StorageCollection, ChildStorageCollection}; +use client::error::Result as ClientResult; use codec::{Decode, Encode}; use hash_db::{Hasher, Prefix}; use kvdb::{KeyValueDB, DBTransaction}; @@ -61,10 +62,11 @@ use state_machine::{ DBValue, ChangesTrieTransaction, ChangesTrieCacheAction, ChangesTrieBuildCache, backend::Backend as StateBackend, }; -use crate::utils::{Meta, db_err, meta_keys, read_db, block_id_to_lookup_key, read_meta}; +use crate::utils::{Meta, db_err, meta_keys, read_db, read_meta}; use client::leaves::{LeafSet, FinalizationDisplaced}; use client::children; use state_db::StateDb; +use header_metadata::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache}; use crate::storage_cache::{CachingState, SharedCache, new_shared_cache}; use log::{trace, debug, warn}; pub use state_db::PruningMode; @@ -271,16 +273,18 @@ pub struct BlockchainDb<Block: BlockT> { db: Arc<dyn KeyValueDB>, meta: Arc<RwLock<Meta<NumberFor<Block>, Block::Hash>>>, leaves: RwLock<LeafSet<Block::Hash, NumberFor<Block>>>, + header_metadata_cache: HeaderMetadataCache<Block>, } impl<Block: BlockT> BlockchainDb<Block> { - fn new(db: Arc<dyn KeyValueDB>) -> Result<Self, client::error::Error> { + fn new(db: Arc<dyn KeyValueDB>) -> ClientResult<Self> { let meta = read_meta::<Block>(&*db, columns::META, columns::HEADER)?; let leaves = LeafSet::read_from_db(&*db, columns::META, meta_keys::LEAF_PREFIX)?; Ok(BlockchainDb { db, leaves: RwLock::new(leaves), meta: Arc::new(RwLock::new(meta)), + header_metadata_cache: HeaderMetadataCache::default(), }) } @@ -310,7 +314,7 @@ impl<Block: BlockT> BlockchainDb<Block> { } impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Block> { - fn header(&self, id: BlockId<Block>) -> Result<Option<Block::Header>, client::error::Error> { + fn header(&self, id: BlockId<Block>) -> ClientResult<Option<Block::Header>> { utils::read_header(&*self.db, columns::KEY_LOOKUP, columns::HEADER, id) } @@ -325,7 +329,7 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl } } - fn status(&self, id: BlockId<Block>) -> Result<client::blockchain::BlockStatus, client::error::Error> { + fn status(&self, id: BlockId<Block>) -> ClientResult<client::blockchain::BlockStatus> { let exists = match id { BlockId::Hash(_) => read_db( &*self.db, @@ -341,16 +345,11 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl } } - fn number(&self, hash: Block::Hash) -> Result<Option<NumberFor<Block>>, client::error::Error> { - if let Some(lookup_key) = block_id_to_lookup_key::<Block>(&*self.db, columns::KEY_LOOKUP, BlockId::Hash(hash))? { - let number = utils::lookup_key_to_number(&lookup_key)?; - Ok(Some(number)) - } else { - Ok(None) - } + fn number(&self, hash: Block::Hash) -> ClientResult<Option<NumberFor<Block>>> { + Ok(self.header_metadata(hash).ok().map(|header_metadata| header_metadata.number)) } - fn hash(&self, number: NumberFor<Block>) -> Result<Option<Block::Hash>, client::error::Error> { + fn hash(&self, number: NumberFor<Block>) -> ClientResult<Option<Block::Hash>> { self.header(BlockId::Number(number)).and_then(|maybe_header| match maybe_header { Some(header) => Ok(Some(header.hash().clone())), None => Ok(None), @@ -359,7 +358,7 @@ impl<Block: BlockT> client::blockchain::HeaderBackend<Block> for BlockchainDb<Bl } impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> { - fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<Block::Extrinsic>>, client::error::Error> { + fn body(&self, id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> { match read_db(&*self.db, columns::KEY_LOOKUP, columns::BODY, id)? { Some(body) => match Decode::decode(&mut &body[..]) { Ok(body) => Ok(Some(body)), @@ -371,7 +370,7 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> { } } - fn justification(&self, id: BlockId<Block>) -> Result<Option<Justification>, client::error::Error> { + fn justification(&self, id: BlockId<Block>) -> ClientResult<Option<Justification>> { match read_db(&*self.db, columns::KEY_LOOKUP, columns::JUSTIFICATION, id)? { Some(justification) => match Decode::decode(&mut &justification[..]) { Ok(justification) => Ok(Some(justification)), @@ -383,7 +382,7 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> { } } - fn last_finalized(&self) -> Result<Block::Hash, client::error::Error> { + fn last_finalized(&self) -> ClientResult<Block::Hash> { Ok(self.meta.read().finalized_hash.clone()) } @@ -391,11 +390,11 @@ impl<Block: BlockT> client::blockchain::Backend<Block> for BlockchainDb<Block> { None } - fn leaves(&self) -> Result<Vec<Block::Hash>, client::error::Error> { + fn leaves(&self) -> ClientResult<Vec<Block::Hash>> { Ok(self.leaves.read().hashes()) } - fn children(&self, parent_hash: Block::Hash) -> Result<Vec<Block::Hash>, client::error::Error> { + fn children(&self, parent_hash: Block::Hash) -> ClientResult<Vec<Block::Hash>> { children::read_children(&*self.db, columns::META, meta_keys::CHILDREN_PREFIX, parent_hash) } } @@ -406,6 +405,31 @@ impl<Block: BlockT> client::blockchain::ProvideCache<Block> for BlockchainDb<Blo } } +impl<Block: BlockT> HeaderMetadata<Block> for BlockchainDb<Block> { + type Error = client::error::Error; + + fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> { + self.header_metadata_cache.header_metadata(hash).or_else(|_| { + self.header(BlockId::hash(hash))?.map(|header| { + let header_metadata = CachedHeaderMetadata::from(&header); + self.header_metadata_cache.insert_header_metadata( + header_metadata.hash, + header_metadata.clone(), + ); + header_metadata + }).ok_or(client::error::Error::UnknownBlock("header not found in db".to_owned())) + }) + } + + fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) { + self.header_metadata_cache.insert_header_metadata(hash, metadata) + } + + fn remove_header_metadata(&self, hash: Block::Hash) { + self.header_metadata_cache.remove_header_metadata(hash); + } +} + /// Database transaction pub struct BlockImportOperation<Block: BlockT, H: Hasher> { old_state: CachingState<Blake2Hasher, RefTrackingState<Block>, Block>, @@ -437,7 +461,7 @@ where Block: BlockT<Hash=H256>, { type State = CachingState<Blake2Hasher, RefTrackingState<Block>, Block>; - fn state(&self) -> Result<Option<&Self::State>, client::error::Error> { + fn state(&self) -> ClientResult<Option<&Self::State>> { Ok(Some(&self.old_state)) } @@ -447,7 +471,7 @@ where Block: BlockT<Hash=H256>, body: Option<Vec<Block::Extrinsic>>, justification: Option<Justification>, leaf_state: NewBlockState, - ) -> Result<(), client::error::Error> { + ) -> ClientResult<()> { assert!(self.pending_block.is_none(), "Only one block per operation is allowed"); self.pending_block = Some(PendingBlock { header, @@ -462,7 +486,7 @@ where Block: BlockT<Hash=H256>, // Currently cache isn't implemented on full nodes. } - fn update_db_storage(&mut self, update: PrefixedMemoryDB<Blake2Hasher>) -> Result<(), client::error::Error> { + fn update_db_storage(&mut self, update: PrefixedMemoryDB<Blake2Hasher>) -> ClientResult<()> { self.db_updates = update; Ok(()) } @@ -471,7 +495,7 @@ where Block: BlockT<Hash=H256>, &mut self, top: StorageOverlay, children: ChildrenStorageOverlay - ) -> Result<H256, client::error::Error> { + ) -> ClientResult<H256> { if top.iter().any(|(k, _)| well_known_keys::is_child_storage_key(k)) { return Err(client::error::Error::GenesisInvalid.into()); @@ -499,13 +523,13 @@ where Block: BlockT<Hash=H256>, fn update_changes_trie( &mut self, update: ChangesTrieTransaction<Blake2Hasher, NumberFor<Block>>, - ) -> Result<(), client::error::Error> { + ) -> ClientResult<()> { self.changes_trie_updates = update.0; self.changes_trie_cache_update = Some(update.1); Ok(()) } - fn insert_aux<I>(&mut self, ops: I) -> Result<(), client::error::Error> + fn insert_aux<I>(&mut self, ops: I) -> ClientResult<()> where I: IntoIterator<Item=(Vec<u8>, Option<Vec<u8>>)> { self.aux_ops.append(&mut ops.into_iter().collect()); @@ -516,18 +540,18 @@ where Block: BlockT<Hash=H256>, &mut self, update: StorageCollection, child_update: ChildStorageCollection, - ) -> Result<(), client::error::Error> { + ) -> ClientResult<()> { self.storage_updates = update; self.child_storage_updates = child_update; Ok(()) } - fn mark_finalized(&mut self, block: BlockId<Block>, justification: Option<Justification>) -> Result<(), client::error::Error> { + fn mark_finalized(&mut self, block: BlockId<Block>, justification: Option<Justification>) -> ClientResult<()> { self.finalized_blocks.push((block, justification)); Ok(()) } - fn mark_head(&mut self, block: BlockId<Block>) -> Result<(), client::error::Error> { + fn mark_head(&mut self, block: BlockId<Block>) -> ClientResult<()> { assert!(self.set_head.is_none(), "Only one set head per operation is allowed"); self.set_head = Some(block); Ok(()) @@ -751,11 +775,11 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { /// Create a new instance of database backend. /// /// The pruning window is how old a block must be before the state is pruned. - pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> client::error::Result<Self> { + pub fn new(config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult<Self> { Self::new_inner(config, canonicalization_delay) } - fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> Result<Self, client::error::Error> { + fn new_inner(config: DatabaseSettings, canonicalization_delay: u64) -> ClientResult<Self> { #[cfg(feature = "kvdb-rocksdb")] let db = crate::utils::open_database(&config, columns::META, "full")?; #[cfg(not(feature = "kvdb-rocksdb"))] @@ -795,7 +819,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { db: Arc<dyn KeyValueDB>, canonicalization_delay: u64, config: &DatabaseSettings - ) -> Result<Self, client::error::Error> { + ) -> ClientResult<Self> { let is_archive_pruning = config.pruning.is_archive(); let blockchain = BlockchainDb::new(db.clone())?; let meta = blockchain.meta.clone(); @@ -881,7 +905,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { /// /// Currently changes tries configuration is set up once (at genesis) and could not /// be changed. Thus, we'll actually read value once and then just use cached value. - fn changes_trie_config(&self, block: Block::Hash) -> Result<Option<ChangesTrieConfiguration>, client::error::Error> { + fn changes_trie_config(&self, block: Block::Hash) -> ClientResult<Option<ChangesTrieConfiguration>> { let mut cached_changes_trie_config = self.changes_trie_config.lock(); match cached_changes_trie_config.clone() { Some(cached_changes_trie_config) => Ok(cached_changes_trie_config), @@ -904,7 +928,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { /// In the case where the new best block is a block to be imported, `route_to` /// should be the parent of `best_to`. In the case where we set an existing block /// to be best, `route_to` should equal to `best_to`. - fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor<Block>, Block::Hash)) -> Result<(Vec<Block::Hash>, Vec<Block::Hash>), client::error::Error> { + fn set_head_with_transaction( + &self, + transaction: &mut DBTransaction, + route_to: Block::Hash, + best_to: (NumberFor<Block>, Block::Hash), + ) -> ClientResult<(Vec<Block::Hash>, Vec<Block::Hash>)> { let mut enacted = Vec::default(); let mut retracted = Vec::default(); @@ -912,12 +941,10 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { // cannot find tree route with empty DB. if meta.best_hash != Default::default() { - let tree_route = ::client::blockchain::tree_route( - |id| self.blockchain.header(id)?.ok_or_else( - || client::error::Error::UnknownBlock(format!("{:?}", id)) - ), - BlockId::Hash(meta.best_hash), - BlockId::Hash(route_to), + let tree_route = header_metadata::tree_route( + &self.blockchain, + meta.best_hash, + route_to, )?; // uncanonicalize: check safety violations and ensure the numbers no longer @@ -968,7 +995,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { &self, header: &Block::Header, last_finalized: Option<Block::Hash>, - ) -> Result<(), client::error::Error> { + ) -> ClientResult<()> { let last_finalized = last_finalized.unwrap_or_else(|| self.blockchain.meta.read().finalized_hash); if *header.parent_hash() != last_finalized { return Err(::client::error::Error::NonSequentialFinalization( @@ -986,7 +1013,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { last_finalized: Option<Block::Hash>, justification: Option<Justification>, finalization_displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>>, - ) -> Result<(Block::Hash, <Block::Header as HeaderT>::Number, bool, bool), client::error::Error> { + ) -> ClientResult<(Block::Hash, <Block::Header as HeaderT>::Number, bool, bool)> { // TODO: ensure best chain contains this block. let number = *header.number(); self.ensure_sequential_finalization(header, last_finalized)?; @@ -1014,7 +1041,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { hash: Block::Hash, number: NumberFor<Block>, ) - -> Result<(), client::error::Error> + -> ClientResult<()> { let number_u64 = number.saturated_into::<u64>(); if number_u64 > self.canonicalization_delay { @@ -1042,7 +1069,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { } fn try_commit_operation(&self, mut operation: BlockImportOperation<Block, Blake2Hasher>) - -> Result<(), client::error::Error> + -> ClientResult<()> { let mut transaction = DBTransaction::new(); let mut finalization_displaced_leaves = None; @@ -1088,6 +1115,12 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { hash, )?; + let header_metadata = CachedHeaderMetadata::from(&pending_block.header); + self.blockchain.insert_header_metadata( + header_metadata.hash, + header_metadata, + ); + transaction.put(columns::HEADER, &lookup_key, &pending_block.header.encode()); if let Some(body) = pending_block.body { transaction.put(columns::BODY, &lookup_key, &body.encode()); @@ -1231,7 +1264,7 @@ impl<Block: BlockT<Hash=H256>> Backend<Block> { f_header: &Block::Header, f_hash: Block::Hash, displaced: &mut Option<FinalizationDisplaced<Block::Hash, NumberFor<Block>>> - ) -> Result<(), client::error::Error> where + ) -> ClientResult<()> where Block: BlockT<Hash=H256>, { let f_num = f_header.number().clone(); @@ -1284,7 +1317,7 @@ impl<Block> client::backend::AuxStore for Backend<Block> where Block: BlockT<Has 'c: 'a, I: IntoIterator<Item=&'a(&'c [u8], &'c [u8])>, D: IntoIterator<Item=&'a &'b [u8]>, - >(&self, insert: I, delete: D) -> client::error::Result<()> { + >(&self, insert: I, delete: D) -> ClientResult<()> { let mut transaction = DBTransaction::new(); for (k, v) in insert { transaction.put(columns::AUX, k, v); @@ -1296,7 +1329,7 @@ impl<Block> client::backend::AuxStore for Backend<Block> where Block: BlockT<Has Ok(()) } - fn get_aux(&self, key: &[u8]) -> Result<Option<Vec<u8>>, client::error::Error> { + fn get_aux(&self, key: &[u8]) -> ClientResult<Option<Vec<u8>>> { Ok(self.storage.db.get(columns::AUX, key).map(|r| r.map(|v| v.to_vec())).map_err(db_err)?) } } @@ -1308,7 +1341,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe type ChangesTrieStorage = DbChangesTrieStorage<Block>; type OffchainStorage = offchain::LocalStorage; - fn begin_operation(&self) -> Result<Self::BlockImportOperation, client::error::Error> { + fn begin_operation(&self) -> ClientResult<Self::BlockImportOperation> { let old_state = self.state_at(BlockId::Hash(Default::default()))?; Ok(BlockImportOperation { pending_block: None, @@ -1324,13 +1357,17 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe }) } - fn begin_state_operation(&self, operation: &mut Self::BlockImportOperation, block: BlockId<Block>) -> Result<(), client::error::Error> { + fn begin_state_operation( + &self, + operation: &mut Self::BlockImportOperation, + block: BlockId<Block>, + ) -> ClientResult<()> { operation.old_state = self.state_at(block)?; Ok(()) } fn commit_operation(&self, operation: Self::BlockImportOperation) - -> Result<(), client::error::Error> + -> ClientResult<()> { match self.try_commit_operation(operation) { Ok(_) => { @@ -1345,7 +1382,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe } fn finalize_block(&self, block: BlockId<Block>, justification: Option<Justification>) - -> Result<(), client::error::Error> + -> ClientResult<()> { let mut transaction = DBTransaction::new(); let hash = self.blockchain.expect_block_hash_from_id(&block)?; @@ -1385,7 +1422,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe Some(self.offchain_storage.clone()) } - fn revert(&self, n: NumberFor<Block>) -> Result<NumberFor<Block>, client::error::Error> { + fn revert(&self, n: NumberFor<Block>) -> ClientResult<NumberFor<Block>> { let mut best = self.blockchain.info().best_number; let finalized = self.blockchain.info().finalized_number; let revertible = best - finalized; @@ -1430,7 +1467,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe Some(used) } - fn state_at(&self, block: BlockId<Block>) -> Result<Self::State, client::error::Error> { + fn state_at(&self, block: BlockId<Block>) -> ClientResult<Self::State> { use client::blockchain::HeaderBackend as BcHeaderBackend; // special case for genesis initialization @@ -1466,7 +1503,7 @@ impl<Block> client::backend::Backend<Block, Blake2Hasher> for Backend<Block> whe !self.storage.state_db.is_pruned(hash, number.saturated_into::<u64>()) } - fn destroy_state(&self, state: Self::State) -> Result<(), client::error::Error> { + fn destroy_state(&self, state: Self::State) -> ClientResult<()> { if let Some(hash) = state.cache.parent_hash.clone() { let is_best = || self.blockchain.meta.read().best_hash == hash; state.release().sync_cache(&[], &[], vec![], vec![], None, None, is_best); @@ -1493,6 +1530,8 @@ mod tests { use sr_primitives::testing::{Header, Block as RawBlock, ExtrinsicWrapper}; use sr_primitives::traits::{Hash, BlakeTwo256}; use state_machine::{TrieMut, TrieDBMut, ChangesTrieRootsStorage, ChangesTrieStorage}; + use header_metadata::{lowest_common_ancestor, tree_route}; + use test_client; type Block = RawBlock<ExtrinsicWrapper<u64>>; @@ -1521,7 +1560,6 @@ mod tests { extrinsics_root: H256, ) -> H256 { use sr_primitives::testing::Digest; - let (changes_root, changes_trie_update) = prepare_changes(changes); let digest = Digest { logs: vec![ @@ -2077,11 +2115,7 @@ mod tests { let b2 = insert_header(&backend, 2, b1, Vec::new(), Default::default()); { - let tree_route = ::client::blockchain::tree_route( - |id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(a3), - BlockId::Hash(b2) - ).unwrap(); + let tree_route = tree_route(blockchain, a3, b2).unwrap(); assert_eq!(tree_route.common_block().hash, block0); assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2, a1]); @@ -2089,11 +2123,7 @@ mod tests { } { - let tree_route = ::client::blockchain::tree_route( - |id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(a1), - BlockId::Hash(a3), - ).unwrap(); + let tree_route = tree_route(blockchain, a1, a3).unwrap(); assert_eq!(tree_route.common_block().hash, a1); assert!(tree_route.retracted().is_empty()); @@ -2101,11 +2131,7 @@ mod tests { } { - let tree_route = ::client::blockchain::tree_route( - |id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(a3), - BlockId::Hash(a1), - ).unwrap(); + let tree_route = tree_route(blockchain, a3, a1).unwrap(); assert_eq!(tree_route.common_block().hash, a1); assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2]); @@ -2113,11 +2139,7 @@ mod tests { } { - let tree_route = ::client::blockchain::tree_route( - |id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(a2), - BlockId::Hash(a2), - ).unwrap(); + let tree_route = tree_route(blockchain, a2, a2).unwrap(); assert_eq!(tree_route.common_block().hash, a2); assert!(tree_route.retracted().is_empty()); @@ -2134,11 +2156,7 @@ mod tests { let block1 = insert_header(&backend, 1, block0, Vec::new(), Default::default()); { - let tree_route = ::client::blockchain::tree_route( - |id| blockchain.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(block0), - BlockId::Hash(block1), - ).unwrap(); + let tree_route = tree_route(blockchain, block0, block1).unwrap(); assert_eq!(tree_route.common_block().hash, block0); assert!(tree_route.retracted().is_empty()); @@ -2146,6 +2164,64 @@ mod tests { } } + #[test] + fn lowest_common_ancestor_works() { + let backend = Backend::<Block>::new_test(1000, 100); + let blockchain = backend.blockchain(); + let block0 = insert_header(&backend, 0, Default::default(), Vec::new(), Default::default()); + + // fork from genesis: 3 prong. + let a1 = insert_header(&backend, 1, block0, Vec::new(), Default::default()); + let a2 = insert_header(&backend, 2, a1, Vec::new(), Default::default()); + let a3 = insert_header(&backend, 3, a2, Vec::new(), Default::default()); + + // fork from genesis: 2 prong. + let b1 = insert_header(&backend, 1, block0, Vec::new(), H256::from([1; 32])); + let b2 = insert_header(&backend, 2, b1, Vec::new(), Default::default()); + + { + let lca = lowest_common_ancestor(blockchain, a3, b2).unwrap(); + + assert_eq!(lca.hash, block0); + assert_eq!(lca.number, 0); + } + + { + let lca = lowest_common_ancestor(blockchain, a1, a3).unwrap(); + + assert_eq!(lca.hash, a1); + assert_eq!(lca.number, 1); + } + + { + let lca = lowest_common_ancestor(blockchain, a3, a1).unwrap(); + + assert_eq!(lca.hash, a1); + assert_eq!(lca.number, 1); + } + + { + let lca = lowest_common_ancestor(blockchain, a2, a3).unwrap(); + + assert_eq!(lca.hash, a2); + assert_eq!(lca.number, 2); + } + + { + let lca = lowest_common_ancestor(blockchain, a2, a1).unwrap(); + + assert_eq!(lca.hash, a1); + assert_eq!(lca.number, 1); + } + + { + let lca = lowest_common_ancestor(blockchain, a2, a2).unwrap(); + + assert_eq!(lca.hash, a2); + assert_eq!(lca.number, 2); + } + } + #[test] fn test_leaves_with_complex_block_tree() { let backend: Arc<Backend<test_client::runtime::Block>> = Arc::new(Backend::new_test(20, 20)); diff --git a/substrate/core/client/db/src/light.rs b/substrate/core/client/db/src/light.rs index 07f805495cc1ee55d8e9c65f024d8ae215725ab5..111b8e0deb43b59a6f06485897e5da8363b40e51 100644 --- a/substrate/core/client/db/src/light.rs +++ b/substrate/core/client/db/src/light.rs @@ -35,6 +35,7 @@ use codec::{Decode, Encode}; use primitives::Blake2Hasher; use sr_primitives::generic::{DigestItem, BlockId}; use sr_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, One, NumberFor}; +use header_metadata::{CachedHeaderMetadata, HeaderMetadata, HeaderMetadataCache}; use crate::cache::{DbCacheSync, DbCache, ComplexBlockId, EntryType as CacheEntryType}; use crate::utils::{self, meta_keys, Meta, db_err, read_db, block_id_to_lookup_key, read_meta}; use crate::DatabaseSettings; @@ -60,6 +61,7 @@ pub struct LightStorage<Block: BlockT> { db: Arc<dyn KeyValueDB>, meta: RwLock<Meta<NumberFor<Block>, Block::Hash>>, cache: Arc<DbCacheSync<Block>>, + header_metadata_cache: HeaderMetadataCache<Block>, } impl<Block> LightStorage<Block> @@ -109,6 +111,7 @@ impl<Block> LightStorage<Block> db, meta: RwLock::new(meta), cache: Arc::new(DbCacheSync(RwLock::new(cache))), + header_metadata_cache: HeaderMetadataCache::default(), }) } @@ -192,6 +195,31 @@ impl<Block> BlockchainHeaderBackend<Block> for LightStorage<Block> } } +impl<Block: BlockT> HeaderMetadata<Block> for LightStorage<Block> { + type Error = ClientError; + + fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> { + self.header_metadata_cache.header_metadata(hash).or_else(|_| { + self.header(BlockId::hash(hash))?.map(|header| { + let header_metadata = CachedHeaderMetadata::from(&header); + self.header_metadata_cache.insert_header_metadata( + header_metadata.hash, + header_metadata.clone(), + ); + header_metadata + }).ok_or(ClientError::UnknownBlock("header not found in db".to_owned())) + }) + } + + fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) { + self.header_metadata_cache.insert_header_metadata(hash, metadata) + } + + fn remove_header_metadata(&self, hash: Block::Hash) { + self.header_metadata_cache.remove_header_metadata(hash); + } +} + impl<Block: BlockT> LightStorage<Block> { // Get block changes trie root, if available. fn changes_trie_root(&self, block: BlockId<Block>) -> ClientResult<Option<Block::Hash>> { @@ -208,17 +236,18 @@ impl<Block: BlockT> LightStorage<Block> { /// In the case where the new best block is a block to be imported, `route_to` /// should be the parent of `best_to`. In the case where we set an existing block /// to be best, `route_to` should equal to `best_to`. - fn set_head_with_transaction(&self, transaction: &mut DBTransaction, route_to: Block::Hash, best_to: (NumberFor<Block>, Block::Hash)) -> Result<(), client::error::Error> { + fn set_head_with_transaction( + &self, + transaction: &mut DBTransaction, + route_to: Block::Hash, + best_to: (NumberFor<Block>, Block::Hash), + ) -> ClientResult<()> { let lookup_key = utils::number_and_hash_to_lookup_key(best_to.0, &best_to.1)?; // handle reorg. let meta = self.meta.read(); if meta.best_hash != Default::default() { - let tree_route = ::client::blockchain::tree_route( - |id| self.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(meta.best_hash), - BlockId::Hash(route_to), - )?; + let tree_route = header_metadata::tree_route(self, meta.best_hash, route_to)?; // update block number to hash lookup entries. for retracted in tree_route.retracted() { @@ -418,6 +447,12 @@ impl<Block> LightBlockchainStorage<Block> for LightStorage<Block> )?; transaction.put(columns::HEADER, &lookup_key, &header.encode()); + let header_metadata = CachedHeaderMetadata::from(&header); + self.header_metadata_cache.insert_header_metadata( + header.hash().clone(), + header_metadata, + ); + let is_genesis = number.is_zero(); if is_genesis { self.cache.0.write().set_genesis_hash(hash); @@ -537,6 +572,7 @@ pub(crate) mod tests { use client::cht; use sr_primitives::generic::DigestItem; use sr_primitives::testing::{H256 as Hash, Header, Block as RawBlock, ExtrinsicWrapper}; + use header_metadata::{lowest_common_ancestor, tree_route}; use super::*; type Block = RawBlock<ExtrinsicWrapper<u32>>; @@ -781,11 +817,7 @@ pub(crate) mod tests { let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2)); { - let tree_route = ::client::blockchain::tree_route( - |id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(a3), - BlockId::Hash(b2) - ).unwrap(); + let tree_route = tree_route(&db, a3, b2).unwrap(); assert_eq!(tree_route.common_block().hash, block0); assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2, a1]); @@ -793,11 +825,7 @@ pub(crate) mod tests { } { - let tree_route = ::client::blockchain::tree_route( - |id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(a1), - BlockId::Hash(a3), - ).unwrap(); + let tree_route = tree_route(&db, a1, a3).unwrap(); assert_eq!(tree_route.common_block().hash, a1); assert!(tree_route.retracted().is_empty()); @@ -805,11 +833,7 @@ pub(crate) mod tests { } { - let tree_route = ::client::blockchain::tree_route( - |id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(a3), - BlockId::Hash(a1), - ).unwrap(); + let tree_route = tree_route(&db, a3, a1).unwrap(); assert_eq!(tree_route.common_block().hash, a1); assert_eq!(tree_route.retracted().iter().map(|r| r.hash).collect::<Vec<_>>(), vec![a3, a2]); @@ -817,11 +841,7 @@ pub(crate) mod tests { } { - let tree_route = ::client::blockchain::tree_route( - |id| db.header(id)?.ok_or_else(|| client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(a2), - BlockId::Hash(a2), - ).unwrap(); + let tree_route = tree_route(&db, a2, a2).unwrap(); assert_eq!(tree_route.common_block().hash, a2); assert!(tree_route.retracted().is_empty()); @@ -829,6 +849,63 @@ pub(crate) mod tests { } } + #[test] + fn lowest_common_ancestor_works() { + let db = LightStorage::new_test(); + let block0 = insert_block(&db, HashMap::new(), || default_header(&Default::default(), 0)); + + // fork from genesis: 3 prong. + let a1 = insert_block(&db, HashMap::new(), || default_header(&block0, 1)); + let a2 = insert_block(&db, HashMap::new(), || default_header(&a1, 2)); + let a3 = insert_block(&db, HashMap::new(), || default_header(&a2, 3)); + + // fork from genesis: 2 prong. + let b1 = insert_block(&db, HashMap::new(), || header_with_extrinsics_root(&block0, 1, Hash::from([1; 32]))); + let b2 = insert_block(&db, HashMap::new(), || default_header(&b1, 2)); + + { + let lca = lowest_common_ancestor(&db, a3, b2).unwrap(); + + assert_eq!(lca.hash, block0); + assert_eq!(lca.number, 0); + } + + { + let lca = lowest_common_ancestor(&db, a1, a3).unwrap(); + + assert_eq!(lca.hash, a1); + assert_eq!(lca.number, 1); + } + + { + let lca = lowest_common_ancestor(&db, a3, a1).unwrap(); + + assert_eq!(lca.hash, a1); + assert_eq!(lca.number, 1); + } + + { + let lca = lowest_common_ancestor(&db, a2, a3).unwrap(); + + assert_eq!(lca.hash, a2); + assert_eq!(lca.number, 2); + } + + { + let lca = lowest_common_ancestor(&db, a2, a1).unwrap(); + + assert_eq!(lca.hash, a1); + assert_eq!(lca.number, 1); + } + + { + let lca = lowest_common_ancestor(&db, a2, a2).unwrap(); + + assert_eq!(lca.hash, a2); + assert_eq!(lca.number, 2); + } + } + #[test] fn authorities_are_cached() { let db = LightStorage::new_test(); diff --git a/substrate/core/client/header-metadata/Cargo.toml b/substrate/core/client/header-metadata/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..43fcebb25bf96647aae8faa8e949c973cea89784 --- /dev/null +++ b/substrate/core/client/header-metadata/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "substrate-header-metadata" +version = "2.0.0" +authors = ["Parity Technologies <admin@parity.io>"] +edition = "2018" + +[dependencies] +lru-cache = { version = "0.1.2" } +parking_lot = { version = "0.9.0" } +sr-primitives = { path = "../../sr-primitives" } diff --git a/substrate/core/client/header-metadata/src/lib.rs b/substrate/core/client/header-metadata/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..cce45f264e8ea08bb543323f2bfcafd6b5c3165a --- /dev/null +++ b/substrate/core/client/header-metadata/src/lib.rs @@ -0,0 +1,281 @@ +// Copyright 2019 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/>. + +//! Implements tree backend, cached header metadata and algorithms +//! to compute routes efficiently over the tree of headers. + +use sr_primitives::traits::{Block as BlockT, NumberFor, Header}; +use parking_lot::RwLock; +use lru_cache::LruCache; + +/// Set to the expected max difference between `best` and `finalized` blocks at sync. +const LRU_CACHE_SIZE: usize = 5_000; + +/// Get lowest common ancestor between two blocks in the tree. +/// +/// This implementation is efficient because our trees have very few and +/// small branches, and because of our current query pattern: +/// lca(best, final), lca(best + 1, final), lca(best + 2, final), etc. +/// The first call is O(h) but the others are O(1). +pub fn lowest_common_ancestor<Block: BlockT, T: HeaderMetadata<Block>>( + backend: &T, + id_one: Block::Hash, + id_two: Block::Hash, +) -> Result<HashAndNumber<Block>, T::Error> { + let mut header_one = backend.header_metadata(id_one)?; + let mut header_two = backend.header_metadata(id_two)?; + + let mut orig_header_one = header_one.clone(); + let mut orig_header_two = header_two.clone(); + + // We move through ancestor links as much as possible, since ancestor >= parent. + + while header_one.number > header_two.number { + let ancestor_one = backend.header_metadata(header_one.ancestor)?; + + if ancestor_one.number >= header_two.number { + header_one = ancestor_one; + } else { + break + } + } + + while header_one.number < header_two.number { + let ancestor_two = backend.header_metadata(header_two.ancestor)?; + + if ancestor_two.number >= header_one.number { + header_two = ancestor_two; + } else { + break + } + } + + // Then we move the remaining path using parent links. + + while header_one.hash != header_two.hash { + if header_one.number > header_two.number { + header_one = backend.header_metadata(header_one.parent)?; + } else { + header_two = backend.header_metadata(header_two.parent)?; + } + } + + // Update cached ancestor links. + + if orig_header_one.number > header_one.number { + orig_header_one.ancestor = header_one.hash; + backend.insert_header_metadata(orig_header_one.hash, orig_header_one); + } + + if orig_header_two.number > header_one.number { + orig_header_two.ancestor = header_one.hash; + backend.insert_header_metadata(orig_header_two.hash, orig_header_two); + } + + Ok(HashAndNumber { + hash: header_one.hash, + number: header_one.number, + }) +} + +/// Compute a tree-route between two blocks. See tree-route docs for more details. +pub fn tree_route<Block: BlockT, T: HeaderMetadata<Block>>( + backend: &T, + from: Block::Hash, + to: Block::Hash, +) -> Result<TreeRoute<Block>, T::Error> { + let mut from = backend.header_metadata(from)?; + let mut to = backend.header_metadata(to)?; + + let mut from_branch = Vec::new(); + let mut to_branch = Vec::new(); + + while to.number > from.number { + to_branch.push(HashAndNumber { + number: to.number, + hash: to.hash, + }); + + to = backend.header_metadata(to.parent)?; + } + + while from.number > to.number { + from_branch.push(HashAndNumber { + number: from.number, + hash: from.hash, + }); + from = backend.header_metadata(from.parent)?; + } + + // numbers are equal now. walk backwards until the block is the same + + while to != from { + to_branch.push(HashAndNumber { + number: to.number, + hash: to.hash, + }); + to = backend.header_metadata(to.parent)?; + + from_branch.push(HashAndNumber { + number: from.number, + hash: from.hash, + }); + from = backend.header_metadata(from.parent)?; + } + + // add the pivot block. and append the reversed to-branch (note that it's reverse order originals) + let pivot = from_branch.len(); + from_branch.push(HashAndNumber { + number: to.number, + hash: to.hash, + }); + from_branch.extend(to_branch.into_iter().rev()); + + Ok(TreeRoute { + route: from_branch, + pivot, + }) +} + +/// Hash and number of a block. +#[derive(Debug)] +pub struct HashAndNumber<Block: BlockT> { + /// The number of the block. + pub number: NumberFor<Block>, + /// The hash of the block. + pub hash: Block::Hash, +} + +/// A tree-route from one block to another in the chain. +/// +/// All blocks prior to the pivot in the deque is the reverse-order unique ancestry +/// of the first block, the block at the pivot index is the common ancestor, +/// and all blocks after the pivot is the ancestry of the second block, in +/// order. +/// +/// The ancestry sets will include the given blocks, and thus the tree-route is +/// never empty. +/// +/// ```text +/// Tree route from R1 to E2. Retracted is [R1, R2, R3], Common is C, enacted [E1, E2] +/// <- R3 <- R2 <- R1 +/// / +/// C +/// \-> E1 -> E2 +/// ``` +/// +/// ```text +/// Tree route from C to E2. Retracted empty. Common is C, enacted [E1, E2] +/// C -> E1 -> E2 +/// ``` +#[derive(Debug)] +pub struct TreeRoute<Block: BlockT> { + route: Vec<HashAndNumber<Block>>, + pivot: usize, +} + +impl<Block: BlockT> TreeRoute<Block> { + /// Get a slice of all retracted blocks in reverse order (towards common ancestor) + pub fn retracted(&self) -> &[HashAndNumber<Block>] { + &self.route[..self.pivot] + } + + /// Get the common ancestor block. This might be one of the two blocks of the + /// route. + pub fn common_block(&self) -> &HashAndNumber<Block> { + self.route.get(self.pivot).expect("tree-routes are computed between blocks; \ + which are included in the route; \ + thus it is never empty; qed") + } + + /// Get a slice of enacted blocks (descendents of the common ancestor) + pub fn enacted(&self) -> &[HashAndNumber<Block>] { + &self.route[self.pivot + 1 ..] + } +} + +/// Handles header metadata: hash, number, parent hash, etc. +pub trait HeaderMetadata<Block: BlockT> { + /// Error used in case the header metadata is not found. + type Error; + + fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error>; + fn insert_header_metadata(&self, hash: Block::Hash, header_metadata: CachedHeaderMetadata<Block>); + fn remove_header_metadata(&self, hash: Block::Hash); +} + +/// Caches header metadata in an in-memory LRU cache. +pub struct HeaderMetadataCache<Block: BlockT> { + cache: RwLock<LruCache<Block::Hash, CachedHeaderMetadata<Block>>>, +} + +impl<Block: BlockT> HeaderMetadataCache<Block> { + /// Creates a new LRU header metadata cache with `capacity`. + pub fn new(capacity: usize) -> Self { + HeaderMetadataCache { + cache: RwLock::new(LruCache::new(capacity)), + } + } +} + +impl<Block: BlockT> Default for HeaderMetadataCache<Block> { + fn default() -> Self { + HeaderMetadataCache { + cache: RwLock::new(LruCache::new(LRU_CACHE_SIZE)), + } + } +} + +impl<Block: BlockT> HeaderMetadata<Block> for HeaderMetadataCache<Block> { + type Error = String; + + fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> { + self.cache.write().get_mut(&hash).cloned() + .ok_or("header metadata not found in cache".to_owned()) + } + + fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) { + self.cache.write().insert(hash, metadata); + } + + fn remove_header_metadata(&self, hash: Block::Hash) { + self.cache.write().remove(&hash); + } +} + +/// Cached header metadata. Used to efficiently traverse the tree. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct CachedHeaderMetadata<Block: BlockT> { + /// Hash of the header. + pub hash: Block::Hash, + /// Block number. + pub number: NumberFor<Block>, + /// Hash of parent header. + pub parent: Block::Hash, + /// Hash of an ancestor header. Used to jump through the tree. + ancestor: Block::Hash, +} + +impl<Block: BlockT> From<&Block::Header> for CachedHeaderMetadata<Block> { + fn from(header: &Block::Header) -> Self { + CachedHeaderMetadata { + hash: header.hash().clone(), + number: header.number().clone(), + parent: header.parent_hash().clone(), + ancestor: header.parent_hash().clone(), + } + } +} diff --git a/substrate/core/client/src/blockchain.rs b/substrate/core/client/src/blockchain.rs index 7cf510faf91be0d82f53a70a3a1cce70e414490e..73b7c138d020e62238750b72fe8bf202e42ec109 100644 --- a/substrate/core/client/src/blockchain.rs +++ b/substrate/core/client/src/blockchain.rs @@ -24,6 +24,8 @@ use sr_primitives::Justification; use log::warn; use parking_lot::Mutex; +use header_metadata::HeaderMetadata; + use crate::error::{Error, Result}; /// Blockchain database header backend. Does not perform any validation. @@ -74,7 +76,7 @@ pub trait HeaderBackend<Block: BlockT>: Send + Sync { } /// Blockchain database backend. Does not perform any validation. -pub trait Backend<Block: BlockT>: HeaderBackend<Block> { +pub trait Backend<Block: BlockT>: HeaderBackend<Block> + HeaderMetadata<Block, Error=Error> { /// Get block body. Returns `None` if block is not found. fn body(&self, id: BlockId<Block>) -> Result<Option<Vec<<Block as BlockT>::Extrinsic>>>; /// Get block justification. Returns `None` if justification does not exist. @@ -257,122 +259,6 @@ pub enum BlockStatus { Unknown, } -/// An entry in a tree route. -#[derive(Debug)] -pub struct RouteEntry<Block: BlockT> { - /// The number of the block. - pub number: <Block::Header as HeaderT>::Number, - /// The hash of the block. - pub hash: Block::Hash, -} - -/// A tree-route from one block to another in the chain. -/// -/// All blocks prior to the pivot in the deque is the reverse-order unique ancestry -/// of the first block, the block at the pivot index is the common ancestor, -/// and all blocks after the pivot is the ancestry of the second block, in -/// order. -/// -/// The ancestry sets will include the given blocks, and thus the tree-route is -/// never empty. -/// -/// ```text -/// Tree route from R1 to E2. Retracted is [R1, R2, R3], Common is C, enacted [E1, E2] -/// <- R3 <- R2 <- R1 -/// / -/// C -/// \-> E1 -> E2 -/// ``` -/// -/// ```text -/// Tree route from C to E2. Retracted empty. Common is C, enacted [E1, E2] -/// C -> E1 -> E2 -/// ``` -#[derive(Debug)] -pub struct TreeRoute<Block: BlockT> { - route: Vec<RouteEntry<Block>>, - pivot: usize, -} - -impl<Block: BlockT> TreeRoute<Block> { - /// Get a slice of all retracted blocks in reverse order (towards common ancestor) - pub fn retracted(&self) -> &[RouteEntry<Block>] { - &self.route[..self.pivot] - } - - /// Get the common ancestor block. This might be one of the two blocks of the - /// route. - pub fn common_block(&self) -> &RouteEntry<Block> { - self.route.get(self.pivot).expect("tree-routes are computed between blocks; \ - which are included in the route; \ - thus it is never empty; qed") - } - - /// Get a slice of enacted blocks (descendents of the common ancestor) - pub fn enacted(&self) -> &[RouteEntry<Block>] { - &self.route[self.pivot + 1 ..] - } -} - -/// Compute a tree-route between two blocks. See tree-route docs for more details. -pub fn tree_route<Block: BlockT, F: Fn(BlockId<Block>) -> Result<<Block as BlockT>::Header>>( - load_header: F, - from: BlockId<Block>, - to: BlockId<Block>, -) -> Result<TreeRoute<Block>> { - let mut from = load_header(from)?; - let mut to = load_header(to)?; - - let mut from_branch = Vec::new(); - let mut to_branch = Vec::new(); - - while to.number() > from.number() { - to_branch.push(RouteEntry { - number: to.number().clone(), - hash: to.hash(), - }); - - to = load_header(BlockId::Hash(*to.parent_hash()))?; - } - - while from.number() > to.number() { - from_branch.push(RouteEntry { - number: from.number().clone(), - hash: from.hash(), - }); - from = load_header(BlockId::Hash(*from.parent_hash()))?; - } - - // numbers are equal now. walk backwards until the block is the same - - while to != from { - to_branch.push(RouteEntry { - number: to.number().clone(), - hash: to.hash(), - }); - to = load_header(BlockId::Hash(*to.parent_hash()))?; - - from_branch.push(RouteEntry { - number: from.number().clone(), - hash: from.hash(), - }); - from = load_header(BlockId::Hash(*from.parent_hash()))?; - } - - // add the pivot block. and append the reversed to-branch (note that it's reverse order originalls) - let pivot = from_branch.len(); - from_branch.push(RouteEntry { - number: to.number().clone(), - hash: to.hash(), - }); - from_branch.extend(to_branch.into_iter().rev()); - - Ok(TreeRoute { - route: from_branch, - pivot, - }) -} - /// A list of all well known keys in the blockchain cache. pub mod well_known_cache_keys { /// The type representing cache keys. diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 595aa0e9c7e7e2c7453c2e6fdc7ca27a2c119078..aff099233a41e05da7441bd55517bb1ab31bedc6 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -51,6 +51,7 @@ use consensus::{ ImportResult, BlockOrigin, ForkChoiceStrategy, SelectChain, self, }; +use header_metadata::{HeaderMetadata, CachedHeaderMetadata}; use crate::{ runtime_api::{ @@ -966,10 +967,10 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where }; let retracted = if is_new_best { - let route_from_best = crate::blockchain::tree_route( - |id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(info.best_hash), - BlockId::Hash(parent_hash), + let route_from_best = header_metadata::tree_route( + self.backend.blockchain(), + info.best_hash, + parent_hash, )?; route_from_best.retracted().iter().rev().map(|e| e.hash.clone()).collect() } else { @@ -1100,11 +1101,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where return Ok(()); } - let route_from_finalized = crate::blockchain::tree_route( - |id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(last_finalized), - BlockId::Hash(block), - )?; + let route_from_finalized = header_metadata::tree_route(self.backend.blockchain(), last_finalized, block)?; if let Some(retracted) = route_from_finalized.retracted().get(0) { warn!("Safety violation: attempted to revert finalized block {:?} which is not in the \ @@ -1113,11 +1110,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where return Err(error::Error::NotInFinalizedChain); } - let route_from_best = crate::blockchain::tree_route( - |id| self.header(&id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(best_block), - BlockId::Hash(block), - )?; + let route_from_best = header_metadata::tree_route(self.backend.blockchain(), best_block, block)?; // if the block is not a direct ancestor of the current best chain, // then some other block is the common ancestor. @@ -1321,6 +1314,26 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where } } +impl<B, E, Block, RA> HeaderMetadata<Block> for Client<B, E, Block, RA> where + B: backend::Backend<Block, Blake2Hasher>, + E: CallExecutor<Block, Blake2Hasher>, + Block: BlockT<Hash=H256>, +{ + type Error = error::Error; + + fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> { + self.backend.blockchain().header_metadata(hash) + } + + fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) { + self.backend.blockchain().insert_header_metadata(hash, metadata) + } + + fn remove_header_metadata(&self, hash: Block::Hash) { + self.backend.blockchain().remove_header_metadata(hash) + } +} + impl<B, E, Block, RA> ProvideUncles<Block> for Client<B, E, Block, RA> where B: backend::Backend<Block, Blake2Hasher>, E: CallExecutor<Block, Blake2Hasher>, @@ -1798,7 +1811,7 @@ where /// Utility methods for the client. pub mod utils { use super::*; - use crate::{blockchain, error}; + use crate::error; use primitives::H256; use std::borrow::Borrow; @@ -1812,7 +1825,7 @@ pub mod utils { client: &'a T, current: Option<(H, H)>, ) -> impl Fn(&H256, &H256) -> Result<bool, error::Error> + 'a - where T: ChainHeaderBackend<Block>, + where T: ChainHeaderBackend<Block> + HeaderMetadata<Block, Error=error::Error>, { move |base, hash| { if base == hash { return Ok(false); } @@ -1831,13 +1844,9 @@ pub mod utils { } } - let tree_route = blockchain::tree_route( - |id| client.header(id)?.ok_or_else(|| Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(*hash), - BlockId::Hash(*base), - )?; + let ancestor = header_metadata::lowest_common_ancestor(client, *hash, *base)?; - Ok(tree_route.common_block().hash == *base) + Ok(ancestor.hash == *base) } } } diff --git a/substrate/core/client/src/in_mem.rs b/substrate/core/client/src/in_mem.rs index debfbbd6c75c01e9b5578adc71623f2d0ec234cb..99dc1b62634ba13d8caa80fe00ca7673c1e0c3ab 100644 --- a/substrate/core/client/src/in_mem.rs +++ b/substrate/core/client/src/in_mem.rs @@ -27,12 +27,15 @@ use state_machine::backend::{Backend as StateBackend, InMemory}; use state_machine::{self, InMemoryChangesTrieStorage, ChangesTrieAnchorBlockId, ChangesTrieTransaction}; use hash_db::{Hasher, Prefix}; use trie::MemoryDB; +use header_metadata::{CachedHeaderMetadata, HeaderMetadata}; use crate::error; use crate::backend::{self, NewBlockState, StorageCollection, ChildStorageCollection}; use crate::light; use crate::leaves::LeafSet; -use crate::blockchain::{self, BlockStatus, HeaderBackend, well_known_cache_keys::Id as CacheKeyId}; +use crate::blockchain::{ + self, BlockStatus, HeaderBackend, well_known_cache_keys::Id as CacheKeyId +}; struct PendingBlock<B: BlockT> { block: StoredBlock<B>, @@ -221,11 +224,7 @@ impl<Block: BlockT> Blockchain<Block> { if &best_hash == header.parent_hash() { None } else { - let route = crate::blockchain::tree_route( - |id| self.header(id)?.ok_or_else(|| error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(best_hash), - BlockId::Hash(*header.parent_hash()), - )?; + let route = header_metadata::tree_route(self, best_hash, *header.parent_hash())?; Some(route) } }; @@ -320,6 +319,21 @@ impl<Block: BlockT> HeaderBackend<Block> for Blockchain<Block> { } } +impl<Block: BlockT> HeaderMetadata<Block> for Blockchain<Block> { + type Error = error::Error; + + fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> { + self.header(BlockId::hash(hash))?.map(|header| CachedHeaderMetadata::from(&header)) + .ok_or(error::Error::UnknownBlock("header not found".to_owned())) + } + + fn insert_header_metadata(&self, _hash: Block::Hash, _metadata: CachedHeaderMetadata<Block>) { + // No need to implement. + } + fn remove_header_metadata(&self, _hash: Block::Hash) { + // No need to implement. + } +} impl<Block: BlockT> blockchain::Backend<Block> for Blockchain<Block> { fn body(&self, id: BlockId<Block>) -> error::Result<Option<Vec<<Block as BlockT>::Extrinsic>>> { diff --git a/substrate/core/client/src/light/blockchain.rs b/substrate/core/client/src/light/blockchain.rs index 8fef351b7caf7ab80fa3ec388cc9e3492fe1a4a6..202f94cd74fed91b731781ef2d5311cb0bc24f28 100644 --- a/substrate/core/client/src/light/blockchain.rs +++ b/substrate/core/client/src/light/blockchain.rs @@ -23,18 +23,20 @@ use std::{sync::Arc, collections::HashMap}; use sr_primitives::{Justification, generic::BlockId}; use sr_primitives::traits::{Block as BlockT, Header as HeaderT, NumberFor, Zero}; +use header_metadata::{HeaderMetadata, CachedHeaderMetadata}; + use crate::backend::{AuxStore, NewBlockState}; use crate::blockchain::{ Backend as BlockchainBackend, BlockStatus, Cache as BlockchainCache, HeaderBackend as BlockchainHeaderBackend, Info as BlockchainInfo, ProvideCache, - well_known_cache_keys, + well_known_cache_keys }; use crate::cht; use crate::error::{Error as ClientError, Result as ClientResult}; use crate::light::fetcher::{Fetcher, RemoteHeaderRequest}; /// Light client blockchain storage. -pub trait Storage<Block: BlockT>: AuxStore + BlockchainHeaderBackend<Block> { +pub trait Storage<Block: BlockT>: AuxStore + BlockchainHeaderBackend<Block> + HeaderMetadata<Block, Error=ClientError> { /// Store new header. Should refuse to revert any finalized blocks. /// /// Takes new authorities, the leaf state of the new block, and @@ -140,6 +142,22 @@ impl<S, Block> BlockchainHeaderBackend<Block> for Blockchain<S> where Block: Blo } } +impl<S, Block> HeaderMetadata<Block> for Blockchain<S> where Block: BlockT, S: Storage<Block> { + type Error = ClientError; + + fn header_metadata(&self, hash: Block::Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> { + self.storage.header_metadata(hash) + } + + fn insert_header_metadata(&self, hash: Block::Hash, metadata: CachedHeaderMetadata<Block>) { + self.storage.insert_header_metadata(hash, metadata) + } + + fn remove_header_metadata(&self, hash: Block::Hash) { + self.storage.remove_header_metadata(hash) + } +} + impl<S, Block> BlockchainBackend<Block> for Blockchain<S> where Block: BlockT, S: Storage<Block> { fn body(&self, _id: BlockId<Block>) -> ClientResult<Option<Vec<Block::Extrinsic>>> { Err(ClientError::NotAvailableOnLightClient) @@ -281,6 +299,17 @@ pub mod tests { } } + impl HeaderMetadata<Block> for DummyStorage { + type Error = ClientError; + + fn header_metadata(&self, hash: Hash) -> Result<CachedHeaderMetadata<Block>, Self::Error> { + self.header(BlockId::hash(hash))?.map(|header| CachedHeaderMetadata::from(&header)) + .ok_or(ClientError::UnknownBlock("header not found".to_owned())) + } + fn insert_header_metadata(&self, _hash: Hash, _metadata: CachedHeaderMetadata<Block>) {} + fn remove_header_metadata(&self, _hash: Hash) {} + } + impl AuxStore for DummyStorage { fn insert_aux< 'a, diff --git a/substrate/core/consensus/babe/Cargo.toml b/substrate/core/consensus/babe/Cargo.toml index a6d1833b292cc455b61dcafdb0140d7d3d1b120c..b268a6f41b6d41b679eec3567287fe16622c9b87 100644 --- a/substrate/core/consensus/babe/Cargo.toml +++ b/substrate/core/consensus/babe/Cargo.toml @@ -21,6 +21,7 @@ substrate-telemetry = { path = "../../telemetry" } keystore = { package = "substrate-keystore", path = "../../keystore" } srml-babe = { path = "../../../srml/babe" } client = { package = "substrate-client", path = "../../client" } +header-metadata = { package = "substrate-header-metadata", path = "../../client/header-metadata" } consensus-common = { package = "substrate-consensus-common", path = "../common" } uncles = { package = "substrate-consensus-uncles", path = "../uncles" } slots = { package = "substrate-consensus-slots", path = "../slots" } diff --git a/substrate/core/consensus/babe/src/epoch_changes.rs b/substrate/core/consensus/babe/src/epoch_changes.rs index 0dbad245e644ee881c7cef4ed6f14b532591d618..311271ae15cc7126e2cfe24b66c4b32248b24371 100644 --- a/substrate/core/consensus/babe/src/epoch_changes.rs +++ b/substrate/core/consensus/babe/src/epoch_changes.rs @@ -28,6 +28,7 @@ use codec::{Encode, Decode}; use client::error::Error as ClientError; use client::utils as client_utils; use client::blockchain::HeaderBackend; +use header_metadata::HeaderMetadata; use primitives::H256; use std::ops::Add; @@ -62,7 +63,7 @@ pub(crate) struct HeaderBackendDescendentBuilder<H, Block>(H, std::marker::Phant // https://github.com/paritytech/substrate/issues/3624 impl<'a, H, Block> IsDescendentOfBuilder<H256> for HeaderBackendDescendentBuilder<&'a H, Block> where - H: HeaderBackend<Block>, + H: HeaderBackend<Block> + HeaderMetadata<Block, Error=ClientError>, Block: BlockT<Hash = H256>, { type Error = ClientError; diff --git a/substrate/core/consensus/babe/src/lib.rs b/substrate/core/consensus/babe/src/lib.rs index bd2830d4e74838b84607d2cfb90074b9cd033a67..d2a16bedb844fbdc3cce76372fd41336da5f0347 100644 --- a/substrate/core/consensus/babe/src/lib.rs +++ b/substrate/core/consensus/babe/src/lib.rs @@ -93,7 +93,7 @@ use consensus_common::import_queue::{Verifier, BasicQueue, CacheKeyId}; use client::{ block_builder::api::BlockBuilder as BlockBuilderApi, blockchain::{self, HeaderBackend, ProvideCache}, BlockchainEvents, CallExecutor, Client, - error::Result as ClientResult, backend::{AuxStore, Backend}, + error::Result as ClientResult, error::Error as ClientError, backend::{AuxStore, Backend}, ProvideUncles, }; use slots::{CheckedHeader, check_equivocation}; @@ -101,6 +101,8 @@ use futures::prelude::*; use log::{warn, debug, info, trace}; use slots::{SlotWorker, SlotData, SlotInfo, SlotCompatible}; use epoch_changes::descendent_query; +use header_metadata::HeaderMetadata; + mod aux_schema; mod verification; mod epoch_changes; @@ -223,7 +225,7 @@ pub fn start_babe<B, C, SC, E, I, SO, Error>(BabeParams { > where B: BlockT<Hash=H256>, C: ProvideRuntimeApi + ProvideCache<B> + ProvideUncles<B> + BlockchainEvents<B> - + HeaderBackend<B> + Send + Sync + 'static, + + HeaderBackend<B> + HeaderMetadata<B, Error=ClientError> + Send + Sync + 'static, C::Api: BabeApi<B>, SC: SelectChain<B> + 'static, E: Environment<B, Error=Error> + Send + Sync, @@ -296,7 +298,7 @@ struct BabeWorker<B: BlockT, C, E, I, SO> { impl<B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<B, C, E, I, SO> where B: BlockT<Hash=H256>, - C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B>, + C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B> + HeaderMetadata<B, Error=ClientError>, C::Api: BabeApi<B>, E: Environment<B, Error=Error>, E::Proposer: Proposer<B, Error=Error>, @@ -408,7 +410,7 @@ impl<B, C, E, I, Error, SO> slots::SimpleSlotWorker<B> for BabeWorker<B, C, E, I impl<B, C, E, I, Error, SO> SlotWorker<B> for BabeWorker<B, C, E, I, SO> where B: BlockT<Hash=H256>, - C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B> + Send + Sync, + C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B> + HeaderMetadata<B, Error=ClientError> + Send + Sync, C::Api: BabeApi<B>, E: Environment<B, Error=Error> + Send + Sync, E::Proposer: Proposer<B, Error=Error>, @@ -1071,7 +1073,7 @@ pub mod test_helpers { link: &BabeLink<B>, ) -> Option<BabePreDigest> where B: BlockT<Hash=H256>, - C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B>, + C: ProvideRuntimeApi + ProvideCache<B> + HeaderBackend<B> + HeaderMetadata<B, Error=ClientError>, C::Api: BabeApi<B>, { let epoch = link.epoch_changes.lock().epoch_for_child_of( diff --git a/substrate/core/finality-grandpa/Cargo.toml b/substrate/core/finality-grandpa/Cargo.toml index f2e1dd490bb370176a6380abcbaff108d5dfa47d..4e835e14f8dfe6aece78f5a65ae26c48d2aca1b2 100644 --- a/substrate/core/finality-grandpa/Cargo.toml +++ b/substrate/core/finality-grandpa/Cargo.toml @@ -21,6 +21,7 @@ substrate-telemetry = { path = "../telemetry" } keystore = { package = "substrate-keystore", path = "../keystore" } serde_json = "1.0" client = { package = "substrate-client", path = "../client" } +header-metadata = { package = "substrate-header-metadata", path = "../client/header-metadata" } inherents = { package = "substrate-inherents", path = "../../core/inherents" } network = { package = "substrate-network", path = "../network" } srml-finality-tracker = { path = "../../srml/finality-tracker" } diff --git a/substrate/core/finality-grandpa/src/environment.rs b/substrate/core/finality-grandpa/src/environment.rs index 5e3abb3ac7b265415337d7d79067e021da5132fa..70e45848be1dd10192cc477466cb81d5aa973cb7 100644 --- a/substrate/core/finality-grandpa/src/environment.rs +++ b/substrate/core/finality-grandpa/src/environment.rs @@ -498,11 +498,7 @@ pub(crate) fn ancestry<B, Block: BlockT<Hash=H256>, E, RA>( { if base == block { return Err(GrandpaError::NotDescendent) } - let tree_route_res = ::client::blockchain::tree_route( - |id| client.header(&id)?.ok_or(client::error::Error::UnknownBlock(format!("{:?}", id))), - BlockId::Hash(block), - BlockId::Hash(base), - ); + let tree_route_res = header_metadata::tree_route(client, block, base); let tree_route = match tree_route_res { Ok(tree_route) => tree_route, diff --git a/substrate/core/network/Cargo.toml b/substrate/core/network/Cargo.toml index 9a0a4fc678be5565e4ae572546ca962916117134..e8d84e1c6f33013127eaea4c67ad62af55952ff3 100644 --- a/substrate/core/network/Cargo.toml +++ b/substrate/core/network/Cargo.toml @@ -19,13 +19,14 @@ futures03 = { package = "futures-preview", version = "0.3.0-alpha.18", features futures-timer = "0.3" linked-hash-map = "0.5" linked_hash_set = "0.1.3" -lru-cache = "0.1.1" +lru-cache = "0.1.2" rustc-hex = "2.0" rand = "0.6" libp2p = { version = "0.12.0", default-features = false, features = ["secp256k1", "libp2p-websocket"] } fork-tree = { path = "../../core/utils/fork-tree" } consensus = { package = "substrate-consensus-common", path = "../../core/consensus/common" } client = { package = "substrate-client", path = "../../core/client" } +header_metadata = { package = "substrate-header-metadata", path = "../../core/client/header-metadata" } sr-primitives = { path = "../../core/sr-primitives" } primitives = { package = "substrate-primitives", path = "../../core/primitives" } codec = { package = "parity-scale-codec", version = "1.0.0", features = ["derive"] } diff --git a/substrate/core/network/src/chain.rs b/substrate/core/network/src/chain.rs index d23068179ff5568a972b84bba1446a67b71e2cf7..1a1f649cae2dd62946678486a5131c357b89b0a9 100644 --- a/substrate/core/network/src/chain.rs +++ b/substrate/core/network/src/chain.rs @@ -153,14 +153,8 @@ impl<B, E, Block, RA> Client<Block> for SubstrateClient<B, E, Block, RA> where return Ok(false); } - let tree_route = ::client::blockchain::tree_route( - |id| self.header(&id)?.ok_or_else(|| - client::error::Error::UnknownBlock(format!("{:?}", id)) - ), - BlockId::Hash(*block), - BlockId::Hash(*base), - )?; - - Ok(tree_route.common_block().hash == *base) + let ancestor = header_metadata::lowest_common_ancestor(self, *block, *base)?; + + Ok(ancestor.hash == *base) } } diff --git a/substrate/core/test-runtime/client/src/lib.rs b/substrate/core/test-runtime/client/src/lib.rs index d2cf704fdfb01481049cd5c3c160ad377e2017fd..722aa0b2b472601b6003bc3bf805ef1324acfecd 100644 --- a/substrate/core/test-runtime/client/src/lib.rs +++ b/substrate/core/test-runtime/client/src/lib.rs @@ -78,7 +78,7 @@ pub type LightExecutor = client::light::call_executor::GenesisCallExecutor< client::LocalCallExecutor< client::light::backend::Backend< client_db::light::LightStorage<runtime::Block>, - Blake2Hasher + Blake2Hasher, >, NativeExecutor<LocalExecutor> >