From 72c66e4cb812f2534eddf547cf50059348a5de33 Mon Sep 17 00:00:00 2001 From: rakita Date: Mon, 3 Apr 2023 17:11:33 +0200 Subject: [PATCH] feat(BlockchainTree): Add getter for pending block hashes (#2091) --- .../src/blockchain_tree/block_indices.rs | 14 +++++++ crates/executor/src/blockchain_tree/mod.rs | 39 ++++++++++++++----- .../executor/src/blockchain_tree/shareable.rs | 13 ++++++- crates/interfaces/src/blockchain_tree.rs | 21 +++++++++- 4 files changed, 73 insertions(+), 14 deletions(-) diff --git a/crates/executor/src/blockchain_tree/block_indices.rs b/crates/executor/src/blockchain_tree/block_indices.rs index 6743beea10..888f32b4c4 100644 --- a/crates/executor/src/blockchain_tree/block_indices.rs +++ b/crates/executor/src/blockchain_tree/block_indices.rs @@ -58,6 +58,20 @@ impl BlockIndices { &self.blocks_to_chain } + /// Return all pending block hashes. Pending blocks are considered blocks + /// that are extending that canonical tip by one block number. + pub fn pending_blocks(&self) -> (BlockNumber, Vec) { + let canonical_tip = self.canonical_tip(); + let pending_blocks = self + .fork_to_child + .get(&canonical_tip.hash) + .cloned() + .unwrap_or_default() + .into_iter() + .collect(); + (canonical_tip.number + 1, pending_blocks) + } + /// 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) diff --git a/crates/executor/src/blockchain_tree/mod.rs b/crates/executor/src/blockchain_tree/mod.rs index f8388aee3c..666cb7633b 100644 --- a/crates/executor/src/blockchain_tree/mod.rs +++ b/crates/executor/src/blockchain_tree/mod.rs @@ -9,8 +9,7 @@ use reth_interfaces::{ Error, }; use reth_primitives::{ - BlockHash, BlockNumHash, BlockNumber, Hardfork, SealedBlock, SealedBlockWithSenders, - SealedHeader, U256, + BlockHash, BlockNumber, Hardfork, SealedBlock, SealedBlockWithSenders, SealedHeader, U256, }; use reth_provider::{post_state::PostState, ExecutorFactory, HeaderProvider, Transaction}; use std::{ @@ -658,11 +657,6 @@ impl BlockchainTree Ok(Chain::new(blocks_and_execution)) } - - /// Return best known canonical tip - pub fn canonical_tip(&self) -> BlockNumHash { - self.block_indices.canonical_tip() - } } #[cfg(test)] @@ -723,6 +717,8 @@ mod tests { block_to_chain: Option>, /// Check fork to child index fork_to_child: Option>>, + /// Pending blocks + pending_blocks: Option<(BlockNumber, HashSet)>, } impl TreeTester { @@ -742,6 +738,14 @@ mod tests { self } + fn with_pending_blocks( + mut self, + pending_blocks: (BlockNumber, HashSet), + ) -> Self { + self.pending_blocks = Some(pending_blocks); + self + } + fn assert( self, tree: &BlockchainTree, @@ -755,6 +759,11 @@ mod tests { if let Some(fork_to_child) = self.fork_to_child { assert_eq!(*tree.block_indices.fork_to_child(), fork_to_child); } + if let Some(pending_blocks) = self.pending_blocks { + let (num, hashes) = tree.block_indices.pending_blocks(); + let hashes = hashes.into_iter().collect::>(); + assert_eq!((num, hashes), pending_blocks); + } } } @@ -818,6 +827,7 @@ mod tests { .with_chain_num(1) .with_block_to_chain(HashMap::from([(block1.hash, 0), (block2.hash, 0)])) .with_fork_to_child(HashMap::from([(block1.parent_hash, HashSet::from([block1.hash]))])) + .with_pending_blocks((block1.number, HashSet::from([block1.hash]))) .assert(&tree); // make block1 canonical @@ -862,6 +872,7 @@ mod tests { block1.parent_hash, HashSet::from([block1a_hash]), )])) + .with_pending_blocks((block2.number + 1, HashSet::from([]))) .assert(&tree); assert_eq!(tree.insert_block_with_senders(block2a.clone()), Ok(BlockStatus::Accepted)); @@ -881,6 +892,7 @@ mod tests { (block1.parent_hash, HashSet::from([block1a_hash])), (block1.hash(), HashSet::from([block2a_hash])), ])) + .with_pending_blocks((block2.number + 1, HashSet::from([]))) .assert(&tree); // make b2a canonical @@ -904,6 +916,7 @@ mod tests { (block1.parent_hash, HashSet::from([block1a_hash])), (block1.hash(), HashSet::from([block2.hash])), ])) + .with_pending_blocks((block2.number + 1, HashSet::new())) .assert(&tree); assert_eq!(tree.make_canonical(&block1a_hash), Ok(())); @@ -930,6 +943,7 @@ mod tests { (block1.parent_hash, HashSet::from([block1.hash])), (block1.hash(), HashSet::from([block2.hash])), ])) + .with_pending_blocks((block1a.number + 1, HashSet::new())) .assert(&tree); // make b2 canonical @@ -955,6 +969,7 @@ mod tests { (block1.parent_hash, HashSet::from([block1a_hash])), (block1.hash(), HashSet::from([block2a_hash])), ])) + .with_pending_blocks((block2.number + 1, HashSet::new())) .assert(&tree); // finalize b1 that would make b1a removed from tree @@ -971,6 +986,7 @@ mod tests { .with_chain_num(1) .with_block_to_chain(HashMap::from([(block2a_hash, 4)])) .with_fork_to_child(HashMap::from([(block1.hash(), HashSet::from([block2a_hash]))])) + .with_pending_blocks((block2.number + 1, HashSet::from([]))) .assert(&tree); // unwind canonical @@ -992,6 +1008,7 @@ mod tests { block1.hash(), HashSet::from([block2a_hash, block2.hash]), )])) + .with_pending_blocks((block2.number, HashSet::from([block2.hash, block2a.hash]))) .assert(&tree); // commit b2a @@ -1003,7 +1020,7 @@ mod tests { // b2 b2a (side chain) // | / // | / - // b1 (canon) + // b1 (finalized) // | // g1 (10) // | @@ -1011,14 +1028,15 @@ mod tests { .with_chain_num(1) .with_block_to_chain(HashMap::from([(block2a_hash, 4)])) .with_fork_to_child(HashMap::from([(block1.hash(), HashSet::from([block2a_hash]))])) + .with_pending_blocks((block2.number + 1, HashSet::new())) .assert(&tree); // update canonical block to b2, this would make b2a be removed assert_eq!(tree.restore_canonical_hashes(12), Ok(())); // Trie state: - // b2 (canon) + // b2 (finalized) // | - // b1 (canon) + // b1 (finalized) // | // g1 (10) // | @@ -1026,6 +1044,7 @@ mod tests { .with_chain_num(0) .with_block_to_chain(HashMap::from([])) .with_fork_to_child(HashMap::from([])) + .with_pending_blocks((block2.number + 1, HashSet::from([]))) .assert(&tree); } } diff --git a/crates/executor/src/blockchain_tree/shareable.rs b/crates/executor/src/blockchain_tree/shareable.rs index a46667c9c7..adbf7ed9b1 100644 --- a/crates/executor/src/blockchain_tree/shareable.rs +++ b/crates/executor/src/blockchain_tree/shareable.rs @@ -61,7 +61,7 @@ impl BlockchainTreeEngine impl BlockchainTreeViewer for ShareableBlockchainTree { - fn pending_blocks(&self) -> BTreeMap> { + fn blocks(&self) -> BTreeMap> { self.tree.read().block_indices().index_of_number_to_pending_blocks().clone() } @@ -70,7 +70,16 @@ impl BlockchainTreeViewer } fn canonical_tip(&self) -> BlockNumHash { - self.tree.read().canonical_tip() + self.tree.read().block_indices().canonical_tip() + } + + fn pending_blocks(&self) -> (BlockNumber, Vec) { + self.tree.read().block_indices().pending_blocks() + } + + fn pending_block(&self) -> Option { + let (number, blocks) = self.tree.read().block_indices().pending_blocks(); + blocks.first().map(|&hash| BlockNumHash { number, hash }) } } diff --git a/crates/interfaces/src/blockchain_tree.rs b/crates/interfaces/src/blockchain_tree.rs index d3966bdba6..8b0c83e752 100644 --- a/crates/interfaces/src/blockchain_tree.rs +++ b/crates/interfaces/src/blockchain_tree.rs @@ -71,13 +71,30 @@ pub enum BlockStatus { } /// Allows read only functionality on the blockchain tree. +/// +/// Tree contains all blocks that are not canonical that can potentially be included +/// as canonical chain. For better explanation we can group blocks into four groups: +/// * Canonical chain blocks +/// * Side chain blocks. Side chain are block that forks from canonical chain but not its tip. +/// * Pending blocks that extend the canonical chain but are not yet included. +/// * Future pending blocks that extend the pending blocks. pub trait BlockchainTreeViewer: Send + Sync { - /// Get all pending block numbers and their hashes. - fn pending_blocks(&self) -> BTreeMap>; + /// Returns both pending and sidechain block numbers and their hashes. + fn blocks(&self) -> BTreeMap>; /// Canonical block number and hashes best known by the tree. fn canonical_blocks(&self) -> BTreeMap; /// Return BlockchainTree best known canonical chain tip (BlockHash, BlockNumber) fn canonical_tip(&self) -> BlockNumHash; + + /// Return block hashes that extends the canonical chain tip by one. + /// This is used to fetch what is considered the pending blocks, blocks that + /// has best chance to become canonical. + fn pending_blocks(&self) -> (BlockNumber, Vec); + + /// Return block hashes that extends the canonical chain tip by one. + /// + /// If there is no such block, return `None`. + fn pending_block(&self) -> Option; }