feat(BlockchainTree): Add getter for pending block hashes (#2091)

This commit is contained in:
rakita
2023-04-03 17:11:33 +02:00
committed by GitHub
parent e4a428e29b
commit 72c66e4cb8
4 changed files with 73 additions and 14 deletions

View File

@@ -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)

View File

@@ -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);
}
}

View File

@@ -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 })
}
}

View File

@@ -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>;
}