From dfd35fabb4d88403b9d2520095785b01c7538ec0 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Thu, 4 May 2023 14:08:53 +0200 Subject: [PATCH] chore(tree): a few refactors and instrumentations (#2549) --- crates/blockchain-tree/src/block_buffer.rs | 15 +- crates/blockchain-tree/src/block_indices.rs | 48 ++- crates/blockchain-tree/src/blockchain_tree.rs | 288 ++++++++++-------- crates/blockchain-tree/src/canonical_chain.rs | 92 ++++++ crates/blockchain-tree/src/externals.rs | 8 +- crates/blockchain-tree/src/lib.rs | 2 + crates/blockchain-tree/src/shareable.rs | 2 +- crates/interfaces/src/executor.rs | 2 +- 8 files changed, 294 insertions(+), 163 deletions(-) create mode 100644 crates/blockchain-tree/src/canonical_chain.rs diff --git a/crates/blockchain-tree/src/block_buffer.rs b/crates/blockchain-tree/src/block_buffer.rs index 06288ccd36..a519a9e786 100644 --- a/crates/blockchain-tree/src/block_buffer.rs +++ b/crates/blockchain-tree/src/block_buffer.rs @@ -11,9 +11,10 @@ pub type BufferedBlocks = BTreeMap Vec { + pub fn take_all_children(&mut self, parent: BlockNumHash) -> Vec { // remove parent block if present let mut taken = Vec::new(); if let Some(block) = self.remove_from_blocks(&parent) { @@ -211,7 +212,7 @@ mod tests { buffer.insert_block(block4); assert_eq!(buffer.len(), 4); - assert_eq!(buffer.take_all_childrens(main_parent), vec![block1, block2, block3]); + assert_eq!(buffer.take_all_children(main_parent), vec![block1, block2, block3]); assert_eq!(buffer.len(), 1); } @@ -233,7 +234,7 @@ mod tests { assert_eq!(buffer.len(), 4); assert_eq!( buffer - .take_all_childrens(main_parent) + .take_all_children(main_parent) .into_iter() .map(|b| (b.hash, b)) .collect::>(), @@ -265,7 +266,7 @@ mod tests { assert_eq!(buffer.len(), 4); assert_eq!( buffer - .take_all_childrens(block1.num_hash()) + .take_all_children(block1.num_hash()) .into_iter() .map(|b| (b.hash, b)) .collect::>(), diff --git a/crates/blockchain-tree/src/block_indices.rs b/crates/blockchain-tree/src/block_indices.rs index 4cd73b68b3..f9902d7e6a 100644 --- a/crates/blockchain-tree/src/block_indices.rs +++ b/crates/blockchain-tree/src/block_indices.rs @@ -1,6 +1,7 @@ //! Implementation of [`BlockIndices`] related to [`super::BlockchainTree`] use super::chain::BlockChainId; +use crate::canonical_chain::CanonicalChain; use reth_primitives::{BlockHash, BlockNumHash, BlockNumber, SealedBlockWithSenders}; use reth_provider::Chain; use std::collections::{btree_map, hash_map, BTreeMap, BTreeSet, HashMap, HashSet}; @@ -18,7 +19,7 @@ pub struct BlockIndices { /// Canonical chain. Contains N number (depends on `finalization_depth`) of blocks. /// These blocks are found in fork_to_child but not inside `blocks_to_chain` or /// `number_to_block` as those are chain specific indices. - canonical_chain: BTreeMap, + canonical_chain: CanonicalChain, /// Index needed when discarding the chain, so we can remove connected chains from tree. /// NOTE: It contains just a blocks that are forks as a key and not all blocks. fork_to_child: HashMap>, @@ -37,7 +38,7 @@ impl BlockIndices { ) -> Self { Self { last_finalized_block, - canonical_chain, + canonical_chain: CanonicalChain::new(canonical_chain), fork_to_child: Default::default(), blocks_to_chain: Default::default(), index_number_to_block: Default::default(), @@ -83,7 +84,7 @@ impl BlockIndices { /// Check if block hash belongs to canonical chain. pub fn is_block_hash_canonical(&self, block_hash: &BlockHash) -> bool { - self.canonical_chain.range(self.last_finalized_block..).any(|(_, &h)| h == *block_hash) + self.canonical_chain.is_block_hash_canonical(self.last_finalized_block, block_hash) } /// Last finalized block @@ -92,7 +93,7 @@ impl BlockIndices { } /// Insert non fork block. - pub fn insert_non_fork_block( + pub(crate) fn insert_non_fork_block( &mut self, block_number: BlockNumber, block_hash: BlockHash, @@ -103,7 +104,7 @@ impl BlockIndices { } /// Insert block to chain and fork child indices of the new chain - pub fn insert_chain(&mut self, chain_id: BlockChainId, chain: &Chain) { + pub(crate) fn insert_chain(&mut self, chain_id: BlockChainId, chain: &Chain) { for (number, block) in chain.blocks().iter() { // add block -> chain_id index self.blocks_to_chain.insert(block.hash(), chain_id); @@ -116,19 +117,19 @@ impl BlockIndices { } /// Get the chain ID the block belongs to - pub fn get_blocks_chain_id(&self, block: &BlockHash) -> Option { + pub(crate) fn get_blocks_chain_id(&self, block: &BlockHash) -> Option { self.blocks_to_chain.get(block).cloned() } /// Update all block hashes. iterate over present and new list of canonical hashes and compare /// them. Remove all missmatches, disconnect them and return all chains that needs to be /// removed. - pub fn update_block_hashes( + pub(crate) fn update_block_hashes( &mut self, hashes: BTreeMap, ) -> (BTreeSet, Vec) { // set new canonical hashes. - self.canonical_chain = hashes.clone(); + self.canonical_chain.replace(hashes.clone()); let mut new_hashes = hashes.into_iter(); let mut old_hashes = self.canonical_chain().clone().into_iter(); @@ -249,7 +250,7 @@ impl BlockIndices { let first_number = *blocks.first_key_value().unwrap().0; // this will remove all blocks numbers that are going to be replaced. - self.canonical_chain.retain(|num, _| *num < first_number); + self.canonical_chain.retain(|&number, _| number < first_number); // remove them from block to chain_id index blocks.iter().map(|(_, b)| (b.number, b.hash(), b.parent_hash)).for_each( @@ -308,8 +309,8 @@ impl BlockIndices { let finalized_blocks: Vec = self .canonical_chain .iter() - .filter(|(&number, _)| number >= self.last_finalized_block && number < finalized_block) - .map(|(_, hash)| *hash) + .filter(|(number, _)| *number >= self.last_finalized_block && *number < finalized_block) + .map(|(_, hash)| hash) .collect(); // remove unneeded canonical hashes. @@ -338,33 +339,26 @@ impl BlockIndices { } /// Returns the block hash of the canonical block with the given number. - pub fn canonical_hash(&self, block_number: BlockNumber) -> Option { - self.canonical_chain.get(&block_number).cloned() + #[inline] + pub fn canonical_hash(&self, block_number: &BlockNumber) -> Option { + self.canonical_chain.canonical_hash(block_number) } /// Returns the block number of the canonical block with the given hash. + #[inline] pub fn canonical_number(&self, block_hash: BlockHash) -> Option { - self.canonical_chain.iter().find_map( - |(number, hash)| { - if *hash == block_hash { - Some(*number) - } else { - None - } - }, - ) + self.canonical_chain.canonical_number(block_hash) } /// get canonical tip + #[inline] pub fn canonical_tip(&self) -> BlockNumHash { - self.canonical_chain - .last_key_value() - .map(|(&number, &hash)| BlockNumHash { number, hash }) - .unwrap_or_default() + self.canonical_chain.tip() } /// Canonical chain needed for execution of EVM. It should contains last 256 block hashes. - pub fn canonical_chain(&self) -> &BTreeMap { + #[inline] + pub(crate) fn canonical_chain(&self) -> &CanonicalChain { &self.canonical_chain } } diff --git a/crates/blockchain-tree/src/blockchain_tree.rs b/crates/blockchain-tree/src/blockchain_tree.rs index fc7842c71c..53d8a2a94d 100644 --- a/crates/blockchain-tree/src/blockchain_tree.rs +++ b/crates/blockchain-tree/src/blockchain_tree.rs @@ -1,7 +1,7 @@ //! Implementation of [`BlockchainTree`] use crate::{ - chain::BlockChainId, AppendableChain, BlockBuffer, BlockIndices, BlockchainTreeConfig, - PostStateData, TreeExternals, + canonical_chain::CanonicalChain, chain::BlockChainId, AppendableChain, BlockBuffer, + BlockIndices, BlockchainTreeConfig, PostStateData, TreeExternals, }; use reth_db::{cursor::DbCursorRO, database::Database, tables, transaction::DbTx}; use reth_interfaces::{ @@ -24,7 +24,7 @@ use std::{ collections::{BTreeMap, HashMap}, sync::Arc, }; -use tracing::{error, info, trace}; +use tracing::{debug, error, info, instrument, trace}; #[cfg_attr(doc, aquamarine::aquamarine)] /// Tree of chains and its identifications. @@ -148,8 +148,8 @@ impl BlockchainTree // check db if block is finalized. if block.number <= last_finalized_block { // check if block is canonical - if let Some(hash) = self.block_indices.canonical_chain().get(&block.number) { - if *hash == block.hash { + if let Some(hash) = self.canonical_chain().canonical_hash(&block.number) { + if hash == block.hash { return Ok(Some(BlockStatus::Valid)) } } @@ -167,7 +167,7 @@ impl BlockchainTree } // check if block is part of canonical chain - if self.block_indices.canonical_hash(block.number) == Some(block.hash) { + if self.block_indices.canonical_hash(&block.number) == Some(block.hash) { return Ok(Some(BlockStatus::Valid)) } @@ -185,10 +185,16 @@ impl BlockchainTree } /// Expose internal indices of the BlockchainTree. + #[inline] pub fn block_indices(&self) -> &BlockIndices { &self.block_indices } + #[inline] + fn canonical_chain(&self) -> &CanonicalChain { + self.block_indices.canonical_chain() + } + /// Returns the block with matching hash. pub fn block_by_hash(&self, block_hash: BlockHash) -> Option<&SealedBlock> { let id = self.block_indices.get_blocks_chain_id(&block_hash)?; @@ -225,10 +231,8 @@ impl BlockchainTree let first_pending_block_number = *parent_block_hashed.first_key_value().expect("There is at least one block hash").0; let canonical_chain = self - .block_indices .canonical_chain() - .clone() - .into_iter() + .iter() .filter(|&(key, _)| key < first_pending_block_number) .collect::>(); parent_block_hashed.extend(canonical_chain.into_iter()); @@ -239,14 +243,12 @@ impl BlockchainTree } // check if there is canonical block - if let Some(canonical_fork) = - self.block_indices().canonical_chain().iter().find(|(_, value)| **value == block_hash) - { + if let Some(canonical_number) = self.canonical_chain().canonical_number(block_hash) { trace!(target: "blockchain_tree", ?block_hash, "Constructing post state data based on canonical chain"); return Some(PostStateData { - canonical_fork: ForkBlock { number: *canonical_fork.0, hash: *canonical_fork.1 }, + canonical_fork: ForkBlock { number: canonical_number, hash: block_hash }, state: PostState::new(), - parent_block_hashed: self.block_indices().canonical_chain().clone(), + parent_block_hashed: self.canonical_chain().inner().clone(), }) } @@ -255,108 +257,24 @@ impl BlockchainTree /// Try inserting block inside the tree. /// - /// If blocks does not have parent [`BlockStatus::Disconnected`] would be returned. + /// If blocks does not have parent [`BlockStatus::Disconnected`] would be returned, in which + /// case it is buffered for future inclusion. + #[instrument(skip_all, fields(block = ?block.num_hash()), target = "blockchain-tree", ret)] pub fn try_insert_block( &mut self, block: SealedBlockWithSenders, ) -> Result { let parent = block.parent_num_hash(); - let block_num_hash = block.num_hash(); // check if block parent can be found in Tree if let Some(chain_id) = self.block_indices.get_blocks_chain_id(&parent.hash) { - // Create a new sidechain by forking the given chain, or append the block if the parent - // block is the top of the given chain. - let block_hashes = self.all_chain_hashes(chain_id); - - // get canonical fork. - let canonical_fork = self - .canonical_fork(chain_id) - .ok_or(ExecError::BlockChainIdConsistency { chain_id })?; - - // get chain that block needs to join to. - let parent_chain = self - .chains - .get_mut(&chain_id) - .ok_or(ExecError::BlockChainIdConsistency { chain_id })?; - let chain_tip = parent_chain.tip().hash(); - - let canonical_block_hashes = self.block_indices.canonical_chain(); - - // append the block if it is continuing the chain. - let status = if chain_tip == block.parent_hash { - let block_hash = block.hash(); - let block_number = block.number; - parent_chain.append_block( - block, - block_hashes, - canonical_block_hashes, - canonical_fork, - &self.externals, - )?; - - self.block_indices.insert_non_fork_block(block_number, block_hash, chain_id); - Ok(BlockStatus::Valid) - } else { - let chain = parent_chain.new_chain_fork( - block, - block_hashes, - canonical_block_hashes, - canonical_fork, - &self.externals, - )?; - self.insert_chain(chain); - Ok(BlockStatus::Accepted) - }; - self.try_connect_buffered_blocks(block_num_hash); - return status + // found parent in side tree, try to insert there + return self.try_insert_block_into_side_chain(block, chain_id) } - let canonical_parent_block = self.block_indices.canonical_hash(parent.number); - // if not found, check if the parent can be found inside canonical chain. - if Some(parent.hash) == canonical_parent_block { - // create new chain that points to that block - //return self.fork_canonical_chain(block.clone()); - // TODO save pending block to database - // https://github.com/paradigmxyz/reth/issues/1713 - - let db = self.externals.shareable_db(); - - // Validate that the block is post merge - let parent_td = db - .header_td(&block.parent_hash)? - .ok_or(ExecError::CanonicalChain { block_hash: block.parent_hash })?; - // Pass the parent total difficulty to short-circuit unnecessary calculations. - if !self.externals.chain_spec.fork(Hardfork::Paris).active_at_ttd(parent_td, U256::ZERO) - { - return Err(ExecError::BlockPreMerge { hash: block.hash }.into()) - } - - // Create state provider - let canonical_block_hashes = self.block_indices.canonical_chain(); - let canonical_tip = - canonical_block_hashes.last_key_value().map(|(_, hash)| *hash).unwrap_or_default(); - let block_status = if block.parent_hash == canonical_tip { - BlockStatus::Valid - } else { - BlockStatus::Accepted - }; - - let parent_header = db - .header(&block.parent_hash)? - .ok_or(ExecError::CanonicalChain { block_hash: block.parent_hash })? - .seal(block.parent_hash); - let chain = AppendableChain::new_canonical_fork( - &block, - &parent_header, - canonical_block_hashes, - parent, - &self.externals, - )?; - self.insert_chain(chain); - self.try_connect_buffered_blocks(block_num_hash); - return Ok(block_status) + if Some(parent.hash) == self.block_indices.canonical_hash(&parent.number) { + return self.try_append_canonical_chain(block, parent) } // this is another check to ensure that if the block points to a canonical block its block @@ -383,6 +301,115 @@ impl BlockchainTree Ok(BlockStatus::Disconnected) } + #[instrument(skip_all, target = "blockchain_tree")] + fn try_append_canonical_chain( + &mut self, + block: SealedBlockWithSenders, + parent: BlockNumHash, + ) -> Result { + let block_num_hash = block.num_hash(); + debug!(target: "blockchain_tree", ?parent, "Appending block to canonical chain"); + // create new chain that points to that block + //return self.fork_canonical_chain(block.clone()); + // TODO save pending block to database + // https://github.com/paradigmxyz/reth/issues/1713 + + let db = self.externals.shareable_db(); + + // Validate that the block is post merge + let parent_td = db + .header_td(&block.parent_hash)? + .ok_or(ExecError::CanonicalChain { block_hash: block.parent_hash })?; + // Pass the parent total difficulty to short-circuit unnecessary calculations. + if !self.externals.chain_spec.fork(Hardfork::Paris).active_at_ttd(parent_td, U256::ZERO) { + return Err(ExecError::BlockPreMerge { hash: block.hash }.into()) + } + + let canonical_chain = self.canonical_chain(); + let block_status = if block.parent_hash == canonical_chain.tip().hash { + BlockStatus::Valid + } else { + BlockStatus::Accepted + }; + + let parent_header = db + .header(&block.parent_hash)? + .ok_or(ExecError::CanonicalChain { block_hash: block.parent_hash })? + .seal(block.parent_hash); + let chain = AppendableChain::new_canonical_fork( + &block, + &parent_header, + canonical_chain.inner(), + parent, + &self.externals, + )?; + self.insert_chain(chain); + self.try_connect_buffered_blocks(block_num_hash); + Ok(block_status) + } + + /// Try inserting a block into the given side chain. + #[instrument(skip_all, target = "blockchain_tree")] + fn try_insert_block_into_side_chain( + &mut self, + block: SealedBlockWithSenders, + chain_id: BlockChainId, + ) -> Result { + debug!(target: "blockchain_tree", "Inserting block into side chain"); + let block_num_hash = block.num_hash(); + // Create a new sidechain by forking the given chain, or append the block if the parent + // block is the top of the given chain. + let block_hashes = self.all_chain_hashes(chain_id); + + // get canonical fork. + let canonical_fork = self + .canonical_fork(chain_id) + .ok_or(ExecError::BlockSideChainIdConsistency { chain_id })?; + + // get chain that block needs to join to. + let parent_chain = self + .chains + .get_mut(&chain_id) + .ok_or(ExecError::BlockSideChainIdConsistency { chain_id })?; + let chain_tip = parent_chain.tip().hash(); + + let canonical_block_hashes = self.block_indices.canonical_chain(); + + // append the block if it is continuing the side chain. + let status = if chain_tip == block.parent_hash { + debug!(target: "blockchain_tree", "Appending block to side chain"); + let block_hash = block.hash(); + let block_number = block.number; + parent_chain.append_block( + block, + block_hashes, + canonical_block_hashes.inner(), + canonical_fork, + &self.externals, + )?; + + self.block_indices.insert_non_fork_block(block_number, block_hash, chain_id); + Ok(BlockStatus::Valid) + } else { + debug!(target: "blockchain_tree", ?canonical_fork, "Starting new fork from side chain"); + // the block starts a new fork + let chain = parent_chain.new_chain_fork( + block, + block_hashes, + canonical_block_hashes.inner(), + canonical_fork, + &self.externals, + )?; + self.insert_chain(chain); + Ok(BlockStatus::Accepted) + }; + + // After we inserted the block, we try to connect any buffered blocks + self.try_connect_buffered_blocks(block_num_hash); + + status + } + /// Get all block hashes from a sidechain that are not part of the canonical chain. /// /// This is a one time operation per block. @@ -429,7 +456,7 @@ impl BlockchainTree } break } - (self.block_indices.canonical_hash(fork.number) == Some(fork.hash)).then_some(fork) + (self.block_indices.canonical_hash(&fork.number) == Some(fork.hash)).then_some(fork) } /// Insert a chain into the tree. @@ -462,13 +489,16 @@ impl BlockchainTree } /// Insert block for future execution. + /// + /// Returns an error if the block is invalid. pub fn buffer_block(&mut self, block: SealedBlockWithSenders) -> Result<(), Error> { self.validate_block(&block)?; self.buffered_blocks.insert_block(block); Ok(()) } - /// Validate if block is correct and if i + /// Validate if block is correct and satisfies all the consensus rules that concern the header + /// and block body itself. fn validate_block(&self, block: &SealedBlockWithSenders) -> Result<(), Error> { if let Err(e) = self.externals.consensus.validate_header_with_total_difficulty(block, U256::MAX) @@ -496,6 +526,7 @@ impl BlockchainTree /// Check if block is found inside chain and if the chain extends the canonical chain /// if it does extends the canonical chain, return `BlockStatus::Valid` /// if it does not extends the canonical chain, return `BlockStatus::Accepted` + #[track_caller] fn is_block_inside_chain(&self, block: &BlockNumHash) -> Option { // check if block known and is already inside Tree if let Some(chain_id) = self.block_indices.get_blocks_chain_id(&block.hash) { @@ -533,7 +564,7 @@ impl BlockchainTree self.insert_block_inner(block, true) } - /// Insert a block (with senders recovered) in the tree. Check [`BlockchainTree::insert_block`] + /// Insert a block (with recovered senders) in the tree. Check [`BlockchainTree::insert_block`] /// for more info pub(crate) fn insert_block_inner( &mut self, @@ -628,13 +659,19 @@ impl BlockchainTree Ok(()) } - /// Connect unconnected blocks + /// Connect unconnected,buffered blocks if the new block closes a gap. fn try_connect_buffered_blocks(&mut self, new_block: BlockNumHash) { - let include_blocks = self.buffered_blocks.take_all_childrens(new_block); - // insert child blocks + let include_blocks = self.buffered_blocks.take_all_children(new_block); + // insert block children for block in include_blocks.into_iter() { // dont fail on error, just ignore the block. - let _ = self.insert_block_inner(block, false); + let _ = self.insert_block_inner(block, false).map_err(|err| { + debug!( + target: "blockchain_tree", ?err, + "Failed to insert buffered block", + ); + err + }); } } @@ -650,7 +687,7 @@ impl BlockchainTree let chain = chain.into_inner(); match chain.split(split_at) { ChainSplit::Split { canonical, pending } => { - // rest of splited chain is inserted back with same chain_id. + // rest of split chain is inserted back with same chain_id. self.block_indices.insert_chain(chain_id, &pending); self.chains.insert(chain_id, AppendableChain::new(pending)); canonical @@ -662,7 +699,7 @@ impl BlockchainTree } } - /// Make a block and its parent part of the canonical chain. + /// Make a block and its parent(s) part of the canonical chain. /// /// # Note /// @@ -672,10 +709,12 @@ impl BlockchainTree /// # Returns /// /// Returns `Ok` if the blocks were canonicalized, or if the blocks were already canonical. + #[track_caller] + #[instrument(skip(self), target = "blockchain_tree")] pub fn make_canonical(&mut self, block_hash: &BlockHash) -> Result<(), Error> { // If block is already canonical don't return error. if self.block_indices.is_block_hash_canonical(block_hash) { - trace!(target: "blockchain_tree", ?block_hash, "Block is already canonical"); + trace!(target: "blockchain_tree", "Block is already canonical"); let td = self .externals .shareable_db() @@ -688,11 +727,12 @@ impl BlockchainTree } let Some(chain_id) = self.block_indices.get_blocks_chain_id(block_hash) else { + debug!(target: "blockchain_tree", "Block hash not found in block indices"); return Err(ExecError::BlockHashNotFoundInChain { block_hash: *block_hash }.into()) }; let chain = self.chains.remove(&chain_id).expect("To be present"); - // we are spliting chain as there is possibility that only part of chain get canonicalized. + // we are splitting chain as there is possibility that only part of chain get canonicalized. let canonical = self.split_chain(chain_id, chain, SplitAt::Hash(*block_hash)); let mut block_fork = canonical.fork_block(); @@ -718,11 +758,12 @@ impl BlockchainTree // update canonical index self.block_indices.canonicalize_blocks(new_canon_chain.blocks()); - let chain_action; + // event about new canonical chain. + let chain_notification; // if joins to the tip; if new_canon_chain.fork_block_hash() == old_tip.hash { - chain_action = + chain_notification = CanonStateNotification::Commit { new: Arc::new(new_canon_chain.clone()) }; // append to database self.commit_canonical(new_canon_chain)?; @@ -731,7 +772,7 @@ impl BlockchainTree let canon_fork = new_canon_chain.fork_block(); // sanity check - if self.block_indices.canonical_hash(canon_fork.number) != Some(canon_fork.hash) { + if self.block_indices.canonical_hash(&canon_fork.number) != Some(canon_fork.hash) { unreachable!("all chains should point to canonical chain."); } @@ -742,7 +783,7 @@ impl BlockchainTree if let Some(old_canon_chain) = old_canon_chain { // state action - chain_action = CanonStateNotification::Reorg { + chain_notification = CanonStateNotification::Reorg { old: Arc::new(old_canon_chain.clone()), new: Arc::new(new_canon_chain.clone()), }; @@ -752,12 +793,13 @@ impl BlockchainTree // error here to confirm that we are reverting nothing from db. error!("Reverting nothing from db on block: #{:?}", block_hash); - chain_action = CanonStateNotification::Commit { new: Arc::new(new_canon_chain) }; + chain_notification = + CanonStateNotification::Commit { new: Arc::new(new_canon_chain) }; } } - // send notification - let _ = self.canon_state_notification_sender.send(chain_action); + // send notification about new canonical chain. + let _ = self.canon_state_notification_sender.send(chain_notification); Ok(()) } diff --git a/crates/blockchain-tree/src/canonical_chain.rs b/crates/blockchain-tree/src/canonical_chain.rs new file mode 100644 index 0000000000..c1b5057746 --- /dev/null +++ b/crates/blockchain-tree/src/canonical_chain.rs @@ -0,0 +1,92 @@ +use reth_primitives::{BlockHash, BlockNumHash, BlockNumber}; +use std::collections::BTreeMap; + +/// This keeps track of all blocks of the canonical chain. +/// +/// This is a wrapper type around an ordered set of block numbers and hashes that belong to the +/// canonical chain. +#[derive(Debug, Clone, Default)] +pub(crate) struct CanonicalChain { + /// All blocks of the canonical chain in order. + chain: BTreeMap, +} + +impl CanonicalChain { + pub(crate) fn new(chain: BTreeMap) -> Self { + Self { chain } + } + + /// Replaces the current chain with the given one. + #[inline] + pub(crate) fn replace(&mut self, chain: BTreeMap) { + self.chain = chain; + } + + /// Returns the block hash of the canonical block with the given number. + #[inline] + pub(crate) fn canonical_hash(&self, number: &BlockNumber) -> Option { + self.chain.get(number).cloned() + } + + /// Returns the block number of the canonical block with the given hash. + #[inline] + pub(crate) fn canonical_number(&self, block_hash: BlockHash) -> Option { + self.chain.iter().find_map( + |(number, hash)| { + if *hash == block_hash { + Some(*number) + } else { + None + } + }, + ) + } + + /// Check if block hash belongs to canonical chain. + #[inline] + pub(crate) fn is_block_hash_canonical( + &self, + last_finalized_block: BlockNumber, + block_hash: &BlockHash, + ) -> bool { + self.chain.range(last_finalized_block..).any(|(_, &h)| h == *block_hash) + } + + /// Extends all items from the given iterator to the chain. + #[inline] + pub(crate) fn extend(&mut self, blocks: impl Iterator) { + self.chain.extend(blocks) + } + + /// Retains only the elements specified by the predicate. + #[inline] + pub(crate) fn retain(&mut self, f: F) + where + F: FnMut(&BlockNumber, &mut BlockHash) -> bool, + { + self.chain.retain(f) + } + + #[inline] + pub(crate) fn inner(&self) -> &BTreeMap { + &self.chain + } + + #[inline] + pub(crate) fn tip(&self) -> BlockNumHash { + self.chain + .last_key_value() + .map(|(&number, &hash)| BlockNumHash { number, hash }) + .unwrap_or_default() + } + + #[inline] + pub(crate) fn iter(&self) -> impl Iterator + '_ { + self.chain.iter().map(|(&number, &hash)| (number, hash)) + } + + #[inline] + pub(crate) fn into_iter(self) -> impl Iterator { + self.chain.into_iter() + } +} diff --git a/crates/blockchain-tree/src/externals.rs b/crates/blockchain-tree/src/externals.rs index 4bd1eaf58b..762e64cdc9 100644 --- a/crates/blockchain-tree/src/externals.rs +++ b/crates/blockchain-tree/src/externals.rs @@ -17,13 +17,13 @@ use std::sync::Arc; #[derive(Debug)] pub struct TreeExternals { /// The database, used to commit the canonical chain, or unwind it. - pub db: DB, + pub(crate) db: DB, /// The consensus engine. - pub consensus: C, + pub(crate) consensus: C, /// The executor factory to execute blocks with. - pub executor_factory: EF, + pub(crate) executor_factory: EF, /// The chain spec. - pub chain_spec: Arc, + pub(crate) chain_spec: Arc, } impl TreeExternals { diff --git a/crates/blockchain-tree/src/lib.rs b/crates/blockchain-tree/src/lib.rs index 4c453264ad..cc7dcb058f 100644 --- a/crates/blockchain-tree/src/lib.rs +++ b/crates/blockchain-tree/src/lib.rs @@ -32,4 +32,6 @@ pub use post_state_data::{PostStateData, PostStateDataRef}; /// Buffer of not executed blocks. pub mod block_buffer; +mod canonical_chain; + pub use block_buffer::BlockBuffer; diff --git a/crates/blockchain-tree/src/shareable.rs b/crates/blockchain-tree/src/shareable.rs index fc343eeea8..7d03b7ea13 100644 --- a/crates/blockchain-tree/src/shareable.rs +++ b/crates/blockchain-tree/src/shareable.rs @@ -92,7 +92,7 @@ impl BlockchainTreeViewer fn canonical_blocks(&self) -> BTreeMap { trace!(target: "blockchain_tree", "Returning canonical blocks in tree"); - self.tree.read().block_indices().canonical_chain().clone() + self.tree.read().block_indices().canonical_chain().inner().clone() } fn find_canonical_ancestor(&self, hash: BlockHash) -> Option { diff --git a/crates/interfaces/src/executor.rs b/crates/interfaces/src/executor.rs index 7a0256a981..455077b2b7 100644 --- a/crates/interfaces/src/executor.rs +++ b/crates/interfaces/src/executor.rs @@ -35,7 +35,7 @@ pub enum Error { #[error("Provider error")] ProviderError, #[error("BlockChainId can't be found in BlockchainTree with internal index {chain_id}")] - BlockChainIdConsistency { chain_id: u64 }, + BlockSideChainIdConsistency { chain_id: u64 }, #[error( "Appending chain on fork (other_chain_fork:?) is not possible as the tip is {chain_tip:?}" )]