Merge remote-tracking branch 'origin/unstable' into tree-states

This commit is contained in:
Michael Sproul
2022-01-25 15:54:02 +11:00
350 changed files with 16324 additions and 4500 deletions

View File

@@ -12,11 +12,11 @@ beacon_chain = { path = "../../beacon_node/beacon_chain" }
bls = { path = "../../crypto/bls" }
integer-sqrt = "0.1.5"
itertools = "0.10.0"
eth2_ssz = "0.4.0"
eth2_ssz_types = "0.2.1"
eth2_ssz = "0.4.1"
eth2_ssz_types = "0.2.2"
merkle_proof = { path = "../merkle_proof" }
safe_arith = { path = "../safe_arith" }
tree_hash = "0.4.0"
tree_hash = "0.4.1"
types = { path = "../types", default-features = false }
rayon = "1.4.1"
eth2_hashing = "0.2.0"

View File

@@ -0,0 +1,313 @@
use crate::{
per_block_processing, per_epoch_processing::EpochProcessingSummary, per_slot_processing,
BlockProcessingError, BlockSignatureStrategy, SlotProcessingError, VerifyBlockRoot,
};
use std::marker::PhantomData;
use types::{BeaconState, ChainSpec, EthSpec, Hash256, SignedBeaconBlock, Slot};
type PreBlockHook<'a, E, Error> =
Box<dyn FnMut(&mut BeaconState<E>, &SignedBeaconBlock<E>) -> Result<(), Error> + 'a>;
type PostBlockHook<'a, E, Error> = PreBlockHook<'a, E, Error>;
type PreSlotHook<'a, E, Error> = Box<dyn FnMut(&mut BeaconState<E>) -> Result<(), Error> + 'a>;
type PostSlotHook<'a, E, Error> = Box<
dyn FnMut(&mut BeaconState<E>, Option<EpochProcessingSummary<E>>, bool) -> Result<(), Error>
+ 'a,
>;
type StateRootIterDefault<Error> = std::iter::Empty<Result<(Hash256, Slot), Error>>;
/// Efficiently apply blocks to a state while configuring various parameters.
///
/// Usage follows a builder pattern.
pub struct BlockReplayer<
'a,
Spec: EthSpec,
Error = BlockReplayError,
StateRootIter = StateRootIterDefault<Error>,
> {
state: BeaconState<Spec>,
spec: &'a ChainSpec,
state_root_strategy: StateRootStrategy,
block_sig_strategy: BlockSignatureStrategy,
verify_block_root: Option<VerifyBlockRoot>,
pre_block_hook: Option<PreBlockHook<'a, Spec, Error>>,
post_block_hook: Option<PostBlockHook<'a, Spec, Error>>,
pre_slot_hook: Option<PreSlotHook<'a, Spec, Error>>,
post_slot_hook: Option<PostSlotHook<'a, Spec, Error>>,
state_root_iter: Option<StateRootIter>,
state_root_miss: bool,
_phantom: PhantomData<Error>,
}
#[derive(Debug)]
pub enum BlockReplayError {
NoBlocks,
SlotProcessing(SlotProcessingError),
BlockProcessing(BlockProcessingError),
}
impl From<SlotProcessingError> for BlockReplayError {
fn from(e: SlotProcessingError) -> Self {
Self::SlotProcessing(e)
}
}
impl From<BlockProcessingError> for BlockReplayError {
fn from(e: BlockProcessingError) -> Self {
Self::BlockProcessing(e)
}
}
/// Defines how state roots should be computed during block replay.
#[derive(PartialEq)]
pub enum StateRootStrategy {
/// Perform all transitions faithfully to the specification.
Accurate,
/// Don't compute state roots, eventually computing an invalid beacon state that can only be
/// used for obtaining shuffling.
Inconsistent,
}
impl<'a, E, Error, StateRootIter> BlockReplayer<'a, E, Error, StateRootIter>
where
E: EthSpec,
StateRootIter: Iterator<Item = Result<(Hash256, Slot), Error>>,
Error: From<BlockReplayError>,
{
/// Create a new replayer that will apply blocks upon `state`.
///
/// Defaults:
///
/// - Full (bulk) signature verification
/// - Accurate state roots
/// - Full block root verification
pub fn new(state: BeaconState<E>, spec: &'a ChainSpec) -> Self {
Self {
state,
spec,
state_root_strategy: StateRootStrategy::Accurate,
block_sig_strategy: BlockSignatureStrategy::VerifyBulk,
verify_block_root: Some(VerifyBlockRoot::True),
pre_block_hook: None,
post_block_hook: None,
pre_slot_hook: None,
post_slot_hook: None,
state_root_iter: None,
state_root_miss: false,
_phantom: PhantomData,
}
}
/// Set the replayer's state root strategy different from the default.
pub fn state_root_strategy(mut self, state_root_strategy: StateRootStrategy) -> Self {
if state_root_strategy == StateRootStrategy::Inconsistent {
self.verify_block_root = None;
}
self.state_root_strategy = state_root_strategy;
self
}
/// Set the replayer's block signature verification strategy.
pub fn block_signature_strategy(mut self, block_sig_strategy: BlockSignatureStrategy) -> Self {
self.block_sig_strategy = block_sig_strategy;
self
}
/// Disable signature verification during replay.
///
/// If you are truly _replaying_ blocks then you will almost certainly want to disable
/// signature checks for performance.
pub fn no_signature_verification(self) -> Self {
self.block_signature_strategy(BlockSignatureStrategy::NoVerification)
}
/// Verify only the block roots of the initial few blocks, and trust the rest.
pub fn minimal_block_root_verification(mut self) -> Self {
self.verify_block_root = None;
self
}
/// Supply a state root iterator to accelerate slot processing.
///
/// If possible the state root iterator should return a state root for every slot from
/// `self.state.slot` to the `target_slot` supplied to `apply_blocks` (inclusive of both
/// endpoints).
pub fn state_root_iter(mut self, iter: StateRootIter) -> Self {
self.state_root_iter = Some(iter);
self
}
/// Run a function immediately before each block that is applied during `apply_blocks`.
///
/// This can be used to inspect the state as blocks are applied.
pub fn pre_block_hook(mut self, hook: PreBlockHook<'a, E, Error>) -> Self {
self.pre_block_hook = Some(hook);
self
}
/// Run a function immediately after each block that is applied during `apply_blocks`.
///
/// This can be used to inspect the state as blocks are applied.
pub fn post_block_hook(mut self, hook: PostBlockHook<'a, E, Error>) -> Self {
self.post_block_hook = Some(hook);
self
}
/// Run a function immediately before slot processing advances the state to the next slot.
pub fn pre_slot_hook(mut self, hook: PreSlotHook<'a, E, Error>) -> Self {
self.pre_slot_hook = Some(hook);
self
}
/// Run a function immediately after slot processing has advanced the state to the next slot.
///
/// The hook receives the state and a bool indicating if this state corresponds to a skipped
/// slot (i.e. it will not have a block applied).
pub fn post_slot_hook(mut self, hook: PostSlotHook<'a, E, Error>) -> Self {
self.post_slot_hook = Some(hook);
self
}
/// Compute the state root for `slot` as efficiently as possible.
///
/// The `blocks` should be the full list of blocks being applied and `i` should be the index of
/// the next block that will be applied, or `blocks.len()` if all blocks have already been
/// applied.
fn get_state_root(
&mut self,
slot: Slot,
blocks: &[SignedBeaconBlock<E>],
i: usize,
) -> Result<Option<Hash256>, Error> {
// If we don't care about state roots then return immediately.
if self.state_root_strategy == StateRootStrategy::Inconsistent {
return Ok(Some(Hash256::zero()));
}
// If a state root iterator is configured, use it to find the root.
if let Some(ref mut state_root_iter) = self.state_root_iter {
let opt_root = state_root_iter
.take_while(|res| res.as_ref().map_or(true, |(_, s)| *s <= slot))
.find(|res| res.as_ref().map_or(true, |(_, s)| *s == slot))
.transpose()?;
if let Some((root, _)) = opt_root {
return Ok(Some(root));
}
}
// Otherwise try to source a root from the previous block.
if let Some(prev_i) = i.checked_sub(1) {
if let Some(prev_block) = blocks.get(prev_i) {
if prev_block.slot() == slot {
return Ok(Some(prev_block.state_root()));
}
}
}
self.state_root_miss = true;
Ok(None)
}
/// Apply `blocks` atop `self.state`, taking care of slot processing.
///
/// If `target_slot` is provided then the state will be advanced through to `target_slot`
/// after the blocks have been applied.
pub fn apply_blocks(
mut self,
blocks: Vec<SignedBeaconBlock<E>>,
target_slot: Option<Slot>,
) -> Result<Self, Error> {
for (i, block) in blocks.iter().enumerate() {
// Allow one additional block at the start which is only used for its state root.
if i == 0 && block.slot() <= self.state.slot() {
continue;
}
while self.state.slot() < block.slot() {
if let Some(ref mut pre_slot_hook) = self.pre_slot_hook {
pre_slot_hook(&mut self.state)?;
}
let state_root = self.get_state_root(self.state.slot(), &blocks, i)?;
let summary = per_slot_processing(&mut self.state, state_root, self.spec)
.map_err(BlockReplayError::from)?;
if let Some(ref mut post_slot_hook) = self.post_slot_hook {
let is_skipped_slot = self.state.slot() < block.slot();
post_slot_hook(&mut self.state, summary, is_skipped_slot)?;
}
}
if let Some(ref mut pre_block_hook) = self.pre_block_hook {
pre_block_hook(&mut self.state, block)?;
}
let verify_block_root = self.verify_block_root.unwrap_or_else(|| {
// If no explicit policy is set, verify only the first 1 or 2 block roots if using
// accurate state roots. Inaccurate state roots require block root verification to
// be off.
if i <= 1 && self.state_root_strategy == StateRootStrategy::Accurate {
VerifyBlockRoot::True
} else {
VerifyBlockRoot::False
}
});
per_block_processing(
&mut self.state,
block,
None,
self.block_sig_strategy,
verify_block_root,
self.spec,
)
.map_err(BlockReplayError::from)?;
if let Some(ref mut post_block_hook) = self.post_block_hook {
post_block_hook(&mut self.state, block)?;
}
}
if let Some(target_slot) = target_slot {
while self.state.slot() < target_slot {
if let Some(ref mut pre_slot_hook) = self.pre_slot_hook {
pre_slot_hook(&mut self.state)?;
}
let state_root = self.get_state_root(self.state.slot(), &blocks, blocks.len())?;
let summary = per_slot_processing(&mut self.state, state_root, self.spec)
.map_err(BlockReplayError::from)?;
if let Some(ref mut post_slot_hook) = self.post_slot_hook {
// No more blocks to apply (from our perspective) so we consider these slots
// skipped.
let is_skipped_slot = true;
post_slot_hook(&mut self.state, summary, is_skipped_slot)?;
}
}
}
Ok(self)
}
/// After block application, check if a state root miss occurred.
pub fn state_root_miss(&self) -> bool {
self.state_root_miss
}
/// Convert the replayer into the state that was built.
pub fn into_state(self) -> BeaconState<E> {
self.state
}
}
impl<'a, E, Error> BlockReplayer<'a, E, Error, StateRootIterDefault<Error>>
where
E: EthSpec,
Error: From<BlockReplayError>,
{
/// If type inference fails to infer the state root iterator type you can use this method
/// to hint that no state root iterator is desired.
pub fn no_state_root_iter(self) -> Self {
self
}
}

