Validator monitor support for sync committees (#2476)

## Issue Addressed

N/A

## Proposed Changes

Add functionality in the validator monitor to provide sync committee related metrics for monitored validators.


Co-authored-by: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
Pawan Dhananjay 2021-08-31 23:31:36 +00:00
parent 44fa54004c
commit 5a3bcd2904
14 changed files with 564 additions and 71 deletions

View File

@ -2389,6 +2389,25 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
} }
} }
// Register sync aggregate with validator monitor
if let Some(sync_aggregate) = block.body().sync_aggregate() {
// `SyncCommittee` for the sync_aggregate should correspond to the duty slot
let duty_epoch = block.slot().epoch(T::EthSpec::slots_per_epoch());
let sync_committee = self.sync_committee_at_epoch(duty_epoch)?;
let participant_pubkeys = sync_committee
.pubkeys
.iter()
.zip(sync_aggregate.sync_committee_bits.iter())
.filter_map(|(pubkey, bit)| bit.then(|| pubkey))
.collect::<Vec<_>>();
validator_monitor.register_sync_aggregate_in_block(
block.slot(),
block.parent_root(),
participant_pubkeys,
);
}
for exit in block.body().voluntary_exits() { for exit in block.body().voluntary_exits() {
validator_monitor.register_block_voluntary_exit(&exit.message) validator_monitor.register_block_voluntary_exit(&exit.message)
} }

View File

