reth_blockchain_tree/
canonical_chain.rs1use alloy_eips::BlockNumHash;
2use alloy_primitives::{BlockHash, BlockNumber};
3use std::collections::BTreeMap;
4
5#[derive(Debug, Clone, Default)]
10pub(crate) struct CanonicalChain {
11 chain: BTreeMap<BlockNumber, BlockHash>,
13}
14
15impl CanonicalChain {
16 pub(crate) const fn new(chain: BTreeMap<BlockNumber, BlockHash>) -> Self {
17 Self { chain }
18 }
19
20 #[inline]
22 pub(crate) fn replace(&mut self, chain: BTreeMap<BlockNumber, BlockHash>) {
23 self.chain = chain;
24 }
25
26 #[inline]
28 pub(crate) fn canonical_hash(&self, number: &BlockNumber) -> Option<BlockHash> {
29 self.chain.get(number).copied()
30 }
31
32 #[inline]
34 pub(crate) fn canonical_number(&self, block_hash: &BlockHash) -> Option<BlockNumber> {
35 self.chain.iter().find_map(|(number, hash)| (hash == block_hash).then_some(*number))
36 }
37
38 #[inline]
40 pub(crate) fn extend(&mut self, blocks: impl Iterator<Item = (BlockNumber, BlockHash)>) {
41 self.chain.extend(blocks)
42 }
43
44 #[inline]
46 pub(crate) fn retain<F>(&mut self, f: F)
47 where
48 F: FnMut(&BlockNumber, &mut BlockHash) -> bool,
49 {
50 self.chain.retain(f)
51 }
52
53 #[inline]
54 pub(crate) const fn inner(&self) -> &BTreeMap<BlockNumber, BlockHash> {
55 &self.chain
56 }
57
58 #[inline]
59 pub(crate) fn tip(&self) -> BlockNumHash {
60 self.chain
61 .last_key_value()
62 .map(|(&number, &hash)| BlockNumHash { number, hash })
63 .unwrap_or_default()
64 }
65
66 #[inline]
67 pub(crate) fn iter(&self) -> impl Iterator<Item = (BlockNumber, BlockHash)> + '_ {
68 self.chain.iter().map(|(&number, &hash)| (number, hash))
69 }
70
71 #[inline]
72 pub(crate) fn into_iter(self) -> impl Iterator<Item = (BlockNumber, BlockHash)> {
73 self.chain.into_iter()
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn test_replace_canonical_chain() {
83 let mut initial_chain = BTreeMap::new();
85 initial_chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
86 initial_chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
87
88 let mut canonical_chain = CanonicalChain::new(initial_chain.clone());
89
90 assert_eq!(canonical_chain.chain.len(), 2);
92 assert_eq!(
93 canonical_chain.chain.get(&BlockNumber::from(1u64)),
94 Some(&BlockHash::from([0x01; 32]))
95 );
96
97 let mut new_chain = BTreeMap::new();
99 new_chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
100 new_chain.insert(BlockNumber::from(4u64), BlockHash::from([0x04; 32]));
101 new_chain.insert(BlockNumber::from(5u64), BlockHash::from([0x05; 32]));
102
103 canonical_chain.replace(new_chain.clone());
104
105 assert_eq!(canonical_chain.chain.len(), 3);
107 assert!(!canonical_chain.chain.contains_key(&BlockNumber::from(1u64)));
108 assert_eq!(
109 canonical_chain.chain.get(&BlockNumber::from(3u64)),
110 Some(&BlockHash::from([0x03; 32]))
111 );
112 }
113
114 #[test]
115 fn test_canonical_hash_canonical_chain() {
116 let mut chain = BTreeMap::new();
118 chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
119 chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
120 chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
121
122 let canonical_chain = CanonicalChain::new(chain.clone());
124
125 let block_number = BlockNumber::from(2u64);
127 let expected_hash = BlockHash::from([0x02; 32]);
128 assert_eq!(canonical_chain.canonical_hash(&block_number), Some(expected_hash));
129
130 let non_existent_block = BlockNumber::from(5u64);
132 assert_eq!(canonical_chain.canonical_hash(&non_existent_block), None);
133 }
134
135 #[test]
136 fn test_canonical_number_canonical_chain() {
137 let mut chain = BTreeMap::new();
139 chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
140 chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
141 chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
142
143 let canonical_chain = CanonicalChain::new(chain.clone());
145
146 let block_hash = BlockHash::from([0x02; 32]);
148 let expected_number = BlockNumber::from(2u64);
149 assert_eq!(canonical_chain.canonical_number(&block_hash), Some(expected_number));
150
151 let non_existent_hash = BlockHash::from([0x05; 32]);
153 assert_eq!(canonical_chain.canonical_number(&non_existent_hash), None);
154 }
155
156 #[test]
157 fn test_extend_canonical_chain() {
158 let mut canonical_chain = CanonicalChain::new(BTreeMap::new());
160
161 let blocks = vec![
163 (BlockNumber::from(1u64), BlockHash::from([0x01; 32])),
164 (BlockNumber::from(2u64), BlockHash::from([0x02; 32])),
165 ]
166 .into_iter();
167
168 canonical_chain.extend(blocks);
170
171 assert_eq!(canonical_chain.chain.len(), 2);
173 assert_eq!(
174 canonical_chain.chain.get(&BlockNumber::from(1u64)),
175 Some(&BlockHash::from([0x01; 32]))
176 );
177 assert_eq!(
178 canonical_chain.chain.get(&BlockNumber::from(2u64)),
179 Some(&BlockHash::from([0x02; 32]))
180 );
181
182 let more_blocks = vec![(BlockNumber::from(3u64), BlockHash::from([0x03; 32]))].into_iter();
184 canonical_chain.extend(more_blocks);
185
186 assert_eq!(canonical_chain.chain.len(), 3);
187 assert_eq!(
188 canonical_chain.chain.get(&BlockNumber::from(3u64)),
189 Some(&BlockHash::from([0x03; 32]))
190 );
191 }
192
193 #[test]
194 fn test_retain_canonical_chain() {
195 let mut chain = BTreeMap::new();
197 chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
198 chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
199 chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
200
201 let mut canonical_chain = CanonicalChain::new(chain);
203
204 canonical_chain.retain(|number, _| number % 2 == 0);
206
207 assert_eq!(canonical_chain.chain.len(), 1);
209 assert_eq!(
210 canonical_chain.chain.get(&BlockNumber::from(2u64)),
211 Some(&BlockHash::from([0x02; 32]))
212 );
213
214 assert_eq!(canonical_chain.chain.get(&BlockNumber::from(1u64)), None);
216 assert_eq!(canonical_chain.chain.get(&BlockNumber::from(3u64)), None);
217 }
218
219 #[test]
220 fn test_tip_canonical_chain() {
221 let mut chain = BTreeMap::new();
223 chain.insert(BlockNumber::from(1u64), BlockHash::from([0x01; 32]));
224 chain.insert(BlockNumber::from(2u64), BlockHash::from([0x02; 32]));
225 chain.insert(BlockNumber::from(3u64), BlockHash::from([0x03; 32]));
226
227 let canonical_chain = CanonicalChain::new(chain);
229
230 let tip = canonical_chain.tip();
232 assert_eq!(tip.number, BlockNumber::from(3u64));
233 assert_eq!(tip.hash, BlockHash::from([0x03; 32]));
234
235 let empty_chain = CanonicalChain::new(BTreeMap::new());
237 let empty_tip = empty_chain.tip();
238 assert_eq!(empty_tip.number, BlockNumber::default());
239 assert_eq!(empty_tip.hash, BlockHash::default());
240 }
241}