mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-01-29 00:58:11 -05:00
feat(BlockchainTree): Add getter for pending block hashes (#2091)
This commit is contained in:
@@ -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<BlockHash>) {
|
||||
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)
|
||||
|
||||
@@ -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<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTree<DB, C, EF>
|
||||
|
||||
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<HashMap<BlockHash, BlockChainId>>,
|
||||
/// Check fork to child index
|
||||
fork_to_child: Option<HashMap<BlockHash, HashSet<BlockHash>>>,
|
||||
/// Pending blocks
|
||||
pending_blocks: Option<(BlockNumber, HashSet<BlockHash>)>,
|
||||
}
|
||||
|
||||
impl TreeTester {
|
||||
@@ -742,6 +738,14 @@ mod tests {
|
||||
self
|
||||
}
|
||||
|
||||
fn with_pending_blocks(
|
||||
mut self,
|
||||
pending_blocks: (BlockNumber, HashSet<BlockHash>),
|
||||
) -> Self {
|
||||
self.pending_blocks = Some(pending_blocks);
|
||||
self
|
||||
}
|
||||
|
||||
fn assert<DB: Database, C: Consensus, EF: ExecutorFactory>(
|
||||
self,
|
||||
tree: &BlockchainTree<DB, C, EF>,
|
||||
@@ -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::<HashSet<_>>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTreeEngine
|
||||
impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTreeViewer
|
||||
for ShareableBlockchainTree<DB, C, EF>
|
||||
{
|
||||
fn pending_blocks(&self) -> BTreeMap<BlockNumber, HashSet<BlockHash>> {
|
||||
fn blocks(&self) -> BTreeMap<BlockNumber, HashSet<BlockHash>> {
|
||||
self.tree.read().block_indices().index_of_number_to_pending_blocks().clone()
|
||||
}
|
||||
|
||||
@@ -70,7 +70,16 @@ impl<DB: Database, C: Consensus, EF: ExecutorFactory> BlockchainTreeViewer
|
||||
}
|
||||
|
||||
fn canonical_tip(&self) -> BlockNumHash {
|
||||
self.tree.read().canonical_tip()
|
||||
self.tree.read().block_indices().canonical_tip()
|
||||
}
|
||||
|
||||
fn pending_blocks(&self) -> (BlockNumber, Vec<BlockHash>) {
|
||||
self.tree.read().block_indices().pending_blocks()
|
||||
}
|
||||
|
||||
fn pending_block(&self) -> Option<BlockNumHash> {
|
||||
let (number, blocks) = self.tree.read().block_indices().pending_blocks();
|
||||
blocks.first().map(|&hash| BlockNumHash { number, hash })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<BlockNumber, HashSet<BlockHash>>;
|
||||
/// Returns both pending and sidechain block numbers and their hashes.
|
||||
fn blocks(&self) -> BTreeMap<BlockNumber, HashSet<BlockHash>>;
|
||||
|
||||
/// Canonical block number and hashes best known by the tree.
|
||||
fn canonical_blocks(&self) -> BTreeMap<BlockNumber, BlockHash>;
|
||||
|
||||
/// 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<BlockHash>);
|
||||
|
||||
/// Return block hashes that extends the canonical chain tip by one.
|
||||
///
|
||||
/// If there is no such block, return `None`.
|
||||
fn pending_block(&self) -> Option<BlockNumHash>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user