@ -553,6 +553,48 @@ lazy_static! {
"The number of attester slashings seen in the previous epoch.", "The number of attester slashings seen in the previous epoch.",
&["validator"] &["validator"]
); );
pub static ref VALIDATOR_MONITOR_PREV_EPOCH_SYNC_COMMITTEE_MESSAGES_TOTAL: Result<IntGaugeVec> =
try_create_int_gauge_vec(
"validator_monitor_prev_epoch_sync_committee_messages_total",
"The number of sync committee messages seen in the previous epoch.",
&["validator"]
);
pub static ref VALIDATOR_MONITOR_PREV_EPOCH_SYNC_COMMITTEE_MESSAGES_MIN_DELAY_SECONDS: Result<HistogramVec> =
try_create_histogram_vec(
"validator_monitor_prev_epoch_sync_committee_messages_min_delay_seconds",
"The min delay between when the validator should send the sync committee message and when it was received.",
&["validator"]
);
pub static ref VALIDATOR_MONITOR_PREV_EPOCH_SYNC_CONTRIBUTION_INCLUSIONS: Result<IntGaugeVec> =
try_create_int_gauge_vec(
"validator_monitor_prev_epoch_sync_contribution_inclusions",
"The count of times a sync signature was seen inside a sync contribution.",
&["validator"]
);
pub static ref VALIDATOR_MONITOR_PREV_EPOCH_SYNC_SIGNATURE_BLOCK_INCLUSIONS: Result<IntGaugeVec> =
try_create_int_gauge_vec(
"validator_monitor_prev_epoch_sync_signature_block_inclusions",
"The count of times a sync signature was seen inside a block.",
&["validator"]
);
pub static ref VALIDATOR_MONITOR_PREV_EPOCH_SYNC_CONTRIBUTIONS_TOTAL: Result<IntGaugeVec> =
try_create_int_gauge_vec(
"validator_monitor_prev_epoch_sync_contributions_total",
"The number of sync contributions seen in the previous epoch.",
&["validator"]
);
pub static ref VALIDATOR_MONITOR_PREV_EPOCH_SYNC_CONTRIBUTION_MIN_DELAY_SECONDS: Result<HistogramVec> =
try_create_histogram_vec(
"validator_monitor_prev_epoch_sync_contribution_min_delay_seconds",
"The min delay between when the validator should send the sync contribution and when it was received.",
&["validator"]
);
pub static ref VALIDATOR_MONITOR_VALIDATOR_IN_CURRENT_SYNC_COMMITTEE: Result<IntGaugeVec> =
try_create_int_gauge_vec(
"validator_monitor_validator_in_current_sync_committee",
"Is the validator in the current sync committee (1 for true and 0 for false)",
&["validator"]
);
/* /*
* Validator Monitor Metrics (real-time) * Validator Monitor Metrics (real-time)
@ -571,6 +613,26 @@ lazy_static! {
"The delay between when the validator should send the attestation and when it was received.", "The delay between when the validator should send the attestation and when it was received.",
&["src", "validator"] &["src", "validator"]
); );
pub static ref VALIDATOR_MONITOR_SYNC_COMMITTEE_MESSAGES_TOTAL: Result<IntCounterVec> = try_create_int_counter_vec(
"validator_monitor_sync_committee_messages_total",
"Number of sync committee messages seen",
&["src", "validator"]
);
pub static ref VALIDATOR_MONITOR_SYNC_COMMITTEE_MESSAGES_DELAY_SECONDS: Result<HistogramVec> = try_create_histogram_vec(
"validator_monitor_sync_committee_messages_delay_seconds",
"The delay between when the validator should send the sync committee message and when it was received.",
&["src", "validator"]
);
pub static ref VALIDATOR_MONITOR_SYNC_CONTRIBUTIONS_TOTAL: Result<IntCounterVec> = try_create_int_counter_vec(
"validator_monitor_sync_contributions_total",
"Number of sync contributions seen",
&["src", "validator"]
);
pub static ref VALIDATOR_MONITOR_SYNC_COONTRIBUTIONS_DELAY_SECONDS: Result<HistogramVec> = try_create_histogram_vec(
"validator_monitor_sync_contribtions_delay_seconds",
"The delay between when the aggregator should send the sync contribution and when it was received.",
&["src", "validator"]
);
pub static ref VALIDATOR_MONITOR_AGGREGATED_ATTESTATION_TOTAL: Result<IntCounterVec> = try_create_int_counter_vec( pub static ref VALIDATOR_MONITOR_AGGREGATED_ATTESTATION_TOTAL: Result<IntCounterVec> = try_create_int_counter_vec(
"validator_monitor_aggregated_attestation_total", "validator_monitor_aggregated_attestation_total",
"Number of aggregated attestations seen", "Number of aggregated attestations seen",
@ -586,6 +648,11 @@ lazy_static! {
"Number of times an attestation has been seen in an aggregate", "Number of times an attestation has been seen in an aggregate",
&["src", "validator"] &["src", "validator"]
); );
pub static ref VALIDATOR_MONITOR_SYNC_COMMITTEE_MESSAGE_IN_CONTRIBUTION_TOTAL: Result<IntCounterVec> = try_create_int_counter_vec(
"validator_monitor_sync_committee_message_in_contribution_total",
"Number of times a sync committee message has been seen in a sync contribution",
&["src", "validator"]
);
pub static ref VALIDATOR_MONITOR_ATTESTATION_IN_AGGREGATE_DELAY_SECONDS: Result<HistogramVec> = try_create_histogram_vec( pub static ref VALIDATOR_MONITOR_ATTESTATION_IN_AGGREGATE_DELAY_SECONDS: Result<HistogramVec> = try_create_histogram_vec(
"validator_monitor_attestation_in_aggregate_delay_seconds", "validator_monitor_attestation_in_aggregate_delay_seconds",
"The delay between when the validator should send the aggregate and when it was received.", "The delay between when the validator should send the aggregate and when it was received.",
@ -596,6 +663,11 @@ lazy_static! {
"Number of times an attestation has been seen in a block", "Number of times an attestation has been seen in a block",
&["src", "validator"] &["src", "validator"]
); );
pub static ref VALIDATOR_MONITOR_SYNC_COMMITTEE_MESSAGE_IN_BLOCK_TOTAL: Result<IntCounterVec> = try_create_int_counter_vec(
"validator_monitor_sync_committee_message_in_block_total",
"Number of times a validator's sync committee message has been seen in a sync aggregate",
&["src", "validator"]
);
pub static ref VALIDATOR_MONITOR_ATTESTATION_IN_BLOCK_DELAY_SLOTS: Result<IntGaugeVec> = try_create_int_gauge_vec( pub static ref VALIDATOR_MONITOR_ATTESTATION_IN_BLOCK_DELAY_SLOTS: Result<IntGaugeVec> = try_create_int_gauge_vec(
"validator_monitor_attestation_in_block_delay_slots", "validator_monitor_attestation_in_block_delay_slots",
"The excess slots (beyond the minimum delay) between the attestation slot and the block slot.", "The excess slots (beyond the minimum delay) between the attestation slot and the block slot.",

View File

@ -251,6 +251,7 @@ impl From<ContributionError> for Error {
#[derivative(Clone(bound = "T: BeaconChainTypes"))] #[derivative(Clone(bound = "T: BeaconChainTypes"))]
pub struct VerifiedSyncContribution<T: BeaconChainTypes> { pub struct VerifiedSyncContribution<T: BeaconChainTypes> {
signed_aggregate: SignedContributionAndProof<T::EthSpec>, signed_aggregate: SignedContributionAndProof<T::EthSpec>,
participant_pubkeys: Vec<PublicKeyBytes>,
} }
/// Wraps a `SyncCommitteeMessage` that has been verified for propagation on the gossip network. /// Wraps a `SyncCommitteeMessage` that has been verified for propagation on the gossip network.
@ -385,7 +386,10 @@ impl<T: BeaconChainTypes> VerifiedSyncContribution<T> {
slot: contribution.slot, slot: contribution.slot,
}); });
} }
Ok(VerifiedSyncContribution { signed_aggregate }) Ok(VerifiedSyncContribution {
signed_aggregate,
participant_pubkeys,
})
} }
/// A helper function to add this aggregate to `beacon_chain.op_pool`. /// A helper function to add this aggregate to `beacon_chain.op_pool`.
@ -402,6 +406,11 @@ impl<T: BeaconChainTypes> VerifiedSyncContribution<T> {
pub fn aggregate(&self) -> &SignedContributionAndProof<T::EthSpec> { pub fn aggregate(&self) -> &SignedContributionAndProof<T::EthSpec> {
&self.signed_aggregate &self.signed_aggregate
} }
/// Returns the pubkeys of all validators that are included in the aggregate.
pub fn participant_pubkeys(&self) -> &[PublicKeyBytes] {
&self.participant_pubkeys
}
} }
impl VerifiedSyncCommitteeMessage { impl VerifiedSyncCommitteeMessage {

View File

@ -4,7 +4,7 @@
use crate::metrics; use crate::metrics;
use parking_lot::RwLock; use parking_lot::RwLock;
use slog::{crit, error, info, warn, Logger}; use slog::{crit, debug, error, info, warn, Logger};
use slot_clock::SlotClock; use slot_clock::SlotClock;
use state_processing::per_epoch_processing::{ use state_processing::per_epoch_processing::{
errors::EpochProcessingError, EpochProcessingSummary, errors::EpochProcessingError, EpochProcessingSummary,
@ -16,9 +16,9 @@ use std::marker::PhantomData;
use std::str::Utf8Error; use std::str::Utf8Error;
use std::time::{Duration, SystemTime, UNIX_EPOCH}; use std::time::{Duration, SystemTime, UNIX_EPOCH};
use types::{ use types::{
AttestationData, AttesterSlashing, BeaconBlockRef, BeaconState, ChainSpec, Epoch, EthSpec, AttesterSlashing, BeaconBlockRef, BeaconState, ChainSpec, Epoch, EthSpec, Hash256,
Hash256, IndexedAttestation, ProposerSlashing, PublicKeyBytes, SignedAggregateAndProof, Slot, IndexedAttestation, ProposerSlashing, PublicKeyBytes, SignedAggregateAndProof,
VoluntaryExit, SignedContributionAndProof, Slot, SyncCommitteeMessage, VoluntaryExit,
}; };
/// The validator monitor collects per-epoch data about each monitored validator. Historical data /// The validator monitor collects per-epoch data about each monitored validator. Historical data
@ -43,7 +43,7 @@ struct EpochSummary {
/// The delay between when the attestation should have been produced and when it was observed. /// The delay between when the attestation should have been produced and when it was observed.
pub attestation_min_delay: Option<Duration>, pub attestation_min_delay: Option<Duration>,
/// The number of times a validators attestation was seen in an aggregate. /// The number of times a validators attestation was seen in an aggregate.
pub attestation_aggregate_incusions: usize, pub attestation_aggregate_inclusions: usize,
/// The number of times a validators attestation was seen in a block. /// The number of times a validators attestation was seen in a block.
pub attestation_block_inclusions: usize, pub attestation_block_inclusions: usize,
/// The minimum observed inclusion distance for an attestation for this epoch.. /// The minimum observed inclusion distance for an attestation for this epoch..
@ -62,6 +62,27 @@ struct EpochSummary {
pub aggregates: usize, pub aggregates: usize,
/// The delay between when the aggregate should have been produced and when it was observed. /// The delay between when the aggregate should have been produced and when it was observed.
pub aggregate_min_delay: Option<Duration>, pub aggregate_min_delay: Option<Duration>,
/*
* SyncCommitteeMessages in the current epoch
*/
/// The number of sync committee messages seen.
sync_committee_messages: usize,
/// The delay between when the sync committee message should have been produced and when it was observed.
sync_committee_message_min_delay: Option<Duration>,
/// The number of times a validator's sync signature was included in the sync aggregate.
sync_signature_block_inclusions: usize,
/// The number of times a validator's sync signature was aggregated into a sync contribution.
sync_signature_contribution_inclusions: usize,
/*
* SyncContributions in the current epoch
*/
/// The number of SyncContributions observed in the current epoch.
sync_contributions: usize,
/// The delay between when the sync committee contribution should have been produced and when it was observed.
sync_contribution_min_delay: Option<Duration>,
/* /*
* Others pertaining to this epoch. * Others pertaining to this epoch.
*/ */
@ -93,13 +114,27 @@ impl EpochSummary {
Self::update_if_lt(&mut self.attestation_min_delay, delay); Self::update_if_lt(&mut self.attestation_min_delay, delay);
} }
pub fn register_sync_committee_message(&mut self, delay: Duration) {
self.sync_committee_messages += 1;
Self::update_if_lt(&mut self.sync_committee_message_min_delay, delay);
}
pub fn register_aggregated_attestation(&mut self, delay: Duration) { pub fn register_aggregated_attestation(&mut self, delay: Duration) {
self.aggregates += 1; self.aggregates += 1;
Self::update_if_lt(&mut self.aggregate_min_delay, delay); Self::update_if_lt(&mut self.aggregate_min_delay, delay);
} }
pub fn register_sync_committee_contribution(&mut self, delay: Duration) {
self.sync_contributions += 1;
Self::update_if_lt(&mut self.sync_contribution_min_delay, delay);
}
pub fn register_aggregate_attestation_inclusion(&mut self) { pub fn register_aggregate_attestation_inclusion(&mut self) {
self.attestation_aggregate_incusions += 1; self.attestation_aggregate_inclusions += 1;
}
pub fn register_sync_signature_contribution_inclusion(&mut self) {
self.sync_signature_contribution_inclusions += 1;
} }
pub fn register_attestation_block_inclusion(&mut self, delay: Slot) { pub fn register_attestation_block_inclusion(&mut self, delay: Slot) {
@ -107,6 +142,10 @@ impl EpochSummary {
Self::update_if_lt(&mut self.attestation_min_block_inclusion_distance, delay); Self::update_if_lt(&mut self.attestation_min_block_inclusion_distance, delay);
} }
pub fn register_sync_signature_block_inclusions(&mut self) {
self.sync_signature_block_inclusions += 1;
}
pub fn register_exit(&mut self) { pub fn register_exit(&mut self) {
self.exits += 1; self.exits += 1;
} }
@ -328,10 +367,10 @@ impl<T: EthSpec> ValidatorMonitor<T> {
pub fn process_validator_statuses( pub fn process_validator_statuses(
&self, &self,
epoch: Epoch, epoch: Epoch,
summary: &EpochProcessingSummary, summary: &EpochProcessingSummary<T>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<(), EpochProcessingError> { ) -> Result<(), EpochProcessingError> {
for monitored_validator in self.validators.values() { for (pubkey, monitored_validator) in self.validators.iter() {
// We subtract two from the state of the epoch that generated these summaries. // We subtract two from the state of the epoch that generated these summaries.
// //
// - One to account for it being the previous epoch. // - One to account for it being the previous epoch.
@ -455,6 +494,45 @@ impl<T: EthSpec> ValidatorMonitor<T> {
inclusion_info.delay as i64, inclusion_info.delay as i64,
); );
} }
// Indicates the number of sync committee signatures that made it into
// a sync aggregate in the current_epoch (state.epoch - 1).
// Note: Unlike attestations, sync committee signatures must be included in the
// immediate next slot. Hence, num included sync aggregates for `state.epoch - 1`
// is available right after state transition to state.epoch.
let current_epoch = epoch - 1;
if let Some(sync_committee) = summary.sync_committee() {
if sync_committee.contains(pubkey) {
metrics::set_int_gauge(
&metrics::VALIDATOR_MONITOR_VALIDATOR_IN_CURRENT_SYNC_COMMITTEE,
&[id],
1,
);
let epoch_summary = monitored_validator.summaries.read();
if let Some(summary) = epoch_summary.get(&current_epoch) {
info!(
self.log,
"Current epoch sync signatures";
"included" => summary.sync_signature_block_inclusions,
"expected" => T::slots_per_epoch(),
"epoch" => current_epoch,
"validator" => id,
);
}
} else {
metrics::set_int_gauge(
&metrics::VALIDATOR_MONITOR_VALIDATOR_IN_CURRENT_SYNC_COMMITTEE,
&[id],
0,
);
debug!(
self.log,
"Validator isn't part of the current sync committee";
"epoch" => current_epoch,
"validator" => id,
);
}
}
} }
} }
@ -555,22 +633,6 @@ impl<T: EthSpec> ValidatorMonitor<T> {
} }
} }
/// Returns the duration between when the attestation `data` could be produced (1/3rd through
/// the slot) and `seen_timestamp`.
fn get_unaggregated_attestation_delay_ms<S: SlotClock>(
seen_timestamp: Duration,
data: &AttestationData,
slot_clock: &S,
) -> Duration {
slot_clock
.start_of(data.slot)
.and_then(|slot_start| seen_timestamp.checked_sub(slot_start))
.and_then(|gross_delay| {
gross_delay.checked_sub(slot_clock.unagg_attestation_production_delay())
})
.unwrap_or_else(|| Duration::from_secs(0))
}
/// Register an attestation seen on the gossip network. /// Register an attestation seen on the gossip network.
pub fn register_gossip_unaggregated_attestation<S: SlotClock>( pub fn register_gossip_unaggregated_attestation<S: SlotClock>(
&self, &self,
@ -610,7 +672,12 @@ impl<T: EthSpec> ValidatorMonitor<T> {
) { ) {
let data = &indexed_attestation.data; let data = &indexed_attestation.data;
let epoch = data.slot.epoch(T::slots_per_epoch()); let epoch = data.slot.epoch(T::slots_per_epoch());
let delay = Self::get_unaggregated_attestation_delay_ms(seen_timestamp, data, slot_clock); let delay = get_message_delay_ms(
seen_timestamp,
data.slot,
slot_clock.unagg_attestation_production_delay(),
slot_clock,
);
indexed_attestation.attesting_indices.iter().for_each(|i| { indexed_attestation.attesting_indices.iter().for_each(|i| {
if let Some(validator) = self.get_validator(*i) { if let Some(validator) = self.get_validator(*i) {
@ -645,22 +712,6 @@ impl<T: EthSpec> ValidatorMonitor<T> {
}) })
} }
/// Returns the duration between when a `AggregateAndproof` with `data` could be produced (2/3rd
/// through the slot) and `seen_timestamp`.
fn get_aggregated_attestation_delay_ms<S: SlotClock>(
seen_timestamp: Duration,
data: &AttestationData,
slot_clock: &S,
) -> Duration {
slot_clock
.start_of(data.slot)
.and_then(|slot_start| seen_timestamp.checked_sub(slot_start))
.and_then(|gross_delay| {
gross_delay.checked_sub(slot_clock.agg_attestation_production_delay())
})
.unwrap_or_else(|| Duration::from_secs(0))
}
/// Register a `signed_aggregate_and_proof` seen on the gossip network. /// Register a `signed_aggregate_and_proof` seen on the gossip network.
pub fn register_gossip_aggregated_attestation<S: SlotClock>( pub fn register_gossip_aggregated_attestation<S: SlotClock>(
&self, &self,
@ -705,7 +756,12 @@ impl<T: EthSpec> ValidatorMonitor<T> {
) { ) {
let data = &indexed_attestation.data; let data = &indexed_attestation.data;
let epoch = data.slot.epoch(T::slots_per_epoch()); let epoch = data.slot.epoch(T::slots_per_epoch());
let delay = Self::get_aggregated_attestation_delay_ms(seen_timestamp, data, slot_clock); let delay = get_message_delay_ms(
seen_timestamp,
data.slot,
slot_clock.agg_attestation_production_delay(),
slot_clock,
);
let aggregator_index = signed_aggregate_and_proof.message.aggregator_index; let aggregator_index = signed_aggregate_and_proof.message.aggregator_index;
if let Some(validator) = self.get_validator(aggregator_index) { if let Some(validator) = self.get_validator(aggregator_index) {
@ -814,6 +870,226 @@ impl<T: EthSpec> ValidatorMonitor<T> {
}) })
} }
/// Register a sync committee message received over gossip.
pub fn register_gossip_sync_committee_message<S: SlotClock>(
&self,
seen_timestamp: Duration,
sync_committee_message: &SyncCommitteeMessage,
slot_clock: &S,
) {
self.register_sync_committee_message(
"gossip",
seen_timestamp,
sync_committee_message,
slot_clock,
)
}
/// Register a sync committee message received over the http api.
pub fn register_api_sync_committee_message<S: SlotClock>(
&self,
seen_timestamp: Duration,
sync_committee_message: &SyncCommitteeMessage,
slot_clock: &S,
) {
self.register_sync_committee_message(
"api",
seen_timestamp,
sync_committee_message,
slot_clock,
)
}
/// Register a sync committee message.
fn register_sync_committee_message<S: SlotClock>(
&self,
src: &str,
seen_timestamp: Duration,
sync_committee_message: &SyncCommitteeMessage,
slot_clock: &S,
) {
if let Some(validator) = self.get_validator(sync_committee_message.validator_index) {
let id = &validator.id;
let epoch = sync_committee_message.slot.epoch(T::slots_per_epoch());
let delay = get_message_delay_ms(
seen_timestamp,
sync_committee_message.slot,
slot_clock.sync_committee_message_production_delay(),
slot_clock,
);
metrics::inc_counter_vec(
&metrics::VALIDATOR_MONITOR_SYNC_COMMITTEE_MESSAGES_TOTAL,
&[src, id],
);
metrics::observe_timer_vec(
&metrics::VALIDATOR_MONITOR_SYNC_COMMITTEE_MESSAGES_DELAY_SECONDS,
&[src, id],
delay,
);
info!(
self.log,
"Sync committee message";
"head" => %sync_committee_message.beacon_block_root,
"delay_ms" => %delay.as_millis(),
"epoch" => %epoch,
"slot" => %sync_committee_message.slot,
"src" => src,
"validator" => %id,
);
validator.with_epoch_summary(epoch, |summary| {
summary.register_sync_committee_message(delay)
});
}
}
/// Register a sync committee contribution received over gossip.
pub fn register_gossip_sync_committee_contribution<S: SlotClock>(
&self,
seen_timestamp: Duration,
sync_contribution: &SignedContributionAndProof<T>,
participant_pubkeys: &[PublicKeyBytes],
slot_clock: &S,
) {
self.register_sync_committee_contribution(
"gossip",
seen_timestamp,
sync_contribution,
participant_pubkeys,
slot_clock,
)
}
/// Register a sync committee contribution received over the http api.
pub fn register_api_sync_committee_contribution<S: SlotClock>(
&self,
seen_timestamp: Duration,
sync_contribution: &SignedContributionAndProof<T>,
participant_pubkeys: &[PublicKeyBytes],
slot_clock: &S,
) {
self.register_sync_committee_contribution(
"api",
seen_timestamp,
sync_contribution,
participant_pubkeys,
slot_clock,
)
}
/// Register a sync committee contribution.
fn register_sync_committee_contribution<S: SlotClock>(
&self,
src: &str,
seen_timestamp: Duration,
sync_contribution: &SignedContributionAndProof<T>,
participant_pubkeys: &[PublicKeyBytes],
slot_clock: &S,
) {
let slot = sync_contribution.message.contribution.slot;
let epoch = slot.epoch(T::slots_per_epoch());
let beacon_block_root = sync_contribution.message.contribution.beacon_block_root;
let delay = get_message_delay_ms(
seen_timestamp,
slot,
slot_clock.sync_committee_contribution_production_delay(),
slot_clock,
);
let aggregator_index = sync_contribution.message.aggregator_index;
if let Some(validator) = self.get_validator(aggregator_index) {
let id = &validator.id;
metrics::inc_counter_vec(
&metrics::VALIDATOR_MONITOR_SYNC_CONTRIBUTIONS_TOTAL,
&[src, id],
);
metrics::observe_timer_vec(
&metrics::VALIDATOR_MONITOR_SYNC_COONTRIBUTIONS_DELAY_SECONDS,
&[src, id],
delay,
);
info!(
self.log,
"Sync contribution";
"head" => %beacon_block_root,
"delay_ms" => %delay.as_millis(),
"epoch" => %epoch,
"slot" => %slot,
"src" => src,
"validator" => %id,
);
validator.with_epoch_summary(epoch, |summary| {
summary.register_sync_committee_contribution(delay)
});
}
for validator_pubkey in participant_pubkeys.iter() {
if let Some(validator) = self.validators.get(validator_pubkey) {
let id = &validator.id;
metrics::inc_counter_vec(
&metrics::VALIDATOR_MONITOR_SYNC_COMMITTEE_MESSAGE_IN_CONTRIBUTION_TOTAL,
&[src, id],
);
info!(
self.log,
"Sync signature included in contribution";
"head" => %beacon_block_root,
"delay_ms" => %delay.as_millis(),
"epoch" => %epoch,
"slot" => %slot,
"src" => src,
"validator" => %id,
);
validator.with_epoch_summary(epoch, |summary| {
summary.register_sync_signature_contribution_inclusion()
});
}
}
}
/// Register that the `sync_aggregate` was included in a *valid* `BeaconBlock`.
pub fn register_sync_aggregate_in_block(
&self,
slot: Slot,
beacon_block_root: Hash256,
participant_pubkeys: Vec<&PublicKeyBytes>,
) {
let epoch = slot.epoch(T::slots_per_epoch());
for validator_pubkey in participant_pubkeys {
if let Some(validator) = self.validators.get(validator_pubkey) {
let id = &validator.id;
metrics::inc_counter_vec(
&metrics::VALIDATOR_MONITOR_SYNC_COMMITTEE_MESSAGE_IN_BLOCK_TOTAL,
&["block", id],
);
info!(
self.log,
"Sync signature included in block";
"head" => %beacon_block_root,
"epoch" => %epoch,
"slot" => %slot,
"validator" => %id,
);
validator.with_epoch_summary(epoch, |summary| {
summary.register_sync_signature_block_inclusions();
});
}
}
}
/// Register an exit from the gossip network. /// Register an exit from the gossip network.
pub fn register_gossip_voluntary_exit(&self, exit: &VoluntaryExit) { pub fn register_gossip_voluntary_exit(&self, exit: &VoluntaryExit) {
self.register_voluntary_exit("gossip", exit) self.register_voluntary_exit("gossip", exit)
@ -995,7 +1271,7 @@ impl<T: EthSpec> ValidatorMonitor<T> {
metrics::set_gauge_vec( metrics::set_gauge_vec(
&metrics::VALIDATOR_MONITOR_PREV_EPOCH_ATTESTATION_AGGREGATE_INCLUSIONS, &metrics::VALIDATOR_MONITOR_PREV_EPOCH_ATTESTATION_AGGREGATE_INCLUSIONS,
&[id], &[id],
summary.attestation_aggregate_incusions as i64, summary.attestation_aggregate_inclusions as i64,
); );
metrics::set_gauge_vec( metrics::set_gauge_vec(
&metrics::VALIDATOR_MONITOR_PREV_EPOCH_ATTESTATION_BLOCK_INCLUSIONS, &metrics::VALIDATOR_MONITOR_PREV_EPOCH_ATTESTATION_BLOCK_INCLUSIONS,
@ -1009,6 +1285,48 @@ impl<T: EthSpec> ValidatorMonitor<T> {
distance.as_u64() as i64, distance.as_u64() as i64,
); );
} }
/*
* Sync committee messages
*/
metrics::set_gauge_vec(
&metrics::VALIDATOR_MONITOR_PREV_EPOCH_SYNC_COMMITTEE_MESSAGES_TOTAL,
&[id],
summary.sync_committee_messages as i64,
);
if let Some(delay) = summary.sync_committee_message_min_delay {
metrics::observe_timer_vec(
&metrics::VALIDATOR_MONITOR_PREV_EPOCH_SYNC_COMMITTEE_MESSAGES_MIN_DELAY_SECONDS,
&[id],
delay,
);
}
metrics::set_gauge_vec(
&metrics::VALIDATOR_MONITOR_PREV_EPOCH_SYNC_CONTRIBUTION_INCLUSIONS,
&[id],
summary.sync_signature_contribution_inclusions as i64,
);
metrics::set_gauge_vec(
&metrics::VALIDATOR_MONITOR_PREV_EPOCH_SYNC_SIGNATURE_BLOCK_INCLUSIONS,
&[id],
summary.sync_signature_block_inclusions as i64,
);
/*
* Sync contributions
*/
metrics::set_gauge_vec(
&metrics::VALIDATOR_MONITOR_PREV_EPOCH_SYNC_CONTRIBUTIONS_TOTAL,
&[id],
summary.sync_contributions as i64,
);
if let Some(delay) = summary.sync_contribution_min_delay {
metrics::observe_timer_vec(
&metrics::VALIDATOR_MONITOR_PREV_EPOCH_SYNC_CONTRIBUTION_MIN_DELAY_SECONDS,
&[id],
delay,
);
}
/* /*
* Blocks * Blocks
*/ */
@ -1094,3 +1412,23 @@ pub fn get_slot_delay_ms<S: SlotClock>(
.and_then(|slot_start| seen_timestamp.checked_sub(slot_start)) .and_then(|slot_start| seen_timestamp.checked_sub(slot_start))
.unwrap_or_else(|| Duration::from_secs(0)) .unwrap_or_else(|| Duration::from_secs(0))
} }
/// Returns the duration between when any message could be produced and the `seen_timestamp`.
///
/// `message_production_delay` is the duration from the beginning of the slot when the message
/// should be produced.
/// e.g. for unagg attestations, `message_production_delay = slot_duration / 3`.
///
/// `slot` is the slot for which the message was produced.
fn get_message_delay_ms<S: SlotClock>(
seen_timestamp: Duration,
slot: Slot,
message_production_delay: Duration,
slot_clock: &S,
) -> Duration {
slot_clock
.start_of(slot)
.and_then(|slot_start| seen_timestamp.checked_sub(slot_start))
.and_then(|gross_delay| gross_delay.checked_sub(message_production_delay))
.unwrap_or_else(|| Duration::from_secs(0))
}

View File

@ -5,8 +5,8 @@ use beacon_chain::sync_committee_verification::{
Error as SyncVerificationError, VerifiedSyncCommitteeMessage, Error as SyncVerificationError, VerifiedSyncCommitteeMessage,
}; };
use beacon_chain::{ use beacon_chain::{
BeaconChain, BeaconChainError, BeaconChainTypes, StateSkipConfig, validator_monitor::timestamp_now, BeaconChain, BeaconChainError, BeaconChainTypes,
MAXIMUM_GOSSIP_CLOCK_DISPARITY, StateSkipConfig, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
}; };
use eth2::types::{self as api_types}; use eth2::types::{self as api_types};
use eth2_libp2p::PubsubMessage; use eth2_libp2p::PubsubMessage;
@ -130,6 +130,8 @@ pub fn process_sync_committee_signatures<T: BeaconChainTypes>(
) -> Result<(), warp::reject::Rejection> { ) -> Result<(), warp::reject::Rejection> {
let mut failures = vec![]; let mut failures = vec![];
let seen_timestamp = timestamp_now();
for (i, sync_committee_signature) in sync_committee_signatures.iter().enumerate() { for (i, sync_committee_signature) in sync_committee_signatures.iter().enumerate() {
let subnet_positions = match get_subnet_positions_for_sync_committee_message( let subnet_positions = match get_subnet_positions_for_sync_committee_message(
sync_committee_signature, sync_committee_signature,
@ -168,6 +170,16 @@ pub fn process_sync_committee_signatures<T: BeaconChainTypes>(
))), ))),
)?; )?;
// Register with validator monitor
chain
.validator_monitor
.read()
.register_api_sync_committee_message(
seen_timestamp,
verified.sync_message(),
&chain.slot_clock,
);
verified_for_pool = Some(verified); verified_for_pool = Some(verified);
} }
Err(e) => { Err(e) => {
@ -231,6 +243,8 @@ pub fn process_signed_contribution_and_proofs<T: BeaconChainTypes>(
let mut verified_contributions = Vec::with_capacity(signed_contribution_and_proofs.len()); let mut verified_contributions = Vec::with_capacity(signed_contribution_and_proofs.len());
let mut failures = vec![]; let mut failures = vec![];
let seen_timestamp = timestamp_now();
// Verify contributions & broadcast to the network. // Verify contributions & broadcast to the network.
for (index, contribution) in signed_contribution_and_proofs.into_iter().enumerate() { for (index, contribution) in signed_contribution_and_proofs.into_iter().enumerate() {
let aggregator_index = contribution.message.aggregator_index; let aggregator_index = contribution.message.aggregator_index;
@ -246,7 +260,17 @@ pub fn process_signed_contribution_and_proofs<T: BeaconChainTypes>(
)), )),
)?; )?;
// FIXME(altair): notify validator monitor // Register with validator monitor
chain
.validator_monitor
.read()
.register_api_sync_committee_contribution(
seen_timestamp,
verified_contribution.aggregate(),
verified_contribution.participant_pubkeys(),
&chain.slot_clock,
);
verified_contributions.push((index, verified_contribution)); verified_contributions.push((index, verified_contribution));
} }
// If we already know the contribution, don't broadcast it or attempt to // If we already know the contribution, don't broadcast it or attempt to

