reth_beacon_consensus/engine/
invalid_headers.rsuse alloy_consensus::Header;
use alloy_primitives::B256;
use reth_metrics::{
metrics::{Counter, Gauge},
Metrics,
};
use reth_primitives::SealedHeader;
use schnellru::{ByLength, LruMap};
use std::sync::Arc;
use tracing::warn;
const INVALID_HEADER_HIT_EVICTION_THRESHOLD: u8 = 128;
#[derive(Debug)]
pub struct InvalidHeaderCache {
headers: LruMap<B256, HeaderEntry>,
metrics: InvalidHeaderCacheMetrics,
}
impl InvalidHeaderCache {
pub fn new(max_length: u32) -> Self {
Self { headers: LruMap::new(ByLength::new(max_length)), metrics: Default::default() }
}
fn insert_entry(&mut self, hash: B256, header: Arc<Header>) {
self.headers.insert(hash, HeaderEntry { header, hit_count: 0 });
}
pub fn get(&mut self, hash: &B256) -> Option<Arc<Header>> {
{
let entry = self.headers.get(hash)?;
entry.hit_count += 1;
if entry.hit_count < INVALID_HEADER_HIT_EVICTION_THRESHOLD {
return Some(entry.header.clone())
}
}
self.headers.remove(hash);
self.metrics.hit_evictions.increment(1);
None
}
pub fn insert_with_invalid_ancestor(
&mut self,
header_hash: B256,
invalid_ancestor: Arc<Header>,
) {
if self.get(&header_hash).is_none() {
warn!(target: "consensus::engine", hash=?header_hash, ?invalid_ancestor, "Bad block with existing invalid ancestor");
self.insert_entry(header_hash, invalid_ancestor);
self.metrics.known_ancestor_inserts.increment(1);
self.metrics.count.set(self.headers.len() as f64);
}
}
pub fn insert(&mut self, invalid_ancestor: SealedHeader) {
if self.get(&invalid_ancestor.hash()).is_none() {
let hash = invalid_ancestor.hash();
let header = invalid_ancestor.unseal();
warn!(target: "consensus::engine", ?hash, ?header, "Bad block with hash");
self.insert_entry(hash, Arc::new(header));
self.metrics.unique_inserts.increment(1);
self.metrics.count.set(self.headers.len() as f64);
}
}
}
struct HeaderEntry {
hit_count: u8,
header: Arc<Header>,
}
#[derive(Metrics)]
#[metrics(scope = "consensus.engine.beacon.invalid_headers")]
struct InvalidHeaderCacheMetrics {
count: Gauge,
known_ancestor_inserts: Counter,
unique_inserts: Counter,
hit_evictions: Counter,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hit_eviction() {
let mut cache = InvalidHeaderCache::new(10);
let header = Header::default();
let header = SealedHeader::seal(header);
cache.insert(header.clone());
assert_eq!(cache.headers.get(&header.hash()).unwrap().hit_count, 0);
for hit in 1..INVALID_HEADER_HIT_EVICTION_THRESHOLD {
assert!(cache.get(&header.hash()).is_some());
assert_eq!(cache.headers.get(&header.hash()).unwrap().hit_count, hit);
}
assert!(cache.get(&header.hash()).is_none());
}
}