Consensus context with proposer index caching (#3604)

## Issue Addressed

Closes https://github.com/sigp/lighthouse/issues/2371

## Proposed Changes

Backport some changes from `tree-states` that remove duplicated calculations of the `proposer_index`.

With this change the proposer index should be calculated only once for each block, and then plumbed through to every place it is required.

## Additional Info

In future I hope to add more data to the consensus context that is cached on a per-epoch basis, like the effective balances of validators and the base rewards.

There are some other changes to remove indexing in tests that were also useful for `tree-states` (the `tree-states` types don't implement `Index`).
This commit is contained in:
Michael Sproul
2022-10-15 22:25:54 +00:00
parent e4cbdc1c77
commit 59ec6b71b8
21 changed files with 388 additions and 100 deletions

View File

@@ -5,14 +5,17 @@ use crate::decode::{ssz_decode_file, ssz_decode_file_with, ssz_decode_state, yam
use crate::testing_spec;
use crate::type_name::TypeName;
use serde_derive::Deserialize;
use state_processing::per_block_processing::{
errors::BlockProcessingError,
process_block_header, process_execution_payload,
process_operations::{
altair, base, process_attester_slashings, process_deposits, process_exits,
process_proposer_slashings,
use state_processing::{
per_block_processing::{
errors::BlockProcessingError,
process_block_header, process_execution_payload,
process_operations::{
altair, base, process_attester_slashings, process_deposits, process_exits,
process_proposer_slashings,
},
process_sync_aggregate, VerifyBlockRoot, VerifySignatures,
},
process_sync_aggregate, VerifyBlockRoot, VerifySignatures,
ConsensusContext,
};
use std::fmt::Debug;
use std::path::Path;
@@ -76,11 +79,16 @@ impl<E: EthSpec> Operation<E> for Attestation<E> {
spec: &ChainSpec,
_: &Operations<E, Self>,
) -> Result<(), BlockProcessingError> {
let proposer_index = state.get_beacon_proposer_index(state.slot(), spec)? as u64;
let mut ctxt = ConsensusContext::new(state.slot());
let proposer_index = ctxt.get_proposer_index(state, spec)?;
match state {
BeaconState::Base(_) => {
base::process_attestations(state, &[self.clone()], VerifySignatures::True, spec)
}
BeaconState::Base(_) => base::process_attestations(
state,
&[self.clone()],
VerifySignatures::True,
&mut ctxt,
spec,
),
BeaconState::Altair(_) | BeaconState::Merge(_) => altair::process_attestation(
state,
self,
@@ -108,7 +116,14 @@ impl<E: EthSpec> Operation<E> for AttesterSlashing<E> {
spec: &ChainSpec,
_: &Operations<E, Self>,
) -> Result<(), BlockProcessingError> {
process_attester_slashings(state, &[self.clone()], VerifySignatures::True, spec)
let mut ctxt = ConsensusContext::new(state.slot());
process_attester_slashings(
state,
&[self.clone()],
VerifySignatures::True,
&mut ctxt,
spec,
)
}
}
@@ -147,7 +162,14 @@ impl<E: EthSpec> Operation<E> for ProposerSlashing {
spec: &ChainSpec,
_: &Operations<E, Self>,
) -> Result<(), BlockProcessingError> {
process_proposer_slashings(state, &[self.clone()], VerifySignatures::True, spec)
let mut ctxt = ConsensusContext::new(state.slot());
process_proposer_slashings(
state,
&[self.clone()],
VerifySignatures::True,
&mut ctxt,
spec,
)
}
}
@@ -189,10 +211,12 @@ impl<E: EthSpec> Operation<E> for BeaconBlock<E> {
spec: &ChainSpec,
_: &Operations<E, Self>,
) -> Result<(), BlockProcessingError> {
let mut ctxt = ConsensusContext::new(state.slot());
process_block_header(
state,
self.to_ref().temporary_block_header(),
VerifyBlockRoot::True,
&mut ctxt,
spec,
)?;
Ok(())

View File

@@ -5,7 +5,7 @@ use crate::decode::{ssz_decode_file_with, ssz_decode_state, yaml_decode_file};
use serde_derive::Deserialize;
use state_processing::{
per_block_processing, per_slot_processing, BlockProcessingError, BlockSignatureStrategy,
VerifyBlockRoot,
ConsensusContext, VerifyBlockRoot,
};
use types::{BeaconState, EthSpec, ForkName, RelativeEpoch, SignedBeaconBlock};
@@ -91,26 +91,28 @@ impl<E: EthSpec> Case for SanityBlocks<E> {
.build_committee_cache(RelativeEpoch::Current, spec)
.unwrap();
let mut ctxt = ConsensusContext::new(indiv_state.slot());
per_block_processing(
&mut indiv_state,
signed_block,
None,
BlockSignatureStrategy::VerifyIndividual,
VerifyBlockRoot::True,
&mut ctxt,
spec,
)?;
let mut ctxt = ConsensusContext::new(indiv_state.slot());
per_block_processing(
&mut bulk_state,
signed_block,
None,
BlockSignatureStrategy::VerifyBulk,
VerifyBlockRoot::True,
&mut ctxt,
spec,
)?;
if block.state_root() == bulk_state.canonical_root()
&& block.state_root() == indiv_state.canonical_root()
if block.state_root() == bulk_state.update_tree_hash_cache().unwrap()
&& block.state_root() == indiv_state.update_tree_hash_cache().unwrap()
{
Ok(())
} else {

View File

@@ -4,7 +4,7 @@ use crate::decode::{ssz_decode_file_with, ssz_decode_state, yaml_decode_file};
use serde_derive::Deserialize;
use state_processing::{
per_block_processing, state_advance::complete_state_advance, BlockSignatureStrategy,
VerifyBlockRoot,
ConsensusContext, VerifyBlockRoot,
};
use std::str::FromStr;
use types::{BeaconState, Epoch, ForkName, SignedBeaconBlock};
@@ -91,12 +91,13 @@ impl<E: EthSpec> Case for TransitionTest<E> {
.map_err(|e| format!("Failed to advance: {:?}", e))?;
// Apply block.
let mut ctxt = ConsensusContext::new(state.slot());
per_block_processing(
&mut state,
block,
None,
BlockSignatureStrategy::VerifyBulk,
VerifyBlockRoot::True,
&mut ctxt,
spec,
)
.map_err(|e| format!("Block processing failed: {:?}", e))?;

View File

@@ -2,7 +2,7 @@ use super::*;
use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType};
use state_processing::{
per_block_processing, per_block_processing::errors::ExitInvalid, BlockProcessingError,
BlockSignatureStrategy, VerifyBlockRoot,
BlockSignatureStrategy, ConsensusContext, VerifyBlockRoot,
};
use types::{BeaconBlock, BeaconState, Epoch, EthSpec, SignedBeaconBlock};
@@ -64,12 +64,13 @@ impl ExitTest {
block: &SignedBeaconBlock<E>,
state: &mut BeaconState<E>,
) -> Result<(), BlockProcessingError> {
let mut ctxt = ConsensusContext::new(block.slot());
per_block_processing(
state,
block,
None,
BlockSignatureStrategy::VerifyIndividual,
VerifyBlockRoot::True,
&mut ctxt,
&E::default_spec(),
)
}
@@ -125,7 +126,7 @@ vectors_and_tests!(
ExitTest {
block_modifier: Box::new(|_, block| {
// Duplicate the exit
let exit = block.body().voluntary_exits()[0].clone();
let exit = block.body().voluntary_exits().get(0).unwrap().clone();
block.body_mut().voluntary_exits_mut().push(exit).unwrap();
}),
expected: Err(BlockProcessingError::ExitInvalid {
@@ -144,7 +145,11 @@ vectors_and_tests!(
invalid_validator_unknown,
ExitTest {
block_modifier: Box::new(|_, block| {
block.body_mut().voluntary_exits_mut()[0]
block
.body_mut()
.voluntary_exits_mut()
.get_mut(0)
.unwrap()
.message
.validator_index = VALIDATOR_COUNT as u64;
}),
@@ -165,7 +170,7 @@ vectors_and_tests!(
invalid_exit_already_initiated,
ExitTest {
state_modifier: Box::new(|state| {
state.validators_mut()[0].exit_epoch = STATE_EPOCH + 1;
state.validators_mut().get_mut(0).unwrap().exit_epoch = STATE_EPOCH + 1;
}),
expected: Err(BlockProcessingError::ExitInvalid {
index: 0,
@@ -184,7 +189,8 @@ vectors_and_tests!(
invalid_not_active_before_activation_epoch,
ExitTest {
state_modifier: Box::new(|state| {
state.validators_mut()[0].activation_epoch = E::default_spec().far_future_epoch;
state.validators_mut().get_mut(0).unwrap().activation_epoch =
E::default_spec().far_future_epoch;
}),
expected: Err(BlockProcessingError::ExitInvalid {
index: 0,
@@ -203,7 +209,7 @@ vectors_and_tests!(
invalid_not_active_after_exit_epoch,
ExitTest {
state_modifier: Box::new(|state| {
state.validators_mut()[0].exit_epoch = STATE_EPOCH;
state.validators_mut().get_mut(0).unwrap().exit_epoch = STATE_EPOCH;
}),
expected: Err(BlockProcessingError::ExitInvalid {
index: 0,
@@ -303,7 +309,11 @@ vectors_and_tests!(
block_modifier: Box::new(|_, block| {
// Shift the validator index by 1 so that it's mismatched from the key that was
// used to sign.
block.body_mut().voluntary_exits_mut()[0]
block
.body_mut()
.voluntary_exits_mut()
.get_mut(0)
.unwrap()
.message
.validator_index = VALIDATOR_INDEX + 1;
}),