View File

@ -27,7 +27,7 @@ fn end_of_epoch_state<T: BeaconChainTypes>(
fn get_epoch_processing_summary<T: EthSpec>( fn get_epoch_processing_summary<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<EpochProcessingSummary, warp::reject::Rejection> { ) -> Result<EpochProcessingSummary<T>, warp::reject::Rejection> {
process_epoch(state, spec) process_epoch(state, spec)
.map_err(|e| warp_utils::reject::custom_server_error(format!("{:?}", e))) .map_err(|e| warp_utils::reject::custom_server_error(format!("{:?}", e)))
} }

View File

@ -716,7 +716,7 @@ impl<T: BeaconChainTypes> Worker<T> {
peer_id: PeerId, peer_id: PeerId,
sync_signature: SyncCommitteeMessage, sync_signature: SyncCommitteeMessage,
subnet_id: SyncSubnetId, subnet_id: SyncSubnetId,
_seen_timestamp: Duration, seen_timestamp: Duration,
) { ) {
let sync_signature = match self let sync_signature = match self
.chain .chain
@ -734,21 +734,19 @@ impl<T: BeaconChainTypes> Worker<T> {
} }
}; };
/*TODO: // Indicate to the `Network` service that this message is valid and can be
// propagated on the gossip network.
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept);
// Register the sync signature with any monitored validators. // Register the sync signature with any monitored validators.
self.chain self.chain
.validator_monitor .validator_monitor
.read() .read()
.register_gossip_unaggregated_attestation( .register_gossip_sync_committee_message(
seen_timestamp, seen_timestamp,
attestation.indexed_attestation(), sync_signature.sync_message(),
&self.chain.slot_clock, &self.chain.slot_clock,
); );
*/
// Indicate to the `Network` service that this message is valid and can be
// propagated on the gossip network.
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept);
metrics::inc_counter(&metrics::BEACON_PROCESSOR_SYNC_MESSAGE_VERIFIED_TOTAL); metrics::inc_counter(&metrics::BEACON_PROCESSOR_SYNC_MESSAGE_VERIFIED_TOTAL);
@ -778,7 +776,7 @@ impl<T: BeaconChainTypes> Worker<T> {
message_id: MessageId, message_id: MessageId,
peer_id: PeerId, peer_id: PeerId,
sync_contribution: SignedContributionAndProof<T::EthSpec>, sync_contribution: SignedContributionAndProof<T::EthSpec>,
_seen_timestamp: Duration, seen_timestamp: Duration,
) { ) {
let sync_contribution = match self let sync_contribution = match self
.chain .chain
@ -801,19 +799,16 @@ impl<T: BeaconChainTypes> Worker<T> {
// propagated on the gossip network. // propagated on the gossip network.
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept); self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Accept);
/* TODO
// Register the attestation with any monitored validators.
self.chain self.chain
.validator_monitor .validator_monitor
.read() .read()
.register_gossip_aggregated_attestation( .register_gossip_sync_committee_contribution(
seen_timestamp, seen_timestamp,
aggregate.aggregate(), sync_contribution.aggregate(),
aggregate.indexed_attestation(), sync_contribution.participant_pubkeys(),
&self.chain.slot_clock, &self.chain.slot_clock,
); );
metrics::inc_counter(&metrics::BEACON_PROCESSOR_AGGREGATED_ATTESTATION_VERIFIED_TOTAL); metrics::inc_counter(&metrics::BEACON_PROCESSOR_SYNC_CONTRIBUTION_VERIFIED_TOTAL);
*/
if let Err(e) = self if let Err(e) = self
.chain .chain

