Skip to content

Commit 62d566c

Browse files
jimmygchenWoodpile37
authored andcommitted
Cache target attester balances for unrealized FFG progression calculation (sigp#4362)
This PR introduces a "progressive balances" cache on the `BeaconState`, which keeps track of the accumulated target attestation balance for the current & previous epochs. The cached values are utilised by fork choice to calculate unrealized justification and finalization (instead of converting epoch participation arrays to balances for each block we receive). This optimization will be rolled out gradually to allow for more testing. A new `--progressive-balances disabled|checked|strict|fast` flag is introduced to support this: - `checked`: enabled with checks against participation cache, and falls back to the existing epoch processing calculation if there is a total target attester balance mismatch. There is no performance gain from this as the participation cache still needs to be computed. **This is the default mode for now.** - `strict`: enabled with checks against participation cache, returns error if there is a mismatch. **Used for testing only**. - `fast`: enabled with no comparative checks and without computing the participation cache. This mode gives us the performance gains from the optimization. This is still experimental and not currently recommended for production usage, but will become the default mode in a future release. - `disabled`: disable the usage of progressive cache, and use the existing method for FFG progression calculation. This mode may be useful if we find a bug and want to stop the frequent error logs. - [x] Initial cache implementation in `BeaconState` - [x] Perform checks in fork choice to compare the progressive balances cache against results from `ParticipationCache` - [x] Add CLI flag, and disable the optimization by default - [x] Testing on Goerli & Benchmarking - [x] Move caching logic from state processing to the `ProgressiveBalancesCache` (see [this comment](sigp#4362 (comment))) - [x] Add attesting balance metrics Co-authored-by: Jimmy Chen <jimmy@sigmaprime.io>
1 parent fd18cef commit 62d566c

43 files changed

Lines changed: 711 additions & 84 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

beacon_node/beacon_chain/src/builder.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ where
342342
let beacon_block = genesis_block(&mut beacon_state, &self.spec)?;
343343

344344
beacon_state
345-
.build_all_caches(&self.spec)
345+
.build_caches(&self.spec)
346346
.map_err(|e| format!("Failed to build genesis state caches: {:?}", e))?;
347347

348348
let beacon_state_root = beacon_block.message().state_root();
@@ -441,7 +441,7 @@ where
441441
// Prime all caches before storing the state in the database and computing the tree hash
442442
// root.
443443
weak_subj_state
444-
.build_all_caches(&self.spec)
444+
.build_caches(&self.spec)
445445
.map_err(|e| format!("Error building caches on checkpoint state: {e:?}"))?;
446446

447447
let computed_state_root = weak_subj_state
@@ -704,6 +704,8 @@ where
704704
store.clone(),
705705
Some(current_slot),
706706
&self.spec,
707+
self.chain_config.progressive_balances_mode,
708+
&log,
707709
)?;
708710
}
709711

@@ -717,7 +719,7 @@ where
717719

718720
head_snapshot
719721
.beacon_state
720-
.build_all_caches(&self.spec)
722+
.build_caches(&self.spec)
721723
.map_err(|e| format!("Failed to build state caches: {:?}", e))?;
722724

723725
// Perform a check to ensure that the finalization points of the head and fork choice are

beacon_node/beacon_chain/src/chain_config.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
pub use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold};
22
use serde_derive::{Deserialize, Serialize};
33
use std::time::Duration;
4-
use types::{Checkpoint, Epoch};
4+
use types::{Checkpoint, Epoch, ProgressiveBalancesMode};
55

66
pub const DEFAULT_RE_ORG_THRESHOLD: ReOrgThreshold = ReOrgThreshold(20);
77
pub const DEFAULT_RE_ORG_MAX_EPOCHS_SINCE_FINALIZATION: Epoch = Epoch::new(2);
@@ -81,6 +81,8 @@ pub struct ChainConfig {
8181
pub always_prepare_payload: bool,
8282
/// Whether backfill sync processing should be rate-limited.
8383
pub enable_backfill_rate_limiting: bool,
84+
/// Whether to use `ProgressiveBalancesCache` in unrealized FFG progression calculation.
85+
pub progressive_balances_mode: ProgressiveBalancesMode,
8486
}
8587

