1use crate::tree::metrics::BlockBufferMetrics;
2use alloy_consensus::BlockHeader;
3use alloy_primitives::{BlockHash, BlockNumber};
4use indexmap::IndexSet;
5use reth_network_p2p::full_block::SealedBlockWithAccessList;
6use reth_primitives_traits::{Block, SealedBlock};
7use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
8
9#[derive(Debug)]
21pub struct BlockBuffer<B: Block> {
22 pub(crate) blocks: HashMap<BlockHash, SealedBlockWithAccessList<B>>,
24 pub(crate) parent_to_child: HashMap<BlockHash, IndexSet<BlockHash>>,
28 pub(crate) earliest_blocks: BTreeMap<BlockNumber, HashSet<BlockHash>>,
31 pub(crate) block_queue: VecDeque<BlockHash>,
34 pub(crate) max_blocks: usize,
36 pub(crate) metrics: BlockBufferMetrics,
38}
39
40impl<B: Block> BlockBuffer<B> {
41 pub fn new(limit: u32) -> Self {
43 Self {
44 blocks: Default::default(),
45 parent_to_child: Default::default(),
46 earliest_blocks: Default::default(),
47 block_queue: VecDeque::default(),
48 max_blocks: limit as usize,
49 metrics: Default::default(),
50 }
51 }
52
53 pub fn block(&self, hash: &BlockHash) -> Option<&SealedBlock<B>> {
55 self.blocks.get(hash).map(|block| &**block)
56 }
57
58 pub fn lowest_ancestor(&self, hash: &BlockHash) -> Option<&SealedBlock<B>> {
60 let mut current_block = self.blocks.get(hash)?;
61 while let Some(parent) = self.blocks.get(¤t_block.parent_hash()) {
62 current_block = parent;
63 }
64 Some(current_block)
65 }
66
67 pub fn insert_block(&mut self, block: SealedBlockWithAccessList<B>) {
69 let hash = block.hash();
70
71 match self.blocks.entry(hash) {
72 std::collections::hash_map::Entry::Occupied(mut entry) => {
73 if entry.get().data().is_none() && block.data().is_some() {
75 entry.insert(block);
76 }
77 return
78 }
79 std::collections::hash_map::Entry::Vacant(entry) => {
80 self.parent_to_child.entry(block.parent_hash()).or_default().insert(hash);
81 self.earliest_blocks.entry(block.number()).or_default().insert(hash);
82 entry.insert(block);
83 }
84 };
85
86 if self.block_queue.len() >= self.max_blocks {
88 if let Some(evicted_hash) = self.block_queue.pop_front() {
90 self.remove_block(&evicted_hash);
91 }
92 }
93 self.block_queue.push_back(hash);
94 self.metrics.blocks.set(self.blocks.len() as f64);
95 }
96
97 pub fn remove_block_with_children(
104 &mut self,
105 parent_hash: &BlockHash,
106 ) -> Vec<SealedBlockWithAccessList<B>> {
107 let removed = self
108 .remove_block(parent_hash)
109 .into_iter()
110 .chain(self.remove_children(vec![*parent_hash]))
111 .collect();
112 self.metrics.blocks.set(self.blocks.len() as f64);
113 removed
114 }
115
116 pub fn remove_old_blocks(&mut self, block_number: BlockNumber) {
118 let mut block_hashes_to_remove = Vec::new();
119
120 while let Some(entry) = self.earliest_blocks.first_entry() {
122 if *entry.key() > block_number {
123 break
124 }
125 let block_hashes = entry.remove();
126 block_hashes_to_remove.extend(block_hashes);
127 }
128
129 for block_hash in &block_hashes_to_remove {
131 self.remove_block(block_hash);
133 }
134
135 self.remove_children(block_hashes_to_remove);
136 self.metrics.blocks.set(self.blocks.len() as f64);
137 }
138
139 fn remove_from_earliest_blocks(&mut self, number: BlockNumber, hash: &BlockHash) {
141 if let Some(entry) = self.earliest_blocks.get_mut(&number) {
142 entry.remove(hash);
143 if entry.is_empty() {
144 self.earliest_blocks.remove(&number);
145 }
146 }
147 }
148
149 fn remove_from_parent(&mut self, parent_hash: BlockHash, hash: &BlockHash) {
151 if let Some(entry) = self.parent_to_child.get_mut(&parent_hash) {
153 entry.swap_remove(hash);
154 if entry.is_empty() {
156 self.parent_to_child.remove(&parent_hash);
157 }
158 }
159 }
160
161 fn remove_block(&mut self, hash: &BlockHash) -> Option<SealedBlockWithAccessList<B>> {
166 let block = self.blocks.remove(hash)?;
167 self.remove_from_earliest_blocks(block.number(), hash);
168 self.remove_from_parent(block.parent_hash(), hash);
169 self.block_queue.retain(|h| h != hash);
170 Some(block)
171 }
172
173 fn remove_children(
175 &mut self,
176 parent_hashes: Vec<BlockHash>,
177 ) -> Vec<SealedBlockWithAccessList<B>> {
178 let mut remove_parent_children = parent_hashes;
181 let mut removed_blocks = Vec::new();
182 while let Some(parent_hash) = remove_parent_children.pop() {
183 if let Some(parent_children) = self.parent_to_child.remove(&parent_hash) {
185 for child_hash in &parent_children {
187 if let Some(block) = self.remove_block(child_hash) {
188 removed_blocks.push(block);
189 }
190 }
191 remove_parent_children.extend(parent_children);
192 }
193 }
194 removed_blocks
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201 use alloy_eip7928::bal::RawBal;
202 use alloy_eips::BlockNumHash;
203 use alloy_primitives::{BlockHash, Bytes};
204 use reth_testing_utils::generators::{self, random_block, BlockParams, Rng};
205 use std::collections::HashMap;
206
207 fn create_block<R: Rng>(
209 rng: &mut R,
210 number: u64,
211 parent: BlockHash,
212 ) -> SealedBlock<reth_ethereum_primitives::Block> {
213 random_block(rng, number, BlockParams { parent: Some(parent), ..Default::default() })
214 }
215
216 fn assert_buffer_lengths<B: Block>(buffer: &BlockBuffer<B>, expected: usize) {
218 assert_eq!(buffer.blocks.len(), expected);
219 assert_eq!(buffer.block_queue.len(), expected);
220 assert_eq!(
221 buffer.parent_to_child.iter().fold(0, |acc, (_, hashes)| acc + hashes.len()),
222 expected
223 );
224 assert_eq!(
225 buffer.earliest_blocks.iter().fold(0, |acc, (_, hashes)| acc + hashes.len()),
226 expected
227 );
228 }
229
230 fn assert_block_removal<B: Block>(
232 buffer: &BlockBuffer<B>,
233 block: &SealedBlock<reth_ethereum_primitives::Block>,
234 ) {
235 assert!(!buffer.blocks.contains_key(&block.hash()));
236 assert!(buffer
237 .parent_to_child
238 .get(&block.parent_hash)
239 .and_then(|p| p.get(&block.hash()))
240 .is_none());
241 assert!(buffer
242 .earliest_blocks
243 .get(&block.number)
244 .and_then(|hashes| hashes.get(&block.hash()))
245 .is_none());
246 }
247
248 #[test]
249 fn simple_insertion() {
250 let mut rng = generators::rng();
251 let parent = rng.random();
252 let block1 = create_block(&mut rng, 10, parent);
253 let mut buffer = BlockBuffer::new(3);
254
255 buffer.insert_block(block1.clone().into());
256 assert_buffer_lengths(&buffer, 1);
257 assert_eq!(buffer.block(&block1.hash()), Some(&block1));
258 }
259
260 fn raw_bal() -> RawBal {
262 RawBal::from(Bytes::from_static(&[alloy_rlp::EMPTY_LIST_CODE]))
263 }
264
265 #[test]
266 fn preserves_access_list_for_buffered_blocks() {
267 let mut rng = generators::rng();
268
269 let access_list = raw_bal();
270 let parent = rng.random();
271 let block = create_block(&mut rng, 10, parent);
272
273 let mut buffer = BlockBuffer::new(1);
274 buffer
275 .insert_block(SealedBlockWithAccessList::new(block.clone(), Some(access_list.clone())));
276
277 let blocks = buffer.remove_block_with_children(&parent);
278 assert_eq!(blocks.len(), 1);
279 assert_eq!(&*blocks[0], &block);
280 assert_eq!(blocks[0].data().as_ref(), Some(&access_list));
281 }
282
283 #[test]
284 fn updates_buffered_duplicate_with_access_list() {
285 let mut rng = generators::rng();
286
287 let access_list = raw_bal();
288 let parent = rng.random();
289 let block = create_block(&mut rng, 10, parent);
290
291 let mut buffer = BlockBuffer::new(1);
292 buffer.insert_block(block.clone().into());
293 buffer
294 .insert_block(SealedBlockWithAccessList::new(block.clone(), Some(access_list.clone())));
295
296 let blocks = buffer.remove_block_with_children(&parent);
297 assert_eq!(blocks.len(), 1);
298 assert_eq!(&*blocks[0], &block);
299 assert_eq!(blocks[0].data().as_ref(), Some(&access_list));
300 }
301
302 #[test]
303 fn take_entire_chain_of_children() {
304 let mut rng = generators::rng();
305
306 let main_parent_hash = rng.random();
307 let block1 = create_block(&mut rng, 10, main_parent_hash);
308 let block2 = create_block(&mut rng, 11, block1.hash());
309 let block3 = create_block(&mut rng, 12, block2.hash());
310 let parent4 = rng.random();
311 let block4 = create_block(&mut rng, 14, parent4);
312
313 let mut buffer = BlockBuffer::new(5);
314
315 buffer.insert_block(block1.clone().into());
316 buffer.insert_block(block2.clone().into());
317 buffer.insert_block(block3.clone().into());
318 buffer.insert_block(block4.clone().into());
319
320 assert_buffer_lengths(&buffer, 4);
321 assert_eq!(buffer.block(&block4.hash()), Some(&block4));
322 assert_eq!(buffer.block(&block2.hash()), Some(&block2));
323 assert_eq!(buffer.block(&main_parent_hash), None);
324
325 assert_eq!(buffer.lowest_ancestor(&block4.hash()), Some(&block4));
326 assert_eq!(buffer.lowest_ancestor(&block3.hash()), Some(&block1));
327 assert_eq!(buffer.lowest_ancestor(&block1.hash()), Some(&block1));
328 assert_eq!(
329 buffer
330 .remove_block_with_children(&main_parent_hash)
331 .into_iter()
332 .map(|b| b.split().0)
333 .collect::<Vec<_>>(),
334 vec![block1, block2, block3]
335 );
336 assert_buffer_lengths(&buffer, 1);
337 }
338
339 #[test]
340 fn take_all_multi_level_children() {
341 let mut rng = generators::rng();
342
343 let main_parent_hash = rng.random();
344 let block1 = create_block(&mut rng, 10, main_parent_hash);
345 let block2 = create_block(&mut rng, 11, block1.hash());
346 let block3 = create_block(&mut rng, 11, block1.hash());
347 let block4 = create_block(&mut rng, 12, block2.hash());
348
349 let mut buffer = BlockBuffer::new(5);
350
351 buffer.insert_block(block1.clone().into());
352 buffer.insert_block(block2.clone().into());
353 buffer.insert_block(block3.clone().into());
354 buffer.insert_block(block4.clone().into());
355
356 assert_buffer_lengths(&buffer, 4);
357 assert_eq!(
358 buffer
359 .remove_block_with_children(&main_parent_hash)
360 .into_iter()
361 .map(|b| (b.hash(), b.split().0))
362 .collect::<HashMap<_, _>>(),
363 HashMap::from([
364 (block1.hash(), block1),
365 (block2.hash(), block2),
366 (block3.hash(), block3),
367 (block4.hash(), block4)
368 ])
369 );
370 assert_buffer_lengths(&buffer, 0);
371 }
372
373 #[test]
374 fn take_block_with_children() {
375 let mut rng = generators::rng();
376
377 let main_parent = BlockNumHash::new(9, rng.random());
378 let block1 = create_block(&mut rng, 10, main_parent.hash);
379 let block2 = create_block(&mut rng, 11, block1.hash());
380 let block3 = create_block(&mut rng, 11, block1.hash());
381 let block4 = create_block(&mut rng, 12, block2.hash());
382
383 let mut buffer = BlockBuffer::new(5);
384
385 buffer.insert_block(block1.clone().into());
386 buffer.insert_block(block2.clone().into());
387 buffer.insert_block(block3.clone().into());
388 buffer.insert_block(block4.clone().into());
389
390 assert_buffer_lengths(&buffer, 4);
391 assert_eq!(
392 buffer
393 .remove_block_with_children(&block1.hash())
394 .into_iter()
395 .map(|b| (b.hash(), b.split().0))
396 .collect::<HashMap<_, _>>(),
397 HashMap::from([
398 (block1.hash(), block1),
399 (block2.hash(), block2),
400 (block3.hash(), block3),
401 (block4.hash(), block4)
402 ])
403 );
404 assert_buffer_lengths(&buffer, 0);
405 }
406
407 #[test]
408 fn remove_chain_of_children() {
409 let mut rng = generators::rng();
410
411 let main_parent = BlockNumHash::new(9, rng.random());
412 let block1 = create_block(&mut rng, 10, main_parent.hash);
413 let block2 = create_block(&mut rng, 11, block1.hash());
414 let block3 = create_block(&mut rng, 12, block2.hash());
415 let parent4 = rng.random();
416 let block4 = create_block(&mut rng, 14, parent4);
417
418 let mut buffer = BlockBuffer::new(5);
419
420 buffer.insert_block(block1.clone().into());
421 buffer.insert_block(block2.into());
422 buffer.insert_block(block3.into());
423 buffer.insert_block(block4.into());
424
425 assert_buffer_lengths(&buffer, 4);
426 buffer.remove_old_blocks(block1.number);
427 assert_buffer_lengths(&buffer, 1);
428 }
429
430 #[test]
431 fn remove_all_multi_level_children() {
432 let mut rng = generators::rng();
433
434 let main_parent = BlockNumHash::new(9, rng.random());
435 let block1 = create_block(&mut rng, 10, main_parent.hash);
436 let block2 = create_block(&mut rng, 11, block1.hash());
437 let block3 = create_block(&mut rng, 11, block1.hash());
438 let block4 = create_block(&mut rng, 12, block2.hash());
439
440 let mut buffer = BlockBuffer::new(5);
441
442 buffer.insert_block(block1.clone().into());
443 buffer.insert_block(block2.into());
444 buffer.insert_block(block3.into());
445 buffer.insert_block(block4.into());
446
447 assert_buffer_lengths(&buffer, 4);
448 buffer.remove_old_blocks(block1.number);
449 assert_buffer_lengths(&buffer, 0);
450 }
451
452 #[test]
453 fn remove_multi_chains() {
454 let mut rng = generators::rng();
455
456 let main_parent = BlockNumHash::new(9, rng.random());
457 let block1 = create_block(&mut rng, 10, main_parent.hash);
458 let block1a = create_block(&mut rng, 10, main_parent.hash);
459 let block2 = create_block(&mut rng, 11, block1.hash());
460 let block2a = create_block(&mut rng, 11, block1.hash());
461 let random_parent1 = rng.random();
462 let random_block1 = create_block(&mut rng, 10, random_parent1);
463 let random_parent2 = rng.random();
464 let random_block2 = create_block(&mut rng, 11, random_parent2);
465 let random_parent3 = rng.random();
466 let random_block3 = create_block(&mut rng, 12, random_parent3);
467
468 let mut buffer = BlockBuffer::new(10);
469
470 buffer.insert_block(block1.clone().into());
471 buffer.insert_block(block1a.clone().into());
472 buffer.insert_block(block2.clone().into());
473 buffer.insert_block(block2a.clone().into());
474 buffer.insert_block(random_block1.clone().into());
475 buffer.insert_block(random_block2.clone().into());
476 buffer.insert_block(random_block3.clone().into());
477
478 assert_eq!(buffer.lowest_ancestor(&random_block1.hash()), Some(&random_block1));
480 assert_eq!(buffer.lowest_ancestor(&random_block2.hash()), Some(&random_block2));
481 assert_eq!(buffer.lowest_ancestor(&random_block3.hash()), Some(&random_block3));
482
483 assert_eq!(buffer.lowest_ancestor(&block2a.hash()), Some(&block1));
485 assert_eq!(buffer.lowest_ancestor(&block2.hash()), Some(&block1));
486
487 assert_eq!(buffer.lowest_ancestor(&block1a.hash()), Some(&block1a));
489 assert_eq!(buffer.lowest_ancestor(&block1.hash()), Some(&block1));
490
491 assert_buffer_lengths(&buffer, 7);
492 buffer.remove_old_blocks(10);
493 assert_buffer_lengths(&buffer, 2);
494 }
495
496 #[test]
497 fn evict_with_gap() {
498 let mut rng = generators::rng();
499
500 let main_parent = BlockNumHash::new(9, rng.random());
501 let block1 = create_block(&mut rng, 10, main_parent.hash);
502 let block2 = create_block(&mut rng, 11, block1.hash());
503 let block3 = create_block(&mut rng, 12, block2.hash());
504 let parent4 = rng.random();
505 let block4 = create_block(&mut rng, 13, parent4);
506
507 let mut buffer = BlockBuffer::new(3);
508
509 buffer.insert_block(block1.clone().into());
510 buffer.insert_block(block2.clone().into());
511 buffer.insert_block(block3.clone().into());
512
513 assert_eq!(buffer.lowest_ancestor(&block3.hash()), Some(&block1));
515 assert_eq!(buffer.lowest_ancestor(&block2.hash()), Some(&block1));
516 assert_eq!(buffer.lowest_ancestor(&block1.hash()), Some(&block1));
517
518 buffer.insert_block(block4.clone().into());
519
520 assert_eq!(buffer.lowest_ancestor(&block4.hash()), Some(&block4));
521
522 assert_block_removal(&buffer, &block1);
524
525 assert_eq!(buffer.lowest_ancestor(&block3.hash()), Some(&block2));
527 assert_eq!(buffer.lowest_ancestor(&block2.hash()), Some(&block2));
528 assert_eq!(buffer.lowest_ancestor(&block1.hash()), None);
529
530 assert_buffer_lengths(&buffer, 3);
531 }
532
533 #[test]
534 fn simple_eviction() {
535 let mut rng = generators::rng();
536
537 let main_parent = BlockNumHash::new(9, rng.random());
538 let block1 = create_block(&mut rng, 10, main_parent.hash);
539 let block2 = create_block(&mut rng, 11, block1.hash());
540 let block3 = create_block(&mut rng, 12, block2.hash());
541 let parent4 = rng.random();
542 let block4 = create_block(&mut rng, 13, parent4);
543
544 let mut buffer = BlockBuffer::new(3);
545
546 buffer.insert_block(block1.clone().into());
547 buffer.insert_block(block2.into());
548 buffer.insert_block(block3.into());
549 buffer.insert_block(block4.into());
550
551 assert_block_removal(&buffer, &block1);
553
554 assert_buffer_lengths(&buffer, 3);
555 }
556
557 #[test]
558 fn eviction_parent_child_cleanup() {
559 let mut rng = generators::rng();
560
561 let main_parent = BlockNumHash::new(9, rng.random());
562 let block1 = create_block(&mut rng, 10, main_parent.hash);
563 let block2 = create_block(&mut rng, 11, block1.hash());
564 let unrelated_parent = rng.random();
566 let unrelated_block = create_block(&mut rng, 12, unrelated_parent);
567
568 let mut buffer = BlockBuffer::new(2);
570
571 buffer.insert_block(block1.clone().into());
572 buffer.insert_block(block2.clone().into());
573
574 assert!(buffer
576 .parent_to_child
577 .get(&main_parent.hash)
578 .and_then(|s| s.get(&block1.hash()))
579 .is_some());
580 assert!(buffer
581 .parent_to_child
582 .get(&block1.hash())
583 .and_then(|s| s.get(&block2.hash()))
584 .is_some());
585
586 buffer.insert_block(unrelated_block.into());
588
589 assert_block_removal(&buffer, &block1);
591
592 assert!(buffer
594 .parent_to_child
595 .get(&main_parent.hash)
596 .and_then(|s| s.get(&block1.hash()))
597 .is_none());
598
599 assert!(buffer
601 .parent_to_child
602 .get(&block1.hash())
603 .and_then(|s| s.get(&block2.hash()))
604 .is_some());
605
606 assert_eq!(buffer.lowest_ancestor(&block2.hash()), Some(&block2));
608 }
609}