33//! Mirrors geth's `BlockStats` / `reportRecentBlocksLoop` functionality by tracking
44//! block timestamps and event timestamps to compute chain delay metrics.
55
6+ use alloy_consensus:: Header ;
67use alloy_primitives:: B256 ;
78use lru:: LruCache ;
89use once_cell:: sync:: Lazy ;
910use std:: { num:: NonZero , sync:: RwLock } ;
1011
12+ use crate :: consensus:: parlia:: util:: calculate_millisecond_timestamp;
1113use crate :: metrics:: BscChainDelayMetrics ;
1214
1315/// Size of the block stats LRU cache.
@@ -18,7 +20,9 @@ const DEFAULT_MAJORITY_THRESHOLD: usize = 14;
1820
1921/// Per-block tracking data for chain delay metrics.
2022struct BlockStat {
21- /// Block timestamp in milliseconds (header.timestamp * 1000).
23+ /// Block timestamp in milliseconds, from `calculate_millisecond_timestamp(header)`
24+ /// (combines `header.timestamp` seconds with the Lorentz-era ms portion stored in
25+ /// `header.mix_hash`).
2226 block_timestamp_ms : i64 ,
2327 /// Whether the first vote delay has been reported.
2428 first_vote_reported : bool ,
@@ -38,23 +42,49 @@ fn now_ms() -> i64 {
3842 . as_millis ( ) as i64
3943}
4044
41- /// Register a block's timestamp when it is first received from the network.
42- /// Also records the `chain.delay.block_recv` metric (delay from block creation to reception).
43- pub fn on_block_received ( block_hash : B256 , block_timestamp_secs : u64 ) {
44- let block_ts_ms = block_timestamp_secs as i64 * 1000 ;
45+ /// Cache a block's millisecond-precision timestamp so subsequent `on_vote_received` calls can
46+ /// compute vote-delay metrics. Does **not** touch the `chain.delay.block_recv` histogram.
47+ ///
48+ /// On Lorentz and later forks, the block timestamp has millisecond precision (split between
49+ /// `header.timestamp` (seconds) and `header.mix_hash` (ms part)); we use
50+ /// `calculate_millisecond_timestamp` so delays are not biased by the 0-999 ms portion.
51+ fn cache_block_timestamp ( block_hash : B256 , header : & Header ) -> i64 {
52+ let block_ts_ms = calculate_millisecond_timestamp ( header) as i64 ;
53+ let mut cache = BLOCK_STATS . write ( ) . expect ( "block stats poisoned" ) ;
54+ cache. get_or_insert ( block_hash, || BlockStat {
55+ block_timestamp_ms : block_ts_ms,
56+ first_vote_reported : false ,
57+ majority_vote_reported : false ,
58+ } ) ;
59+ block_ts_ms
60+ }
61+
62+ /// Register a block's timestamp when it is first received from the network, and record the
63+ /// `chain.delay.block_recv` metric (delay from block creation to first network reception).
64+ ///
65+ /// This is the network-receive path. For locally mined blocks call [`register_self_mined_block`]
66+ /// instead — they would otherwise pollute `block_recv` with samples that actually measure local
67+ /// mining/finalize latency rather than true network propagation delay (mirrors geth-bsc, where
68+ /// `RecvNewBlockTime` is only set in `handleBlockBroadcast`).
69+ pub fn on_block_received ( block_hash : B256 , header : & Header ) {
70+ let block_ts_ms = cache_block_timestamp ( block_hash, header) ;
4571 let recv_time = now_ms ( ) ;
4672
4773 let delay_ms = recv_time - block_ts_ms;
4874 if delay_ms >= 0 {
4975 CHAIN_DELAY_METRICS . block_recv . record ( delay_ms as f64 ) ;
5076 }
77+ }
5178
52- let mut cache = BLOCK_STATS . write ( ) . expect ( "block stats poisoned" ) ;
53- cache. get_or_insert ( block_hash, || BlockStat {
54- block_timestamp_ms : block_ts_ms,
55- first_vote_reported : false ,
56- majority_vote_reported : false ,
57- } ) ;
79+ /// Register a self-mined block's timestamp so subsequent `on_vote_received` calls work, **without**
80+ /// recording `chain.delay.block_recv`.
81+ ///
82+ /// Mirrors geth-bsc's split between `SendBlockTime` (miner path) and `RecvNewBlockTime`
83+ /// (network path): we want votes for our own block to still count toward `vote_first` /
84+ /// `vote_majority`, but the block-recv histogram must stay clean of self-mined samples so it
85+ /// can be used to diagnose cross-region network propagation delays.
86+ pub fn register_self_mined_block ( block_hash : B256 , header : & Header ) {
87+ cache_block_timestamp ( block_hash, header) ;
5888}
5989
6090/// Called when a vote is added for a block. Records first-vote and majority-vote delay
0 commit comments