8688
impl Default for ChainConfig {
@@ -111,6 +113,7 @@ impl Default for ChainConfig {
111113
genesis_backfill: false,
112114
always_prepare_payload: false,
113115
enable_backfill_rate_limiting: true,
116+
progressive_balances_mode: ProgressiveBalancesMode::Checked,
114117
}
115118
}
116119
}

beacon_node/beacon_chain/src/fork_revert.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use state_processing::{
1010
use std::sync::Arc;
1111
use std::time::Duration;
1212
use store::{iter::ParentRootBlockIterator, HotColdDB, ItemStore};
13-
use types::{BeaconState, ChainSpec, EthSpec, ForkName, Hash256, SignedBeaconBlock, Slot};
13+
use types::{
14+
BeaconState, ChainSpec, EthSpec, ForkName, Hash256, ProgressiveBalancesMode, SignedBeaconBlock,
15+
Slot,
16+
};
1417

1518
const CORRUPT_DB_MESSAGE: &str = "The database could be corrupt. Check its file permissions or \
1619
consider deleting it by running with the --purge-db flag.";
@@ -100,6 +103,8 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
100103
store: Arc<HotColdDB<E, Hot, Cold>>,
101104
current_slot: Option<Slot>,
102105
spec: &ChainSpec,
106+
progressive_balances_mode: ProgressiveBalancesMode,
107+
log: &Logger,
103108
) -> Result<ForkChoice<BeaconForkChoiceStore<E, Hot, Cold>, E>, String> {
104109
// Fetch finalized block.
105110
let finalized_checkpoint = head_state.finalized_checkpoint();
@@ -197,7 +202,9 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
197202
Duration::from_secs(0),
198203
&state,
199204
payload_verification_status,
205+
progressive_balances_mode,
200206
spec,
207+
log,
201208
)
202209
.map_err(|e| format!("Error applying replayed block to fork choice: {:?}", e))?;
203210
}

beacon_node/beacon_chain/src/test_utils.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -774,9 +774,7 @@ where
774774
complete_state_advance(&mut state, None, slot, &self.spec)
775775
.expect("should be able to advance state to slot");
776776

777-
state
778-
.build_all_caches(&self.spec)
779-
.expect("should build caches");
777+
state.build_caches(&self.spec).expect("should build caches");
780778

781779
let proposer_index = state.get_beacon_proposer_index(slot, &self.spec).unwrap();
782780

@@ -823,9 +821,7 @@ where
823821
complete_state_advance(&mut state, None, slot, &self.spec)
824822
.expect("should be able to advance state to slot");
825823

826-
state
827-
.build_all_caches(&self.spec)
828-
.expect("should build caches");
824+
state.build_caches(&self.spec).expect("should build caches");
829825

830826
let proposer_index = state.get_beacon_proposer_index(slot, &self.spec).unwrap();
831827

@@ -1524,6 +1520,36 @@ where
15241520
.sign(sk, &fork, genesis_validators_root, &self.chain.spec)
15251521
}
15261522

