Fix attestation withdrawals root mismatch (#4249)
## Issue Addressed Addresses #4234 ## Proposed Changes - Skip withdrawals processing in an inconsistent state replay. - Repurpose `StateRootStrategy`: rename to `StateProcessingStrategy` and always skip withdrawals if using `StateProcessingStrategy::Inconsistent` - Add a test to reproduce the scenario Co-authored-by: Jimmy Chen <jimmy@sigmaprime.io>
This commit is contained in:
parent
c7c51062ab
commit
8d9c748025
@ -88,7 +88,8 @@ use state_processing::{
|
|||||||
},
|
},
|
||||||
per_slot_processing,
|
per_slot_processing,
|
||||||
state_advance::{complete_state_advance, partial_state_advance},
|
state_advance::{complete_state_advance, partial_state_advance},
|
||||||
BlockSignatureStrategy, ConsensusContext, SigVerifiedOp, VerifyBlockRoot, VerifyOperation,
|
BlockSignatureStrategy, ConsensusContext, SigVerifiedOp, StateProcessingStrategy,
|
||||||
|
VerifyBlockRoot, VerifyOperation,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
@ -4651,6 +4652,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&block,
|
&block,
|
||||||
signature_strategy,
|
signature_strategy,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&self.spec,
|
&self.spec,
|
||||||
|
@ -78,7 +78,7 @@ use state_processing::{
|
|||||||
per_block_processing, per_slot_processing,
|
per_block_processing, per_slot_processing,
|
||||||
state_advance::partial_state_advance,
|
state_advance::partial_state_advance,
|
||||||
BlockProcessingError, BlockSignatureStrategy, ConsensusContext, SlotProcessingError,
|
BlockProcessingError, BlockSignatureStrategy, ConsensusContext, SlotProcessingError,
|
||||||
VerifyBlockRoot,
|
StateProcessingStrategy, VerifyBlockRoot,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
@ -1400,6 +1400,7 @@ impl<T: BeaconChainTypes> ExecutionPendingBlock<T> {
|
|||||||
&block,
|
&block,
|
||||||
// Signatures were verified earlier in this function.
|
// Signatures were verified earlier in this function.
|
||||||
BlockSignatureStrategy::NoVerification,
|
BlockSignatureStrategy::NoVerification,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut consensus_context,
|
&mut consensus_context,
|
||||||
&chain.spec,
|
&chain.spec,
|
||||||
|
@ -5,7 +5,7 @@ use slog::{info, warn, Logger};
|
|||||||
use state_processing::state_advance::complete_state_advance;
|
use state_processing::state_advance::complete_state_advance;
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
per_block_processing, per_block_processing::BlockSignatureStrategy, ConsensusContext,
|
per_block_processing, per_block_processing::BlockSignatureStrategy, ConsensusContext,
|
||||||
VerifyBlockRoot,
|
StateProcessingStrategy, VerifyBlockRoot,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -177,6 +177,7 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
|
|||||||
&mut state,
|
&mut state,
|
||||||
&block,
|
&block,
|
||||||
BlockSignatureStrategy::NoVerification,
|
BlockSignatureStrategy::NoVerification,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
spec,
|
spec,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::observed_operations::ObservationOutcome;
|
||||||
pub use crate::persisted_beacon_chain::PersistedBeaconChain;
|
pub use crate::persisted_beacon_chain::PersistedBeaconChain;
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
|
beacon_chain::{BEACON_CHAIN_DB_KEY, ETH1_CACHE_DB_KEY, FORK_CHOICE_DB_KEY, OP_POOL_DB_KEY},
|
||||||
@ -26,6 +27,7 @@ use futures::channel::mpsc::Receiver;
|
|||||||
pub use genesis::{interop_genesis_state_with_eth1, DEFAULT_ETH1_BLOCK_HASH};
|
pub use genesis::{interop_genesis_state_with_eth1, DEFAULT_ETH1_BLOCK_HASH};
|
||||||
use int_to_bytes::int_to_bytes32;
|
use int_to_bytes::int_to_bytes32;
|
||||||
use merkle_proof::MerkleTree;
|
use merkle_proof::MerkleTree;
|
||||||
|
use operation_pool::ReceivedPreCapella;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use parking_lot::RwLockWriteGuard;
|
use parking_lot::RwLockWriteGuard;
|
||||||
use rand::rngs::StdRng;
|
use rand::rngs::StdRng;
|
||||||
@ -38,7 +40,7 @@ use slot_clock::{SlotClock, TestingSlotClock};
|
|||||||
use state_processing::per_block_processing::compute_timestamp_at_slot;
|
use state_processing::per_block_processing::compute_timestamp_at_slot;
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
state_advance::{complete_state_advance, partial_state_advance},
|
state_advance::{complete_state_advance, partial_state_advance},
|
||||||
StateRootStrategy,
|
StateProcessingStrategy,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
@ -709,7 +711,7 @@ where
|
|||||||
pub fn get_hot_state(&self, state_hash: BeaconStateHash) -> Option<BeaconState<E>> {
|
pub fn get_hot_state(&self, state_hash: BeaconStateHash) -> Option<BeaconState<E>> {
|
||||||
self.chain
|
self.chain
|
||||||
.store
|
.store
|
||||||
.load_hot_state(&state_hash.into(), StateRootStrategy::Accurate)
|
.load_hot_state(&state_hash.into(), StateProcessingStrategy::Accurate)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1494,6 +1496,26 @@ where
|
|||||||
.sign(sk, &fork, genesis_validators_root, &self.chain.spec)
|
.sign(sk, &fork, genesis_validators_root, &self.chain.spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_bls_to_execution_change(
|
||||||
|
&self,
|
||||||
|
validator_index: u64,
|
||||||
|
address: Address,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let signed_bls_change = self.make_bls_to_execution_change(validator_index, address);
|
||||||
|
if let ObservationOutcome::New(verified_bls_change) = self
|
||||||
|
.chain
|
||||||
|
.verify_bls_to_execution_change_for_gossip(signed_bls_change)
|
||||||
|
.expect("should verify BLS to execution change for gossip")
|
||||||
|
{
|
||||||
|
self.chain
|
||||||
|
.import_bls_to_execution_change(verified_bls_change, ReceivedPreCapella::No)
|
||||||
|
.then_some(())
|
||||||
|
.ok_or("should import BLS to execution change to the op pool".to_string())
|
||||||
|
} else {
|
||||||
|
Err("should observe new BLS to execution change".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn make_bls_to_execution_change(
|
pub fn make_bls_to_execution_change(
|
||||||
&self,
|
&self,
|
||||||
validator_index: u64,
|
validator_index: u64,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![cfg(not(debug_assertions))]
|
#![cfg(not(debug_assertions))]
|
||||||
|
|
||||||
|
use beacon_chain::test_utils::HARNESS_GENESIS_TIME;
|
||||||
use beacon_chain::{
|
use beacon_chain::{
|
||||||
attestation_verification::Error as AttnError,
|
attestation_verification::Error as AttnError,
|
||||||
test_utils::{
|
test_utils::{
|
||||||
@ -7,6 +8,7 @@ use beacon_chain::{
|
|||||||
},
|
},
|
||||||
BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped,
|
BeaconChain, BeaconChainError, BeaconChainTypes, WhenSlotSkipped,
|
||||||
};
|
};
|
||||||
|
use genesis::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
|
||||||
use int_to_bytes::int_to_bytes32;
|
use int_to_bytes::int_to_bytes32;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
@ -14,9 +16,9 @@ use state_processing::{
|
|||||||
};
|
};
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
use types::{
|
use types::{
|
||||||
test_utils::generate_deterministic_keypair, AggregateSignature, Attestation, BeaconStateError,
|
test_utils::generate_deterministic_keypair, Address, AggregateSignature, Attestation,
|
||||||
BitList, Epoch, EthSpec, Hash256, Keypair, MainnetEthSpec, SecretKey, SelectionProof,
|
BeaconStateError, BitList, Epoch, EthSpec, Hash256, Keypair, MainnetEthSpec, SecretKey,
|
||||||
SignedAggregateAndProof, Slot, SubnetId, Unsigned,
|
SelectionProof, SignedAggregateAndProof, Slot, SubnetId, Unsigned,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub type E = MainnetEthSpec;
|
pub type E = MainnetEthSpec;
|
||||||
@ -50,6 +52,48 @@ fn get_harness(validator_count: usize) -> BeaconChainHarness<EphemeralHarnessTyp
|
|||||||
harness
|
harness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a beacon chain harness with Capella fork enabled at epoch 1, and
|
||||||
|
/// all genesis validators start with BLS withdrawal credentials.
|
||||||
|
fn get_harness_capella_spec(validator_count: usize) -> BeaconChainHarness<EphemeralHarnessType<E>> {
|
||||||
|
let mut spec = E::default_spec();
|
||||||
|
spec.altair_fork_epoch = Some(Epoch::new(0));
|
||||||
|
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
|
||||||
|
spec.capella_fork_epoch = Some(Epoch::new(1));
|
||||||
|
|
||||||
|
let validator_keypairs = KEYPAIRS[0..validator_count].to_vec();
|
||||||
|
let genesis_state = interop_genesis_state(
|
||||||
|
&validator_keypairs,
|
||||||
|
HARNESS_GENESIS_TIME,
|
||||||
|
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
|
||||||
|
None,
|
||||||
|
&spec,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let harness = BeaconChainHarness::builder(MainnetEthSpec)
|
||||||
|
.spec(spec)
|
||||||
|
.keypairs(validator_keypairs)
|
||||||
|
.withdrawal_keypairs(
|
||||||
|
KEYPAIRS[0..validator_count]
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(Some)
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
.genesis_state_ephemeral_store(genesis_state)
|
||||||
|
.mock_execution_layer()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
harness
|
||||||
|
.execution_block_generator()
|
||||||
|
.move_to_terminal_block()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
harness.advance_slot();
|
||||||
|
|
||||||
|
harness
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns an attestation that is valid for some slot in the given `chain`.
|
/// Returns an attestation that is valid for some slot in the given `chain`.
|
||||||
///
|
///
|
||||||
/// Also returns some info about who created it.
|
/// Also returns some info about who created it.
|
||||||
@ -998,6 +1042,100 @@ async fn attestation_that_skips_epochs() {
|
|||||||
.expect("should gossip verify attestation that skips slots");
|
.expect("should gossip verify attestation that skips slots");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Ensures that an attestation can be processed when a validator receives proposer reward
|
||||||
|
/// in an epoch _and_ is scheduled for a withdrawal. This is a regression test for a scenario where
|
||||||
|
/// inconsistent state lookup could cause withdrawal root mismatch.
|
||||||
|
#[tokio::test]
|
||||||
|
async fn attestation_validator_receive_proposer_reward_and_withdrawals() {
|
||||||
|
let harness = get_harness_capella_spec(VALIDATOR_COUNT);
|
||||||
|
|
||||||
|
// Advance to a Capella block. Make sure the blocks have attestations.
|
||||||
|
let two_thirds = (VALIDATOR_COUNT / 3) * 2;
|
||||||
|
let attesters = (0..two_thirds).collect();
|
||||||
|
harness
|
||||||
|
.extend_chain(
|
||||||
|
// To trigger the bug we need the proposer attestation reward to be signed at a block
|
||||||
|
// that isn't the first in the epoch.
|
||||||
|
MainnetEthSpec::slots_per_epoch() as usize + 1,
|
||||||
|
BlockStrategy::OnCanonicalHead,
|
||||||
|
AttestationStrategy::SomeValidators(attesters),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Add BLS change for the block proposer at slot 33. This sets up a withdrawal for the block proposer.
|
||||||
|
let proposer_index = harness
|
||||||
|
.chain
|
||||||
|
.block_at_slot(harness.get_current_slot(), WhenSlotSkipped::None)
|
||||||
|
.expect("should not error getting block at slot")
|
||||||
|
.expect("should find block at slot")
|
||||||
|
.message()
|
||||||
|
.proposer_index();
|
||||||
|
harness
|
||||||
|
.add_bls_to_execution_change(proposer_index, Address::from_low_u64_be(proposer_index))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Apply two blocks: one to process the BLS change, and another to process the withdrawal.
|
||||||
|
harness.advance_slot();
|
||||||
|
harness
|
||||||
|
.extend_chain(
|
||||||
|
2,
|
||||||
|
BlockStrategy::OnCanonicalHead,
|
||||||
|
AttestationStrategy::SomeValidators(vec![]),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
let earlier_slot = harness.get_current_slot();
|
||||||
|
let earlier_block = harness
|
||||||
|
.chain
|
||||||
|
.block_at_slot(earlier_slot, WhenSlotSkipped::None)
|
||||||
|
.expect("should not error getting block at slot")
|
||||||
|
.expect("should find block at slot");
|
||||||
|
|
||||||
|
// Extend the chain out a few epochs so we have some chain depth to play with.
|
||||||
|
harness.advance_slot();
|
||||||
|
harness
|
||||||
|
.extend_chain(
|
||||||
|
MainnetEthSpec::slots_per_epoch() as usize * 2,
|
||||||
|
BlockStrategy::OnCanonicalHead,
|
||||||
|
AttestationStrategy::SomeValidators(vec![]),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let current_slot = harness.get_current_slot();
|
||||||
|
let mut state = harness
|
||||||
|
.chain
|
||||||
|
.get_state(&earlier_block.state_root(), Some(earlier_slot))
|
||||||
|
.expect("should not error getting state")
|
||||||
|
.expect("should find state");
|
||||||
|
|
||||||
|
while state.slot() < current_slot {
|
||||||
|
per_slot_processing(&mut state, None, &harness.spec).expect("should process slot");
|
||||||
|
}
|
||||||
|
|
||||||
|
let state_root = state.update_tree_hash_cache().unwrap();
|
||||||
|
|
||||||
|
// Get an attestation pointed to an old block (where we do not have its shuffling cached).
|
||||||
|
// Verifying the attestation triggers an inconsistent state replay.
|
||||||
|
let remaining_attesters = (two_thirds..VALIDATOR_COUNT).collect();
|
||||||
|
let (attestation, subnet_id) = harness
|
||||||
|
.get_unaggregated_attestations(
|
||||||
|
&AttestationStrategy::SomeValidators(remaining_attesters),
|
||||||
|
&state,
|
||||||
|
state_root,
|
||||||
|
earlier_block.canonical_root(),
|
||||||
|
current_slot,
|
||||||
|
)
|
||||||
|
.first()
|
||||||
|
.expect("should have at least one committee")
|
||||||
|
.first()
|
||||||
|
.cloned()
|
||||||
|
.expect("should have at least one attestation in committee");
|
||||||
|
|
||||||
|
harness
|
||||||
|
.chain
|
||||||
|
.verify_unaggregated_attestation_for_gossip(&attestation, Some(subnet_id))
|
||||||
|
.expect("should gossip verify attestation without checking withdrawals root");
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn attestation_to_finalized_block() {
|
async fn attestation_to_finalized_block() {
|
||||||
let harness = get_harness(VALIDATOR_COUNT);
|
let harness = get_harness(VALIDATOR_COUNT);
|
||||||
|
@ -11,7 +11,8 @@ use slasher::{Config as SlasherConfig, Slasher};
|
|||||||
use state_processing::{
|
use state_processing::{
|
||||||
common::get_indexed_attestation,
|
common::get_indexed_attestation,
|
||||||
per_block_processing::{per_block_processing, BlockSignatureStrategy},
|
per_block_processing::{per_block_processing, BlockSignatureStrategy},
|
||||||
per_slot_processing, BlockProcessingError, ConsensusContext, VerifyBlockRoot,
|
per_slot_processing, BlockProcessingError, ConsensusContext, StateProcessingStrategy,
|
||||||
|
VerifyBlockRoot,
|
||||||
};
|
};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -1167,6 +1168,7 @@ async fn add_base_block_to_altair_chain() {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&base_block,
|
&base_block,
|
||||||
BlockSignatureStrategy::NoVerification,
|
BlockSignatureStrategy::NoVerification,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&harness.chain.spec,
|
&harness.chain.spec,
|
||||||
@ -1305,6 +1307,7 @@ async fn add_altair_block_to_base_chain() {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&altair_block,
|
&altair_block,
|
||||||
BlockSignatureStrategy::NoVerification,
|
BlockSignatureStrategy::NoVerification,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&harness.chain.spec,
|
&harness.chain.spec,
|
||||||
|
@ -30,7 +30,7 @@ use slog::{debug, error, info, trace, warn, Logger};
|
|||||||
use ssz::{Decode, Encode};
|
use ssz::{Decode, Encode};
|
||||||
use ssz_derive::{Decode, Encode};
|
use ssz_derive::{Decode, Encode};
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
BlockProcessingError, BlockReplayer, SlotProcessingError, StateRootStrategy,
|
BlockProcessingError, BlockReplayer, SlotProcessingError, StateProcessingStrategy,
|
||||||
};
|
};
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
@ -531,10 +531,10 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
|
|||||||
// chain. This way we avoid returning a state that doesn't match `state_root`.
|
// chain. This way we avoid returning a state that doesn't match `state_root`.
|
||||||
self.load_cold_state(state_root)
|
self.load_cold_state(state_root)
|
||||||
} else {
|
} else {
|
||||||
self.load_hot_state(state_root, StateRootStrategy::Accurate)
|
self.load_hot_state(state_root, StateProcessingStrategy::Accurate)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
match self.load_hot_state(state_root, StateRootStrategy::Accurate)? {
|
match self.load_hot_state(state_root, StateProcessingStrategy::Accurate)? {
|
||||||
Some(state) => Ok(Some(state)),
|
Some(state) => Ok(Some(state)),
|
||||||
None => self.load_cold_state(state_root),
|
None => self.load_cold_state(state_root),
|
||||||
}
|
}
|
||||||
@ -572,7 +572,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
|
|||||||
}
|
}
|
||||||
.into())
|
.into())
|
||||||
} else {
|
} else {
|
||||||
self.load_hot_state(state_root, StateRootStrategy::Inconsistent)
|
self.load_hot_state(state_root, StateProcessingStrategy::Inconsistent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -662,10 +662,13 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
|
|||||||
{
|
{
|
||||||
// NOTE: minor inefficiency here because we load an unnecessary hot state summary
|
// NOTE: minor inefficiency here because we load an unnecessary hot state summary
|
||||||
//
|
//
|
||||||
// `StateRootStrategy` should be irrelevant here since we never replay blocks for an epoch
|
// `StateProcessingStrategy` should be irrelevant here since we never replay blocks for an epoch
|
||||||
// boundary state in the hot DB.
|
// boundary state in the hot DB.
|
||||||
let state = self
|
let state = self
|
||||||
.load_hot_state(&epoch_boundary_state_root, StateRootStrategy::Accurate)?
|
.load_hot_state(
|
||||||
|
&epoch_boundary_state_root,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
|
)?
|
||||||
.ok_or(HotColdDBError::MissingEpochBoundaryState(
|
.ok_or(HotColdDBError::MissingEpochBoundaryState(
|
||||||
epoch_boundary_state_root,
|
epoch_boundary_state_root,
|
||||||
))?;
|
))?;
|
||||||
@ -834,7 +837,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
|
|||||||
pub fn load_hot_state(
|
pub fn load_hot_state(
|
||||||
&self,
|
&self,
|
||||||
state_root: &Hash256,
|
state_root: &Hash256,
|
||||||
state_root_strategy: StateRootStrategy,
|
state_processing_strategy: StateProcessingStrategy,
|
||||||
) -> Result<Option<BeaconState<E>>, Error> {
|
) -> Result<Option<BeaconState<E>>, Error> {
|
||||||
metrics::inc_counter(&metrics::BEACON_STATE_HOT_GET_COUNT);
|
metrics::inc_counter(&metrics::BEACON_STATE_HOT_GET_COUNT);
|
||||||
|
|
||||||
@ -867,7 +870,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
|
|||||||
blocks,
|
blocks,
|
||||||
slot,
|
slot,
|
||||||
no_state_root_iter(),
|
no_state_root_iter(),
|
||||||
state_root_strategy,
|
state_processing_strategy,
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1038,7 +1041,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
|
|||||||
blocks,
|
blocks,
|
||||||
slot,
|
slot,
|
||||||
Some(state_root_iter),
|
Some(state_root_iter),
|
||||||
StateRootStrategy::Accurate,
|
StateProcessingStrategy::Accurate,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// If state is not error, put it in the cache.
|
// If state is not error, put it in the cache.
|
||||||
@ -1130,10 +1133,10 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
|
|||||||
blocks: Vec<SignedBeaconBlock<E, BlindedPayload<E>>>,
|
blocks: Vec<SignedBeaconBlock<E, BlindedPayload<E>>>,
|
||||||
target_slot: Slot,
|
target_slot: Slot,
|
||||||
state_root_iter: Option<impl Iterator<Item = Result<(Hash256, Slot), Error>>>,
|
state_root_iter: Option<impl Iterator<Item = Result<(Hash256, Slot), Error>>>,
|
||||||
state_root_strategy: StateRootStrategy,
|
state_processing_strategy: StateProcessingStrategy,
|
||||||
) -> Result<BeaconState<E>, Error> {
|
) -> Result<BeaconState<E>, Error> {
|
||||||
let mut block_replayer = BlockReplayer::new(state, &self.spec)
|
let mut block_replayer = BlockReplayer::new(state, &self.spec)
|
||||||
.state_root_strategy(state_root_strategy)
|
.state_processing_strategy(state_processing_strategy)
|
||||||
.no_signature_verification()
|
.no_signature_verification()
|
||||||
.minimal_block_root_verification();
|
.minimal_block_root_verification();
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use itertools::{process_results, Itertools};
|
|||||||
use slog::info;
|
use slog::info;
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
per_block_processing, per_slot_processing, BlockSignatureStrategy, ConsensusContext,
|
per_block_processing, per_slot_processing, BlockSignatureStrategy, ConsensusContext,
|
||||||
VerifyBlockRoot,
|
StateProcessingStrategy, VerifyBlockRoot,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use types::{EthSpec, Hash256};
|
use types::{EthSpec, Hash256};
|
||||||
@ -96,6 +96,7 @@ where
|
|||||||
&mut state,
|
&mut state,
|
||||||
&block,
|
&block,
|
||||||
BlockSignatureStrategy::NoVerification,
|
BlockSignatureStrategy::NoVerification,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&self.spec,
|
&self.spec,
|
||||||
|
@ -29,7 +29,7 @@ pub struct BlockReplayer<
|
|||||||
> {
|
> {
|
||||||
state: BeaconState<Spec>,
|
state: BeaconState<Spec>,
|
||||||
spec: &'a ChainSpec,
|
spec: &'a ChainSpec,
|
||||||
state_root_strategy: StateRootStrategy,
|
state_processing_strategy: StateProcessingStrategy,
|
||||||
block_sig_strategy: BlockSignatureStrategy,
|
block_sig_strategy: BlockSignatureStrategy,
|
||||||
verify_block_root: Option<VerifyBlockRoot>,
|
verify_block_root: Option<VerifyBlockRoot>,
|
||||||
pre_block_hook: Option<PreBlockHook<'a, Spec, Error>>,
|
pre_block_hook: Option<PreBlockHook<'a, Spec, Error>>,
|
||||||
@ -60,13 +60,13 @@ impl From<BlockProcessingError> for BlockReplayError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines how state roots should be computed during block replay.
|
/// Defines how state roots should be computed and whether to perform all state transitions during block replay.
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq, Clone, Copy)]
|
||||||
pub enum StateRootStrategy {
|
pub enum StateProcessingStrategy {
|
||||||
/// Perform all transitions faithfully to the specification.
|
/// Perform all transitions faithfully to the specification.
|
||||||
Accurate,
|
Accurate,
|
||||||
/// Don't compute state roots, eventually computing an invalid beacon state that can only be
|
/// Don't compute state roots and process withdrawals, eventually computing an invalid beacon
|
||||||
/// used for obtaining shuffling.
|
/// state that can only be used for obtaining shuffling.
|
||||||
Inconsistent,
|
Inconsistent,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +87,7 @@ where
|
|||||||
Self {
|
Self {
|
||||||
state,
|
state,
|
||||||
spec,
|
spec,
|
||||||
state_root_strategy: StateRootStrategy::Accurate,
|
state_processing_strategy: StateProcessingStrategy::Accurate,
|
||||||
block_sig_strategy: BlockSignatureStrategy::VerifyBulk,
|
block_sig_strategy: BlockSignatureStrategy::VerifyBulk,
|
||||||
verify_block_root: Some(VerifyBlockRoot::True),
|
verify_block_root: Some(VerifyBlockRoot::True),
|
||||||
pre_block_hook: None,
|
pre_block_hook: None,
|
||||||
@ -100,12 +100,15 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the replayer's state root strategy different from the default.
|
/// Set the replayer's state processing strategy different from the default.
|
||||||
pub fn state_root_strategy(mut self, state_root_strategy: StateRootStrategy) -> Self {
|
pub fn state_processing_strategy(
|
||||||
if state_root_strategy == StateRootStrategy::Inconsistent {
|
mut self,
|
||||||
|
state_processing_strategy: StateProcessingStrategy,
|
||||||
|
) -> Self {
|
||||||
|
if state_processing_strategy == StateProcessingStrategy::Inconsistent {
|
||||||
self.verify_block_root = None;
|
self.verify_block_root = None;
|
||||||
}
|
}
|
||||||
self.state_root_strategy = state_root_strategy;
|
self.state_processing_strategy = state_processing_strategy;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,7 +185,7 @@ where
|
|||||||
i: usize,
|
i: usize,
|
||||||
) -> Result<Option<Hash256>, Error> {
|
) -> Result<Option<Hash256>, Error> {
|
||||||
// If we don't care about state roots then return immediately.
|
// If we don't care about state roots then return immediately.
|
||||||
if self.state_root_strategy == StateRootStrategy::Inconsistent {
|
if self.state_processing_strategy == StateProcessingStrategy::Inconsistent {
|
||||||
return Ok(Some(Hash256::zero()));
|
return Ok(Some(Hash256::zero()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,7 +252,7 @@ where
|
|||||||
// If no explicit policy is set, verify only the first 1 or 2 block roots if using
|
// 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
|
// accurate state roots. Inaccurate state roots require block root verification to
|
||||||
// be off.
|
// be off.
|
||||||
if i <= 1 && self.state_root_strategy == StateRootStrategy::Accurate {
|
if i <= 1 && self.state_processing_strategy == StateProcessingStrategy::Accurate {
|
||||||
VerifyBlockRoot::True
|
VerifyBlockRoot::True
|
||||||
} else {
|
} else {
|
||||||
VerifyBlockRoot::False
|
VerifyBlockRoot::False
|
||||||
@ -263,6 +266,7 @@ where
|
|||||||
&mut self.state,
|
&mut self.state,
|
||||||
block,
|
block,
|
||||||
self.block_sig_strategy,
|
self.block_sig_strategy,
|
||||||
|
self.state_processing_strategy,
|
||||||
verify_block_root,
|
verify_block_root,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
self.spec,
|
self.spec,
|
||||||
|
@ -27,7 +27,7 @@ pub mod state_advance;
|
|||||||
pub mod upgrade;
|
pub mod upgrade;
|
||||||
pub mod verify_operation;
|
pub mod verify_operation;
|
||||||
|
|
||||||
pub use block_replayer::{BlockReplayError, BlockReplayer, StateRootStrategy};
|
pub use block_replayer::{BlockReplayError, BlockReplayer, StateProcessingStrategy};
|
||||||
pub use consensus_context::{ConsensusContext, ContextError};
|
pub use consensus_context::{ConsensusContext, ContextError};
|
||||||
pub use genesis::{
|
pub use genesis::{
|
||||||
eth2_genesis_time, initialize_beacon_state_from_eth1, is_valid_genesis_state,
|
eth2_genesis_time, initialize_beacon_state_from_eth1, is_valid_genesis_state,
|
||||||
|
@ -39,6 +39,7 @@ mod verify_exit;
|
|||||||
mod verify_proposer_slashing;
|
mod verify_proposer_slashing;
|
||||||
|
|
||||||
use crate::common::decrease_balance;
|
use crate::common::decrease_balance;
|
||||||
|
use crate::StateProcessingStrategy;
|
||||||
|
|
||||||
#[cfg(feature = "arbitrary-fuzz")]
|
#[cfg(feature = "arbitrary-fuzz")]
|
||||||
use arbitrary::Arbitrary;
|
use arbitrary::Arbitrary;
|
||||||
@ -96,6 +97,7 @@ pub fn per_block_processing<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
|||||||
state: &mut BeaconState<T>,
|
state: &mut BeaconState<T>,
|
||||||
signed_block: &SignedBeaconBlock<T, Payload>,
|
signed_block: &SignedBeaconBlock<T, Payload>,
|
||||||
block_signature_strategy: BlockSignatureStrategy,
|
block_signature_strategy: BlockSignatureStrategy,
|
||||||
|
state_processing_strategy: StateProcessingStrategy,
|
||||||
verify_block_root: VerifyBlockRoot,
|
verify_block_root: VerifyBlockRoot,
|
||||||
ctxt: &mut ConsensusContext<T>,
|
ctxt: &mut ConsensusContext<T>,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
@ -160,7 +162,9 @@ pub fn per_block_processing<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
|||||||
// previous block.
|
// previous block.
|
||||||
if is_execution_enabled(state, block.body()) {
|
if is_execution_enabled(state, block.body()) {
|
||||||
let payload = block.body().execution_payload()?;
|
let payload = block.body().execution_payload()?;
|
||||||
process_withdrawals::<T, Payload>(state, payload, spec)?;
|
if state_processing_strategy == StateProcessingStrategy::Accurate {
|
||||||
|
process_withdrawals::<T, Payload>(state, payload, spec)?;
|
||||||
|
}
|
||||||
process_execution_payload::<T, Payload>(state, payload, spec)?;
|
process_execution_payload::<T, Payload>(state, payload, spec)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
#![cfg(all(test, not(feature = "fake_crypto")))]
|
#![cfg(all(test, not(feature = "fake_crypto")))]
|
||||||
|
|
||||||
use crate::per_block_processing;
|
|
||||||
use crate::per_block_processing::errors::{
|
use crate::per_block_processing::errors::{
|
||||||
AttestationInvalid, AttesterSlashingInvalid, BlockOperationError, BlockProcessingError,
|
AttestationInvalid, AttesterSlashingInvalid, BlockOperationError, BlockProcessingError,
|
||||||
DepositInvalid, HeaderInvalid, IndexedAttestationInvalid, IntoWithIndex,
|
DepositInvalid, HeaderInvalid, IndexedAttestationInvalid, IntoWithIndex,
|
||||||
ProposerSlashingInvalid,
|
ProposerSlashingInvalid,
|
||||||
};
|
};
|
||||||
|
use crate::{per_block_processing, StateProcessingStrategy};
|
||||||
use crate::{
|
use crate::{
|
||||||
per_block_processing::{process_operations, verify_exit::verify_exit},
|
per_block_processing::{process_operations, verify_exit::verify_exit},
|
||||||
BlockSignatureStrategy, ConsensusContext, VerifyBlockRoot, VerifySignatures,
|
BlockSignatureStrategy, ConsensusContext, VerifyBlockRoot, VerifySignatures,
|
||||||
@ -72,6 +72,7 @@ async fn valid_block_ok() {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&block,
|
&block,
|
||||||
BlockSignatureStrategy::VerifyIndividual,
|
BlockSignatureStrategy::VerifyIndividual,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&spec,
|
&spec,
|
||||||
@ -97,6 +98,7 @@ async fn invalid_block_header_state_slot() {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&SignedBeaconBlock::from_block(block, signature),
|
&SignedBeaconBlock::from_block(block, signature),
|
||||||
BlockSignatureStrategy::VerifyIndividual,
|
BlockSignatureStrategy::VerifyIndividual,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&spec,
|
&spec,
|
||||||
@ -129,6 +131,7 @@ async fn invalid_parent_block_root() {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&SignedBeaconBlock::from_block(block, signature),
|
&SignedBeaconBlock::from_block(block, signature),
|
||||||
BlockSignatureStrategy::VerifyIndividual,
|
BlockSignatureStrategy::VerifyIndividual,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&spec,
|
&spec,
|
||||||
@ -162,6 +165,7 @@ async fn invalid_block_signature() {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&SignedBeaconBlock::from_block(block, Signature::empty()),
|
&SignedBeaconBlock::from_block(block, Signature::empty()),
|
||||||
BlockSignatureStrategy::VerifyIndividual,
|
BlockSignatureStrategy::VerifyIndividual,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&spec,
|
&spec,
|
||||||
@ -195,6 +199,7 @@ async fn invalid_randao_reveal_signature() {
|
|||||||
&mut state,
|
&mut state,
|
||||||
&signed_block,
|
&signed_block,
|
||||||
BlockSignatureStrategy::VerifyIndividual,
|
BlockSignatureStrategy::VerifyIndividual,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&spec,
|
&spec,
|
||||||
|
@ -74,7 +74,7 @@ use eth2::{
|
|||||||
use ssz::Encode;
|
use ssz::Encode;
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
block_signature_verifier::BlockSignatureVerifier, per_block_processing, per_slot_processing,
|
block_signature_verifier::BlockSignatureVerifier, per_block_processing, per_slot_processing,
|
||||||
BlockSignatureStrategy, ConsensusContext, VerifyBlockRoot,
|
BlockSignatureStrategy, ConsensusContext, StateProcessingStrategy, VerifyBlockRoot,
|
||||||
};
|
};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
@ -381,6 +381,7 @@ fn do_transition<T: EthSpec>(
|
|||||||
&mut pre_state,
|
&mut pre_state,
|
||||||
&block,
|
&block,
|
||||||
BlockSignatureStrategy::NoVerification,
|
BlockSignatureStrategy::NoVerification,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
spec,
|
spec,
|
||||||
|
@ -5,7 +5,7 @@ use crate::decode::{ssz_decode_file_with, ssz_decode_state, yaml_decode_file};
|
|||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
per_block_processing, per_slot_processing, BlockProcessingError, BlockSignatureStrategy,
|
per_block_processing, per_slot_processing, BlockProcessingError, BlockSignatureStrategy,
|
||||||
ConsensusContext, VerifyBlockRoot,
|
ConsensusContext, StateProcessingStrategy, VerifyBlockRoot,
|
||||||
};
|
};
|
||||||
use types::{BeaconState, EthSpec, ForkName, RelativeEpoch, SignedBeaconBlock};
|
use types::{BeaconState, EthSpec, ForkName, RelativeEpoch, SignedBeaconBlock};
|
||||||
|
|
||||||
@ -96,6 +96,7 @@ impl<E: EthSpec> Case for SanityBlocks<E> {
|
|||||||
&mut indiv_state,
|
&mut indiv_state,
|
||||||
signed_block,
|
signed_block,
|
||||||
BlockSignatureStrategy::VerifyIndividual,
|
BlockSignatureStrategy::VerifyIndividual,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
spec,
|
spec,
|
||||||
@ -106,6 +107,7 @@ impl<E: EthSpec> Case for SanityBlocks<E> {
|
|||||||
&mut bulk_state,
|
&mut bulk_state,
|
||||||
signed_block,
|
signed_block,
|
||||||
BlockSignatureStrategy::VerifyBulk,
|
BlockSignatureStrategy::VerifyBulk,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
spec,
|
spec,
|
||||||
|
@ -4,7 +4,7 @@ use crate::decode::{ssz_decode_file_with, ssz_decode_state, yaml_decode_file};
|
|||||||
use serde_derive::Deserialize;
|
use serde_derive::Deserialize;
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
per_block_processing, state_advance::complete_state_advance, BlockSignatureStrategy,
|
per_block_processing, state_advance::complete_state_advance, BlockSignatureStrategy,
|
||||||
ConsensusContext, VerifyBlockRoot,
|
ConsensusContext, StateProcessingStrategy, VerifyBlockRoot,
|
||||||
};
|
};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use types::{BeaconState, Epoch, ForkName, SignedBeaconBlock};
|
use types::{BeaconState, Epoch, ForkName, SignedBeaconBlock};
|
||||||
@ -101,6 +101,7 @@ impl<E: EthSpec> Case for TransitionTest<E> {
|
|||||||
&mut state,
|
&mut state,
|
||||||
block,
|
block,
|
||||||
BlockSignatureStrategy::VerifyBulk,
|
BlockSignatureStrategy::VerifyBulk,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
spec,
|
spec,
|
||||||
|
@ -2,7 +2,7 @@ use super::*;
|
|||||||
use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType};
|
use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType};
|
||||||
use state_processing::{
|
use state_processing::{
|
||||||
per_block_processing, per_block_processing::errors::ExitInvalid, BlockProcessingError,
|
per_block_processing, per_block_processing::errors::ExitInvalid, BlockProcessingError,
|
||||||
BlockSignatureStrategy, ConsensusContext, VerifyBlockRoot,
|
BlockSignatureStrategy, ConsensusContext, StateProcessingStrategy, VerifyBlockRoot,
|
||||||
};
|
};
|
||||||
use types::{BeaconBlock, BeaconState, Epoch, EthSpec, SignedBeaconBlock};
|
use types::{BeaconBlock, BeaconState, Epoch, EthSpec, SignedBeaconBlock};
|
||||||
|
|
||||||
@ -69,6 +69,7 @@ impl ExitTest {
|
|||||||
state,
|
state,
|
||||||
block,
|
block,
|
||||||
BlockSignatureStrategy::VerifyIndividual,
|
BlockSignatureStrategy::VerifyIndividual,
|
||||||
|
StateProcessingStrategy::Accurate,
|
||||||
VerifyBlockRoot::True,
|
VerifyBlockRoot::True,
|
||||||
&mut ctxt,
|
&mut ctxt,
|
||||||
&E::default_spec(),
|
&E::default_spec(),
|
||||||
|
Loading…
Reference in New Issue
Block a user