View File

@ -82,9 +82,21 @@ pub trait SlotClock: Send + Sync + Sized + Clone {
self.slot_duration() / 3 self.slot_duration() / 3
} }
/// Returns the delay between the start of the slot and when sync committee messages should be
/// produced.
fn sync_committee_message_production_delay(&self) -> Duration {
self.slot_duration() / 3
}
/// Returns the delay between the start of the slot and when aggregated attestations should be /// Returns the delay between the start of the slot and when aggregated attestations should be
/// produced. /// produced.
fn agg_attestation_production_delay(&self) -> Duration { fn agg_attestation_production_delay(&self) -> Duration {
self.slot_duration() * 2 / 3 self.slot_duration() * 2 / 3
} }
/// Returns the delay between the start of the slot and when partially aggregated `SyncCommitteeContribution` should be
/// produced.
fn sync_committee_contribution_production_delay(&self) -> Duration {
self.slot_duration() * 2 / 3
}
} }

View File

@ -27,7 +27,7 @@ pub mod weigh_justification_and_finalization;
pub fn process_epoch<T: EthSpec>( pub fn process_epoch<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<EpochProcessingSummary, Error> { ) -> Result<EpochProcessingSummary<T>, Error> {
// Verify that the `BeaconState` instantiation matches the fork at `state.slot()`. // Verify that the `BeaconState` instantiation matches the fork at `state.slot()`.
state state
.fork_name(spec) .fork_name(spec)

View File

@ -22,7 +22,7 @@ pub mod sync_committee_updates;
pub fn process_epoch<T: EthSpec>( pub fn process_epoch<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<EpochProcessingSummary, Error> { ) -> Result<EpochProcessingSummary<T>, Error> {
// Ensure the committee caches are built. // Ensure the committee caches are built.
state.build_committee_cache(RelativeEpoch::Previous, spec)?; state.build_committee_cache(RelativeEpoch::Previous, spec)?;
state.build_committee_cache(RelativeEpoch::Current, spec)?; state.build_committee_cache(RelativeEpoch::Current, spec)?;
@ -30,6 +30,7 @@ pub fn process_epoch<T: EthSpec>(
// Pre-compute participating indices and total balances. // Pre-compute participating indices and total balances.
let participation_cache = ParticipationCache::new(state, spec)?; let participation_cache = ParticipationCache::new(state, spec)?;
let sync_committee = state.current_sync_committee()?.clone();
// Justification and finalization. // Justification and finalization.
process_justification_and_finalization(state, &participation_cache)?; process_justification_and_finalization(state, &participation_cache)?;
@ -75,5 +76,6 @@ pub fn process_epoch<T: EthSpec>(
Ok(EpochProcessingSummary::Altair { Ok(EpochProcessingSummary::Altair {
participation_cache, participation_cache,
sync_committee,
}) })
} }

View File

@ -18,7 +18,7 @@ pub mod validator_statuses;
pub fn process_epoch<T: EthSpec>( pub fn process_epoch<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<EpochProcessingSummary, Error> { ) -> Result<EpochProcessingSummary<T>, Error> {
// Ensure the committee caches are built. // Ensure the committee caches are built.
state.build_committee_cache(RelativeEpoch::Previous, spec)?; state.build_committee_cache(RelativeEpoch::Previous, spec)?;
state.build_committee_cache(RelativeEpoch::Current, spec)?; state.build_committee_cache(RelativeEpoch::Current, spec)?;

View File

@ -3,20 +3,23 @@ use super::{
base::{validator_statuses::InclusionInfo, TotalBalances, ValidatorStatus}, base::{validator_statuses::InclusionInfo, TotalBalances, ValidatorStatus},
}; };
use crate::metrics; use crate::metrics;
use std::sync::Arc;
use types::{EthSpec, SyncCommittee};
/// Provides a summary of validator participation during the epoch. /// Provides a summary of validator participation during the epoch.
#[derive(PartialEq, Debug)] #[derive(PartialEq, Debug)]
pub enum EpochProcessingSummary { pub enum EpochProcessingSummary<T: EthSpec> {
Base { Base {
total_balances: TotalBalances, total_balances: TotalBalances,
statuses: Vec<ValidatorStatus>, statuses: Vec<ValidatorStatus>,
}, },
Altair { Altair {
participation_cache: ParticipationCache, participation_cache: ParticipationCache,
sync_committee: Arc<SyncCommittee<T>>,
}, },
} }
impl EpochProcessingSummary { impl<T: EthSpec> EpochProcessingSummary<T> {
/// Updates some Prometheus metrics with some values in `self`. /// Updates some Prometheus metrics with some values in `self`.
#[cfg(feature = "metrics")] #[cfg(feature = "metrics")]
pub fn observe_metrics(&self) -> Result<(), ParticipationCacheError> { pub fn observe_metrics(&self) -> Result<(), ParticipationCacheError> {
@ -40,12 +43,21 @@ impl EpochProcessingSummary {
Ok(()) Ok(())
} }
/// Returns the sync committee indices for the current epoch for altair.
pub fn sync_committee(&self) -> Option<&SyncCommittee<T>> {
match self {
EpochProcessingSummary::Altair { sync_committee, .. } => Some(sync_committee),
EpochProcessingSummary::Base { .. } => None,
}
}
/// Returns the sum of the effective balance of all validators in the current epoch. /// Returns the sum of the effective balance of all validators in the current epoch.
pub fn current_epoch_total_active_balance(&self) -> u64 { pub fn current_epoch_total_active_balance(&self) -> u64 {
match self { match self {
EpochProcessingSummary::Base { total_balances, .. } => total_balances.current_epoch(), EpochProcessingSummary::Base { total_balances, .. } => total_balances.current_epoch(),
EpochProcessingSummary::Altair { EpochProcessingSummary::Altair {
participation_cache, participation_cache,
..
} => participation_cache.current_epoch_total_active_balance(), } => participation_cache.current_epoch_total_active_balance(),
} }
} }
@ -59,6 +71,7 @@ impl EpochProcessingSummary {
} }
EpochProcessingSummary::Altair { EpochProcessingSummary::Altair {
participation_cache, participation_cache,
..
} => participation_cache.current_epoch_target_attesting_balance(), } => participation_cache.current_epoch_target_attesting_balance(),
} }
} }
@ -69,6 +82,7 @@ impl EpochProcessingSummary {
EpochProcessingSummary::Base { total_balances, .. } => total_balances.previous_epoch(), EpochProcessingSummary::Base { total_balances, .. } => total_balances.previous_epoch(),
EpochProcessingSummary::Altair { EpochProcessingSummary::Altair {
participation_cache, participation_cache,
..
} => participation_cache.previous_epoch_total_active_balance(), } => participation_cache.previous_epoch_total_active_balance(),
} }
} }
@ -126,6 +140,7 @@ impl EpochProcessingSummary {
} }
EpochProcessingSummary::Altair { EpochProcessingSummary::Altair {
participation_cache, participation_cache,
..
} => participation_cache.previous_epoch_target_attesting_balance(), } => participation_cache.previous_epoch_target_attesting_balance(),
} }
} }
@ -144,6 +159,7 @@ impl EpochProcessingSummary {
} }
EpochProcessingSummary::Altair { EpochProcessingSummary::Altair {
participation_cache, participation_cache,
..
} => participation_cache.previous_epoch_head_attesting_balance(), } => participation_cache.previous_epoch_head_attesting_balance(),
} }
} }
@ -162,6 +178,7 @@ impl EpochProcessingSummary {
} }
EpochProcessingSummary::Altair { EpochProcessingSummary::Altair {
participation_cache, participation_cache,
..
} => participation_cache.previous_epoch_source_attesting_balance(), } => participation_cache.previous_epoch_source_attesting_balance(),
} }
} }

View File

@ -26,7 +26,7 @@ pub fn per_slot_processing<T: EthSpec>(
state: &mut BeaconState<T>, state: &mut BeaconState<T>,
state_root: Option<Hash256>, state_root: Option<Hash256>,
spec: &ChainSpec, spec: &ChainSpec,
) -> Result<Option<EpochProcessingSummary>, Error> { ) -> Result<Option<EpochProcessingSummary<T>>, Error> {
// Verify that the `BeaconState` instantiation matches the fork at `state.slot()`. // Verify that the `BeaconState` instantiation matches the fork at `state.slot()`.
state state
.fork_name(spec) .fork_name(spec)

View File

@ -84,4 +84,9 @@ impl<T: EthSpec> SyncCommittee<T> {
} }
Ok(subnet_positions) Ok(subnet_positions)
} }
/// Returns `true` if the pubkey exists in the `SyncCommittee`.
pub fn contains(&self, pubkey: &PublicKeyBytes) -> bool {
self.pubkeys.contains(pubkey)
}
} }