Skip to content

Commit cc1d413

Browse files
authored
Merge branch 'main' into feat/chain-events-bus
2 parents 8464e6a + c61165a commit cc1d413

5 files changed

Lines changed: 54 additions & 15 deletions

File tree

crates/blockchain/src/metrics.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -706,9 +706,13 @@ pub fn set_node_start_time() {
706706
LEAN_NODE_START_TIME_SECONDS.set(timestamp as i64);
707707
}
708708

709-
/// Increment the valid attestations counter.
710-
pub fn inc_attestations_valid(count: u64) {
711-
LEAN_ATTESTATIONS_VALID_TOTAL.inc_by(count);
709+
/// Increment the valid attestations counter by one.
710+
///
711+
/// Counts one per gossip attestation (single or aggregated) that passes all
712+
/// validation checks, symmetric with [`inc_attestations_invalid`]. Matches
713+
/// leanSpec's `lean_attestations_valid_total`, a validation-pipeline counter.
714+
pub fn inc_attestations_valid() {
715+
LEAN_ATTESTATIONS_VALID_TOTAL.inc();
712716
}
713717

714718
/// Increment the invalid attestations counter.

crates/blockchain/src/store.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ pub fn on_gossip_attestation(
427427
metrics::update_gossip_signatures(store.gossip_signatures_count());
428428
}
429429

430-
metrics::inc_attestations_valid(1);
430+
metrics::inc_attestations_valid();
431431

432432
let slot = attestation.data.slot;
433433
let target_slot = attestation.data.target.slot;
@@ -536,7 +536,7 @@ fn on_gossip_aggregated_attestation_core(
536536
"Aggregated attestation processed"
537537
);
538538

539-
metrics::inc_attestations_valid(1);
539+
metrics::inc_attestations_valid();
540540

541541
Ok(())
542542
}
@@ -569,7 +569,7 @@ fn on_block_core(
569569
signed_block: SignedBlock,
570570
verify: bool,
571571
) -> Result<(), StoreError> {
572-
let _timing = metrics::time_fork_choice_block_processing();
572+
let timing = metrics::time_fork_choice_block_processing();
573573
let block_start = std::time::Instant::now();
574574

575575
let block = &signed_block.message;
@@ -581,6 +581,7 @@ fn on_block_core(
581581
.has_state(&block_root)
582582
.expect("DB read should succeed")
583583
{
584+
timing.discard();
584585
return Ok(());
585586
}
586587

@@ -682,11 +683,11 @@ fn on_block_core(
682683
.insert_state(block_root, post_state)
683684
.expect("DB insert should succeed");
684685

685-
for att in block.body.attestations.iter() {
686-
// Count each participating validator as a valid attestation.
687-
let count = validator_indices(&att.aggregation_bits).count() as u64;
688-
metrics::inc_attestations_valid(count);
689-
}
686+
// Block-included attestations are intentionally not counted here.
687+
// `lean_attestations_valid_total` tracks the gossip validation pipeline
688+
// (symmetric with `inc_attestations_invalid`), matching leanSpec. Votes
689+
// arriving inside a block are counted by
690+
// `lean_state_transition_attestations_processed_total` instead.
690691

691692
// Update forkchoice head based on new block and attestations
692693
update_head(store, false);

crates/blockchain/state_transition/tests/stf_spectests.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@ fn run(path: &Path) -> datatest_stable::Result<()> {
5353
match (result, test.post) {
5454
(Ok(_), Some(expected_post)) => {
5555
compare_post_states(&post_state, &expected_post, &block_registry)?;
56+
// Hardening: the full post-state hash_tree_root must equal the
57+
// fixture's `postStateRoot`. The per-field `post` checks only
58+
// pin the fields the fixture chose to enumerate; this catches
59+
// any state field they omit.
60+
if let Some(expected_root) = test.post_state_root {
61+
let actual_root = post_state.hash_tree_root();
62+
if actual_root != expected_root {
63+
return Err(format!(
64+
"Test '{name}' post-state root mismatch: \
65+
expected {expected_root:?}, got {actual_root:?}"
66+
)
67+
.into());
68+
}
69+
}
5670
}
5771
(Ok(_), None) => {
5872
return Err(

crates/blockchain/state_transition/tests/types.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,10 @@ pub struct StateTransitionTest {
3333
pub pre: TestState,
3434
pub blocks: Vec<Block>,
3535
pub post: Option<PostState>,
36-
/// Expected post-state `hash_tree_root`, present alongside `post`. Captured
37-
/// only so `deny_unknown_fields` accepts it; the per-field `post` checks
38-
/// already pin the post-state.
36+
/// Expected post-state `hash_tree_root`, present alongside `post`. Asserted
37+
/// against the full post-state root after the per-field `post` checks, so
38+
/// any state field those checks don't enumerate is still pinned.
3939
#[serde(rename = "postStateRoot")]
40-
#[allow(dead_code)]
4140
pub post_state_root: Option<H256>,
4241
/// Expected rejection reason for negative cases. Captured only so
4342
/// `deny_unknown_fields` accepts it; failure is asserted via a missing

crates/common/metrics/src/timing.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ use std::time::Instant;
55
use crate::Histogram;
66

77
/// A guard that records elapsed time to a histogram when dropped.
8+
///
9+
/// The measurement can be cancelled with [`TimingGuard::discard`], which
10+
/// consumes the guard without recording a sample, for a timed path that turns
11+
/// out to be a no-op whose duration would only skew the histogram (e.g. an
12+
/// idempotent early return).
813
pub struct TimingGuard {
914
histogram: &'static Histogram,
1015
start: Instant,
@@ -17,6 +22,22 @@ impl TimingGuard {
1722
start: Instant::now(),
1823
}
1924
}
25+
26+
/// Consume the guard without recording a sample.
27+
///
28+
/// Use when the timed work should not contribute a sample, such as a
29+
/// duplicate/idempotent request that returns early: the elapsed time is
30+
/// real but recording it would skew the histogram toward near-zero.
31+
///
32+
/// `TimingGuard` implements [`Drop`], so its fields cannot be moved out to
33+
/// destructure it directly. Wrapping in [`std::mem::ManuallyDrop`] inhibits
34+
/// the recording `Drop`; the fields are `Copy`, so they are then read out
35+
/// and their copies dropped here, leaving nothing to record.
36+
pub fn discard(self) {
37+
let guard = std::mem::ManuallyDrop::new(self);
38+
let _histogram = guard.histogram;
39+
let _start = guard.start;
40+
}
2041
}
2142

2243
impl Drop for TimingGuard {

0 commit comments

Comments
 (0)