mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-17 21:08:32 +00:00
Altair consensus changes and refactors (#2279)
## Proposed Changes Implement the consensus changes necessary for the upcoming Altair hard fork. ## Additional Info This is quite a heavy refactor, with pivotal types like the `BeaconState` and `BeaconBlock` changing from structs to enums. This ripples through the whole codebase with field accesses changing to methods, e.g. `state.slot` => `state.slot()`. Co-authored-by: realbigsean <seananderson33@gmail.com>
This commit is contained in:
@@ -13,7 +13,7 @@ pub struct BlsAggregateSigs {
|
||||
impl BlsCase for BlsAggregateSigs {}
|
||||
|
||||
impl Case for BlsAggregateSigs {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
let mut aggregate_signature = AggregateSignature::infinity();
|
||||
|
||||
for key_str in &self.input {
|
||||
|
||||
@@ -21,7 +21,7 @@ pub struct BlsAggregateVerify {
|
||||
impl BlsCase for BlsAggregateVerify {}
|
||||
|
||||
impl Case for BlsAggregateVerify {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
let messages = self
|
||||
.input
|
||||
.messages
|
||||
|
||||
@@ -23,7 +23,7 @@ pub struct BlsFastAggregateVerify {
|
||||
impl BlsCase for BlsFastAggregateVerify {}
|
||||
|
||||
impl Case for BlsFastAggregateVerify {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
let message = Hash256::from_slice(
|
||||
&hex::decode(&self.input.message[2..])
|
||||
.map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?,
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct BlsSign {
|
||||
impl BlsCase for BlsSign {}
|
||||
|
||||
impl Case for BlsSign {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
// Convert private_key and message to required types
|
||||
let sk = hex::decode(&self.input.privkey[2..])
|
||||
.map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?;
|
||||
|
||||
@@ -22,7 +22,7 @@ pub struct BlsVerify {
|
||||
impl BlsCase for BlsVerify {}
|
||||
|
||||
impl Case for BlsVerify {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
let message = hex::decode(&self.input.message[2..])
|
||||
.map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?;
|
||||
|
||||
|
||||
@@ -2,18 +2,19 @@ use crate::cases::LoadCase;
|
||||
use crate::decode::yaml_decode_file;
|
||||
use crate::error::Error;
|
||||
use serde_derive::Deserialize;
|
||||
use ssz::{Decode, Encode};
|
||||
use ssz::Encode;
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Debug;
|
||||
use std::path::Path;
|
||||
use tree_hash::TreeHash;
|
||||
use types::ForkName;
|
||||
|
||||
/// Trait for all BLS cases to eliminate some boilerplate.
|
||||
pub trait BlsCase: serde::de::DeserializeOwned {}
|
||||
|
||||
impl<T: BlsCase> LoadCase for T {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
|
||||
yaml_decode_file(&path.join("data.yaml"))
|
||||
}
|
||||
}
|
||||
@@ -60,13 +61,21 @@ macro_rules! uint_wrapper {
|
||||
uint_wrapper!(TestU128, ethereum_types::U128);
|
||||
uint_wrapper!(TestU256, ethereum_types::U256);
|
||||
|
||||
/// Trait alias for all deez bounds
|
||||
/// Trait for types that can be used in SSZ static tests.
|
||||
pub trait SszStaticType:
|
||||
serde::de::DeserializeOwned + Decode + Encode + TreeHash + Clone + PartialEq + Debug + Sync
|
||||
serde::de::DeserializeOwned + Encode + TreeHash + Clone + PartialEq + Debug + Sync
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> SszStaticType for T where
|
||||
T: serde::de::DeserializeOwned + Decode + Encode + TreeHash + Clone + PartialEq + Debug + Sync
|
||||
T: serde::de::DeserializeOwned + Encode + TreeHash + Clone + PartialEq + Debug + Sync
|
||||
{
|
||||
}
|
||||
|
||||
/// Return the fork immediately prior to a fork.
|
||||
pub fn previous_fork(fork_name: ForkName) -> ForkName {
|
||||
match fork_name {
|
||||
ForkName::Base => ForkName::Base,
|
||||
ForkName::Altair => ForkName::Base,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
use super::*;
|
||||
use crate::bls_setting::BlsSetting;
|
||||
use crate::case_result::compare_beacon_state_results_without_caches;
|
||||
use crate::decode::{ssz_decode_file, yaml_decode_file};
|
||||
use crate::decode::{ssz_decode_state, yaml_decode_file};
|
||||
use crate::type_name;
|
||||
use crate::type_name::TypeName;
|
||||
use serde_derive::Deserialize;
|
||||
use state_processing::per_epoch_processing::validator_statuses::ValidatorStatuses;
|
||||
use state_processing::per_epoch_processing::{
|
||||
errors::EpochProcessingError, process_final_updates, process_justification_and_finalization,
|
||||
process_registry_updates, process_rewards_and_penalties, process_slashings,
|
||||
validator_statuses::ValidatorStatuses,
|
||||
altair, base,
|
||||
effective_balance_updates::process_effective_balance_updates,
|
||||
historical_roots_update::process_historical_roots_update,
|
||||
process_registry_updates, process_slashings,
|
||||
resets::{process_eth1_data_reset, process_randao_mixes_reset, process_slashings_reset},
|
||||
};
|
||||
use state_processing::EpochProcessingError;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::{Path, PathBuf};
|
||||
use types::{BeaconState, ChainSpec, EthSpec};
|
||||
use types::{BeaconState, ChainSpec, EthSpec, ForkName};
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub struct Metadata {
|
||||
@@ -44,7 +48,23 @@ pub struct RegistryUpdates;
|
||||
#[derive(Debug)]
|
||||
pub struct Slashings;
|
||||
#[derive(Debug)]
|
||||
pub struct FinalUpdates;
|
||||
pub struct Eth1DataReset;
|
||||
#[derive(Debug)]
|
||||
pub struct EffectiveBalanceUpdates;
|
||||
#[derive(Debug)]
|
||||
pub struct SlashingsReset;
|
||||
#[derive(Debug)]
|
||||
pub struct RandaoMixesReset;
|
||||
#[derive(Debug)]
|
||||
pub struct HistoricalRootsUpdate;
|
||||
#[derive(Debug)]
|
||||
pub struct ParticipationRecordUpdates;
|
||||
#[derive(Debug)]
|
||||
pub struct SyncCommitteeUpdates;
|
||||
#[derive(Debug)]
|
||||
pub struct InactivityUpdates;
|
||||
#[derive(Debug)]
|
||||
pub struct ParticipationFlagUpdates;
|
||||
|
||||
type_name!(
|
||||
JustificationAndFinalization,
|
||||
@@ -53,21 +73,43 @@ type_name!(
|
||||
type_name!(RewardsAndPenalties, "rewards_and_penalties");
|
||||
type_name!(RegistryUpdates, "registry_updates");
|
||||
type_name!(Slashings, "slashings");
|
||||
type_name!(FinalUpdates, "final_updates");
|
||||
type_name!(Eth1DataReset, "eth1_data_reset");
|
||||
type_name!(EffectiveBalanceUpdates, "effective_balance_updates");
|
||||
type_name!(SlashingsReset, "slashings_reset");
|
||||
type_name!(RandaoMixesReset, "randao_mixes_reset");
|
||||
type_name!(HistoricalRootsUpdate, "historical_roots_update");
|
||||
type_name!(ParticipationRecordUpdates, "participation_record_updates");
|
||||
type_name!(SyncCommitteeUpdates, "sync_committee_updates");
|
||||
type_name!(InactivityUpdates, "inactivity_updates");
|
||||
type_name!(ParticipationFlagUpdates, "participation_flag_updates");
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for JustificationAndFinalization {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
let mut validator_statuses = ValidatorStatuses::new(state, spec)?;
|
||||
validator_statuses.process_attestations(state, spec)?;
|
||||
process_justification_and_finalization(state, &validator_statuses.total_balances)
|
||||
match state {
|
||||
BeaconState::Base(_) => {
|
||||
let mut validator_statuses = ValidatorStatuses::new(state, spec)?;
|
||||
validator_statuses.process_attestations(state)?;
|
||||
base::process_justification_and_finalization(
|
||||
state,
|
||||
&validator_statuses.total_balances,
|
||||
spec,
|
||||
)
|
||||
}
|
||||
BeaconState::Altair(_) => altair::process_justification_and_finalization(state, spec),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for RewardsAndPenalties {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
let mut validator_statuses = ValidatorStatuses::new(state, spec)?;
|
||||
validator_statuses.process_attestations(state, spec)?;
|
||||
process_rewards_and_penalties(state, &mut validator_statuses, spec)
|
||||
match state {
|
||||
BeaconState::Base(_) => {
|
||||
let mut validator_statuses = ValidatorStatuses::new(state, spec)?;
|
||||
validator_statuses.process_attestations(state)?;
|
||||
base::process_rewards_and_penalties(state, &mut validator_statuses, spec)
|
||||
}
|
||||
BeaconState::Altair(_) => altair::process_rewards_and_penalties(state, spec),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,35 +121,110 @@ impl<E: EthSpec> EpochTransition<E> for RegistryUpdates {
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for Slashings {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
||||
validator_statuses.process_attestations(&state, spec)?;
|
||||
process_slashings(
|
||||
state,
|
||||
validator_statuses.total_balances.current_epoch(),
|
||||
spec,
|
||||
)?;
|
||||
match state {
|
||||
BeaconState::Base(_) => {
|
||||
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
||||
validator_statuses.process_attestations(&state)?;
|
||||
process_slashings(
|
||||
state,
|
||||
validator_statuses.total_balances.current_epoch(),
|
||||
spec.proportional_slashing_multiplier,
|
||||
spec,
|
||||
)?;
|
||||
}
|
||||
BeaconState::Altair(_) => {
|
||||
process_slashings(
|
||||
state,
|
||||
state.get_total_active_balance(spec)?,
|
||||
spec.proportional_slashing_multiplier_altair,
|
||||
spec,
|
||||
)?;
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for FinalUpdates {
|
||||
impl<E: EthSpec> EpochTransition<E> for Eth1DataReset {
|
||||
fn run(state: &mut BeaconState<E>, _spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
process_eth1_data_reset(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for EffectiveBalanceUpdates {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
process_final_updates(state, spec)
|
||||
process_effective_balance_updates(state, spec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for SlashingsReset {
|
||||
fn run(state: &mut BeaconState<E>, _spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
process_slashings_reset(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for RandaoMixesReset {
|
||||
fn run(state: &mut BeaconState<E>, _spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
process_randao_mixes_reset(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for HistoricalRootsUpdate {
|
||||
fn run(state: &mut BeaconState<E>, _spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
process_historical_roots_update(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for ParticipationRecordUpdates {
|
||||
fn run(state: &mut BeaconState<E>, _spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
if let BeaconState::Base(_) = state {
|
||||
base::process_participation_record_updates(state)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for SyncCommitteeUpdates {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
match state {
|
||||
BeaconState::Base(_) => Ok(()),
|
||||
BeaconState::Altair(_) => altair::process_sync_committee_updates(state, spec),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for InactivityUpdates {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
match state {
|
||||
BeaconState::Base(_) => Ok(()),
|
||||
BeaconState::Altair(_) => altair::process_inactivity_updates(state, spec),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for ParticipationFlagUpdates {
|
||||
fn run(state: &mut BeaconState<E>, _: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
match state {
|
||||
BeaconState::Base(_) => Ok(()),
|
||||
BeaconState::Altair(_) => altair::process_participation_flag_updates(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, T: EpochTransition<E>> LoadCase for EpochProcessing<E, T> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
let metadata_path = path.join("meta.yaml");
|
||||
let metadata: Metadata = if metadata_path.is_file() {
|
||||
yaml_decode_file(&metadata_path)?
|
||||
} else {
|
||||
Metadata::default()
|
||||
};
|
||||
let pre = ssz_decode_file(&path.join("pre.ssz"))?;
|
||||
let post_file = path.join("post.ssz");
|
||||
let pre = ssz_decode_state(&path.join("pre.ssz_snappy"), spec)?;
|
||||
let post_file = path.join("post.ssz_snappy");
|
||||
let post = if post_file.is_file() {
|
||||
Some(ssz_decode_file(&post_file)?)
|
||||
Some(ssz_decode_state(&post_file, spec)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -130,11 +247,25 @@ impl<E: EthSpec, T: EpochTransition<E>> Case for EpochProcessing<E, T> {
|
||||
.unwrap_or_else(String::new)
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
|
||||
match fork_name {
|
||||
// No Altair tests for genesis fork.
|
||||
ForkName::Base => {
|
||||
T::name() != "sync_committee_updates"
|
||||
&& T::name() != "inactivity_updates"
|
||||
&& T::name() != "participation_flag_updates"
|
||||
}
|
||||
ForkName::Altair => true,
|
||||
}
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
self.metadata.bls_setting.unwrap_or_default().check()?;
|
||||
|
||||
let mut state = self.pre.clone();
|
||||
let mut expected = self.post.clone();
|
||||
|
||||
let spec = &E::default_spec();
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
|
||||
let mut result = (|| {
|
||||
// Processing requires the committee caches.
|
||||
|
||||
67
testing/ef_tests/src/cases/fork.rs
Normal file
67
testing/ef_tests/src/cases/fork.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_beacon_state_results_without_caches;
|
||||
use crate::cases::common::previous_fork;
|
||||
use crate::decode::{ssz_decode_state, yaml_decode_file};
|
||||
use serde_derive::Deserialize;
|
||||
use state_processing::upgrade::upgrade_to_altair;
|
||||
use types::{BeaconState, ForkName};
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub struct Metadata {
|
||||
pub fork: String,
|
||||
}
|
||||
|
||||
impl Metadata {
|
||||
fn fork_name(&self) -> ForkName {
|
||||
self.fork.parse().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ForkTest<E: EthSpec> {
|
||||
pub metadata: Metadata,
|
||||
pub pre: BeaconState<E>,
|
||||
pub post: BeaconState<E>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> LoadCase for ForkTest<E> {
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let metadata: Metadata = yaml_decode_file(&path.join("meta.yaml"))?;
|
||||
assert_eq!(metadata.fork_name(), fork_name);
|
||||
|
||||
// Decode pre-state with previous fork.
|
||||
let pre_spec = &previous_fork(fork_name).make_genesis_spec(E::default_spec());
|
||||
let pre = ssz_decode_state(&path.join("pre.ssz_snappy"), pre_spec)?;
|
||||
|
||||
// Decode post-state with target fork.
|
||||
let post_spec = &fork_name.make_genesis_spec(E::default_spec());
|
||||
let post = ssz_decode_state(&path.join("post.ssz_snappy"), post_spec)?;
|
||||
|
||||
Ok(Self {
|
||||
metadata,
|
||||
pre,
|
||||
post,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Case for ForkTest<E> {
|
||||
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
|
||||
// Upgrades exist targeting all forks except phase0/base.
|
||||
// Fork tests also need BLS.
|
||||
cfg!(not(feature = "fake_crypto")) && fork_name != ForkName::Base
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
let mut result_state = self.pre.clone();
|
||||
let mut expected = Some(self.post.clone());
|
||||
let spec = &E::default_spec();
|
||||
|
||||
let mut result = match fork_name {
|
||||
ForkName::Altair => upgrade_to_altair(&mut result_state, spec).map(|_| result_state),
|
||||
_ => panic!("unknown fork: {:?}", fork_name),
|
||||
};
|
||||
|
||||
compare_beacon_state_results_without_caches(&mut result, &mut expected)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,22 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_beacon_state_results_without_caches;
|
||||
use crate::decode::{ssz_decode_file, yaml_decode_file};
|
||||
use crate::decode::{ssz_decode_file, ssz_decode_state, yaml_decode_file};
|
||||
use serde_derive::Deserialize;
|
||||
use state_processing::initialize_beacon_state_from_eth1;
|
||||
use std::path::PathBuf;
|
||||
use types::{BeaconState, Deposit, EthSpec, Hash256};
|
||||
use types::{BeaconState, Deposit, EthSpec, ForkName, Hash256};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct Metadata {
|
||||
deposits_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct Eth1 {
|
||||
eth1_block_hash: Hash256,
|
||||
eth1_timestamp: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(bound = "E: EthSpec")]
|
||||
pub struct GenesisInitialization<E: EthSpec> {
|
||||
@@ -22,17 +28,20 @@ pub struct GenesisInitialization<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> LoadCase for GenesisInitialization<E> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
let eth1_block_hash = ssz_decode_file(&path.join("eth1_block_hash.ssz"))?;
|
||||
let eth1_timestamp = yaml_decode_file(&path.join("eth1_timestamp.yaml"))?;
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let Eth1 {
|
||||
eth1_block_hash,
|
||||
eth1_timestamp,
|
||||
} = yaml_decode_file(&path.join("eth1.yaml"))?;
|
||||
let meta: Metadata = yaml_decode_file(&path.join("meta.yaml"))?;
|
||||
let deposits: Vec<Deposit> = (0..meta.deposits_count)
|
||||
.map(|i| {
|
||||
let filename = format!("deposits_{}.ssz", i);
|
||||
let filename = format!("deposits_{}.ssz_snappy", i);
|
||||
ssz_decode_file(&path.join(filename))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
let state = ssz_decode_file(&path.join("state.ssz"))?;
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
let state = ssz_decode_state(&path.join("state.ssz_snappy"), spec)?;
|
||||
|
||||
Ok(Self {
|
||||
path: path.into(),
|
||||
@@ -45,8 +54,13 @@ impl<E: EthSpec> LoadCase for GenesisInitialization<E> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Case for GenesisInitialization<E> {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
let spec = &E::default_spec();
|
||||
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
|
||||
// Altair genesis and later requires real crypto.
|
||||
fork_name == ForkName::Base || cfg!(not(feature = "fake_crypto"))
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
|
||||
let mut result = initialize_beacon_state_from_eth1(
|
||||
self.eth1_block_hash,
|
||||
|
||||
@@ -1,29 +1,46 @@
|
||||
use super::*;
|
||||
use crate::decode::{ssz_decode_file, yaml_decode_file};
|
||||
use crate::decode::{ssz_decode_state, yaml_decode_file};
|
||||
use serde_derive::Deserialize;
|
||||
use state_processing::is_valid_genesis_state;
|
||||
use std::path::Path;
|
||||
use types::{BeaconState, EthSpec};
|
||||
use types::{BeaconState, EthSpec, ForkName};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Metadata {
|
||||
description: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(bound = "E: EthSpec")]
|
||||
pub struct GenesisValidity<E: EthSpec> {
|
||||
pub metadata: Option<Metadata>,
|
||||
pub genesis: BeaconState<E>,
|
||||
pub is_valid: bool,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> LoadCase for GenesisValidity<E> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
let genesis = ssz_decode_file(&path.join("genesis.ssz"))?;
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
let genesis = ssz_decode_state(&path.join("genesis.ssz_snappy"), spec)?;
|
||||
let is_valid = yaml_decode_file(&path.join("is_valid.yaml"))?;
|
||||
let meta_path = path.join("meta.yaml");
|
||||
let metadata = if meta_path.exists() {
|
||||
Some(yaml_decode_file(&meta_path)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self { genesis, is_valid })
|
||||
Ok(Self {
|
||||
metadata,
|
||||
genesis,
|
||||
is_valid,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Case for GenesisValidity<E> {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
let spec = &E::default_spec();
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
|
||||
let is_valid = is_valid_genesis_state(&self.genesis, spec);
|
||||
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
use super::*;
|
||||
use crate::bls_setting::BlsSetting;
|
||||
use crate::case_result::compare_beacon_state_results_without_caches;
|
||||
use crate::decode::{ssz_decode_file, yaml_decode_file};
|
||||
use crate::decode::{ssz_decode_file, ssz_decode_file_with, ssz_decode_state, yaml_decode_file};
|
||||
use crate::testing_spec;
|
||||
use crate::type_name::TypeName;
|
||||
use serde_derive::Deserialize;
|
||||
use ssz::Decode;
|
||||
use state_processing::per_block_processing::{
|
||||
errors::BlockProcessingError, process_attestations, process_attester_slashings,
|
||||
process_block_header, process_deposits, process_exits, process_proposer_slashings,
|
||||
VerifySignatures,
|
||||
errors::BlockProcessingError,
|
||||
process_block_header,
|
||||
process_operations::{
|
||||
altair, base, process_attester_slashings, process_deposits, process_exits,
|
||||
process_proposer_slashings,
|
||||
},
|
||||
process_sync_aggregate, VerifySignatures,
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use std::path::Path;
|
||||
use types::{
|
||||
Attestation, AttesterSlashing, BeaconBlock, BeaconState, ChainSpec, Deposit, EthSpec,
|
||||
ProposerSlashing, SignedVoluntaryExit,
|
||||
Attestation, AttesterSlashing, BeaconBlock, BeaconState, ChainSpec, Deposit, EthSpec, ForkName,
|
||||
ProposerSlashing, SignedVoluntaryExit, SyncAggregate,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
@@ -31,15 +35,21 @@ pub struct Operations<E: EthSpec, O: Operation<E>> {
|
||||
pub post: Option<BeaconState<E>>,
|
||||
}
|
||||
|
||||
pub trait Operation<E: EthSpec>: Decode + TypeName + Debug + Sync {
|
||||
pub trait Operation<E: EthSpec>: TypeName + Debug + Sync + Sized {
|
||||
fn handler_name() -> String {
|
||||
Self::name().to_lowercase()
|
||||
}
|
||||
|
||||
fn filename() -> String {
|
||||
format!("{}.ssz", Self::handler_name())
|
||||
format!("{}.ssz_snappy", Self::handler_name())
|
||||
}
|
||||
|
||||
fn is_enabled_for_fork(_fork_name: ForkName) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn decode(path: &Path, spec: &ChainSpec) -> Result<Self, Error>;
|
||||
|
||||
fn apply_to(
|
||||
&self,
|
||||
state: &mut BeaconState<E>,
|
||||
@@ -48,12 +58,23 @@ pub trait Operation<E: EthSpec>: Decode + TypeName + Debug + Sync {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Operation<E> for Attestation<E> {
|
||||
fn decode(path: &Path, _spec: &ChainSpec) -> Result<Self, Error> {
|
||||
ssz_decode_file(path)
|
||||
}
|
||||
|
||||
fn apply_to(
|
||||
&self,
|
||||
state: &mut BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
process_attestations(state, &[self.clone()], VerifySignatures::True, spec)
|
||||
match state {
|
||||
BeaconState::Base(_) => {
|
||||
base::process_attestations(state, &[self.clone()], VerifySignatures::True, spec)
|
||||
}
|
||||
BeaconState::Altair(_) => {
|
||||
altair::process_attestation(state, self, 0, VerifySignatures::True, spec)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +83,10 @@ impl<E: EthSpec> Operation<E> for AttesterSlashing<E> {
|
||||
"attester_slashing".into()
|
||||
}
|
||||
|
||||
fn decode(path: &Path, _spec: &ChainSpec) -> Result<Self, Error> {
|
||||
ssz_decode_file(path)
|
||||
}
|
||||
|
||||
fn apply_to(
|
||||
&self,
|
||||
state: &mut BeaconState<E>,
|
||||
@@ -72,6 +97,10 @@ impl<E: EthSpec> Operation<E> for AttesterSlashing<E> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Operation<E> for Deposit {
|
||||
fn decode(path: &Path, _spec: &ChainSpec) -> Result<Self, Error> {
|
||||
ssz_decode_file(path)
|
||||
}
|
||||
|
||||
fn apply_to(
|
||||
&self,
|
||||
state: &mut BeaconState<E>,
|
||||
@@ -86,6 +115,10 @@ impl<E: EthSpec> Operation<E> for ProposerSlashing {
|
||||
"proposer_slashing".into()
|
||||
}
|
||||
|
||||
fn decode(path: &Path, _spec: &ChainSpec) -> Result<Self, Error> {
|
||||
ssz_decode_file(path)
|
||||
}
|
||||
|
||||
fn apply_to(
|
||||
&self,
|
||||
state: &mut BeaconState<E>,
|
||||
@@ -100,6 +133,10 @@ impl<E: EthSpec> Operation<E> for SignedVoluntaryExit {
|
||||
"voluntary_exit".into()
|
||||
}
|
||||
|
||||
fn decode(path: &Path, _spec: &ChainSpec) -> Result<Self, Error> {
|
||||
ssz_decode_file(path)
|
||||
}
|
||||
|
||||
fn apply_to(
|
||||
&self,
|
||||
state: &mut BeaconState<E>,
|
||||
@@ -115,7 +152,11 @@ impl<E: EthSpec> Operation<E> for BeaconBlock<E> {
|
||||
}
|
||||
|
||||
fn filename() -> String {
|
||||
"block.ssz".into()
|
||||
"block.ssz_snappy".into()
|
||||
}
|
||||
|
||||
fn decode(path: &Path, spec: &ChainSpec) -> Result<Self, Error> {
|
||||
ssz_decode_file_with(path, |bytes| BeaconBlock::from_ssz_bytes(bytes, spec))
|
||||
}
|
||||
|
||||
fn apply_to(
|
||||
@@ -123,12 +164,41 @@ impl<E: EthSpec> Operation<E> for BeaconBlock<E> {
|
||||
state: &mut BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
Ok(process_block_header(state, self, spec)?)
|
||||
process_block_header(state, self.to_ref(), spec)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Operation<E> for SyncAggregate<E> {
|
||||
fn handler_name() -> String {
|
||||
"sync_aggregate".into()
|
||||
}
|
||||
|
||||
fn filename() -> String {
|
||||
"sync_aggregate.ssz_snappy".into()
|
||||
}
|
||||
|
||||
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
|
||||
fork_name != ForkName::Base
|
||||
}
|
||||
|
||||
fn decode(path: &Path, _spec: &ChainSpec) -> Result<Self, Error> {
|
||||
ssz_decode_file(path)
|
||||
}
|
||||
|
||||
fn apply_to(
|
||||
&self,
|
||||
state: &mut BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
let proposer_index = state.get_beacon_proposer_index(state.slot(), spec)? as u64;
|
||||
process_sync_aggregate(state, self, proposer_index, spec)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec, O: Operation<E>> LoadCase for Operations<E, O> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
let metadata_path = path.join("meta.yaml");
|
||||
let metadata: Metadata = if metadata_path.is_file() {
|
||||
yaml_decode_file(&metadata_path)?
|
||||
@@ -136,12 +206,12 @@ impl<E: EthSpec, O: Operation<E>> LoadCase for Operations<E, O> {
|
||||
Metadata::default()
|
||||
};
|
||||
|
||||
let pre = ssz_decode_file(&path.join("pre.ssz"))?;
|
||||
let pre = ssz_decode_state(&path.join("pre.ssz_snappy"), spec)?;
|
||||
|
||||
// Check BLS setting here before SSZ deserialization, as most types require signatures
|
||||
// to be valid.
|
||||
let (operation, bls_error) = if metadata.bls_setting.unwrap_or_default().check().is_ok() {
|
||||
match ssz_decode_file(&path.join(O::filename())) {
|
||||
match O::decode(&path.join(O::filename()), spec) {
|
||||
Ok(op) => (Some(op), None),
|
||||
Err(Error::InvalidBLSInput(error)) => (None, Some(error)),
|
||||
Err(e) => return Err(e),
|
||||
@@ -149,12 +219,12 @@ impl<E: EthSpec, O: Operation<E>> LoadCase for Operations<E, O> {
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let post_filename = path.join("post.ssz");
|
||||
let post_filename = path.join("post.ssz_snappy");
|
||||
let post = if post_filename.is_file() {
|
||||
if let Some(bls_error) = bls_error {
|
||||
panic!("input is unexpectedly invalid: {}", bls_error);
|
||||
}
|
||||
Some(ssz_decode_file(&post_filename)?)
|
||||
Some(ssz_decode_state(&post_filename, spec)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -176,8 +246,12 @@ impl<E: EthSpec, O: Operation<E>> Case for Operations<E, O> {
|
||||
.unwrap_or_else(String::new)
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
let spec = &E::default_spec();
|
||||
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
|
||||
O::is_enabled_for_fork(fork_name)
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
let mut state = self.pre.clone();
|
||||
let mut expected = self.post.clone();
|
||||
|
||||
|
||||
206
testing/ef_tests/src/cases/rewards.rs
Normal file
206
testing/ef_tests/src/cases/rewards.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_result_detailed;
|
||||
use crate::decode::{ssz_decode_file, ssz_decode_state, yaml_decode_file};
|
||||
use compare_fields_derive::CompareFields;
|
||||
use serde_derive::Deserialize;
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use state_processing::per_epoch_processing::validator_statuses::ValidatorStatuses;
|
||||
use state_processing::{
|
||||
per_epoch_processing::{
|
||||
altair::{self, rewards_and_penalties::get_flag_index_deltas},
|
||||
base::{self, rewards_and_penalties::AttestationDelta},
|
||||
Delta,
|
||||
},
|
||||
EpochProcessingError,
|
||||
};
|
||||
use std::path::{Path, PathBuf};
|
||||
use types::{
|
||||
consts::altair::{TIMELY_HEAD_FLAG_INDEX, TIMELY_SOURCE_FLAG_INDEX, TIMELY_TARGET_FLAG_INDEX},
|
||||
BeaconState, EthSpec, ForkName,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Decode, Encode, CompareFields)]
|
||||
pub struct Deltas {
|
||||
#[compare_fields(as_slice)]
|
||||
rewards: Vec<u64>,
|
||||
#[compare_fields(as_slice)]
|
||||
penalties: Vec<u64>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Decode, Encode, CompareFields)]
|
||||
pub struct AllDeltas {
|
||||
source_deltas: Deltas,
|
||||
target_deltas: Deltas,
|
||||
head_deltas: Deltas,
|
||||
inclusion_delay_deltas: Option<Deltas>,
|
||||
inactivity_penalty_deltas: Deltas,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub struct Metadata {
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RewardsTest<E: EthSpec> {
|
||||
pub path: PathBuf,
|
||||
pub metadata: Metadata,
|
||||
pub pre: BeaconState<E>,
|
||||
pub deltas: AllDeltas,
|
||||
}
|
||||
|
||||
/// Function that extracts a delta for a single component from an `AttestationDelta`.
|
||||
type Accessor = fn(&AttestationDelta) -> Δ
|
||||
|
||||
fn load_optional_deltas_file(path: &Path) -> Result<Option<Deltas>, Error> {
|
||||
let deltas = if path.is_file() {
|
||||
Some(ssz_decode_file(&path)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Ok(deltas)
|
||||
}
|
||||
|
||||
impl<E: EthSpec> LoadCase for RewardsTest<E> {
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
let metadata_path = path.join("meta.yaml");
|
||||
let metadata: Metadata = if metadata_path.is_file() {
|
||||
yaml_decode_file(&metadata_path)?
|
||||
} else {
|
||||
Metadata::default()
|
||||
};
|
||||
let pre = ssz_decode_state(&path.join("pre.ssz_snappy"), spec)?;
|
||||
let source_deltas = ssz_decode_file(&path.join("source_deltas.ssz_snappy"))?;
|
||||
let target_deltas = ssz_decode_file(&path.join("target_deltas.ssz_snappy"))?;
|
||||
let head_deltas = ssz_decode_file(&path.join("head_deltas.ssz_snappy"))?;
|
||||
let inclusion_delay_deltas =
|
||||
load_optional_deltas_file(&path.join("inclusion_delay_deltas.ssz_snappy"))?;
|
||||
let inactivity_penalty_deltas =
|
||||
ssz_decode_file(&path.join("inactivity_penalty_deltas.ssz_snappy"))?;
|
||||
|
||||
let deltas = AllDeltas {
|
||||
source_deltas,
|
||||
target_deltas,
|
||||
head_deltas,
|
||||
inclusion_delay_deltas,
|
||||
inactivity_penalty_deltas,
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
path: path.into(),
|
||||
metadata,
|
||||
pre,
|
||||
deltas,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Case for RewardsTest<E> {
|
||||
fn description(&self) -> String {
|
||||
self.metadata
|
||||
.description
|
||||
.clone()
|
||||
.unwrap_or_else(String::new)
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
let mut state = self.pre.clone();
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
|
||||
let deltas: Result<AllDeltas, EpochProcessingError> = (|| {
|
||||
// Processing requires the committee caches.
|
||||
state.build_all_committee_caches(spec)?;
|
||||
|
||||
if let BeaconState::Base(_) = state {
|
||||
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
|
||||
validator_statuses.process_attestations(&state)?;
|
||||
|
||||
let deltas = base::rewards_and_penalties::get_attestation_deltas(
|
||||
&state,
|
||||
&validator_statuses,
|
||||
spec,
|
||||
)?;
|
||||
|
||||
Ok(convert_all_base_deltas(&deltas))
|
||||
} else {
|
||||
let total_active_balance = state.get_total_active_balance(spec)?;
|
||||
|
||||
let source_deltas = compute_altair_flag_deltas(
|
||||
&state,
|
||||
TIMELY_SOURCE_FLAG_INDEX,
|
||||
total_active_balance,
|
||||
spec,
|
||||
)?;
|
||||
let target_deltas = compute_altair_flag_deltas(
|
||||
&state,
|
||||
TIMELY_TARGET_FLAG_INDEX,
|
||||
total_active_balance,
|
||||
spec,
|
||||
)?;
|
||||
let head_deltas = compute_altair_flag_deltas(
|
||||
&state,
|
||||
TIMELY_HEAD_FLAG_INDEX,
|
||||
total_active_balance,
|
||||
spec,
|
||||
)?;
|
||||
let inactivity_penalty_deltas = compute_altair_inactivity_deltas(&state, spec)?;
|
||||
Ok(AllDeltas {
|
||||
source_deltas,
|
||||
target_deltas,
|
||||
head_deltas,
|
||||
inclusion_delay_deltas: None,
|
||||
inactivity_penalty_deltas,
|
||||
})
|
||||
}
|
||||
})();
|
||||
|
||||
compare_result_detailed(&deltas, &Some(self.deltas.clone()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_all_base_deltas(ad: &[AttestationDelta]) -> AllDeltas {
|
||||
AllDeltas {
|
||||
source_deltas: convert_base_deltas(ad, |d| &d.source_delta),
|
||||
target_deltas: convert_base_deltas(ad, |d| &d.target_delta),
|
||||
head_deltas: convert_base_deltas(ad, |d| &d.head_delta),
|
||||
inclusion_delay_deltas: Some(convert_base_deltas(ad, |d| &d.inclusion_delay_delta)),
|
||||
inactivity_penalty_deltas: convert_base_deltas(ad, |d| &d.inactivity_penalty_delta),
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_base_deltas(attestation_deltas: &[AttestationDelta], accessor: Accessor) -> Deltas {
|
||||
let (rewards, penalties) = attestation_deltas
|
||||
.iter()
|
||||
.map(accessor)
|
||||
.map(|delta| (delta.rewards, delta.penalties))
|
||||
.unzip();
|
||||
Deltas { rewards, penalties }
|
||||
}
|
||||
|
||||
fn compute_altair_flag_deltas<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
flag_index: usize,
|
||||
total_active_balance: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Deltas, EpochProcessingError> {
|
||||
let mut deltas = vec![Delta::default(); state.validators().len()];
|
||||
get_flag_index_deltas(&mut deltas, state, flag_index, total_active_balance, spec)?;
|
||||
Ok(convert_altair_deltas(deltas))
|
||||
}
|
||||
|
||||
fn compute_altair_inactivity_deltas<E: EthSpec>(
|
||||
state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Deltas, EpochProcessingError> {
|
||||
let mut deltas = vec![Delta::default(); state.validators().len()];
|
||||
altair::rewards_and_penalties::get_inactivity_penalty_deltas(&mut deltas, state, spec)?;
|
||||
Ok(convert_altair_deltas(deltas))
|
||||
}
|
||||
|
||||
fn convert_altair_deltas(deltas: Vec<Delta>) -> Deltas {
|
||||
let (rewards, penalties) = deltas.into_iter().map(|d| (d.rewards, d.penalties)).unzip();
|
||||
Deltas { rewards, penalties }
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
use super::*;
|
||||
use crate::bls_setting::BlsSetting;
|
||||
use crate::case_result::compare_beacon_state_results_without_caches;
|
||||
use crate::decode::{ssz_decode_file, yaml_decode_file};
|
||||
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,
|
||||
};
|
||||
use types::{BeaconState, EthSpec, RelativeEpoch, SignedBeaconBlock};
|
||||
use types::{BeaconState, EthSpec, ForkName, RelativeEpoch, SignedBeaconBlock};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Metadata {
|
||||
@@ -25,18 +25,21 @@ pub struct SanityBlocks<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> LoadCase for SanityBlocks<E> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
let metadata: Metadata = yaml_decode_file(&path.join("meta.yaml"))?;
|
||||
let pre = ssz_decode_file(&path.join("pre.ssz"))?;
|
||||
let blocks: Vec<SignedBeaconBlock<E>> = (0..metadata.blocks_count)
|
||||
let pre = ssz_decode_state(&path.join("pre.ssz_snappy"), spec)?;
|
||||
let blocks = (0..metadata.blocks_count)
|
||||
.map(|i| {
|
||||
let filename = format!("blocks_{}.ssz", i);
|
||||
ssz_decode_file(&path.join(filename))
|
||||
let filename = format!("blocks_{}.ssz_snappy", i);
|
||||
ssz_decode_file_with(&path.join(filename), |bytes| {
|
||||
SignedBeaconBlock::from_ssz_bytes(bytes, spec)
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
let post_file = path.join("post.ssz");
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let post_file = path.join("post.ssz_snappy");
|
||||
let post = if post_file.is_file() {
|
||||
Some(ssz_decode_file(&post_file)?)
|
||||
Some(ssz_decode_state(&post_file, spec)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -58,12 +61,12 @@ impl<E: EthSpec> Case for SanityBlocks<E> {
|
||||
.unwrap_or_else(String::new)
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
self.metadata.bls_setting.unwrap_or_default().check()?;
|
||||
|
||||
let mut bulk_state = self.pre.clone();
|
||||
let mut expected = self.post.clone();
|
||||
let spec = &E::default_spec();
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
|
||||
// Processing requires the epoch cache.
|
||||
bulk_state.build_all_caches(spec).unwrap();
|
||||
@@ -76,8 +79,8 @@ impl<E: EthSpec> Case for SanityBlocks<E> {
|
||||
.blocks
|
||||
.iter()
|
||||
.try_for_each(|signed_block| {
|
||||
let block = &signed_block.message;
|
||||
while bulk_state.slot < block.slot {
|
||||
let block = signed_block.message();
|
||||
while bulk_state.slot() < block.slot() {
|
||||
per_slot_processing(&mut bulk_state, None, spec).unwrap();
|
||||
per_slot_processing(&mut indiv_state, None, spec).unwrap();
|
||||
}
|
||||
@@ -106,8 +109,8 @@ impl<E: EthSpec> Case for SanityBlocks<E> {
|
||||
spec,
|
||||
)?;
|
||||
|
||||
if block.state_root == bulk_state.canonical_root()
|
||||
&& block.state_root == indiv_state.canonical_root()
|
||||
if block.state_root() == bulk_state.canonical_root()
|
||||
&& block.state_root() == indiv_state.canonical_root()
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use super::*;
|
||||
use crate::bls_setting::BlsSetting;
|
||||
use crate::case_result::compare_beacon_state_results_without_caches;
|
||||
use crate::decode::{ssz_decode_file, yaml_decode_file};
|
||||
use crate::decode::{ssz_decode_state, yaml_decode_file};
|
||||
use serde_derive::Deserialize;
|
||||
use state_processing::per_slot_processing;
|
||||
use types::{BeaconState, EthSpec};
|
||||
use types::{BeaconState, EthSpec, ForkName};
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
pub struct Metadata {
|
||||
@@ -22,18 +22,19 @@ pub struct SanitySlots<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> LoadCase for SanitySlots<E> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
let metadata_path = path.join("meta.yaml");
|
||||
let metadata: Metadata = if metadata_path.is_file() {
|
||||
yaml_decode_file(&metadata_path)?
|
||||
} else {
|
||||
Metadata::default()
|
||||
};
|
||||
let pre = ssz_decode_file(&path.join("pre.ssz"))?;
|
||||
let pre = ssz_decode_state(&path.join("pre.ssz_snappy"), spec)?;
|
||||
let slots: u64 = yaml_decode_file(&path.join("slots.yaml"))?;
|
||||
let post_file = path.join("post.ssz");
|
||||
let post_file = path.join("post.ssz_snappy");
|
||||
let post = if post_file.is_file() {
|
||||
Some(ssz_decode_file(&post_file)?)
|
||||
Some(ssz_decode_state(&post_file, spec)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@@ -55,12 +56,12 @@ impl<E: EthSpec> Case for SanitySlots<E> {
|
||||
.unwrap_or_else(String::new)
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
self.metadata.bls_setting.unwrap_or_default().check()?;
|
||||
|
||||
let mut state = self.pre.clone();
|
||||
let mut expected = self.post.clone();
|
||||
let spec = &E::default_spec();
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
|
||||
// Processing requires the epoch cache.
|
||||
state.build_all_caches(spec).unwrap();
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::decode::yaml_decode_file;
|
||||
use serde_derive::Deserialize;
|
||||
use std::marker::PhantomData;
|
||||
use swap_or_not_shuffle::{compute_shuffled_index, shuffle_list};
|
||||
use types::ForkName;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Shuffling<T> {
|
||||
@@ -15,13 +16,13 @@ pub struct Shuffling<T> {
|
||||
}
|
||||
|
||||
impl<T: EthSpec> LoadCase for Shuffling<T> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
|
||||
yaml_decode_file(&path.join("mapping.yaml"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EthSpec> Case for Shuffling<T> {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
if self.count == 0 {
|
||||
compare_result::<_, Error>(&Ok(vec![]), &Some(self.mapping.clone()))?;
|
||||
} else {
|
||||
|
||||
@@ -3,15 +3,14 @@
|
||||
use super::*;
|
||||
use crate::cases::common::{SszStaticType, TestU128, TestU256};
|
||||
use crate::cases::ssz_static::{check_serialization, check_tree_hash};
|
||||
use crate::decode::yaml_decode_file;
|
||||
use crate::decode::{snappy_decode_file, yaml_decode_file};
|
||||
use serde::{de::Error as SerdeError, Deserializer};
|
||||
use serde_derive::Deserialize;
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tree_hash_derive::TreeHash;
|
||||
use types::typenum::*;
|
||||
use types::{BitList, BitVector, FixedVector, VariableList};
|
||||
use types::{BitList, BitVector, FixedVector, ForkName, VariableList};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct Metadata {
|
||||
@@ -27,7 +26,7 @@ pub struct SszGeneric {
|
||||
}
|
||||
|
||||
impl LoadCase for SszGeneric {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
|
||||
let components = path
|
||||
.components()
|
||||
.map(|c| c.as_os_str().to_string_lossy().into_owned())
|
||||
@@ -119,7 +118,7 @@ macro_rules! type_dispatch {
|
||||
}
|
||||
|
||||
impl Case for SszGeneric {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
let parts = self.case_name.split('_').collect::<Vec<_>>();
|
||||
|
||||
match self.handler_name.as_str() {
|
||||
@@ -195,7 +194,7 @@ impl Case for SszGeneric {
|
||||
}
|
||||
}
|
||||
|
||||
fn ssz_generic_test<T: SszStaticType>(path: &Path) -> Result<(), Error> {
|
||||
fn ssz_generic_test<T: SszStaticType + ssz::Decode>(path: &Path) -> Result<(), Error> {
|
||||
let meta_path = path.join("meta.yaml");
|
||||
let meta: Option<Metadata> = if meta_path.is_file() {
|
||||
Some(yaml_decode_file(&meta_path)?)
|
||||
@@ -203,7 +202,8 @@ fn ssz_generic_test<T: SszStaticType>(path: &Path) -> Result<(), Error> {
|
||||
None
|
||||
};
|
||||
|
||||
let serialized = fs::read(&path.join("serialized.ssz")).expect("serialized.ssz exists");
|
||||
let serialized = snappy_decode_file(&path.join("serialized.ssz_snappy"))
|
||||
.expect("serialized.ssz_snappy exists");
|
||||
|
||||
let value_path = path.join("value.yaml");
|
||||
let value: Option<T> = if value_path.is_file() {
|
||||
@@ -215,7 +215,7 @@ fn ssz_generic_test<T: SszStaticType>(path: &Path) -> Result<(), Error> {
|
||||
// Valid
|
||||
// TODO: signing root (annoying because of traits)
|
||||
if let Some(value) = value {
|
||||
check_serialization(&value, &serialized)?;
|
||||
check_serialization(&value, &serialized, T::from_ssz_bytes)?;
|
||||
|
||||
if let Some(ref meta) = meta {
|
||||
check_tree_hash(&meta.root, value.tree_hash_root().as_bytes())?;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_result;
|
||||
use crate::cases::common::SszStaticType;
|
||||
use crate::decode::yaml_decode_file;
|
||||
use cached_tree_hash::{CacheArena, CachedTreeHash};
|
||||
use crate::decode::{snappy_decode_file, yaml_decode_file};
|
||||
use serde_derive::Deserialize;
|
||||
use std::fs;
|
||||
use std::marker::PhantomData;
|
||||
use types::Hash256;
|
||||
use ssz::Decode;
|
||||
use tree_hash::TreeHash;
|
||||
use types::{BeaconBlock, BeaconState, ForkName, Hash256, SignedBeaconBlock};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
struct SszStaticRoots {
|
||||
@@ -14,6 +13,7 @@ struct SszStaticRoots {
|
||||
signing_root: Option<String>,
|
||||
}
|
||||
|
||||
/// Runner for types that implement `ssz::Decode`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SszStatic<T> {
|
||||
roots: SszStaticRoots,
|
||||
@@ -21,24 +21,33 @@ pub struct SszStatic<T> {
|
||||
value: T,
|
||||
}
|
||||
|
||||
/// Runner for `BeaconState` (with tree hash cache).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SszStaticTHC<T, C> {
|
||||
pub struct SszStaticTHC<T> {
|
||||
roots: SszStaticRoots,
|
||||
serialized: Vec<u8>,
|
||||
value: T,
|
||||
}
|
||||
|
||||
/// Runner for types that require a `ChainSpec` to be decoded (`BeaconBlock`, etc).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SszStaticWithSpec<T> {
|
||||
roots: SszStaticRoots,
|
||||
serialized: Vec<u8>,
|
||||
value: T,
|
||||
_phantom: PhantomData<C>,
|
||||
}
|
||||
|
||||
fn load_from_dir<T: SszStaticType>(path: &Path) -> Result<(SszStaticRoots, Vec<u8>, T), Error> {
|
||||
let roots = yaml_decode_file(&path.join("roots.yaml"))?;
|
||||
let serialized = fs::read(&path.join("serialized.ssz")).expect("serialized.ssz exists");
|
||||
let serialized = snappy_decode_file(&path.join("serialized.ssz_snappy"))
|
||||
.expect("serialized.ssz_snappy exists");
|
||||
let value = yaml_decode_file(&path.join("value.yaml"))?;
|
||||
|
||||
Ok((roots, serialized, value))
|
||||
}
|
||||
|
||||
impl<T: SszStaticType> LoadCase for SszStatic<T> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
|
||||
load_from_dir(path).map(|(roots, serialized, value)| Self {
|
||||
roots,
|
||||
serialized,
|
||||
@@ -47,25 +56,38 @@ impl<T: SszStaticType> LoadCase for SszStatic<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SszStaticType + CachedTreeHash<C>, C: Debug + Sync> LoadCase for SszStaticTHC<T, C> {
|
||||
fn load_from_dir(path: &Path) -> Result<Self, Error> {
|
||||
impl<T: SszStaticType> LoadCase for SszStaticTHC<T> {
|
||||
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
|
||||
load_from_dir(path).map(|(roots, serialized, value)| Self {
|
||||
roots,
|
||||
serialized,
|
||||
value,
|
||||
_phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_serialization<T: SszStaticType>(value: &T, serialized: &[u8]) -> Result<(), Error> {
|
||||
impl<T: SszStaticType> LoadCase for SszStaticWithSpec<T> {
|
||||
fn load_from_dir(path: &Path, _fork_name: ForkName) -> Result<Self, Error> {
|
||||
load_from_dir(path).map(|(roots, serialized, value)| Self {
|
||||
roots,
|
||||
serialized,
|
||||
value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_serialization<T: SszStaticType>(
|
||||
value: &T,
|
||||
serialized: &[u8],
|
||||
deserializer: impl FnOnce(&[u8]) -> Result<T, ssz::DecodeError>,
|
||||
) -> Result<(), Error> {
|
||||
// Check serialization
|
||||
let serialized_result = value.as_ssz_bytes();
|
||||
compare_result::<usize, Error>(&Ok(value.ssz_bytes_len()), &Some(serialized.len()))?;
|
||||
compare_result::<Vec<u8>, Error>(&Ok(serialized_result), &Some(serialized.to_vec()))?;
|
||||
|
||||
// Check deserialization
|
||||
let deserialized_result = T::from_ssz_bytes(serialized);
|
||||
let deserialized_result = deserializer(serialized);
|
||||
compare_result(&deserialized_result, &Some(value.clone()))?;
|
||||
|
||||
Ok(())
|
||||
@@ -79,27 +101,49 @@ pub fn check_tree_hash(expected_str: &str, actual_root: &[u8]) -> Result<(), Err
|
||||
compare_result::<Hash256, Error>(&Ok(tree_hash_root), &Some(expected_root))
|
||||
}
|
||||
|
||||
impl<T: SszStaticType> Case for SszStatic<T> {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
check_serialization(&self.value, &self.serialized)?;
|
||||
impl<T: SszStaticType + Decode> Case for SszStatic<T> {
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
check_serialization(&self.value, &self.serialized, T::from_ssz_bytes)?;
|
||||
check_tree_hash(&self.roots.root, self.value.tree_hash_root().as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SszStaticType + CachedTreeHash<C>, C: Debug + Sync> Case for SszStaticTHC<T, C> {
|
||||
fn result(&self, _case_index: usize) -> Result<(), Error> {
|
||||
check_serialization(&self.value, &self.serialized)?;
|
||||
impl<E: EthSpec> Case for SszStaticTHC<BeaconState<E>> {
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
check_serialization(&self.value, &self.serialized, |bytes| {
|
||||
BeaconState::from_ssz_bytes(bytes, spec)
|
||||
})?;
|
||||
check_tree_hash(&self.roots.root, self.value.tree_hash_root().as_bytes())?;
|
||||
|
||||
let arena = &mut CacheArena::default();
|
||||
let mut cache = self.value.new_tree_hash_cache(arena);
|
||||
let cached_tree_hash_root = self
|
||||
.value
|
||||
.recalculate_tree_hash_root(arena, &mut cache)
|
||||
.unwrap();
|
||||
let mut state = self.value.clone();
|
||||
state.initialize_tree_hash_cache();
|
||||
let cached_tree_hash_root = state.update_tree_hash_cache().unwrap();
|
||||
check_tree_hash(&self.roots.root, cached_tree_hash_root.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Case for SszStaticWithSpec<BeaconBlock<E>> {
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
check_serialization(&self.value, &self.serialized, |bytes| {
|
||||
BeaconBlock::from_ssz_bytes(bytes, spec)
|
||||
})?;
|
||||
check_tree_hash(&self.roots.root, self.value.tree_hash_root().as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Case for SszStaticWithSpec<SignedBeaconBlock<E>> {
|
||||
fn result(&self, _case_index: usize, fork_name: ForkName) -> Result<(), Error> {
|
||||
let spec = &testing_spec::<E>(fork_name);
|
||||
check_serialization(&self.value, &self.serialized, |bytes| {
|
||||
SignedBeaconBlock::from_ssz_bytes(bytes, spec)
|
||||
})?;
|
||||
check_tree_hash(&self.roots.root, self.value.tree_hash_root().as_bytes())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
114
testing/ef_tests/src/cases/transition.rs
Normal file
114
testing/ef_tests/src/cases/transition.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
use super::*;
|
||||
use crate::case_result::compare_beacon_state_results_without_caches;
|
||||
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,
|
||||
};
|
||||
use std::str::FromStr;
|
||||
use types::{BeaconState, Epoch, ForkName, SignedBeaconBlock};
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct Metadata {
|
||||
pub post_fork: String,
|
||||
pub fork_epoch: Epoch,
|
||||
pub fork_block: Option<usize>,
|
||||
pub blocks_count: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TransitionTest<E: EthSpec> {
|
||||
pub metadata: Metadata,
|
||||
pub pre: BeaconState<E>,
|
||||
pub blocks: Vec<SignedBeaconBlock<E>>,
|
||||
pub post: BeaconState<E>,
|
||||
pub spec: ChainSpec,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> LoadCase for TransitionTest<E> {
|
||||
fn load_from_dir(path: &Path, fork_name: ForkName) -> Result<Self, Error> {
|
||||
let metadata: Metadata = yaml_decode_file(&path.join("meta.yaml"))?;
|
||||
assert_eq!(ForkName::from_str(&metadata.post_fork).unwrap(), fork_name);
|
||||
|
||||
// Make spec with appropriate fork block.
|
||||
let mut spec = E::default_spec();
|
||||
match fork_name {
|
||||
ForkName::Base => panic!("cannot fork to base/phase0"),
|
||||
ForkName::Altair => {
|
||||
spec.altair_fork_epoch = Some(metadata.fork_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
// Load blocks
|
||||
let blocks = (0..metadata.blocks_count)
|
||||
.map(|i| {
|
||||
let filename = format!("blocks_{}.ssz_snappy", i);
|
||||
ssz_decode_file_with(&path.join(filename), |bytes| {
|
||||
SignedBeaconBlock::from_ssz_bytes(bytes, &spec)
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
// Decode pre-state.
|
||||
let pre = ssz_decode_state(&path.join("pre.ssz_snappy"), &spec)?;
|
||||
|
||||
// Decode post-state.
|
||||
let post = ssz_decode_state(&path.join("post.ssz_snappy"), &spec)?;
|
||||
|
||||
Ok(Self {
|
||||
metadata,
|
||||
pre,
|
||||
blocks,
|
||||
post,
|
||||
spec,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Case for TransitionTest<E> {
|
||||
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
|
||||
// Upgrades exist targeting all forks except phase0/base.
|
||||
// Transition tests also need BLS.
|
||||
cfg!(not(feature = "fake_crypto")) && fork_name != ForkName::Base
|
||||
}
|
||||
|
||||
fn result(&self, _case_index: usize, _fork_name: ForkName) -> Result<(), Error> {
|
||||
let mut state = self.pre.clone();
|
||||
let mut expected = Some(self.post.clone());
|
||||
let spec = &self.spec;
|
||||
|
||||
let mut result: Result<_, String> = self
|
||||
.blocks
|
||||
.iter()
|
||||
.try_for_each(|block| {
|
||||
// Advance to block slot.
|
||||
complete_state_advance(&mut state, None, block.slot(), spec)
|
||||
.map_err(|e| format!("Failed to advance: {:?}", e))?;
|
||||
|
||||
// Apply block.
|
||||
per_block_processing(
|
||||
&mut state,
|
||||
block,
|
||||
None,
|
||||
BlockSignatureStrategy::VerifyBulk,
|
||||
spec,
|
||||
)
|
||||
.map_err(|e| format!("Block processing failed: {:?}", e))?;
|
||||
|
||||
let state_root = state.update_tree_hash_cache().unwrap();
|
||||
if block.state_root() != state_root {
|
||||
return Err(format!(
|
||||
"Mismatched state root at slot {}, got: {:?}, expected: {:?}",
|
||||
block.slot(),
|
||||
state_root,
|
||||
block.state_root()
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map(move |()| state);
|
||||
|
||||
compare_beacon_state_results_without_caches(&mut result, &mut expected)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user