From abc62a9ef0323aa7fde21f5a66aab1bf42ced8ab Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Fri, 21 Oct 2022 18:01:03 +1100 Subject: [PATCH] Add epoch cache --- beacon_node/operation_pool/src/attestation.rs | 15 +- consensus/state_processing/src/common/base.rs | 41 ++++-- .../state_processing/src/consensus_context.rs | 39 ++++- consensus/state_processing/src/epoch_cache.rs | 135 ++++++++++++++++++ consensus/state_processing/src/lib.rs | 2 + .../altair/sync_committee.rs | 4 +- .../src/per_block_processing/errors.rs | 9 +- .../process_operations.rs | 22 +-- .../base/rewards_and_penalties.rs | 17 ++- .../base/validator_statuses.rs | 22 +-- consensus/types/src/execution_payload.rs | 3 +- consensus/types/src/validator.rs | 13 ++ lcli/src/transition_blocks.rs | 33 ++++- 13 files changed, 291 insertions(+), 64 deletions(-) create mode 100644 consensus/state_processing/src/epoch_cache.rs diff --git a/beacon_node/operation_pool/src/attestation.rs b/beacon_node/operation_pool/src/attestation.rs index dc57a20a2c..6e6c9a0fe4 100644 --- a/beacon_node/operation_pool/src/attestation.rs +++ b/beacon_node/operation_pool/src/attestation.rs @@ -47,18 +47,17 @@ impl<'a, T: EthSpec> AttMaxCover<'a, T> { .get_beacon_committee(att.data.slot, att.data.index) .ok()?; let indices = get_attesting_indices::(committee.committee, &fresh_validators).ok()?; + let sqrt_total_active_balance = base::SqrtTotalActiveBalance::new(total_active_balance); let fresh_validators_rewards: HashMap = indices .iter() .map(|i| *i as u64) .flat_map(|validator_index| { - let reward = base::get_base_reward( - state, - validator_index as usize, - total_active_balance, - spec, - ) - .ok()? - .checked_div(spec.proposer_reward_quotient)?; + let effective_balance = + state.get_effective_balance(validator_index as usize).ok()?; + let reward = + base::get_base_reward(effective_balance, sqrt_total_active_balance, spec) + .ok()? + .checked_div(spec.proposer_reward_quotient)?; Some((validator_index, reward)) }) .collect(); diff --git a/consensus/state_processing/src/common/base.rs b/consensus/state_processing/src/common/base.rs index b5cb382721..47b0de9ef1 100644 --- a/consensus/state_processing/src/common/base.rs +++ b/consensus/state_processing/src/common/base.rs @@ -1,19 +1,30 @@ use integer_sqrt::IntegerSquareRoot; -use safe_arith::SafeArith; +use safe_arith::{ArithError, SafeArith}; use types::*; -/// Returns the base reward for some validator. -pub fn get_base_reward( - state: &BeaconState, - index: usize, - // Should be == get_total_active_balance(state, spec) - total_active_balance: u64, - spec: &ChainSpec, -) -> Result { - state - .get_effective_balance(index)? - .safe_mul(spec.base_reward_factor)? - .safe_div(total_active_balance.integer_sqrt())? - .safe_div(spec.base_rewards_per_epoch) - .map_err(Into::into) +/// This type exists to avoid confusing `total_active_balance` with `sqrt_total_active_balance`, +/// since they are used in close proximity and the same type (`u64`). +#[derive(Copy, Clone)] +pub struct SqrtTotalActiveBalance(u64); + +impl SqrtTotalActiveBalance { + pub fn new(total_active_balance: u64) -> Self { + Self(total_active_balance.integer_sqrt()) + } + + pub fn as_u64(&self) -> u64 { + self.0 + } +} + +/// Returns the base reward for some validator. +pub fn get_base_reward( + validator_effective_balance: u64, + sqrt_total_active_balance: SqrtTotalActiveBalance, + spec: &ChainSpec, +) -> Result { + validator_effective_balance + .safe_mul(spec.base_reward_factor)? + .safe_div(sqrt_total_active_balance.as_u64())? + .safe_div(spec.base_rewards_per_epoch) } diff --git a/consensus/state_processing/src/consensus_context.rs b/consensus/state_processing/src/consensus_context.rs index fdd3f95a65..1e19587350 100644 --- a/consensus/state_processing/src/consensus_context.rs +++ b/consensus/state_processing/src/consensus_context.rs @@ -1,3 +1,5 @@ +use crate::{EpochCache, EpochCacheError}; +use std::borrow::Cow; use std::marker::PhantomData; use tree_hash::TreeHash; use types::{ @@ -5,7 +7,7 @@ use types::{ Slot, }; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ConsensusContext { /// Slot to act as an identifier/safeguard slot: Slot, @@ -13,12 +15,15 @@ pub struct ConsensusContext { proposer_index: Option, /// Block root of the block at `slot`. current_block_root: Option, + /// Epoch cache of values that are useful for block processing that are static over an epoch. + epoch_cache: Option, _phantom: PhantomData, } #[derive(Debug, PartialEq, Clone)] pub enum ContextError { BeaconState(BeaconStateError), + EpochCache(EpochCacheError), SlotMismatch { slot: Slot, expected: Slot }, } @@ -28,12 +33,19 @@ impl From for ContextError { } } +impl From for ContextError { + fn from(e: EpochCacheError) -> Self { + Self::EpochCache(e) + } +} + impl ConsensusContext { pub fn new(slot: Slot) -> Self { Self { slot, proposer_index: None, current_block_root: None, + epoch_cache: None, _phantom: PhantomData, } } @@ -89,4 +101,29 @@ impl ConsensusContext { }) } } + + pub fn set_epoch_cache(mut self, epoch_cache: EpochCache) -> Self { + self.epoch_cache = Some(epoch_cache); + self + } + + pub fn get_base_reward( + &mut self, + state: &BeaconState, + validator_index: usize, + spec: &ChainSpec, + ) -> Result { + self.check_slot(state.slot())?; + + // Build epoch cache if not already built. + let epoch_cache = if let Some(ref cache) = self.epoch_cache { + Cow::Borrowed(cache) + } else { + let cache = EpochCache::new(state, spec)?; + self.epoch_cache = Some(cache.clone()); + Cow::Owned(cache) + }; + + Ok(epoch_cache.get_base_reward(validator_index)?) + } } diff --git a/consensus/state_processing/src/epoch_cache.rs b/consensus/state_processing/src/epoch_cache.rs new file mode 100644 index 0000000000..e391fa0a11 --- /dev/null +++ b/consensus/state_processing/src/epoch_cache.rs @@ -0,0 +1,135 @@ +use crate::common::{ + altair::{self, BaseRewardPerIncrement}, + base::{self, SqrtTotalActiveBalance}, +}; +use safe_arith::ArithError; +use std::sync::Arc; +use types::{BeaconState, BeaconStateError, ChainSpec, Epoch, EthSpec, Hash256, Slot}; + +/// Cache of values which are uniquely determined at the start of an epoch. +/// +/// The values are fixed with respect to the last block of the _prior_ epoch, which we refer +/// to as the "decision block". This cache is very similar to the `BeaconProposerCache` in that +/// beacon proposers are determined at exactly the same time as the values in this cache, so +/// the keys for the two caches are identical. +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct EpochCache { + inner: Arc, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +struct Inner { + /// Unique identifier for this cache, which can be used to check its validity before use + /// with any `BeaconState`. + key: EpochCacheKey, + /// Base reward for every validator in this epoch. + base_rewards: Vec, +} + +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub struct EpochCacheKey { + pub epoch: Epoch, + pub decision_block_root: Hash256, +} + +#[derive(Debug, PartialEq, Clone)] +pub enum EpochCacheError { + IncorrectEpoch { cache: Epoch, state: Epoch }, + IncorrectDecisionBlock { cache: Hash256, state: Hash256 }, + ValidatorIndexOutOfBounds { validator_index: usize }, + InvalidSlot { slot: Slot }, + Arith(ArithError), + BeaconState(BeaconStateError), +} + +impl From for EpochCacheError { + fn from(e: BeaconStateError) -> Self { + Self::BeaconState(e) + } +} + +impl From for EpochCacheError { + fn from(e: ArithError) -> Self { + Self::Arith(e) + } +} + +impl EpochCache { + pub fn new( + state: &BeaconState, + spec: &ChainSpec, + ) -> Result { + let epoch = state.current_epoch(); + let decision_block_root = state + .proposer_shuffling_decision_root(Hash256::zero()) + .map_err(EpochCacheError::BeaconState)?; + + // The cache should never be constructed at slot 0 because it should only be used for + // block processing (which implies slot > 0) or epoch processing (which implies slot >= 32). + if decision_block_root.is_zero() { + return Err(EpochCacheError::InvalidSlot { slot: state.slot() }); + } + + // Compute base rewards. + let total_active_balance = state.get_total_active_balance()?; + let sqrt_total_active_balance = SqrtTotalActiveBalance::new(total_active_balance); + let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?; + + let mut base_rewards = Vec::with_capacity(state.validators().len()); + + for validator in state.validators().iter() { + let effective_balance = validator.effective_balance(); + + let base_reward = if spec + .altair_fork_epoch + .map_or(false, |altair_epoch| epoch < altair_epoch) + { + base::get_base_reward(effective_balance, sqrt_total_active_balance, spec)? + } else { + altair::get_base_reward(effective_balance, base_reward_per_increment, spec)? + }; + base_rewards.push(base_reward); + } + + Ok(Self { + inner: Arc::new(Inner { + key: EpochCacheKey { + epoch, + decision_block_root, + }, + base_rewards, + }), + }) + } + + pub fn check_validity( + &self, + state: &BeaconState, + ) -> Result<(), EpochCacheError> { + if self.inner.key.epoch != state.current_epoch() { + return Err(EpochCacheError::IncorrectEpoch { + cache: self.inner.key.epoch, + state: state.current_epoch(), + }); + } + let state_decision_root = state + .proposer_shuffling_decision_root(Hash256::zero()) + .map_err(EpochCacheError::BeaconState)?; + if self.inner.key.decision_block_root != state_decision_root { + return Err(EpochCacheError::IncorrectDecisionBlock { + cache: self.inner.key.decision_block_root, + state: state_decision_root, + }); + } + Ok(()) + } + + #[inline] + pub fn get_base_reward(&self, validator_index: usize) -> Result { + self.inner + .base_rewards + .get(validator_index) + .copied() + .ok_or(EpochCacheError::ValidatorIndexOutOfBounds { validator_index }) + } +} diff --git a/consensus/state_processing/src/lib.rs b/consensus/state_processing/src/lib.rs index 1c764f4c24..5889c838f8 100644 --- a/consensus/state_processing/src/lib.rs +++ b/consensus/state_processing/src/lib.rs @@ -19,6 +19,7 @@ mod metrics; pub mod block_replayer; pub mod common; pub mod consensus_context; +pub mod epoch_cache; pub mod genesis; pub mod per_block_processing; pub mod per_epoch_processing; @@ -29,6 +30,7 @@ pub mod verify_operation; pub use block_replayer::{BlockReplayError, BlockReplayer}; pub use consensus_context::{ConsensusContext, ContextError}; +pub use epoch_cache::{EpochCache, EpochCacheError, EpochCacheKey}; pub use genesis::{ eth2_genesis_time, initialize_beacon_state_from_eth1, is_valid_genesis_state, process_activations, diff --git a/consensus/state_processing/src/per_block_processing/altair/sync_committee.rs b/consensus/state_processing/src/per_block_processing/altair/sync_committee.rs index 306e86714c..3ce8223390 100644 --- a/consensus/state_processing/src/per_block_processing/altair/sync_committee.rs +++ b/consensus/state_processing/src/per_block_processing/altair/sync_committee.rs @@ -47,17 +47,19 @@ pub fn process_sync_aggregate( // Apply participant and proposer rewards let committee_indices = state.get_sync_committee_indices(¤t_sync_committee)?; + let mut total_proposer_reward = 0; for (participant_index, participation_bit) in committee_indices .into_iter() .zip(aggregate.sync_committee_bits.iter()) { if participation_bit { increase_balance(state, participant_index as usize, participant_reward)?; - increase_balance(state, proposer_index as usize, proposer_reward)?; + total_proposer_reward.safe_add_assign(proposer_reward)?; } else { decrease_balance(state, participant_index as usize, participant_reward)?; } } + increase_balance(state, proposer_index as usize, total_proposer_reward)?; Ok(()) } diff --git a/consensus/state_processing/src/per_block_processing/errors.rs b/consensus/state_processing/src/per_block_processing/errors.rs index a139e6f944..7eb03e7ba5 100644 --- a/consensus/state_processing/src/per_block_processing/errors.rs +++ b/consensus/state_processing/src/per_block_processing/errors.rs @@ -1,5 +1,5 @@ use super::signature_sets::Error as SignatureSetError; -use crate::ContextError; +use crate::{ContextError, EpochCacheError}; use merkle_proof::MerkleTreeError; use safe_arith::ArithError; use types::*; @@ -73,6 +73,7 @@ pub enum BlockProcessingError { ExecutionInvalid, ConsensusContext(ContextError), MilhouseError(milhouse::Error), + EpochCacheError(EpochCacheError), } impl From for BlockProcessingError { @@ -111,6 +112,12 @@ impl From for BlockProcessingError { } } +impl From for BlockProcessingError { + fn from(e: EpochCacheError) -> Self { + BlockProcessingError::EpochCacheError(e) + } +} + impl From for BlockProcessingError { fn from(e: milhouse::Error) -> Self { Self::MilhouseError(e) diff --git a/consensus/state_processing/src/per_block_processing/process_operations.rs b/consensus/state_processing/src/per_block_processing/process_operations.rs index f68a2b64a3..d164721440 100644 --- a/consensus/state_processing/src/per_block_processing/process_operations.rs +++ b/consensus/state_processing/src/per_block_processing/process_operations.rs @@ -1,6 +1,5 @@ use super::*; use crate::common::{ - altair::{get_base_reward, BaseRewardPerIncrement}, get_attestation_participation_flag_indices, increase_balance, initiate_validator_exit, slash_validator, }; @@ -95,19 +94,11 @@ pub mod altair { ctxt: &mut ConsensusContext, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { - let proposer_index = ctxt.get_proposer_index(state, spec)?; attestations .iter() .enumerate() .try_for_each(|(i, attestation)| { - process_attestation( - state, - attestation, - i, - proposer_index, - verify_signatures, - spec, - ) + process_attestation(state, attestation, i, ctxt, verify_signatures, spec) }) } @@ -115,13 +106,15 @@ pub mod altair { state: &mut BeaconState, attestation: &Attestation, att_index: usize, - proposer_index: u64, + ctxt: &mut ConsensusContext, verify_signatures: VerifySignatures, spec: &ChainSpec, ) -> Result<(), BlockProcessingError> { state.build_committee_cache(RelativeEpoch::Previous, spec)?; state.build_committee_cache(RelativeEpoch::Current, spec)?; + let proposer_index = ctxt.get_proposer_index(state, spec)?; + let indexed_attestation = verify_attestation_for_block_inclusion(state, attestation, verify_signatures, spec) .map_err(|e| e.into_with_index(att_index))?; @@ -133,8 +126,6 @@ pub mod altair { get_attestation_participation_flag_indices(state, data, inclusion_delay, spec)?; // Update epoch participation flags. - let total_active_balance = state.get_total_active_balance()?; - let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?; let mut proposer_reward_numerator = 0; for index in &indexed_attestation.attesting_indices { let index = *index as usize; @@ -148,12 +139,9 @@ pub mod altair { if participation_flag_indices.contains(&flag_index) && !validator_participation.has_flag(flag_index)? { - // FIXME(sproul): add effective balance cache here? validator_participation.add_flag(flag_index)?; - let effective_balance = state.get_validator(index)?.effective_balance(); proposer_reward_numerator.safe_add_assign( - get_base_reward(effective_balance, base_reward_per_increment, spec)? - .safe_mul(weight)?, + ctxt.get_base_reward(state, index, spec)?.safe_mul(weight)?, )?; } } diff --git a/consensus/state_processing/src/per_epoch_processing/base/rewards_and_penalties.rs b/consensus/state_processing/src/per_epoch_processing/base/rewards_and_penalties.rs index 3825567125..b0e4af0daf 100644 --- a/consensus/state_processing/src/per_epoch_processing/base/rewards_and_penalties.rs +++ b/consensus/state_processing/src/per_epoch_processing/base/rewards_and_penalties.rs @@ -1,4 +1,7 @@ -use crate::common::{base::get_base_reward, decrease_balance, increase_balance}; +use crate::common::{ + base::{get_base_reward, SqrtTotalActiveBalance}, + decrease_balance, increase_balance, +}; use crate::per_epoch_processing::{ base::{TotalBalances, ValidatorStatus, ValidatorStatuses}, Delta, Error, @@ -78,7 +81,6 @@ pub fn get_attestation_deltas( validator_statuses: &ValidatorStatuses, spec: &ChainSpec, ) -> Result, Error> { - let previous_epoch = state.previous_epoch(); let finality_delay = state .previous_epoch() .safe_sub(state.finalized_checkpoint().epoch)? @@ -87,19 +89,22 @@ pub fn get_attestation_deltas( let mut deltas = vec![AttestationDelta::default(); state.validators().len()]; let total_balances = &validator_statuses.total_balances; + let sqrt_total_active_balance = SqrtTotalActiveBalance::new(total_balances.current_epoch()); for (index, validator) in validator_statuses.statuses.iter().enumerate() { // Ignore ineligible validators. All sub-functions of the spec do this except for // `get_inclusion_delay_deltas`. It's safe to do so here because any validator that is in // the unslashed indices of the matching source attestations is active, and therefore // eligible. - // FIXME(sproul): this is inefficient - let full_validator = state.get_validator(index)?; - if !state.is_eligible_validator(previous_epoch, full_validator) { + if !validator.is_eligible { continue; } - let base_reward = get_base_reward(state, index, total_balances.current_epoch(), spec)?; + let base_reward = get_base_reward( + validator.current_epoch_effective_balance, + sqrt_total_active_balance, + spec, + )?; let source_delta = get_source_delta(validator, base_reward, total_balances, finality_delay, spec)?; diff --git a/consensus/state_processing/src/per_epoch_processing/base/validator_statuses.rs b/consensus/state_processing/src/per_epoch_processing/base/validator_statuses.rs index 8a29bbb072..38ece87cf6 100644 --- a/consensus/state_processing/src/per_epoch_processing/base/validator_statuses.rs +++ b/consensus/state_processing/src/per_epoch_processing/base/validator_statuses.rs @@ -53,6 +53,8 @@ impl InclusionInfo { pub struct ValidatorStatus { /// True if the validator has been slashed, ever. pub is_slashed: bool, + /// True if the validator is eligible. + pub is_eligible: bool, /// True if the validator can withdraw in the current epoch. pub is_withdrawable_in_current_epoch: bool, /// True if the validator was active in the state's _current_ epoch. @@ -92,6 +94,7 @@ impl ValidatorStatus { // Update all the bool fields, only updating `self` if `other` is true (never setting // `self` to false). set_self_if_other_is_true!(self, other, is_slashed); + set_self_if_other_is_true!(self, other, is_eligible); set_self_if_other_is_true!(self, other, is_withdrawable_in_current_epoch); set_self_if_other_is_true!(self, other, is_active_in_current_epoch); set_self_if_other_is_true!(self, other, is_active_in_previous_epoch); @@ -195,24 +198,27 @@ impl ValidatorStatuses { let mut statuses = Vec::with_capacity(state.validators().len()); let mut total_balances = TotalBalances::new(spec); - for (i, validator) in state.validators().iter().enumerate() { - let effective_balance = state.get_effective_balance(i)?; + let current_epoch = state.current_epoch(); + let previous_epoch = state.previous_epoch(); + + for validator in state.validators().iter() { + let effective_balance = validator.effective_balance(); let mut status = ValidatorStatus { is_slashed: validator.slashed(), - is_withdrawable_in_current_epoch: validator - .is_withdrawable_at(state.current_epoch()), + is_eligible: state.is_eligible_validator(previous_epoch, validator), + is_withdrawable_in_current_epoch: validator.is_withdrawable_at(current_epoch), current_epoch_effective_balance: effective_balance, ..ValidatorStatus::default() }; - if validator.is_active_at(state.current_epoch()) { + if validator.is_active_at(current_epoch) { status.is_active_in_current_epoch = true; total_balances .current_epoch .safe_add_assign(effective_balance)?; } - if validator.is_active_at(state.previous_epoch()) { + if validator.is_active_at(previous_epoch) { status.is_active_in_previous_epoch = true; total_balances .previous_epoch @@ -285,10 +291,10 @@ impl ValidatorStatuses { } // Compute the total balances - for (index, v) in self.statuses.iter().enumerate() { + for v in self.statuses.iter() { // According to the spec, we only count unslashed validators towards the totals. if !v.is_slashed { - let validator_balance = state.get_effective_balance(index)?; + let validator_balance = v.current_epoch_effective_balance; if v.is_current_epoch_attester { self.total_balances diff --git a/consensus/types/src/execution_payload.rs b/consensus/types/src/execution_payload.rs index 315fe071eb..08ad4531f9 100644 --- a/consensus/types/src/execution_payload.rs +++ b/consensus/types/src/execution_payload.rs @@ -6,7 +6,8 @@ use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash_derive::TreeHash; -use ssz_types::{FixedVector, VariableList}; +// FIXME(sproul): try milhouse FixedVector +use ssz_types::FixedVector; pub type Transaction = VariableList; pub type Transactions = VariableList< diff --git a/consensus/types/src/validator.rs b/consensus/types/src/validator.rs index 377561e454..31c06f7aab 100644 --- a/consensus/types/src/validator.rs +++ b/consensus/types/src/validator.rs @@ -73,50 +73,61 @@ impl Validator { }); } + #[inline] pub fn withdrawal_credentials(&self) -> Hash256 { self.immutable.withdrawal_credentials } + #[inline] pub fn effective_balance(&self) -> u64 { self.mutable.effective_balance } + #[inline] pub fn slashed(&self) -> bool { self.mutable.slashed } + #[inline] pub fn activation_eligibility_epoch(&self) -> Epoch { self.mutable.activation_eligibility_epoch } + #[inline] pub fn activation_epoch(&self) -> Epoch { self.mutable.activation_epoch } + #[inline] pub fn exit_epoch(&self) -> Epoch { self.mutable.exit_epoch } + #[inline] pub fn withdrawable_epoch(&self) -> Epoch { self.mutable.withdrawable_epoch } /// Returns `true` if the validator is considered active at some epoch. + #[inline] pub fn is_active_at(&self, epoch: Epoch) -> bool { self.activation_epoch() <= epoch && epoch < self.exit_epoch() } /// Returns `true` if the validator is slashable at some epoch. + #[inline] pub fn is_slashable_at(&self, epoch: Epoch) -> bool { !self.slashed() && self.activation_epoch() <= epoch && epoch < self.withdrawable_epoch() } /// Returns `true` if the validator is considered exited at some epoch. + #[inline] pub fn is_exited_at(&self, epoch: Epoch) -> bool { self.exit_epoch() <= epoch } /// Returns `true` if the validator is able to withdraw at some epoch. + #[inline] pub fn is_withdrawable_at(&self, epoch: Epoch) -> bool { epoch >= self.withdrawable_epoch() } @@ -124,6 +135,7 @@ impl Validator { /// Returns `true` if the validator is eligible to join the activation queue. /// /// Spec v0.12.1 + #[inline] pub fn is_eligible_for_activation_queue(&self, spec: &ChainSpec) -> bool { self.activation_eligibility_epoch() == spec.far_future_epoch && self.effective_balance() == spec.max_effective_balance @@ -132,6 +144,7 @@ impl Validator { /// Returns `true` if the validator is eligible to be activated. /// /// Spec v0.12.1 + #[inline] pub fn is_eligible_for_activation( &self, state: &BeaconState, diff --git a/lcli/src/transition_blocks.rs b/lcli/src/transition_blocks.rs index 2ff9039035..dbdb33b351 100644 --- a/lcli/src/transition_blocks.rs +++ b/lcli/src/transition_blocks.rs @@ -74,7 +74,7 @@ use eth2::{ use ssz::Encode; use state_processing::{ block_signature_verifier::BlockSignatureVerifier, per_block_processing, per_slot_processing, - BlockSignatureStrategy, ConsensusContext, VerifyBlockRoot, + BlockSignatureStrategy, ConsensusContext, EpochCache, VerifyBlockRoot, }; use std::borrow::Cow; use std::fs::File; @@ -195,7 +195,10 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< let store = Arc::new(store); debug!("Building pubkey cache (might take some time)"); - let validator_pubkey_cache = ValidatorPubkeyCache::new(&pre_state, store) + let validator_pubkey_cache = store.immutable_validators.clone(); + validator_pubkey_cache + .write() + .import_new_pubkeys(&pre_state, &store) .map_err(|e| format!("Failed to create pubkey cache: {:?}", e))?; /* @@ -226,6 +229,7 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< */ let mut output_post_state = None; + let mut saved_ctxt = None; for i in 0..runs { let pre_state = pre_state.clone(); let block = block.clone(); @@ -238,7 +242,8 @@ pub fn run(mut env: Environment, matches: &ArgMatches) -> Result< block, state_root_opt, &config, - &validator_pubkey_cache, + &*validator_pubkey_cache.read(), + &mut saved_ctxt, spec, )?; @@ -300,6 +305,7 @@ fn do_transition( mut state_root_opt: Option, config: &Config, validator_pubkey_cache: &ValidatorPubkeyCache>, + saved_ctxt: &mut Option>, spec: &ChainSpec, ) -> Result, String> { if !config.exclude_cache_builds { @@ -369,10 +375,25 @@ fn do_transition( debug!("Batch verify block signatures: {:?}", t.elapsed()); } + let mut ctxt = if let Some(ctxt) = saved_ctxt { + ctxt.clone() + } else { + let mut ctxt = ConsensusContext::new(pre_state.slot()) + .set_current_block_root(block_root) + .set_proposer_index(block.message().proposer_index()); + + if config.exclude_cache_builds { + ctxt = ctxt.set_epoch_cache( + EpochCache::new(&pre_state, spec) + .map_err(|e| format!("unable to build epoch cache: {e:?}"))?, + ); + *saved_ctxt = Some(ctxt.clone()); + } + ctxt + }; + let t = Instant::now(); - let mut ctxt = ConsensusContext::new(pre_state.slot()) - .set_current_block_root(block_root) - .set_proposer_index(block.message().proposer_index()); + per_block_processing( &mut pre_state, &block,