Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
8ec62d2
add missed_block metric
v4lproik Sep 13, 2023
bb545e1
init missed_block in constructor
v4lproik Sep 13, 2023
da53390
declare beaconproposercache in ValidatorMonitor
v4lproik Sep 13, 2023
710ff13
refacto proposer_shuffling_decision_root to use epoch instead of curr…
v4lproik Sep 13, 2023
a457a48
imple new proposer_shuffling_decision_root in callers
v4lproik Sep 13, 2023
ddd45a9
push missed_blocks
v4lproik Sep 13, 2023
f88f691
prune missed_blocks
v4lproik Sep 13, 2023
3845616
only add to hashmap if it's a monitored validator
v4lproik Sep 13, 2023
e13280d
remove current_epoch dup + typos
v4lproik Sep 14, 2023
2dbf8e7
extract in func
v4lproik Sep 14, 2023
9370ea8
add prom metrics
v4lproik Sep 14, 2023
751c88f
checkpoint is not only epoch but slot as well
v4lproik Sep 14, 2023
83da21b
add safeguard if we start a new chain at slot 0
v4lproik Sep 14, 2023
518648f
clean
v4lproik Sep 14, 2023
5026f0e
remove unnecessary negative value for a slot
v4lproik Sep 16, 2023
fe4958a
typo in comment
v4lproik Sep 16, 2023
84c1f8a
remove unused current_epoch
v4lproik Sep 16, 2023
3a3bf91
share beacon_proposer_cache between validator_monitor and beacon_chain
v4lproik Sep 16, 2023
4129d34
pass Hash256::zero()
v4lproik Sep 16, 2023
18970e5
debug objects
v4lproik Sep 16, 2023
724c161
fix loop: lag is at the head
v4lproik Sep 16, 2023
ac034fb
sed s/get_slot/get_epoch
v4lproik Sep 16, 2023
0d09546
fewer calls to cache.get_epoch
v4lproik Sep 17, 2023
8ec9288
fix typos
v4lproik Sep 17, 2023
a7ff5be
remove cache first call
v4lproik Sep 17, 2023
8d7a9f3
export TYPICAL_SLOTS_PER_EPOCH and use it in validator_monitor
v4lproik Sep 17, 2023
6bd4fb8
switch to gauge & loop over missed_blocks hashset
v4lproik Sep 17, 2023
d132dd6
fix subnet_service tests
v4lproik Sep 17, 2023
62a6ca8
remove unused var
v4lproik Sep 18, 2023
eb85595
clean + fix nits
v4lproik Sep 24, 2023
3dd8839
add beacon_proposer_cache + validator_monitor in builder
v4lproik Sep 24, 2023
e395c07
fix store_tests
v4lproik Sep 24, 2023
179b145
fix builder tests
v4lproik Sep 24, 2023
4151571
add tests
v4lproik Sep 24, 2023
4b1213f
add validator monitor set of tests
v4lproik Sep 24, 2023
c8cf973
clean tests
v4lproik Sep 24, 2023
265ae08
nits
v4lproik Sep 24, 2023
67923bb
optimise imports
v4lproik Sep 24, 2023
1c45e05
lint
v4lproik Sep 24, 2023
e1364bf
typo
v4lproik Sep 28, 2023
c92ab4a
added self.aggregatable
v4lproik Sep 28, 2023
e3fadcb
duplicate proposer_shuffling_decision_root
v4lproik Sep 28, 2023
b59d879
remove duplication in passing beacon_proposer_cache
v4lproik Sep 28, 2023
7b4bb21
remove duplication in passing beacon_proposer_cache
v4lproik Sep 28, 2023
37706b8
using indices
v4lproik Sep 28, 2023
ac46a0e
fmt
v4lproik Sep 28, 2023
704de81
implement missed blocks total
v4lproik Sep 28, 2023
dc19c4c
nits
v4lproik Oct 1, 2023
bfc8379
avoid heap allocation
v4lproik Oct 4, 2023
ed99d71
remove recursion limit
v4lproik Oct 4, 2023
18c6a73
fix lint
v4lproik Oct 4, 2023
c517e3d
Fix valdiator monitor builder pattern
paulhauner Oct 3, 2023
7ab3814
renaming metrics
v4lproik Oct 5, 2023
1660b36
renaming metrics in validator monitor
v4lproik Oct 5, 2023
3461c09
add log if there's a missing validator index
v4lproik Oct 5, 2023
724419c
consistent log
v4lproik Oct 5, 2023
2102655
fix loop
v4lproik Oct 5, 2023
54aa915
better loop
v4lproik Oct 7, 2023
bc3e396
move gauge to counter
v4lproik Oct 7, 2023
127c86a
fmt
v4lproik Oct 9, 2023
8c2d5f9
add error message
v4lproik Oct 9, 2023
3e1110d
lint
v4lproik Oct 9, 2023
4bd1b7c
fix prom metrics
v4lproik Oct 12, 2023
55add09
set gauge to 0 when non-finalized epochs
v4lproik Oct 12, 2023
648673d
better wording
v4lproik Oct 12, 2023
fae3ff1
remove hash256::zero in favour of block_root
v4lproik Oct 15, 2023
c1f0754
fix gauge total label
v4lproik Oct 15, 2023
109ed39
fix last missed block validator
v4lproik Oct 15, 2023
8672725
Add `MissedBlock` struct
paulhauner Nov 2, 2023
9840037
Fix comment
paulhauner Nov 2, 2023
0f27882
Refactor non-finalized block loop
paulhauner Nov 2, 2023
5e21607
Fix off-by-one
paulhauner Nov 2, 2023
73924cf
Avoid string allocation
paulhauner Nov 2, 2023
062e5d2
Fix compile error
paulhauner Nov 2, 2023
c8ce880
Remove non-finalized blocks metric
paulhauner Nov 2, 2023
6e9e108
Merge branch 'unstable' into add-missed-blocks-to-monitored-validators
v4lproik Nov 5, 2023
9771fb3
fix func clojure
v4lproik Nov 5, 2023
5aab7c3
remove unused variable
v4lproik Nov 5, 2023
0392b3f
remove unused DEFAULT_INDIVIDUAL_TRACKING_THRESHOLD
v4lproik Nov 5, 2023
586c00f
remove unused DEFAULT_INDIVIDUAL_TRACKING_THRESHOLD in builder
v4lproik Nov 5, 2023
4684aff
add validator index depending on the fork name
v4lproik Nov 6, 2023
5b1374e
typos
v4lproik Nov 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3896,7 +3896,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
cached_head
.snapshot
.beacon_state
.proposer_shuffling_decision_root(proposer_head)?
.proposer_shuffling_decision_root(head_epoch, proposer_head)?
} else {
proposer_head
};
Expand Down
3 changes: 2 additions & 1 deletion beacon_node/beacon_chain/src/beacon_proposer_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,10 @@ pub fn compute_proposer_duties_from_head<T: BeaconChainTypes>(
.get_beacon_proposer_indices(&chain.spec)
.map_err(BeaconChainError::from)?;


let dependent_root = state
// The only block which decides its own shuffling is the genesis block.
.proposer_shuffling_decision_root(chain.genesis_block_root)
.proposer_shuffling_decision_root(state.current_epoch(), chain.genesis_block_root)
.map_err(BeaconChainError::from)?;

Ok((indices, dependent_root, execution_status, state.fork()))
Expand Down
3 changes: 2 additions & 1 deletion beacon_node/beacon_chain/src/canonical_head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,9 +827,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
// These fields are used for server-sent events.
let state_root = new_snapshot.beacon_state_root();
let head_slot = new_snapshot.beacon_state.slot();
let head_epoch = head_slot.epoch(T::EthSpec::slots_per_epoch());
let dependent_root = new_snapshot
.beacon_state
.proposer_shuffling_decision_root(self.genesis_block_root);
.proposer_shuffling_decision_root(head_epoch, self.genesis_block_root);
let prev_dependent_root = new_snapshot
.beacon_state
.attester_shuffling_decision_root(self.genesis_block_root, RelativeEpoch::Current);
Expand Down
5 changes: 5 additions & 0 deletions beacon_node/beacon_chain/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,11 @@ lazy_static! {
"Number of attester slashings seen",
&["src", "validator"]
);
pub static ref VALIDATOR_MONITOR_MISSED_NON_FINALIZED_BLOCKS_TOTAL: Result<IntCounterVec> = try_create_int_counter_vec(
"validator_monitor_missed_non_finalized_blocks_total",
"Number of non-finalized blocks missed",
&["epoch", "slot", "validator"]
);

/*
* Block Delay Metrics
Expand Down
71 changes: 71 additions & 0 deletions beacon_node/beacon_chain/src/validator_monitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use types::{
IndexedAttestation, ProposerSlashing, PublicKeyBytes, SignedAggregateAndProof,
SignedContributionAndProof, Slot, SyncCommitteeMessage, VoluntaryExit,
};
use crate::beacon_proposer_cache::BeaconProposerCache;

/// Used for Prometheus labels.
///
Expand All @@ -37,6 +38,9 @@ pub const HISTORIC_EPOCHS: usize = 10;
/// Prometheus cardinality and log volume.
pub const DEFAULT_INDIVIDUAL_TRACKING_THRESHOLD: usize = 64;

/// Lag slots used in detecting missed blocks for the monitored validators
pub const MISSED_BLOCK_LAG_SLOTS: usize = 4;

#[derive(Debug)]
pub enum Error {
InvalidPubkey(String),
Expand Down Expand Up @@ -343,7 +347,10 @@ pub struct ValidatorMonitor<T> {
/// large validator counts causing infeasibly high cardinailty for
/// Prometheus and high log volumes.
individual_tracking_threshold: usize,
/// A Map representing the missed blocks by epoch, validator_index(state.validators) and slot
missed_blocks: HashSet<(Epoch, u64, Slot)>,
log: Logger,
beacon_proposer_cache: BeaconProposerCache,
_phantom: PhantomData<T>,
}

Expand All @@ -352,14 +359,17 @@ impl<T: EthSpec> ValidatorMonitor<T> {
pubkeys: Vec<PublicKeyBytes>,
auto_register: bool,
individual_tracking_threshold: usize,
beacon_proposer_cache: BeaconProposerCache,
log: Logger,
) -> Self {
let mut s = Self {
validators: <_>::default(),
indices: <_>::default(),
auto_register,
individual_tracking_threshold,
missed_blocks: <_>::default(),
log,
beacon_proposer_cache,
_phantom: PhantomData,
};
for pubkey in pubkeys {
Expand Down Expand Up @@ -411,6 +421,9 @@ impl<T: EthSpec> ValidatorMonitor<T> {
self.indices.insert(i, validator.pubkey);
});

// Add missed blocks for the monitored validators
self.add_validators_missed_blocks(current_epoch, state);

// Update metrics for individual validators.
for monitored_validator in self.validators.values() {
if let Some(i) = monitored_validator.index {
Expand Down Expand Up @@ -487,8 +500,66 @@ impl<T: EthSpec> ValidatorMonitor<T> {
u64_to_i64(validator.withdrawable_epoch),
);
}

// Add non finalized missed blocks for the monitored validators for the current slot of the current epoch
let current_slot = state.slot();
self.missed_blocks.contains(&(current_epoch, i as u64, current_slot))
.then(|| {
metrics::inc_counter_vec(
&metrics::VALIDATOR_MONITOR_MISSED_NON_FINALIZED_BLOCKS_TOTAL,
&[current_epoch.to_string().as_str(), current_slot.to_string().as_str(), id],
);
});
}
}
}

fn add_validators_missed_blocks(&mut self, current_epoch: Epoch, state: &BeaconState<T>) {
// Determine missed (non-finalized) blocks (can contain false positives)
let range_of_slot = (T::slots_per_epoch() as usize - MISSED_BLOCK_LAG_SLOTS) - 1;
for n in 0..range_of_slot {
let slot = state.slot() - Slot::new(n as u64);
let prev_slot = slot - 1;

// safeguard if we are on a new chain, the slot - 1 can be negative
if prev_slot < 0 {
break;
}

// condition for missed_block is defined such as block_root == block_root - n
// where the proposer who missed the block is the proposer of the block at block_root - n
if let (Ok(block_root), Ok(prev_block_root)) = (state.get_block_root(slot), state.get_block_root(prev_slot)) {
if block_root == prev_block_root {
let epoch = slot.epoch(T::slots_per_epoch());
if let Ok(shuffling_decision_block) = state.proposer_shuffling_decision_root(epoch, *block_root) {
// TODO: AI(Joel) Ask the team if we can use the beacon proposer cache without the mutex
// mutex is implemented in the caller (BeaconChain), not in the cache implementation
if let Some(proposer) = self.beacon_proposer_cache.get_slot::<T>(shuffling_decision_block, slot) {
// only add missed blocks for the proposer if it's in the list of monitored validators
if self.validators
.values()
.into_iter()
.find(|validator| validator.index == Some(proposer.index as u64))
.is_some() {
self.missed_blocks.insert((current_epoch, proposer.index as u64, slot));
}
} else {
debug!(
self.log,
"Could not find proposer for a missed non-finalized block in beacon proposer cache";
"slot" => slot,
"block_root" => format!("{}", block_root),
"shuffling_decision_block" => format!("{}", shuffling_decision_block),
);
}
}
}
}
}

// Prune missed blocks that are prior to last finalized epoch
let finalized_epoch = state.finalized_checkpoint().epoch;
self.missed_blocks.retain(|(epoch, _, _)| *epoch >= finalized_epoch);
}

/// Run `func` with the `TOTAL_LABEL` and optionally the
Expand Down
6 changes: 3 additions & 3 deletions beacon_node/http_api/src/proposer_duties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ fn try_proposer_duties_from_cache<T: BeaconChainTypes>(
let head = chain.canonical_head.cached_head();
let head_block = &head.snapshot.beacon_block;
let head_block_root = head.head_block_root();
let head_epoch = head_block.slot().epoch(T::EthSpec::slots_per_epoch());
let head_decision_root = head
.snapshot
.beacon_state
.proposer_shuffling_decision_root(head_block_root)
.proposer_shuffling_decision_root(head_epoch, head_block_root)
.map_err(warp_utils::reject::beacon_state_error)?;
let head_epoch = head_block.slot().epoch(T::EthSpec::slots_per_epoch());
let execution_optimistic = chain
.is_optimistic_or_invalid_head_block(head_block)
.map_err(warp_utils::reject::beacon_chain_error)?;
Expand Down Expand Up @@ -231,7 +231,7 @@ fn compute_historic_proposer_duties<T: BeaconChainTypes>(
// We can supply the genesis block root as the block root since we know that the only block that
// decides its own root is the genesis block.
let dependent_root = state
.proposer_shuffling_decision_root(chain.genesis_block_root)
.proposer_shuffling_decision_root(state.current_epoch(), chain.genesis_block_root)
.map_err(BeaconChainError::from)
.map_err(warp_utils::reject::beacon_chain_error)?;

Expand Down
11 changes: 6 additions & 5 deletions consensus/types/src/beacon_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -613,15 +613,16 @@ impl<T: EthSpec> BeaconState<T> {
cache.get_all_beacon_committees()
}

/// Returns the block root which decided the proposer shuffling for the current epoch. This root
/// Returns the block root which decided the proposer shuffling for the epoch passed in parameter. This root
/// can be used to key this proposer shuffling.
///
/// ## Notes
///
/// The `block_root` covers the one-off scenario where the genesis block decides its own
/// shuffling. It should be set to the latest block applied to `self` or the genesis block root.
pub fn proposer_shuffling_decision_root(&self, block_root: Hash256) -> Result<Hash256, Error> {
let decision_slot = self.proposer_shuffling_decision_slot();
/// The `epoch` should be set to the epoch of the block root.
pub fn proposer_shuffling_decision_root(&self, epoch: Epoch, block_root: Hash256) -> Result<Hash256, Error> {
let decision_slot = self.proposer_shuffling_decision_slot(epoch);
if self.slot() == decision_slot {
Ok(block_root)
} else {
Expand All @@ -631,8 +632,8 @@ impl<T: EthSpec> BeaconState<T> {

/// Returns the slot at which the proposer shuffling was decided. The block root at this slot
/// can be used to key the proposer shuffling for the current epoch.
fn proposer_shuffling_decision_slot(&self) -> Slot {
self.current_epoch()
fn proposer_shuffling_decision_slot(&self, epoch: Epoch) -> Slot {
epoch
.start_slot(T::slots_per_epoch())
.saturating_sub(1_u64)
}
Expand Down