1523+
pub fn add_proposer_slashing(&self, validator_index: u64) -> Result<(), String> {
1524+
let propposer_slashing = self.make_proposer_slashing(validator_index);
1525+
if let ObservationOutcome::New(verified_proposer_slashing) = self
1526+
.chain
1527+
.verify_proposer_slashing_for_gossip(propposer_slashing)
1528+
.expect("should verify proposer slashing for gossip")
1529+
{
1530+
self.chain
1531+
.import_proposer_slashing(verified_proposer_slashing);
1532+
Ok(())
1533+
} else {
1534+
Err("should observe new proposer slashing".to_string())
1535+
}
1536+
}
1537+
1538+
pub fn add_attester_slashing(&self, validator_indices: Vec<u64>) -> Result<(), String> {
1539+
let attester_slashing = self.make_attester_slashing(validator_indices);
1540+
if let ObservationOutcome::New(verified_attester_slashing) = self
1541+
.chain
1542+
.verify_attester_slashing_for_gossip(attester_slashing)
1543+
.expect("should verify attester slashing for gossip")
1544+
{
1545+
self.chain
1546+
.import_attester_slashing(verified_attester_slashing);
1547+
Ok(())
1548+
} else {
1549+
Err("should observe new attester slashing".to_string())
1550+
}
1551+
}
1552+
15271553
pub fn add_bls_to_execution_change(
15281554
&self,
15291555
validator_index: u64,

beacon_node/beacon_chain/tests/capella.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,8 @@ async fn base_altair_merge_capella() {
133133
for _ in (merge_fork_slot.as_u64() + 3)..capella_fork_slot.as_u64() {
134134
harness.extend_slots(1).await;
135135
let block = &harness.chain.head_snapshot().beacon_block;
136-
let full_payload: FullPayload<E> = block
137-
.message()
138-
.body()
139-
.execution_payload()
140-
.unwrap()
141-
.clone()
142-
.into();
136+
let full_payload: FullPayload<E> =
137+
block.message().body().execution_payload().unwrap().into();
143138
// pre-capella shouldn't have withdrawals
144139
assert!(full_payload.withdrawals_root().is_err());
145140
execution_payloads.push(full_payload);
@@ -151,13 +146,8 @@ async fn base_altair_merge_capella() {
151146
for _ in 0..16 {
152147
harness.extend_slots(1).await;
153148
let block = &harness.chain.head_snapshot().beacon_block;
154-
let full_payload: FullPayload<E> = block
155-
.message()
156-
.body()
157-
.execution_payload()
158-
.unwrap()
159-
.clone()
160-
.into();
149+
let full_payload: FullPayload<E> =
150+
block.message().body().execution_payload().unwrap().into();
161151
// post-capella should have withdrawals
162152
assert!(full_payload.withdrawals_root().is_ok());
163153
execution_payloads.push(full_payload);

beacon_node/beacon_chain/tests/payload_invalidation.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1064,8 +1064,9 @@ async fn invalid_parent() {
10641064
Duration::from_secs(0),
10651065
&state,
10661066
PayloadVerificationStatus::Optimistic,
1067+
rig.harness.chain.config.progressive_balances_mode,
10671068
&rig.harness.chain.spec,
1068-
1069+
rig.harness.logger()
10691070
),
10701071
Err(ForkChoiceError::ProtoArrayStringError(message))
10711072
if message.contains(&format!(

beacon_node/http_api/src/block_rewards.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pub fn get_block_rewards<T: BeaconChainTypes>(
4949
.map_err(beacon_chain_error)?;
5050

5151
state
52-
.build_all_caches(&chain.spec)
52+
.build_caches(&chain.spec)
5353
.map_err(beacon_state_error)?;
5454

5555
let mut reward_cache = Default::default();

beacon_node/src/cli.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use clap::{App, Arg};
22
use strum::VariantNames;
3+
use types::ProgressiveBalancesMode;
34

45
pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
56
App::new("beacon_node")
@@ -1159,4 +1160,17 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
11591160
developers. This directory is not pruned, users should be careful to avoid \
11601161
filling up their disks.")
11611162
)
1163+
.arg(
1164+
Arg::with_name("progressive-balances")
1165+
.long("progressive-balances")
1166+
.value_name("MODE")
1167+
.help("Options to enable or disable the progressive balances cache for \
1168+
unrealized FFG progression calculation. The default `checked` mode compares \
1169+
the progressive balances from the cache against results from the existing \
1170+
method. If there is a mismatch, it falls back to the existing method. The \
1171+
optimized mode (`fast`) is faster but is still experimental, and is \
1172+
not recommended for mainnet usage at this time.")
1173+
.takes_value(true)
1174+
.possible_values(ProgressiveBalancesMode::VARIANTS)
1175+
)
11621176
}

beacon_node/src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,12 @@ pub fn get_config<E: EthSpec>(
837837
client_config.network.invalid_block_storage = Some(path);
838838
}
839839

840+
if let Some(progressive_balances_mode) =
841+
clap_utils::parse_optional(cli_args, "progressive-balances")?
842+
{
843+
client_config.chain.progressive_balances_mode = progressive_balances_mode;
844+
}
845+
840846
Ok(client_config)
841847
}
842848

0 commit comments

Comments
 (0)