use alloy_primitives::BlockNumber;
use parking_lot::RwLock;
use reth_chainspec::ChainInfo;
use reth_primitives::{BlockNumHash, SealedHeader};
use std::{
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
time::Instant,
};
use tokio::sync::watch;
#[derive(Debug, Clone)]
pub struct ChainInfoTracker {
inner: Arc<ChainInfoInner>,
}
impl ChainInfoTracker {
pub fn new(head: SealedHeader, finalized: Option<SealedHeader>) -> Self {
let (finalized_block, _) = watch::channel(finalized);
let (safe_block, _) = watch::channel(None);
Self {
inner: Arc::new(ChainInfoInner {
last_forkchoice_update: RwLock::new(None),
last_transition_configuration_exchange: RwLock::new(None),
canonical_head_number: AtomicU64::new(head.number),
canonical_head: RwLock::new(head),
safe_block,
finalized_block,
}),
}
}
pub fn chain_info(&self) -> ChainInfo {
let inner = self.inner.canonical_head.read();
ChainInfo { best_hash: inner.hash(), best_number: inner.number }
}
pub fn on_forkchoice_update_received(&self) {
self.inner.last_forkchoice_update.write().replace(Instant::now());
}
pub fn last_forkchoice_update_received_at(&self) -> Option<Instant> {
*self.inner.last_forkchoice_update.read()
}
pub fn on_transition_configuration_exchanged(&self) {
self.inner.last_transition_configuration_exchange.write().replace(Instant::now());
}
pub fn last_transition_configuration_exchanged_at(&self) -> Option<Instant> {
*self.inner.last_transition_configuration_exchange.read()
}
pub fn get_canonical_head(&self) -> SealedHeader {
self.inner.canonical_head.read().clone()
}
pub fn get_safe_header(&self) -> Option<SealedHeader> {
self.inner.safe_block.borrow().clone()
}
pub fn get_finalized_header(&self) -> Option<SealedHeader> {
self.inner.finalized_block.borrow().clone()
}
#[allow(dead_code)]
pub fn get_canonical_num_hash(&self) -> BlockNumHash {
self.inner.canonical_head.read().num_hash()
}
pub fn get_canonical_block_number(&self) -> BlockNumber {
self.inner.canonical_head_number.load(Ordering::Relaxed)
}
pub fn get_safe_num_hash(&self) -> Option<BlockNumHash> {
let h = self.inner.safe_block.borrow();
h.as_ref().map(|h| h.num_hash())
}
pub fn get_finalized_num_hash(&self) -> Option<BlockNumHash> {
let h = self.inner.finalized_block.borrow();
h.as_ref().map(|h| h.num_hash())
}
pub fn set_canonical_head(&self, header: SealedHeader) {
let number = header.number;
*self.inner.canonical_head.write() = header;
self.inner.canonical_head_number.store(number, Ordering::Relaxed);
}
pub fn set_safe(&self, header: SealedHeader) {
self.inner.safe_block.send_modify(|h| {
let _ = h.replace(header);
});
}
pub fn set_finalized(&self, header: SealedHeader) {
self.inner.finalized_block.send_modify(|h| {
let _ = h.replace(header);
});
}
pub fn subscribe_finalized_block(&self) -> watch::Receiver<Option<SealedHeader>> {
self.inner.finalized_block.subscribe()
}
pub fn subscribe_safe_block(&self) -> watch::Receiver<Option<SealedHeader>> {
self.inner.safe_block.subscribe()
}
}
#[derive(Debug)]
struct ChainInfoInner {
last_forkchoice_update: RwLock<Option<Instant>>,
last_transition_configuration_exchange: RwLock<Option<Instant>>,
canonical_head_number: AtomicU64,
canonical_head: RwLock<SealedHeader>,
safe_block: watch::Sender<Option<SealedHeader>>,
finalized_block: watch::Sender<Option<SealedHeader>>,
}