View File

@@ -34,14 +34,11 @@ pub fn slash_validator<T: EthSpec>(
.safe_add(validator_effective_balance)?,
)?;
let min_slashing_penalty_quotient = match state {
BeaconState::Base(_) => spec.min_slashing_penalty_quotient,
BeaconState::Altair(_) => spec.min_slashing_penalty_quotient_altair,
};
decrease_balance(
state,
slashed_index,
validator_effective_balance.safe_div(min_slashing_penalty_quotient)?,
validator_effective_balance
.safe_div(spec.min_slashing_penalty_quotient_for_state(state))?,
)?;
// Apply proposer and whistleblower rewards
@@ -51,7 +48,7 @@ pub fn slash_validator<T: EthSpec>(
validator_effective_balance.safe_div(spec.whistleblower_reward_quotient)?;
let proposer_reward = match state {
BeaconState::Base(_) => whistleblower_reward.safe_div(spec.proposer_reward_quotient)?,
BeaconState::Altair(_) => whistleblower_reward
BeaconState::Altair(_) | BeaconState::Merge(_) => whistleblower_reward
.safe_mul(PROPOSER_WEIGHT)?
.safe_div(WEIGHT_DENOMINATOR)?,
};

View File

@@ -2,7 +2,7 @@ use super::per_block_processing::{
errors::BlockProcessingError, process_operations::process_deposit,
};
use crate::common::DepositDataTree;
use crate::upgrade::upgrade_to_altair;
use crate::upgrade::{upgrade_to_altair, upgrade_to_bellatrix};
use safe_arith::{ArithError, SafeArith};
use tree_hash::TreeHash;
use types::DEPOSIT_TREE_DEPTH;
@@ -13,6 +13,7 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
eth1_block_hash: Hash256,
eth1_timestamp: u64,
deposits: Vec<Deposit>,
execution_payload_header: Option<ExecutionPayloadHeader<T>>,
spec: &ChainSpec,
) -> Result<BeaconState<T>, BlockProcessingError> {
let genesis_time = eth2_genesis_time(eth1_timestamp, spec)?;
@@ -46,12 +47,31 @@ pub fn initialize_beacon_state_from_eth1<T: EthSpec>(
// use of `BeaconBlock::empty` in `BeaconState::new` is sufficient to correctly initialise
// the `latest_block_header` as per:
// https://github.com/ethereum/eth2.0-specs/pull/2323
if spec.fork_name_at_epoch(state.current_epoch()) == ForkName::Altair {
if spec
.altair_fork_epoch
.map_or(false, |fork_epoch| fork_epoch == T::genesis_epoch())
{
upgrade_to_altair(&mut state, spec)?;
state.fork_mut().previous_version = spec.altair_fork_version;
}
// Similarly, perform an upgrade to the merge if configured from genesis.
if spec
.bellatrix_fork_epoch
.map_or(false, |fork_epoch| fork_epoch == T::genesis_epoch())
{
upgrade_to_bellatrix(&mut state, spec)?;
// Remove intermediate Altair fork from `state.fork`.
state.fork_mut().previous_version = spec.bellatrix_fork_version;
// Override latest execution payload header.
// See https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/merge/beacon-chain.md#testing
*state.latest_execution_payload_header_mut()? =
execution_payload_header.unwrap_or_default();
}
// Now that we have our validators, initialize the caches (including the committees)
state.build_all_caches(spec)?;

View File

@@ -16,6 +16,7 @@
mod macros;
mod metrics;
pub mod block_replayer;
pub mod common;
pub mod genesis;
pub mod per_block_processing;
@@ -25,13 +26,14 @@ pub mod state_advance;
pub mod upgrade;
pub mod verify_operation;
pub use block_replayer::{BlockReplayError, BlockReplayer, StateRootStrategy};
pub use genesis::{
eth2_genesis_time, initialize_beacon_state_from_eth1, is_valid_genesis_state,
process_activations,
};
pub use per_block_processing::{
block_signature_verifier, errors::BlockProcessingError, per_block_processing, signature_sets,
BlockSignatureStrategy, BlockSignatureVerifier, VerifySignatures,
BlockSignatureStrategy, BlockSignatureVerifier, VerifyBlockRoot, VerifySignatures,
};
pub use per_epoch_processing::{
errors::EpochProcessingError, process_epoch as per_epoch_processing,

View File

@@ -68,6 +68,14 @@ impl VerifySignatures {
}
}
/// Control verification of the latest block header.
#[cfg_attr(feature = "arbitrary-fuzz", derive(Arbitrary))]
#[derive(PartialEq, Clone, Copy)]
pub enum VerifyBlockRoot {
True,
False,
}
/// Updates the state for a new block, whilst validating that the block is valid, optionally
/// checking the block proposer signature.
///
@@ -84,6 +92,7 @@ pub fn per_block_processing<T: EthSpec>(
signed_block: &SignedBeaconBlock<T>,
block_root: Option<Hash256>,
block_signature_strategy: BlockSignatureStrategy,
verify_block_root: VerifyBlockRoot,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
let block = signed_block.message();
@@ -120,7 +129,7 @@ pub fn per_block_processing<T: EthSpec>(
BlockSignatureStrategy::VerifyRandao => VerifySignatures::False,
};
let proposer_index = process_block_header(state, block, spec)?;
let proposer_index = process_block_header(state, block, verify_block_root, spec)?;
if verify_signatures.is_true() {
verify_block_signature(state, signed_block, block_root, spec)?;
@@ -135,14 +144,22 @@ pub fn per_block_processing<T: EthSpec>(
state.build_committee_cache(RelativeEpoch::Previous, spec)?;
state.build_committee_cache(RelativeEpoch::Current, spec)?;
// The call to the `process_execution_payload` must happen before the call to the
// `process_randao` as the former depends on the `randao_mix` computed with the reveal of the
// previous block.
if is_execution_enabled(state, block.body()) {
let payload = block.body().execution_payload()?;
process_execution_payload(state, payload, spec)?;
}
process_randao(state, block, verify_randao, spec)?;
process_eth1_data(state, block.body().eth1_data())?;
process_operations(state, block.body(), proposer_index, verify_signatures, spec)?;
if let BeaconBlockRef::Altair(inner) = block {
if let Ok(sync_aggregate) = block.body().sync_aggregate() {
process_sync_aggregate(
state,
&inner.body.sync_aggregate,
sync_aggregate,
proposer_index,
verify_signatures,
spec,
@@ -156,6 +173,7 @@ pub fn per_block_processing<T: EthSpec>(
pub fn process_block_header<T: EthSpec>(
state: &mut BeaconState<T>,
block: BeaconBlockRef<'_, T>,
verify_block_root: VerifyBlockRoot,
spec: &ChainSpec,
) -> Result<u64, BlockOperationError<HeaderInvalid>> {
// Verify that the slots match
@@ -184,14 +202,16 @@ pub fn process_block_header<T: EthSpec>(
}
);
let expected_previous_block_root = state.latest_block_header().tree_hash_root();
verify!(
block.parent_root() == expected_previous_block_root,
HeaderInvalid::ParentBlockRootMismatch {
state: expected_previous_block_root,
block: block.parent_root(),
}
);
if verify_block_root == VerifyBlockRoot::True {
let expected_previous_block_root = state.latest_block_header().tree_hash_root();
verify!(
block.parent_root() == expected_previous_block_root,
HeaderInvalid::ParentBlockRootMismatch {
state: expected_previous_block_root,
block: block.parent_root(),
}
);
}
*state.latest_block_header_mut() = block.temporary_block_header();
@@ -283,3 +303,122 @@ pub fn get_new_eth1_data<T: EthSpec>(
Ok(None)
}
}
/// Performs *partial* verification of the `payload`.
///
/// The verification is partial, since the execution payload is not verified against an execution
/// engine. That is expected to be performed by an upstream function.
///
/// ## Specification
///
/// Contains a partial set of checks from the `process_execution_payload` function:
///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload
pub fn partially_verify_execution_payload<T: EthSpec>(
state: &BeaconState<T>,
payload: &ExecutionPayload<T>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
if is_merge_transition_complete(state) {
block_verify!(
payload.parent_hash == state.latest_execution_payload_header()?.block_hash,
BlockProcessingError::ExecutionHashChainIncontiguous {
expected: state.latest_execution_payload_header()?.block_hash,
found: payload.parent_hash,
}
);
}
block_verify!(
payload.random == *state.get_randao_mix(state.current_epoch())?,
BlockProcessingError::ExecutionRandaoMismatch {
expected: *state.get_randao_mix(state.current_epoch())?,
found: payload.random,
}
);
let timestamp = compute_timestamp_at_slot(state, spec)?;
block_verify!(
payload.timestamp == timestamp,
BlockProcessingError::ExecutionInvalidTimestamp {
expected: timestamp,
found: payload.timestamp,
}
);
Ok(())
}
/// Calls `partially_verify_execution_payload` and then updates the payload header in the `state`.
///
/// ## Specification
///
/// Partially equivalent to the `process_execution_payload` function:
///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/beacon-chain.md#process_execution_payload
pub fn process_execution_payload<T: EthSpec>(
state: &mut BeaconState<T>,
payload: &ExecutionPayload<T>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
partially_verify_execution_payload(state, payload, spec)?;
*state.latest_execution_payload_header_mut()? = ExecutionPayloadHeader {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipt_root: payload.receipt_root,
logs_bloom: payload.logs_bloom.clone(),
random: payload.random,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data.clone(),
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: payload.transactions.tree_hash_root(),
};
Ok(())
}
/// These functions will definitely be called before the merge. Their entire purpose is to check if
/// the merge has happened or if we're on the transition block. Thus we don't want to propagate
/// errors from the `BeaconState` being an earlier variant than `BeaconStateMerge` as we'd have to
/// repeaetedly write code to treat these errors as false.
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#is_merge_transition_complete
pub fn is_merge_transition_complete<T: EthSpec>(state: &BeaconState<T>) -> bool {
state
.latest_execution_payload_header()
.map(|header| *header != <ExecutionPayloadHeader<T>>::default())
.unwrap_or(false)
}
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#is_merge_transition_block
pub fn is_merge_transition_block<T: EthSpec>(
state: &BeaconState<T>,
body: BeaconBlockBodyRef<T>,
) -> bool {
body.execution_payload()
.map(|payload| {
!is_merge_transition_complete(state) && *payload != <ExecutionPayload<T>>::default()
})
.unwrap_or(false)
}
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#is_execution_enabled
pub fn is_execution_enabled<T: EthSpec>(
state: &BeaconState<T>,
body: BeaconBlockBodyRef<T>,
) -> bool {
is_merge_transition_block(state, body) || is_merge_transition_complete(state)
}
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/beacon-chain.md#compute_timestamp_at_slot
pub fn compute_timestamp_at_slot<T: EthSpec>(
state: &BeaconState<T>,
spec: &ChainSpec,
) -> Result<u64, ArithError> {
let slots_since_genesis = state.slot().as_u64().safe_sub(spec.genesis_slot.as_u64())?;
slots_since_genesis
.safe_mul(spec.seconds_per_slot)
.and_then(|since_genesis| state.genesis_time().safe_add(since_genesis))
}

View File

@@ -302,7 +302,7 @@ where
/// Include the signature of the block's sync aggregate (if it exists) for verification.
pub fn include_sync_aggregate(&mut self, block: &'a SignedBeaconBlock<T>) -> Result<()> {
if let Some(sync_aggregate) = block.message().body().sync_aggregate() {
if let Ok(sync_aggregate) = block.message().body().sync_aggregate() {
if let Some(signature_set) = sync_aggregate_signature_set(
&self.decompressor,
sync_aggregate,

View File

@@ -57,6 +57,19 @@ pub enum BlockProcessingError {
ArithError(ArithError),
InconsistentBlockFork(InconsistentFork),
InconsistentStateFork(InconsistentFork),
ExecutionHashChainIncontiguous {
expected: Hash256,
found: Hash256,
},
ExecutionRandaoMismatch {
expected: Hash256,
found: Hash256,
},
ExecutionInvalidTimestamp {
expected: u64,
found: u64,
},
ExecutionInvalid,
#[cfg(feature = "milhouse")]
MilhouseError(milhouse::Error),
}

View File

@@ -228,7 +228,7 @@ pub fn process_attestations<'a, T: EthSpec>(
BeaconBlockBodyRef::Base(_) => {
base::process_attestations(state, block_body.attestations(), verify_signatures, spec)?;
}
BeaconBlockBodyRef::Altair(_) => {
BeaconBlockBodyRef::Altair(_) | BeaconBlockBodyRef::Merge(_) => {
altair::process_attestations(
state,
block_body.attestations(),
@@ -353,15 +353,15 @@ pub fn process_deposit<T: EthSpec>(
state.validators_mut().push(validator)?;
state.balances_mut().push(deposit.data.amount)?;
// Altair-specific initializations.
if let BeaconState::Altair(altair_state) = state {
altair_state
.previous_epoch_participation
.push(ParticipationFlags::default())?;
altair_state
.current_epoch_participation
.push(ParticipationFlags::default())?;
altair_state.inactivity_scores.push(0)?;
// Altair or later initializations.
if let Ok(previous_epoch_participation) = state.previous_epoch_participation_mut() {
previous_epoch_participation.push(ParticipationFlags::default())?;
}
if let Ok(current_epoch_participation) = state.current_epoch_participation_mut() {
current_epoch_participation.push(ParticipationFlags::default())?;
}
if let Ok(inactivity_scores) = state.inactivity_scores_mut() {
inactivity_scores.push(0)?;
}
}

View File

@@ -6,7 +6,10 @@ use crate::per_block_processing::errors::{
DepositInvalid, HeaderInvalid, IndexedAttestationInvalid, IntoWithIndex,
ProposerSlashingInvalid,
};
use crate::{per_block_processing::process_operations, BlockSignatureStrategy, VerifySignatures};
use crate::{
per_block_processing::process_operations, BlockSignatureStrategy, VerifyBlockRoot,
VerifySignatures,
};
use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType};
use lazy_static::lazy_static;
use ssz_types::Bitfield;
@@ -65,6 +68,7 @@ fn valid_block_ok() {
&block,
None,
BlockSignatureStrategy::VerifyIndividual,
VerifyBlockRoot::True,
&spec,
);
@@ -88,6 +92,7 @@ fn invalid_block_header_state_slot() {
&SignedBeaconBlock::from_block(block, signature),
None,
BlockSignatureStrategy::VerifyIndividual,
VerifyBlockRoot::True,
&spec,
);
@@ -116,6 +121,7 @@ fn invalid_parent_block_root() {
&SignedBeaconBlock::from_block(block, signature),
None,
BlockSignatureStrategy::VerifyIndividual,
VerifyBlockRoot::True,
&spec,
);
@@ -145,6 +151,7 @@ fn invalid_block_signature() {
&SignedBeaconBlock::from_block(block, Signature::empty()),
None,
BlockSignatureStrategy::VerifyIndividual,
VerifyBlockRoot::True,
&spec,
);
@@ -174,6 +181,7 @@ fn invalid_randao_reveal_signature() {
&signed_block,
None,
BlockSignatureStrategy::VerifyIndividual,
VerifyBlockRoot::True,
&spec,
);
@@ -187,14 +195,13 @@ fn valid_4_deposits() {
let harness = get_harness::<MainnetEthSpec>(EPOCH_OFFSET, VALIDATOR_COUNT);
let mut state = harness.get_current_state();
let (deposits, mut state) = harness.make_deposits(&mut state, 4, None, None);
let (deposits, state) = harness.make_deposits(&mut state, 4, None, None);
let deposits = VariableList::from(deposits);
let mut head_block = harness.chain.head_beacon_block().unwrap().deconstruct().0;
*head_block.to_mut().body_mut().deposits_mut() = deposits;
let result =
process_operations::process_deposits(&mut state, head_block.body().deposits(), &spec);
let result = process_operations::process_deposits(state, head_block.body().deposits(), &spec);
// Expecting Ok because these are valid deposits.
assert_eq!(result, Ok(()));
@@ -206,7 +213,7 @@ fn invalid_deposit_deposit_count_too_big() {
let harness = get_harness::<MainnetEthSpec>(EPOCH_OFFSET, VALIDATOR_COUNT);
let mut state = harness.get_current_state();
let (deposits, mut state) = harness.make_deposits(&mut state, 1, None, None);
let (deposits, state) = harness.make_deposits(&mut state, 1, None, None);
let deposits = VariableList::from(deposits);
let mut head_block = harness.chain.head_beacon_block().unwrap().deconstruct().0;
@@ -214,8 +221,7 @@ fn invalid_deposit_deposit_count_too_big() {
let big_deposit_count = NUM_DEPOSITS + 1;
state.eth1_data_mut().deposit_count = big_deposit_count;
let result =
process_operations::process_deposits(&mut state, head_block.body().deposits(), &spec);
let result = process_operations::process_deposits(state, head_block.body().deposits(), &spec);
// Expecting DepositCountInvalid because we incremented the deposit_count
assert_eq!(
@@ -233,7 +239,7 @@ fn invalid_deposit_count_too_small() {
let harness = get_harness::<MainnetEthSpec>(EPOCH_OFFSET, VALIDATOR_COUNT);
let mut state = harness.get_current_state();
let (deposits, mut state) = harness.make_deposits(&mut state, 1, None, None);
let (deposits, state) = harness.make_deposits(&mut state, 1, None, None);
let deposits = VariableList::from(deposits);
let mut head_block = harness.chain.head_beacon_block().unwrap().deconstruct().0;
@@ -241,8 +247,7 @@ fn invalid_deposit_count_too_small() {
let small_deposit_count = NUM_DEPOSITS - 1;
state.eth1_data_mut().deposit_count = small_deposit_count;
let result =
process_operations::process_deposits(&mut state, head_block.body().deposits(), &spec);
let result = process_operations::process_deposits(state, head_block.body().deposits(), &spec);
// Expecting DepositCountInvalid because we decremented the deposit_count
assert_eq!(
@@ -260,7 +265,7 @@ fn invalid_deposit_bad_merkle_proof() {
let harness = get_harness::<MainnetEthSpec>(EPOCH_OFFSET, VALIDATOR_COUNT);
let mut state = harness.get_current_state();
let (deposits, mut state) = harness.make_deposits(&mut state, 1, None, None);
let (deposits, state) = harness.make_deposits(&mut state, 1, None, None);
let deposits = VariableList::from(deposits);
let mut head_block = harness.chain.head_beacon_block().unwrap().deconstruct().0;
@@ -270,8 +275,7 @@ fn invalid_deposit_bad_merkle_proof() {
// Manually offsetting deposit count and index to trigger bad merkle proof
state.eth1_data_mut().deposit_count += 1;
*state.eth1_deposit_index_mut() += 1;
let result =
process_operations::process_deposits(&mut state, head_block.body().deposits(), &spec);
let result = process_operations::process_deposits(state, head_block.body().deposits(), &spec);
// Expecting BadMerkleProof because the proofs were created with different indices
assert_eq!(
@@ -289,15 +293,14 @@ fn invalid_deposit_wrong_sig() {
let harness = get_harness::<MainnetEthSpec>(EPOCH_OFFSET, VALIDATOR_COUNT);
let mut state = harness.get_current_state();
let (deposits, mut state) =
let (deposits, state) =
harness.make_deposits(&mut state, 1, None, Some(SignatureBytes::empty()));
let deposits = VariableList::from(deposits);
let mut head_block = harness.chain.head_beacon_block().unwrap().deconstruct().0;
*head_block.to_mut().body_mut().deposits_mut() = deposits;
let result =
process_operations::process_deposits(&mut state, head_block.body().deposits(), &spec);
let result = process_operations::process_deposits(state, head_block.body().deposits(), &spec);
// Expecting Ok(()) even though the block signature does not correspond to the correct public key
assert_eq!(result, Ok(()));
}
@@ -308,15 +311,14 @@ fn invalid_deposit_invalid_pub_key() {
let harness = get_harness::<MainnetEthSpec>(EPOCH_OFFSET, VALIDATOR_COUNT);
let mut state = harness.get_current_state();
let (deposits, mut state) =
let (deposits, state) =
harness.make_deposits(&mut state, 1, Some(PublicKeyBytes::empty()), None);
let deposits = VariableList::from(deposits);
let mut head_block = harness.chain.head_beacon_block().unwrap().deconstruct().0;
*head_block.to_mut().body_mut().deposits_mut() = deposits;
let result =
process_operations::process_deposits(&mut state, head_block.body().deposits(), &spec);
let result = process_operations::process_deposits(state, head_block.body().deposits(), &spec);
// Expecting Ok(()) even though we passed in invalid publickeybytes in the public key field of the deposit data.
assert_eq!(result, Ok(()));

View File

@@ -35,7 +35,7 @@ pub fn process_epoch<T: EthSpec>(
match state {
BeaconState::Base(_) => base::process_epoch(state, spec),
BeaconState::Altair(_) => altair::process_epoch(state, spec),
BeaconState::Altair(_) | BeaconState::Merge(_) => altair::process_epoch(state, spec),
}
}

View File

@@ -47,7 +47,6 @@ pub fn process_epoch<T: EthSpec>(
process_slashings(
state,
participation_cache.current_epoch_total_active_balance(),
spec.proportional_slashing_multiplier_altair,
spec,
)?;

View File

@@ -119,7 +119,7 @@ pub fn get_inactivity_penalty_deltas<T: EthSpec>(
.safe_mul(state.get_inactivity_score(index)?)?;
let penalty_denominator = spec
.inactivity_score_bias
.safe_mul(spec.inactivity_penalty_quotient_altair)?;
.safe_mul(spec.inactivity_penalty_quotient_for_state(state))?;
delta.penalize(penalty_numerator.safe_div(penalty_denominator)?)?;
}
deltas

View File

@@ -43,7 +43,6 @@ pub fn process_epoch<T: EthSpec>(
process_slashings(
state,
validator_statuses.total_balances.current_epoch(),
spec.proportional_slashing_multiplier,
spec,
)?;

View File

@@ -6,14 +6,15 @@ use types::{BeaconState, ChainSpec, EthSpec, GetBalanceMut, GetValidatorMut, Uns
pub fn process_slashings<T: EthSpec>(
state: &mut BeaconState<T>,
total_balance: u64,
slashing_multiplier: u64,
spec: &ChainSpec,
) -> Result<(), Error> {
let epoch = state.current_epoch();
let sum_slashings = state.get_all_slashings().iter().copied().safe_sum()?;
let adjusted_total_slashing_balance =
std::cmp::min(sum_slashings.safe_mul(slashing_multiplier)?, total_balance);
let adjusted_total_slashing_balance = std::cmp::min(
sum_slashings.safe_mul(spec.proportional_slashing_multiplier_for_state(state))?,
total_balance,
);
let (validators, mut balances) = state.validators_and_balances_mut();
for index in 0..validators.len() {

View File

@@ -1,4 +1,4 @@
use crate::upgrade::upgrade_to_altair;
use crate::upgrade::{upgrade_to_altair, upgrade_to_bellatrix};
use crate::{per_epoch_processing::EpochProcessingSummary, *};
use safe_arith::{ArithError, SafeArith};
use types::*;
@@ -44,11 +44,17 @@ pub fn per_slot_processing<T: EthSpec>(
state.slot_mut().safe_add_assign(1)?;
// If the Altair fork epoch is reached, perform an irregular state upgrade.
if state.slot().safe_rem(T::slots_per_epoch())? == 0
&& spec.altair_fork_epoch == Some(state.current_epoch())
{
upgrade_to_altair(state, spec)?;
// Process fork upgrades here. Note that multiple upgrades can potentially run
// in sequence if they are scheduled in the same Epoch (common in testnets)
if state.slot().safe_rem(T::slots_per_epoch())? == 0 {
// If the Altair fork epoch is reached, perform an irregular state upgrade.
if spec.altair_fork_epoch == Some(state.current_epoch()) {
upgrade_to_altair(state, spec)?;
}
// If the Merge fork epoch is reached, perform an irregular state upgrade.
if spec.bellatrix_fork_epoch == Some(state.current_epoch()) {
upgrade_to_bellatrix(state, spec)?;
}
}
Ok(summary)

View File

@@ -1,3 +1,5 @@
pub mod altair;
pub mod merge;
pub use altair::upgrade_to_altair;
pub use merge::upgrade_to_bellatrix;

View File

@@ -0,0 +1,72 @@
use std::mem;
use types::{
BeaconState, BeaconStateError as Error, BeaconStateMerge, ChainSpec, EthSpec,
ExecutionPayloadHeader, Fork,
};
/// Transform a `Altair` state into an `Merge` state.
pub fn upgrade_to_bellatrix<E: EthSpec>(
pre_state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), Error> {
let epoch = pre_state.current_epoch();
let pre = pre_state.as_altair_mut()?;
// Where possible, use something like `mem::take` to move fields from behind the &mut
// reference. For other fields that don't have a good default value, use `clone`.
//
// Fixed size vectors get cloned because replacing them would require the same size
// allocation as cloning.
let post = BeaconState::Merge(BeaconStateMerge {
// Versioning
genesis_time: pre.genesis_time,
genesis_validators_root: pre.genesis_validators_root,
slot: pre.slot,
fork: Fork {
previous_version: pre.fork.current_version,
current_version: spec.bellatrix_fork_version,
epoch,
},
// History
latest_block_header: pre.latest_block_header.clone(),
block_roots: pre.block_roots.clone(),
state_roots: pre.state_roots.clone(),
historical_roots: mem::take(&mut pre.historical_roots),
// Eth1
eth1_data: pre.eth1_data.clone(),
eth1_data_votes: mem::take(&mut pre.eth1_data_votes),
eth1_deposit_index: pre.eth1_deposit_index,
// Registry
validators: mem::take(&mut pre.validators),
balances: mem::take(&mut pre.balances),
// Randomness
randao_mixes: pre.randao_mixes.clone(),
// Slashings
slashings: pre.slashings.clone(),
// `Participation
previous_epoch_participation: mem::take(&mut pre.previous_epoch_participation),
current_epoch_participation: mem::take(&mut pre.current_epoch_participation),
// Finality
justification_bits: pre.justification_bits.clone(),
previous_justified_checkpoint: pre.previous_justified_checkpoint,
current_justified_checkpoint: pre.current_justified_checkpoint,
finalized_checkpoint: pre.finalized_checkpoint,
// Inactivity
inactivity_scores: mem::take(&mut pre.inactivity_scores),
// Sync committees
current_sync_committee: pre.current_sync_committee.clone(),
next_sync_committee: pre.next_sync_committee.clone(),
// Execution
latest_execution_payload_header: <ExecutionPayloadHeader<E>>::default(),
// Caches
total_active_balance: pre.total_active_balance,
committee_caches: mem::take(&mut pre.committee_caches),
pubkey_cache: mem::take(&mut pre.pubkey_cache),
exit_cache: mem::take(&mut pre.exit_cache),
tree_hash_cache: mem::take(&mut pre.tree_hash_cache),
});
*pre_state = post;
Ok(())
}