reth_blockchain_tree/
canonical_chain.rsuse alloy_eips::BlockNumHash;
use alloy_primitives::{BlockHash, BlockNumber};
use std::collections::BTreeMap;
#[derive(Debug, Clone, Default)]
pub(crate) struct CanonicalChain {
chain: BTreeMap<BlockNumber, BlockHash>,
}
impl CanonicalChain {
pub(crate) const fn new(chain: BTreeMap<BlockNumber, BlockHash>) -> Self {
Self { chain }
}
#[inline]
pub(crate) fn replace(&mut self, chain: BTreeMap<BlockNumber, BlockHash>) {
self.chain = chain;
}
#[inline]
pub(crate) fn canonical_hash(&self, number: &BlockNumber) -> Option<BlockHash> {
self.chain.get(number).copied()
}
#[inline]
pub(crate) fn canonical_number(&self, block_hash: &BlockHash) -> Option<BlockNumber> {
self.chain.iter().find_map(|(number, hash)| (hash == block_hash).then_some(*number))
}
#[inline]
pub(crate) fn extend(&mut self, blocks: impl Iterator<Item = (BlockNumber, BlockHash)>) {
self.chain.extend(blocks)
}
#[inline]
pub(crate) fn retain<F>(&mut self, f: F)
where
F: FnMut(&BlockNumber, &mut BlockHash) -> bool,
{
self.chain.retain(f)
}
#[inline]
pub(crate) const fn inner(&self) -> &BTreeMap<BlockNumber, BlockHash> {
&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<Item = (BlockNumber, BlockHash)> + '_ {
self.chain.iter().map(|(&number, &hash)| (number, hash))
}
#[inline]
pub(crate) fn into_iter(self) -> impl Iterator<Item = (BlockNumber, BlockHash)> {
self.chain.into_iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_replace_canonical_chain() {
let mut initial_chain = BTreeMap::new();
initial_chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
initial_chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
let mut canonical_chain = CanonicalChain::new(initial_chain.clone());
assert_eq!(canonical_chain.chain.len(), 2);
assert_eq!(
canonical_chain.chain.get(&BlockNumber::from(1u64)),
Some(&BlockHash::from([0x01; 32]))
);
let mut new_chain = BTreeMap::new();
new_chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
new_chain.insert(BlockNumber::from(4u64), BlockHash::from([0x04; 32]));
new_chain.insert(BlockNumber::from(5u64), BlockHash::from([0x05; 32]));
canonical_chain.replace(new_chain.clone());
assert_eq!(canonical_chain.chain.len(), 3);
assert!(!canonical_chain.chain.contains_key(&BlockNumber::from(1u64)));
assert_eq!(
canonical_chain.chain.get(&BlockNumber::from(3u64)),
Some(&BlockHash::from([0x03; 32]))
);
}
#[test]
fn test_canonical_hash_canonical_chain() {
let mut chain = BTreeMap::new();
chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
let canonical_chain = CanonicalChain::new(chain.clone());
let block_number = BlockNumber::from(2u64);
let expected_hash = BlockHash::from([0x02; 32]);
assert_eq!(canonical_chain.canonical_hash(&block_number), Some(expected_hash));
let non_existent_block = BlockNumber::from(5u64);
assert_eq!(canonical_chain.canonical_hash(&non_existent_block), None);
}
#[test]
fn test_canonical_number_canonical_chain() {
let mut chain = BTreeMap::new();
chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
let canonical_chain = CanonicalChain::new(chain.clone());
let block_hash = BlockHash::from([0x02; 32]);
let expected_number = BlockNumber::from(2u64);
assert_eq!(canonical_chain.canonical_number(&block_hash), Some(expected_number));
let non_existent_hash = BlockHash::from([0x05; 32]);
assert_eq!(canonical_chain.canonical_number(&non_existent_hash), None);
}
#[test]
fn test_extend_canonical_chain() {
let mut canonical_chain = CanonicalChain::new(BTreeMap::new());
let blocks = vec![
(BlockNumber::from(1u64), BlockHash::from([0x01; 32])),
(BlockNumber::from(2u64), BlockHash::from([0x02; 32])),
]
.into_iter();
canonical_chain.extend(blocks);
assert_eq!(canonical_chain.chain.len(), 2);
assert_eq!(
canonical_chain.chain.get(&BlockNumber::from(1u64)),
Some(&BlockHash::from([0x01; 32]))
);
assert_eq!(
canonical_chain.chain.get(&BlockNumber::from(2u64)),
Some(&BlockHash::from([0x02; 32]))
);
let more_blocks = vec![(BlockNumber::from(3u64), BlockHash::from([0x03; 32]))].into_iter();
canonical_chain.extend(more_blocks);
assert_eq!(canonical_chain.chain.len(), 3);
assert_eq!(
canonical_chain.chain.get(&BlockNumber::from(3u64)),
Some(&BlockHash::from([0x03; 32]))
);
}
#[test]
fn test_retain_canonical_chain() {
let mut chain = BTreeMap::new();
chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
let mut canonical_chain = CanonicalChain::new(chain);
canonical_chain.retain(|number, _| number % 2 == 0);
assert_eq!(canonical_chain.chain.len(), 1);
assert_eq!(
canonical_chain.chain.get(&BlockNumber::from(2u64)),
Some(&BlockHash::from([0x02; 32]))
);
assert_eq!(canonical_chain.chain.get(&BlockNumber::from(1u64)), None);
assert_eq!(canonical_chain.chain.get(&BlockNumber::from(3u64)), None);
}
#[test]
fn test_tip_canonical_chain() {
let mut chain = BTreeMap::new();
chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
let canonical_chain = CanonicalChain::new(chain);
let tip = canonical_chain.tip();
assert_eq!(tip.number, BlockNumber::from(3u64));
assert_eq!(tip.hash, BlockHash::from([0x03; 32]));
let empty_chain = CanonicalChain::new(BTreeMap::new());
let empty_tip = empty_chain.tip();
assert_eq!(empty_tip.number, BlockNumber::default());
assert_eq!(empty_tip.hash, BlockHash::default());
}
}