diff --git a/crates/blockchain/src/key_manager.rs b/crates/blockchain/src/key_manager.rs index 5358d146..aa84b873 100644 --- a/crates/blockchain/src/key_manager.rs +++ b/crates/blockchain/src/key_manager.rs @@ -6,6 +6,8 @@ use ethlambda_types::{ signature::{ValidatorSecretKey, ValidatorSignature}, }; +use crate::metrics; + /// Error types for KeyManager operations. #[derive(Debug, thiserror::Error)] pub enum KeyManagerError { @@ -100,9 +102,13 @@ impl KeyManager { .get_mut(&validator_id) .ok_or(KeyManagerError::ValidatorKeyNotFound(validator_id))?; - let signature: ValidatorSignature = secret_key - .sign(slot, message) - .map_err(|e| KeyManagerError::SigningError(e.to_string()))?; + let signature: ValidatorSignature = { + let _timing = metrics::time_pq_sig_attestation_signing(); + secret_key + .sign(slot, message) + .map_err(|e| KeyManagerError::SigningError(e.to_string())) + }?; + metrics::inc_pq_sig_attestation_signatures(); // Convert ValidatorSignature to XmssSignature (FixedVector) let sig_bytes = signature.to_bytes(); diff --git a/crates/blockchain/src/lib.rs b/crates/blockchain/src/lib.rs index 50dd6f4e..0418770b 100644 --- a/crates/blockchain/src/lib.rs +++ b/crates/blockchain/src/lib.rs @@ -54,6 +54,7 @@ impl BlockChain { validator_keys: HashMap, is_aggregator: bool, ) -> BlockChain { + metrics::set_is_aggregator(is_aggregator); let genesis_time = store.config().genesis_time; let key_manager = key_manager::KeyManager::new(validator_keys); let handle = BlockChainServer { diff --git a/crates/blockchain/src/metrics.rs b/crates/blockchain/src/metrics.rs index 8bd39dbc..c44d3690 100644 --- a/crates/blockchain/src/metrics.rs +++ b/crates/blockchain/src/metrics.rs @@ -79,35 +79,29 @@ pub fn set_node_start_time() { } /// Increment the valid attestations counter. -pub fn inc_attestations_valid(source: &str) { - static LEAN_ATTESTATIONS_VALID_TOTAL: std::sync::LazyLock = +pub fn inc_attestations_valid() { + static LEAN_ATTESTATIONS_VALID_TOTAL: std::sync::LazyLock = std::sync::LazyLock::new(|| { - register_int_counter_vec!( + register_int_counter!( "lean_attestations_valid_total", - "Count of valid attestations", - &["source"] + "Total number of valid attestations" ) .unwrap() }); - LEAN_ATTESTATIONS_VALID_TOTAL - .with_label_values(&[source]) - .inc(); + LEAN_ATTESTATIONS_VALID_TOTAL.inc(); } /// Increment the invalid attestations counter. -pub fn inc_attestations_invalid(source: &str) { - static LEAN_ATTESTATIONS_INVALID_TOTAL: std::sync::LazyLock = +pub fn inc_attestations_invalid() { + static LEAN_ATTESTATIONS_INVALID_TOTAL: std::sync::LazyLock = std::sync::LazyLock::new(|| { - register_int_counter_vec!( + register_int_counter!( "lean_attestations_invalid_total", - "Count of invalid attestations", - &["source"] + "Total number of invalid attestations" ) .unwrap() }); - LEAN_ATTESTATIONS_INVALID_TOTAL - .with_label_values(&[source]) - .inc(); + LEAN_ATTESTATIONS_INVALID_TOTAL.inc(); } /// Increment the fork choice reorgs counter. @@ -130,7 +124,7 @@ pub fn time_fork_choice_block_processing() -> TimingGuard { register_histogram!( "lean_fork_choice_block_processing_time_seconds", "Duration to process a block", - vec![0.005, 0.01, 0.025, 0.05, 0.1, 1.0] + vec![0.005, 0.01, 0.025, 0.05, 0.1, 1.0, 1.25, 1.5, 2.0, 4.0] ) .unwrap() }); @@ -203,3 +197,177 @@ pub fn inc_pq_sig_aggregated_signatures_invalid() { }); LEAN_PQ_SIG_AGGREGATED_SIGNATURES_INVALID_TOTAL.inc(); } + +/// Increment the individual attestation signatures counter. +pub fn inc_pq_sig_attestation_signatures() { + static LEAN_PQ_SIG_ATTESTATION_SIGNATURES_TOTAL: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_int_counter!( + "lean_pq_sig_attestation_signatures_total", + "Total number of individual attestation signatures" + ) + .unwrap() + }); + LEAN_PQ_SIG_ATTESTATION_SIGNATURES_TOTAL.inc(); +} + +/// Increment the valid individual attestation signatures counter. +pub fn inc_pq_sig_attestation_signatures_valid() { + static LEAN_PQ_SIG_ATTESTATION_SIGNATURES_VALID_TOTAL: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_int_counter!( + "lean_pq_sig_attestation_signatures_valid_total", + "Total number of valid individual attestation signatures" + ) + .unwrap() + }); + LEAN_PQ_SIG_ATTESTATION_SIGNATURES_VALID_TOTAL.inc(); +} + +/// Increment the invalid individual attestation signatures counter. +pub fn inc_pq_sig_attestation_signatures_invalid() { + static LEAN_PQ_SIG_ATTESTATION_SIGNATURES_INVALID_TOTAL: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_int_counter!( + "lean_pq_sig_attestation_signatures_invalid_total", + "Total number of invalid individual attestation signatures" + ) + .unwrap() + }); + LEAN_PQ_SIG_ATTESTATION_SIGNATURES_INVALID_TOTAL.inc(); +} + +/// Start timing individual attestation signing. Records duration when the guard is dropped. +pub fn time_pq_sig_attestation_signing() -> TimingGuard { + static LEAN_PQ_SIG_ATTESTATION_SIGNING_TIME_SECONDS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_histogram!( + "lean_pq_sig_attestation_signing_time_seconds", + "Time taken to sign an attestation", + vec![0.005, 0.01, 0.025, 0.05, 0.1, 1.0] + ) + .unwrap() + }); + TimingGuard::new(&LEAN_PQ_SIG_ATTESTATION_SIGNING_TIME_SECONDS) +} + +/// Start timing individual attestation signature verification. Records duration when the guard is dropped. +pub fn time_pq_sig_attestation_verification() -> TimingGuard { + static LEAN_PQ_SIG_ATTESTATION_VERIFICATION_TIME_SECONDS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_histogram!( + "lean_pq_sig_attestation_verification_time_seconds", + "Time taken to verify an attestation signature", + vec![0.005, 0.01, 0.025, 0.05, 0.1, 1.0] + ) + .unwrap() + }); + TimingGuard::new(&LEAN_PQ_SIG_ATTESTATION_VERIFICATION_TIME_SECONDS) +} + +/// Start timing aggregated signature building. Records duration when the guard is dropped. +pub fn time_pq_sig_aggregated_signatures_building() -> TimingGuard { + static LEAN_PQ_SIG_AGGREGATED_SIGNATURES_BUILDING_TIME_SECONDS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_histogram!( + "lean_pq_sig_aggregated_signatures_building_time_seconds", + "Time taken to build an aggregated attestation signature", + vec![0.1, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 4.0] + ) + .unwrap() + }); + TimingGuard::new(&LEAN_PQ_SIG_AGGREGATED_SIGNATURES_BUILDING_TIME_SECONDS) +} + +/// Start timing aggregated signature verification. Records duration when the guard is dropped. +pub fn time_pq_sig_aggregated_signatures_verification() -> TimingGuard { + static LEAN_PQ_SIG_AGGREGATED_SIGNATURES_VERIFICATION_TIME_SECONDS: std::sync::LazyLock< + Histogram, + > = std::sync::LazyLock::new(|| { + register_histogram!( + "lean_pq_sig_aggregated_signatures_verification_time_seconds", + "Time taken to verify an aggregated attestation signature", + vec![0.1, 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 2.0, 4.0] + ) + .unwrap() + }); + TimingGuard::new(&LEAN_PQ_SIG_AGGREGATED_SIGNATURES_VERIFICATION_TIME_SECONDS) +} + +/// Start timing committee signatures aggregation. Records duration when the guard is dropped. +pub fn time_committee_signatures_aggregation() -> TimingGuard { + static LEAN_COMMITTEE_SIGNATURES_AGGREGATION_TIME_SECONDS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_histogram!( + "lean_committee_signatures_aggregation_time_seconds", + "Time taken to aggregate committee signatures", + vec![0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 1.0] + ) + .unwrap() + }); + TimingGuard::new(&LEAN_COMMITTEE_SIGNATURES_AGGREGATION_TIME_SECONDS) +} + +/// Update the gossip signatures gauge. +pub fn update_gossip_signatures(count: usize) { + static LEAN_GOSSIP_SIGNATURES: std::sync::LazyLock = std::sync::LazyLock::new(|| { + register_int_gauge!( + "lean_gossip_signatures", + "Number of gossip signatures in fork-choice store" + ) + .unwrap() + }); + LEAN_GOSSIP_SIGNATURES.set(count as i64); +} + +/// Update the new aggregated payloads gauge. +pub fn update_latest_new_aggregated_payloads(count: usize) { + static LEAN_LATEST_NEW_AGGREGATED_PAYLOADS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_int_gauge!( + "lean_latest_new_aggregated_payloads", + "Number of new aggregated payload items" + ) + .unwrap() + }); + LEAN_LATEST_NEW_AGGREGATED_PAYLOADS.set(count as i64); +} + +/// Update the known aggregated payloads gauge. +pub fn update_latest_known_aggregated_payloads(count: usize) { + static LEAN_LATEST_KNOWN_AGGREGATED_PAYLOADS: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_int_gauge!( + "lean_latest_known_aggregated_payloads", + "Number of known aggregated payload items" + ) + .unwrap() + }); + LEAN_LATEST_KNOWN_AGGREGATED_PAYLOADS.set(count as i64); +} + +/// Set the is_aggregator gauge. +pub fn set_is_aggregator(is_aggregator: bool) { + static LEAN_IS_AGGREGATOR: std::sync::LazyLock = std::sync::LazyLock::new(|| { + register_int_gauge!( + "lean_is_aggregator", + "Validator's is_aggregator status. True=1, False=0" + ) + .unwrap() + }); + LEAN_IS_AGGREGATOR.set(i64::from(is_aggregator)); +} + +/// Observe the depth of a fork choice reorg. +pub fn observe_fork_choice_reorg_depth(depth: u64) { + static LEAN_FORK_CHOICE_REORG_DEPTH: std::sync::LazyLock = + std::sync::LazyLock::new(|| { + register_histogram!( + "lean_fork_choice_reorg_depth", + "Depth of fork choice reorgs (in blocks)", + vec![1.0, 2.0, 3.0, 5.0, 7.0, 10.0, 20.0, 30.0, 50.0, 100.0] + ) + .unwrap() + }); + LEAN_FORK_CHOICE_REORG_DEPTH.observe(depth as f64); +} diff --git a/crates/blockchain/src/store.rs b/crates/blockchain/src/store.rs index c66d9d3e..a20a71d8 100644 --- a/crates/blockchain/src/store.rs +++ b/crates/blockchain/src/store.rs @@ -29,6 +29,8 @@ const JUSTIFICATION_LOOKBACK_SLOTS: u64 = 3; /// Accept new aggregated payloads, promoting them to known for fork choice. fn accept_new_attestations(store: &mut Store, log_tree: bool) { store.promote_new_aggregated_payloads(); + metrics::update_latest_new_aggregated_payloads(store.new_aggregated_payloads_count()); + metrics::update_latest_known_aggregated_payloads(store.known_aggregated_payloads_count()); update_head(store, log_tree); } @@ -46,9 +48,10 @@ fn update_head(store: &mut Store, log_tree: bool) { &attestations, 0, ); - if is_reorg(old_head, new_head, store) { + if let Some(depth) = reorg_depth(old_head, new_head, store) { metrics::inc_fork_choice_reorgs(); - info!(%old_head, %new_head, "Fork choice reorg detected"); + metrics::observe_fork_choice_reorg_depth(depth); + info!(%old_head, %new_head, depth, "Fork choice reorg detected"); } store.update_checkpoints(ForkCheckpoints::head_only(new_head)); @@ -121,6 +124,7 @@ fn aggregate_committee_signatures(store: &mut Store) -> Vec = Vec::new(); @@ -169,9 +173,11 @@ fn aggregate_committee_signatures(store: &mut Store) -> Vec Vec Result<(), StoreError> { validate_attestation_data(store, &aggregated.data) - .inspect_err(|_| metrics::inc_attestations_invalid("aggregated"))?; + .inspect_err(|_| metrics::inc_attestations_invalid())?; // Verify aggregated proof signature let target_state = store @@ -439,12 +454,15 @@ pub fn on_gossip_aggregated_attestation( let data_root = aggregated.data.tree_hash_root(); let slot: u32 = aggregated.data.slot.try_into().expect("slot exceeds u32"); - ethlambda_crypto::verify_aggregated_signature( - &aggregated.proof.proof_data, - pubkeys, - &data_root, - slot, - ) + { + let _timing = metrics::time_pq_sig_aggregated_signatures_verification(); + ethlambda_crypto::verify_aggregated_signature( + &aggregated.proof.proof_data, + pubkeys, + &data_root, + slot, + ) + } .map_err(StoreError::AggregateVerificationFailed)?; // Store attestation data by root (content-addressed, idempotent) @@ -463,6 +481,7 @@ pub fn on_gossip_aggregated_attestation( }) .collect(); store.insert_new_aggregated_payloads_batch(entries); + metrics::update_latest_new_aggregated_payloads(store.new_aggregated_payloads_count()); let slot = aggregated.data.slot; let num_participants = aggregated.proof.participants.num_set_bits(); @@ -475,7 +494,7 @@ pub fn on_gossip_aggregated_attestation( "Aggregated attestation processed" ); - metrics::inc_attestations_valid("aggregated"); + metrics::inc_attestations_valid(); Ok(()) } @@ -586,7 +605,7 @@ fn on_block_core( for validator_id in &validator_ids { known_entries.push(((*validator_id, data_root), payload.clone())); - metrics::inc_attestations_valid("block"); + metrics::inc_attestations_valid(); } } @@ -1173,8 +1192,11 @@ fn verify_signatures( }) .collect::>()?; - match verify_aggregated_signature(&aggregated_proof.proof_data, public_keys, &message, slot) - { + let verification_result = { + let _timing = metrics::time_pq_sig_aggregated_signatures_verification(); + verify_aggregated_signature(&aggregated_proof.proof_data, public_keys, &message, slot) + }; + match verification_result { Ok(()) => metrics::inc_pq_sig_aggregated_signatures_valid(), Err(e) => { metrics::inc_pq_sig_aggregated_signatures_invalid(); @@ -1210,22 +1232,19 @@ fn verify_signatures( Ok(()) } -/// Check if a head change represents a reorg. +/// Check if a head change represents a reorg, returning the depth if so. /// /// A reorg occurs when the chains diverge - i.e., when walking back from the higher /// slot head to the lower slot head's slot, we don't arrive at the lower slot head. -fn is_reorg(old_head: H256, new_head: H256, store: &Store) -> bool { +/// Returns `Some(depth)` where depth is the number of blocks walked back, or `None` +/// if no reorg occurred. +fn reorg_depth(old_head: H256, new_head: H256, store: &Store) -> Option { if new_head == old_head { - return false; + return None; } - let Some(old_head_header) = store.get_block_header(&old_head) else { - return false; - }; - - let Some(new_head_header) = store.get_block_header(&new_head) else { - return false; - }; + let old_head_header = store.get_block_header(&old_head)?; + let new_head_header = store.get_block_header(&new_head)?; let old_slot = old_head_header.slot; let new_slot = new_head_header.slot; @@ -1237,16 +1256,18 @@ fn is_reorg(old_head: H256, new_head: H256, store: &Store) -> bool { (old_head, new_slot, new_head) }; - // Walk back through the chain until we reach the target slot + // Walk back through the chain until we reach the target slot, counting steps + let mut depth: u64 = 0; while let Some(current_header) = store.get_block_header(¤t_root) { if current_header.slot <= target_slot { // We've reached the target slot - check if we're at the target block - return current_root != target_root; + return (current_root != target_root).then_some(depth); } current_root = current_header.parent_root; + depth += 1; } // Couldn't walk back far enough (missing blocks in chain) // Assume the ancestor is behind the latest finalized block - false + None } diff --git a/crates/storage/src/store.rs b/crates/storage/src/store.rs index 9bc1b307..3ce043e1 100644 --- a/crates/storage/src/store.rs +++ b/crates/storage/src/store.rs @@ -1,4 +1,5 @@ use std::collections::{HashMap, HashSet, VecDeque}; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, LazyLock, Mutex}; /// The tree hash root of an empty block body. @@ -151,6 +152,11 @@ impl PayloadBuffer { fn unique_keys(&self) -> HashSet { self.entries.iter().map(|(key, _)| *key).collect() } + + /// Return the number of entries in the buffer. + fn len(&self) -> usize { + self.entries.len() + } } // ============ Key Encoding Helpers ============ @@ -206,6 +212,7 @@ pub struct Store { backend: Arc, new_payloads: Arc>, known_payloads: Arc>, + gossip_signatures_count: Arc, } impl Store { @@ -345,6 +352,7 @@ impl Store { backend, new_payloads: Arc::new(Mutex::new(PayloadBuffer::new(NEW_PAYLOAD_CAP))), known_payloads: Arc::new(Mutex::new(PayloadBuffer::new(AGGREGATED_PAYLOAD_CAP))), + gossip_signatures_count: Arc::new(AtomicUsize::new(0)), } } @@ -569,9 +577,12 @@ impl Store { /// /// Returns the number of signatures pruned. pub fn prune_gossip_signatures(&mut self, finalized_slot: u64) -> usize { - self.prune_by_slot(Table::GossipSignatures, finalized_slot, |bytes| { + let pruned = self.prune_by_slot(Table::GossipSignatures, finalized_slot, |bytes| { StoredSignature::from_ssz_bytes(bytes).ok().map(|s| s.slot) - }) + }); + self.gossip_signatures_count + .fetch_sub(pruned, Ordering::Relaxed); + pruned } /// Prune attestation data by root for slots <= finalized_slot. @@ -983,17 +994,35 @@ impl Store { self.known_payloads.lock().unwrap().push_batch(drained); } + /// Returns the number of entries in the new (pending) aggregated payloads buffer. + pub fn new_aggregated_payloads_count(&self) -> usize { + self.new_payloads.lock().unwrap().len() + } + + /// Returns the number of entries in the known (fork-choice-active) aggregated payloads buffer. + pub fn known_aggregated_payloads_count(&self) -> usize { + self.known_payloads.lock().unwrap().len() + } + + /// Returns the number of gossip signatures stored. + pub fn gossip_signatures_count(&self) -> usize { + self.gossip_signatures_count.load(Ordering::Relaxed) + } + /// Delete specific gossip signatures by key. pub fn delete_gossip_signatures(&mut self, keys: &[SignatureKey]) { if keys.is_empty() { return; } + let count = keys.len(); let encoded_keys: Vec<_> = keys.iter().map(encode_signature_key).collect(); let mut batch = self.backend.begin_write().expect("write batch"); batch .delete_batch(Table::GossipSignatures, encoded_keys) .expect("delete gossip signatures"); batch.commit().expect("commit"); + self.gossip_signatures_count + .fetch_sub(count, Ordering::Relaxed); } // ============ Gossip Signatures ============ @@ -1037,6 +1066,7 @@ impl Store { .put_batch(Table::GossipSignatures, entries) .expect("put signature"); batch.commit().expect("commit"); + self.gossip_signatures_count.fetch_add(1, Ordering::Relaxed); } // ============ Derived Accessors ============ @@ -1182,6 +1212,7 @@ mod tests { backend, new_payloads: Arc::new(Mutex::new(PayloadBuffer::new(NEW_PAYLOAD_CAP))), known_payloads: Arc::new(Mutex::new(PayloadBuffer::new(AGGREGATED_PAYLOAD_CAP))), + gossip_signatures_count: Arc::new(AtomicUsize::new(0)), } } @@ -1192,6 +1223,7 @@ mod tests { backend, new_payloads: Arc::new(Mutex::new(PayloadBuffer::new(NEW_PAYLOAD_CAP))), known_payloads: Arc::new(Mutex::new(PayloadBuffer::new(AGGREGATED_PAYLOAD_CAP))), + gossip_signatures_count: Arc::new(AtomicUsize::new(0)), } } } diff --git a/docs/metrics.md b/docs/metrics.md index 414f1f30..3e9b239c 100644 --- a/docs/metrics.md +++ b/docs/metrics.md @@ -4,7 +4,7 @@ We collect various metrics and serve them via a Prometheus-compatible HTTP endpo A ready-to-use Grafana + Prometheus monitoring stack with pre-configured [leanMetrics](https://github.com/leanEthereum/leanMetrics) dashboards is available in [lean-quickstart](https://github.com/blockblaz/lean-quickstart). -The exposed metrics follow [the leanMetrics specification](https://github.com/leanEthereum/leanMetrics/blob/3b32b300cca5ed7a7a2b3f142273fae9dbc171bf/metrics.md), with some metrics not yet implemented. We have a full list of implemented metrics below, with a checkbox indicating whether each metric is currently supported or not. +The exposed metrics follow [the leanMetrics specification](https://github.com/leanEthereum/leanMetrics/blob/main/metrics.md), with some metrics not yet implemented. We have a full list of implemented metrics below, with a checkbox indicating whether each metric is currently supported or not. ## Node Info Metrics @@ -18,14 +18,17 @@ The exposed metrics follow [the leanMetrics specification](https://github.com/le | Name | Type | Usage | Sample collection event | Labels | Buckets | Supported | |--------|-------|-------|-------------------------|--------|---------|-----------| -| `lean_pq_sig_attestation_signing_time_seconds` | Histogram | Time taken to sign an attestation | On each attestation signing | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | □ | -| `lean_pq_sig_attestation_verification_time_seconds` | Histogram | Time taken to verify an attestation signature | On each `signature.verify()` on an attestation | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | □ | -| `lean_pq_sig_aggregated_signatures_total` | Counter | Total number of aggregated signatures | On `build_attestation_signatures()` | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | ✅ | -| `lean_pq_sig_attestations_in_aggregated_signatures_total` | Counter | Total number of attestations included into aggregated signatures | On `build_attestation_signatures()` | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | ✅ | -| `lean_pq_sig_attestation_signatures_building_time_seconds` | Histogram | Time taken to verify an aggregated attestation signature | On `build_attestation_signatures()` | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | □ | -| `lean_pq_sig_aggregated_signatures_verification_time_seconds` | Histogram | Time taken to verify an aggregated attestation signature | On validate aggregated signature | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | □ | -| `lean_pq_sig_aggregated_signatures_valid_total`| Counter | Total number of valid aggregated signatures | On validate aggregated signature | | | ✅ | -| `lean_pq_sig_aggregated_signatures_invalid_total`| Counter | Total number of invalid aggregated signatures | On validate aggregated signature | | | ✅ | +| `lean_pq_sig_attestation_signatures_total` | Counter | Total number of individual attestation signatures | On each attestation signing | | | ✅ | +| `lean_pq_sig_attestation_signatures_valid_total` | Counter | Total number of valid individual attestation signatures | On each attestation signature verification | | | ✅ | +| `lean_pq_sig_attestation_signatures_invalid_total` | Counter | Total number of invalid individual attestation signatures | On each attestation signature verification | | | ✅ | +| `lean_pq_sig_attestation_signing_time_seconds` | Histogram | Time taken to sign an attestation | On each attestation signing | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | ✅ | +| `lean_pq_sig_attestation_verification_time_seconds` | Histogram | Time taken to verify an attestation signature | On each attestation signature verification | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | ✅ | +| `lean_pq_sig_aggregated_signatures_total` | Counter | Total number of aggregated signatures | On aggregated signature production | | | ✅ | +| `lean_pq_sig_aggregated_signatures_valid_total` | Counter | Total number of valid aggregated signatures | On aggregated signature verification | | | ✅ | +| `lean_pq_sig_aggregated_signatures_invalid_total` | Counter | Total number of invalid aggregated signatures | On aggregated signature verification | | | ✅ | +| `lean_pq_sig_attestations_in_aggregated_signatures_total` | Counter | Total number of attestations included into aggregated signatures | On aggregated signature production | | | ✅ | +| `lean_pq_sig_aggregated_signatures_building_time_seconds` | Histogram | Time taken to build an aggregated attestation signature | On aggregated signature production | | 0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4 | ✅ | +| `lean_pq_sig_aggregated_signatures_verification_time_seconds` | Histogram | Time taken to verify an aggregated attestation signature | On aggregated signature verification | | 0.1, 0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 4 | ✅ | ## Fork-Choice Metrics @@ -34,12 +37,16 @@ The exposed metrics follow [the leanMetrics specification](https://github.com/le | `lean_head_slot` | Gauge | Latest slot of the lean chain | On get fork choice head | | | ✅ | | `lean_current_slot` | Gauge | Current slot of the lean chain | On scrape | | | ✅(*) | | `lean_safe_target_slot` | Gauge | Safe target slot | On safe target update | | | ✅ | -|`lean_fork_choice_block_processing_time_seconds`| Histogram | Time taken to process block | On fork choice process block | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | ✅ | -|`lean_attestations_valid_total`| Counter | Total number of valid attestations | On validate attestation | source=block,gossip | | ✅ | -|`lean_attestations_invalid_total`| Counter | Total number of invalid attestations | On validate attestation | source=block,gossip | | ✅ | +|`lean_fork_choice_block_processing_time_seconds`| Histogram | Time taken to process block | On fork choice process block | | 0.005, 0.01, 0.025, 0.05, 0.1, 1, 1.25, 1.5, 2, 4 | ✅ | +|`lean_attestations_valid_total`| Counter | Total number of valid attestations | On validate attestation | | | ✅ | +|`lean_attestations_invalid_total`| Counter | Total number of invalid attestations | On validate attestation | | | ✅ | |`lean_attestation_validation_time_seconds`| Histogram | Time taken to validate attestation | On validate attestation | | 0.005, 0.01, 0.025, 0.05, 0.1, 1 | ✅ | | `lean_fork_choice_reorgs_total` | Counter | Total number of fork choice reorgs | On fork choice reorg | | | ✅ | -| `lean_fork_choice_reorg_depth` | Histogram | Depth of fork choice reorgs (in blocks) | On fork choice reorg | | 1, 2, 3, 5, 7, 10, 20, 30, 50, 100 | □ | +| `lean_fork_choice_reorg_depth` | Histogram | Depth of fork choice reorgs (in blocks) | On fork choice reorg | | 1, 2, 3, 5, 7, 10, 20, 30, 50, 100 | ✅ | +| `lean_gossip_signatures` | Gauge | Number of gossip signatures in fork-choice store | On gossip signatures update | | | ✅ | +| `lean_latest_new_aggregated_payloads` | Gauge | Number of new aggregated payload items | On `latest_new_aggregated_payloads` update | | | ✅ | +| `lean_latest_known_aggregated_payloads` | Gauge | Number of known aggregated payload items | On `latest_known_aggregated_payloads` update | | | ✅ | +| `lean_committee_signatures_aggregation_time_seconds` | Histogram | Time taken to aggregate committee signatures | On committee signatures aggregation | | 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.75, 1 | ✅ | ## State Transition Metrics @@ -60,12 +67,13 @@ The exposed metrics follow [the leanMetrics specification](https://github.com/le | Name | Type | Usage | Sample collection event | Labels | Supported | |--------|-------|-------|-------------------------|--------|-----------| |`lean_validators_count`| Gauge | Number of validators managed by a node | On scrape | | ✅(*) | +|`lean_is_aggregator`| Gauge | Validator's `is_aggregator` status. True=1, False=0 | On node start | | ✅ | ## Network Metrics | Name | Type | Usage | Sample collection event | Labels | Supported | |--------|-------|-------|-------------------------|--------|-----------| -|`lean_connected_peers`| Gauge | Number of connected peers | On scrape | client=lantern,qlean,ream,zeam | ✅(*) | +|`lean_connected_peers`| Gauge | Number of connected peers | On scrape | client=ethlambda,grandine,lantern,lighthouse,qlean,ream,zeam | ✅(*) | |`lean_peer_connection_events_total`| Counter | Total number of peer connection events | On peer connection | direction=inbound,outbound
result=success,timeout,error | ✅ | |`lean_peer_disconnection_events_total`| Counter | Total number of peer disconnection events | On peer disconnection | direction=inbound,outbound
reason=timeout,remote_close,local_close,error | ✅ |