mirror of
https://github.com/sigp/lighthouse.git
synced 2026-06-29 10:54:24 +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:
1
testing/ef_tests/.gitignore
vendored
1
testing/ef_tests/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
/eth2.0-spec-tests
|
||||
.accessed_file_log.txt
|
||||
|
||||
@@ -13,6 +13,8 @@ fake_crypto = ["bls/fake_crypto"]
|
||||
[dependencies]
|
||||
bls = { path = "../../crypto/bls", default-features = false }
|
||||
compare_fields = { path = "../../common/compare_fields" }
|
||||
compare_fields_derive = { path = "../../common/compare_fields_derive" }
|
||||
derivative = "2.1.1"
|
||||
ethereum-types = "0.9.2"
|
||||
hex = "0.4.2"
|
||||
rayon = "1.4.1"
|
||||
@@ -28,3 +30,6 @@ cached_tree_hash = { path = "../../consensus/cached_tree_hash" }
|
||||
state_processing = { path = "../../consensus/state_processing" }
|
||||
swap_or_not_shuffle = { path = "../../consensus/swap_or_not_shuffle" }
|
||||
types = { path = "../../consensus/types" }
|
||||
snap = "1.0.1"
|
||||
parking_lot = "0.11.0"
|
||||
fs2 = "0.4.3"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
TESTS_TAG := v1.0.1
|
||||
TESTS_TAG := v1.1.0-alpha.7
|
||||
TESTS = general minimal mainnet
|
||||
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
||||
|
||||
|
||||
108
testing/ef_tests/check_all_files_accessed.py
Executable file
108
testing/ef_tests/check_all_files_accessed.py
Executable file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# The purpose of this script is to compare a list of file names that were accessed during testing
|
||||
# against all the file names in the eth2.0-spec-tests repository. It then checks to see which files
|
||||
# were not accessed and returns an error if any non-intentionally-ignored files are detected.
|
||||
#
|
||||
# The ultimate goal is to detect any accidentally-missed spec tests.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# First argument should the path to a file which contains a list of accessed file names.
|
||||
accessed_files_filename = sys.argv[1]
|
||||
|
||||
# Second argument should be the path to the eth2.0-spec-tests directory.
|
||||
tests_dir_filename = sys.argv[2]
|
||||
|
||||
# If any of the file names found in the eth2.0-spec-tests directory *starts with* one of the
|
||||
# following strings, we will assume they are to be ignored (i.e., we are purposefully *not* running
|
||||
# the spec tests).
|
||||
excluded_paths = [
|
||||
# Configs from future phases
|
||||
"tests/mainnet/config/custody_game.yaml",
|
||||
"tests/mainnet/config/sharding.yaml",
|
||||
"tests/mainnet/config/merge.yaml",
|
||||
"tests/minimal/config/custody_game.yaml",
|
||||
"tests/minimal/config/sharding.yaml",
|
||||
"tests/minimal/config/merge.yaml",
|
||||
# Merge tests
|
||||
"tests/minimal/merge",
|
||||
"tests/mainnet/merge",
|
||||
# Eth1Block
|
||||
#
|
||||
# Intentionally omitted, as per https://github.com/sigp/lighthouse/issues/1835
|
||||
"tests/minimal/phase0/ssz_static/Eth1Block/",
|
||||
"tests/mainnet/phase0/ssz_static/Eth1Block/",
|
||||
"tests/minimal/altair/ssz_static/Eth1Block/",
|
||||
"tests/mainnet/altair/ssz_static/Eth1Block/",
|
||||
# LightClientStore
|
||||
"tests/minimal/altair/ssz_static/LightClientStore",
|
||||
"tests/mainnet/altair/ssz_static/LightClientStore",
|
||||
# LightClientUpdate
|
||||
"tests/minimal/altair/ssz_static/LightClientUpdate",
|
||||
"tests/mainnet/altair/ssz_static/LightClientUpdate",
|
||||
# LightClientSnapshot
|
||||
"tests/minimal/altair/ssz_static/LightClientSnapshot",
|
||||
"tests/mainnet/altair/ssz_static/LightClientSnapshot",
|
||||
# ContributionAndProof
|
||||
"tests/minimal/altair/ssz_static/ContributionAndProof",
|
||||
"tests/mainnet/altair/ssz_static/ContributionAndProof",
|
||||
# SignedContributionAndProof
|
||||
"tests/minimal/altair/ssz_static/SignedContributionAndProof",
|
||||
"tests/mainnet/altair/ssz_static/SignedContributionAndProof",
|
||||
# SyncCommitteeContribution
|
||||
"tests/minimal/altair/ssz_static/SyncCommitteeContribution",
|
||||
"tests/mainnet/altair/ssz_static/SyncCommitteeContribution",
|
||||
# SyncCommitteeMessage
|
||||
"tests/minimal/altair/ssz_static/SyncCommitteeMessage",
|
||||
"tests/mainnet/altair/ssz_static/SyncCommitteeMessage",
|
||||
# SyncCommitteeSigningData
|
||||
"tests/minimal/altair/ssz_static/SyncCommitteeSigningData",
|
||||
"tests/mainnet/altair/ssz_static/SyncCommitteeSigningData",
|
||||
# SyncAggregatorSelectionData
|
||||
"tests/minimal/altair/ssz_static/SyncAggregatorSelectionData",
|
||||
"tests/mainnet/altair/ssz_static/SyncAggregatorSelectionData",
|
||||
# Fork choice
|
||||
"tests/mainnet/phase0/fork_choice",
|
||||
"tests/minimal/phase0/fork_choice",
|
||||
"tests/mainnet/altair/fork_choice",
|
||||
"tests/minimal/altair/fork_choice",
|
||||
]
|
||||
|
||||
def normalize_path(path):
|
||||
return path.split("eth2.0-spec-tests/", )[1]
|
||||
|
||||
# Determine the list of filenames which were accessed during tests.
|
||||
passed = set()
|
||||
for line in open(accessed_files_filename, 'r').readlines():
|
||||
file = normalize_path(line.strip().strip('"'))
|
||||
passed.add(file)
|
||||
|
||||
missed = set()
|
||||
accessed_files = 0
|
||||
excluded_files = 0
|
||||
|
||||
# Iterate all files in the tests directory, ensure that all files were either accessed
|
||||
# or intentionally missed.
|
||||
for root, dirs, files in os.walk(tests_dir_filename):
|
||||
for name in files:
|
||||
name = normalize_path(os.path.join(root, name))
|
||||
if name not in passed:
|
||||
excluded = False
|
||||
for excluded_path in excluded_paths:
|
||||
if name.startswith(excluded_path):
|
||||
excluded = True
|
||||
break
|
||||
if excluded:
|
||||
excluded_files += 1
|
||||
else:
|
||||
print(name)
|
||||
missed.add(name)
|
||||
else:
|
||||
accessed_files += 1
|
||||
|
||||
# Exit with an error if there were any files missed.
|
||||
assert len(missed) == 0, "{} missed files".format(len(missed))
|
||||
|
||||
print("Accessed {} files ({} intentionally excluded)".format(accessed_files, excluded_files))
|
||||
@@ -37,8 +37,8 @@ pub fn compare_beacon_state_results_without_caches<T: EthSpec, E: Debug>(
|
||||
expected: &mut Option<BeaconState<T>>,
|
||||
) -> Result<(), Error> {
|
||||
if let (Ok(ref mut result), Some(ref mut expected)) = (result.as_mut(), expected.as_mut()) {
|
||||
result.drop_all_caches();
|
||||
expected.drop_all_caches();
|
||||
result.drop_all_caches().unwrap();
|
||||
expected.drop_all_caches().unwrap();
|
||||
}
|
||||
|
||||
compare_result_detailed(&result, &expected)
|
||||
@@ -94,7 +94,7 @@ where
|
||||
(Err(_), None) => Ok(()),
|
||||
// Fail: The test failed when it should have produced a result (fail).
|
||||
(Err(e), Some(expected)) => Err(Error::NotEqual(format!(
|
||||
"Got {:?} | Expected {:?}",
|
||||
"Got {:?} | Expected {}",
|
||||
e,
|
||||
fmt_val(expected)
|
||||
))),
|
||||
@@ -106,7 +106,7 @@ where
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::NotEqual(format!(
|
||||
"Got {:?} | Expected {:?}",
|
||||
"Got {} | Expected {}",
|
||||
fmt_val(result),
|
||||
fmt_val(expected)
|
||||
)))
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::*;
|
||||
use rayon::prelude::*;
|
||||
use std::fmt::Debug;
|
||||
use std::path::{Path, PathBuf};
|
||||
use types::ForkName;
|
||||
|
||||
mod bls_aggregate_sigs;
|
||||
mod bls_aggregate_verify;
|
||||
@@ -10,14 +11,17 @@ mod bls_sign_msg;
|
||||
mod bls_verify_msg;
|
||||
mod common;
|
||||
mod epoch_processing;
|
||||
mod fork;
|
||||
mod genesis_initialization;
|
||||
mod genesis_validity;
|
||||
mod operations;
|
||||
mod rewards;
|
||||
mod sanity_blocks;
|
||||
mod sanity_slots;
|
||||
mod shuffling;
|
||||
mod ssz_generic;
|
||||
mod ssz_static;
|
||||
mod transition;
|
||||
|
||||
pub use bls_aggregate_sigs::*;
|
||||
pub use bls_aggregate_verify::*;
|
||||
@@ -26,18 +30,21 @@ pub use bls_sign_msg::*;
|
||||
pub use bls_verify_msg::*;
|
||||
pub use common::SszStaticType;
|
||||
pub use epoch_processing::*;
|
||||
pub use fork::ForkTest;
|
||||
pub use genesis_initialization::*;
|
||||
pub use genesis_validity::*;
|
||||
pub use operations::*;
|
||||
pub use rewards::RewardsTest;
|
||||
pub use sanity_blocks::*;
|
||||
pub use sanity_slots::*;
|
||||
pub use shuffling::*;
|
||||
pub use ssz_generic::*;
|
||||
pub use ssz_static::*;
|
||||
pub use transition::TransitionTest;
|
||||
|
||||
pub trait LoadCase: Sized {
|
||||
/// Load the test case from a test case directory.
|
||||
fn load_from_dir(_path: &Path) -> Result<Self, Error>;
|
||||
fn load_from_dir(_path: &Path, _fork_name: ForkName) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
pub trait Case: Debug + Sync {
|
||||
@@ -48,11 +55,18 @@ pub trait Case: Debug + Sync {
|
||||
"no description".to_string()
|
||||
}
|
||||
|
||||
/// Whether or not this test exists for the given `fork_name`.
|
||||
///
|
||||
/// Returns `true` by default.
|
||||
fn is_enabled_for_fork(_fork_name: ForkName) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Execute a test and return the result.
|
||||
///
|
||||
/// `case_index` reports the index of the case in the set of test cases. It is not strictly
|
||||
/// necessary, but it's useful when troubleshooting specific failing tests.
|
||||
fn result(&self, case_index: usize) -> Result<(), Error>;
|
||||
fn result(&self, case_index: usize, fork_name: ForkName) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -61,11 +75,11 @@ pub struct Cases<T> {
|
||||
}
|
||||
|
||||
impl<T: Case> Cases<T> {
|
||||
pub fn test_results(&self) -> Vec<CaseResult> {
|
||||
pub fn test_results(&self, fork_name: ForkName) -> Vec<CaseResult> {
|
||||
self.test_cases
|
||||
.into_par_iter()
|
||||
.enumerate()
|
||||
.map(|(i, (ref path, ref tc))| CaseResult::new(i, path, tc, tc.result(i)))
|
||||
.map(|(i, (ref path, ref tc))| CaseResult::new(i, path, tc, tc.result(i, fork_name)))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,42 @@
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use fs2::FileExt;
|
||||
use snap::raw::Decoder;
|
||||
use std::fs::{self};
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use types::{BeaconState, EthSpec};
|
||||
|
||||
/// See `log_file_access` for details.
|
||||
const ACCESSED_FILE_LOG_FILENAME: &str = ".accessed_file_log.txt";
|
||||
|
||||
/// Writes `path` to a file that contains a log of all files accessed during testing.
|
||||
///
|
||||
/// That log file might later be used to ensure that all spec tests were accessed and none were
|
||||
/// accidentally missed.
|
||||
pub fn log_file_access<P: AsRef<Path>>(file_accessed: P) {
|
||||
let passed_test_list_path =
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(ACCESSED_FILE_LOG_FILENAME);
|
||||
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(passed_test_list_path)
|
||||
.expect("should open file");
|
||||
|
||||
file.lock_exclusive().expect("unable to lock file");
|
||||
|
||||
writeln!(&mut file, "{:?}", file_accessed.as_ref()).expect("should write to file");
|
||||
|
||||
file.unlock().expect("unable to unlock file");
|
||||
}
|
||||
|
||||
pub fn yaml_decode<T: serde::de::DeserializeOwned>(string: &str) -> Result<T, Error> {
|
||||
serde_yaml::from_str(string).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))
|
||||
}
|
||||
|
||||
pub fn yaml_decode_file<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T, Error> {
|
||||
log_file_access(path);
|
||||
fs::read_to_string(path)
|
||||
.map_err(|e| {
|
||||
Error::FailedToParseTest(format!("Unable to load {}: {:?}", path.display(), e))
|
||||
@@ -14,26 +44,56 @@ pub fn yaml_decode_file<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T
|
||||
.and_then(|s| yaml_decode(&s))
|
||||
}
|
||||
|
||||
pub fn ssz_decode_file<T: ssz::Decode>(path: &Path) -> Result<T, Error> {
|
||||
fs::read(path)
|
||||
.map_err(|e| {
|
||||
Error::FailedToParseTest(format!("Unable to load {}: {:?}", path.display(), e))
|
||||
})
|
||||
.and_then(|s| {
|
||||
T::from_ssz_bytes(&s).map_err(|e| {
|
||||
match e {
|
||||
// NOTE: this is a bit hacky, but seemingly better than the alternatives
|
||||
ssz::DecodeError::BytesInvalid(message)
|
||||
if message.contains("Blst") || message.contains("Milagro") =>
|
||||
{
|
||||
Error::InvalidBLSInput(message)
|
||||
}
|
||||
e => Error::FailedToParseTest(format!(
|
||||
"Unable to parse SSZ at {}: {:?}",
|
||||
path.display(),
|
||||
e
|
||||
)),
|
||||
}
|
||||
})
|
||||
})
|
||||
/// Decode a Snappy encoded file.
|
||||
///
|
||||
/// Files in the EF tests are unframed, so we need to use `snap::raw::Decoder`.
|
||||
pub fn snappy_decode_file(path: &Path) -> Result<Vec<u8>, Error> {
|
||||
log_file_access(path);
|
||||
let bytes = fs::read(path).map_err(|e| {
|
||||
Error::FailedToParseTest(format!("Unable to load {}: {:?}", path.display(), e))
|
||||
})?;
|
||||
let mut decoder = Decoder::new();
|
||||
decoder.decompress_vec(&bytes).map_err(|e| {
|
||||
Error::FailedToParseTest(format!(
|
||||
"Error decoding snappy encoding for {}: {:?}",
|
||||
path.display(),
|
||||
e
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ssz_decode_file_with<T, F>(path: &Path, f: F) -> Result<T, Error>
|
||||
where
|
||||
F: FnOnce(&[u8]) -> Result<T, ssz::DecodeError>,
|
||||
{
|
||||
log_file_access(path);
|
||||
let bytes = snappy_decode_file(path)?;
|
||||
f(&bytes).map_err(|e| {
|
||||
match e {
|
||||
// NOTE: this is a bit hacky, but seemingly better than the alternatives
|
||||
ssz::DecodeError::BytesInvalid(message)
|
||||
if message.contains("Blst") || message.contains("Milagro") =>
|
||||
{
|
||||
Error::InvalidBLSInput(message)
|
||||
}
|
||||
e => Error::FailedToParseTest(format!(
|
||||
"Unable to parse SSZ at {}: {:?}",
|
||||
path.display(),
|
||||
e
|
||||
)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ssz_decode_file<T: ssz::Decode>(path: &Path) -> Result<T, Error> {
|
||||
log_file_access(path);
|
||||
ssz_decode_file_with(path, T::from_ssz_bytes)
|
||||
}
|
||||
|
||||
pub fn ssz_decode_state<E: EthSpec>(
|
||||
path: &Path,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconState<E>, Error> {
|
||||
log_file_access(path);
|
||||
ssz_decode_file_with(path, |bytes| BeaconState::from_ssz_bytes(bytes, spec))
|
||||
}
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use crate::cases::{self, Case, Cases, EpochTransition, LoadCase, Operation};
|
||||
use crate::type_name;
|
||||
use crate::type_name::TypeName;
|
||||
use cached_tree_hash::CachedTreeHash;
|
||||
use std::fmt::Debug;
|
||||
use derivative::Derivative;
|
||||
use std::fs;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::PathBuf;
|
||||
use types::EthSpec;
|
||||
use types::{BeaconState, EthSpec, ForkName};
|
||||
|
||||
pub trait Handler {
|
||||
type Case: Case + LoadCase;
|
||||
@@ -15,22 +14,35 @@ pub trait Handler {
|
||||
"general"
|
||||
}
|
||||
|
||||
fn fork_name() -> &'static str {
|
||||
"phase0"
|
||||
}
|
||||
|
||||
fn runner_name() -> &'static str;
|
||||
|
||||
fn handler_name() -> String;
|
||||
fn handler_name(&self) -> String;
|
||||
|
||||
fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool {
|
||||
Self::Case::is_enabled_for_fork(fork_name)
|
||||
}
|
||||
|
||||
fn run(&self) {
|
||||
for fork_name in ForkName::list_all() {
|
||||
if self.is_enabled_for_fork(fork_name) {
|
||||
self.run_for_fork(fork_name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_for_fork(&self, fork_name: ForkName) {
|
||||
let fork_name_str = match fork_name {
|
||||
ForkName::Base => "phase0",
|
||||
ForkName::Altair => "altair",
|
||||
};
|
||||
|
||||
fn run() {
|
||||
let handler_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("eth2.0-spec-tests")
|
||||
.join("tests")
|
||||
.join(Self::config_name())
|
||||
.join(Self::fork_name())
|
||||
.join(fork_name_str)
|
||||
.join(Self::runner_name())
|
||||
.join(Self::handler_name());
|
||||
.join(self.handler_name());
|
||||
|
||||
// Iterate through test suites
|
||||
let test_cases = fs::read_dir(&handler_path)
|
||||
@@ -44,30 +56,41 @@ pub trait Handler {
|
||||
.flat_map(Result::ok)
|
||||
.map(|test_case_dir| {
|
||||
let path = test_case_dir.path();
|
||||
let case = Self::Case::load_from_dir(&path).expect("test should load");
|
||||
let case = Self::Case::load_from_dir(&path, fork_name).expect("test should load");
|
||||
(path, case)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let results = Cases { test_cases }.test_results();
|
||||
let results = Cases { test_cases }.test_results(fork_name);
|
||||
|
||||
let name = format!("{}/{}", Self::runner_name(), Self::handler_name());
|
||||
let name = format!(
|
||||
"{}/{}/{}",
|
||||
fork_name_str,
|
||||
Self::runner_name(),
|
||||
self.handler_name()
|
||||
);
|
||||
crate::results::assert_tests_pass(&name, &handler_path, &results);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! bls_handler {
|
||||
($runner_name: ident, $case_name:ident, $handler_name:expr) => {
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct $runner_name;
|
||||
|
||||
impl Handler for $runner_name {
|
||||
type Case = cases::$case_name;
|
||||
|
||||
fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool {
|
||||
fork_name == ForkName::Base
|
||||
}
|
||||
|
||||
fn runner_name() -> &'static str {
|
||||
"bls"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
$handler_name.into()
|
||||
}
|
||||
}
|
||||
@@ -89,14 +112,47 @@ bls_handler!(
|
||||
);
|
||||
|
||||
/// Handler for SSZ types.
|
||||
pub struct SszStaticHandler<T, E>(PhantomData<(T, E)>);
|
||||
pub struct SszStaticHandler<T, E> {
|
||||
supported_forks: Vec<ForkName>,
|
||||
_phantom: PhantomData<(T, E)>,
|
||||
}
|
||||
|
||||
impl<T, E> Default for SszStaticHandler<T, E> {
|
||||
fn default() -> Self {
|
||||
Self::for_forks(ForkName::list_all())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> SszStaticHandler<T, E> {
|
||||
pub fn for_forks(supported_forks: Vec<ForkName>) -> Self {
|
||||
SszStaticHandler {
|
||||
supported_forks,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn base_only() -> Self {
|
||||
Self::for_forks(vec![ForkName::Base])
|
||||
}
|
||||
|
||||
pub fn altair_only() -> Self {
|
||||
Self::for_forks(vec![ForkName::Altair])
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for SSZ types that implement `CachedTreeHash`.
|
||||
pub struct SszStaticTHCHandler<T, C, E>(PhantomData<(T, C, E)>);
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct SszStaticTHCHandler<T, E>(PhantomData<(T, E)>);
|
||||
|
||||
/// Handler for SSZ types that don't implement `ssz::Decode`.
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct SszStaticWithSpecHandler<T, E>(PhantomData<(T, E)>);
|
||||
|
||||
impl<T, E> Handler for SszStaticHandler<T, E>
|
||||
where
|
||||
T: cases::SszStaticType + TypeName,
|
||||
T: cases::SszStaticType + ssz::Decode + TypeName,
|
||||
E: TypeName,
|
||||
{
|
||||
type Case = cases::SszStatic<T>;
|
||||
@@ -109,18 +165,20 @@ where
|
||||
"ssz_static"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
T::name().into()
|
||||
}
|
||||
|
||||
fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool {
|
||||
self.supported_forks.contains(&fork_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, C, E> Handler for SszStaticTHCHandler<T, C, E>
|
||||
impl<E> Handler for SszStaticTHCHandler<BeaconState<E>, E>
|
||||
where
|
||||
T: cases::SszStaticType + CachedTreeHash<C> + TypeName,
|
||||
C: Debug + Sync,
|
||||
E: TypeName,
|
||||
E: EthSpec + TypeName,
|
||||
{
|
||||
type Case = cases::SszStaticTHC<T, C>;
|
||||
type Case = cases::SszStaticTHC<BeaconState<E>>;
|
||||
|
||||
fn config_name() -> &'static str {
|
||||
E::name()
|
||||
@@ -130,11 +188,34 @@ where
|
||||
"ssz_static"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
BeaconState::<E>::name().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> Handler for SszStaticWithSpecHandler<T, E>
|
||||
where
|
||||
T: TypeName,
|
||||
E: EthSpec + TypeName,
|
||||
cases::SszStaticWithSpec<T>: Case + LoadCase,
|
||||
{
|
||||
type Case = cases::SszStaticWithSpec<T>;
|
||||
|
||||
fn config_name() -> &'static str {
|
||||
E::name()
|
||||
}
|
||||
|
||||
fn runner_name() -> &'static str {
|
||||
"ssz_static"
|
||||
}
|
||||
|
||||
fn handler_name(&self) -> String {
|
||||
T::name().into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct ShufflingHandler<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for ShufflingHandler<E> {
|
||||
@@ -148,11 +229,17 @@ impl<E: EthSpec + TypeName> Handler for ShufflingHandler<E> {
|
||||
"shuffling"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
"core".into()
|
||||
}
|
||||
|
||||
fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool {
|
||||
fork_name == ForkName::Base
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct SanityBlocksHandler<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for SanityBlocksHandler<E> {
|
||||
@@ -166,11 +253,19 @@ impl<E: EthSpec + TypeName> Handler for SanityBlocksHandler<E> {
|
||||
"sanity"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
"blocks".into()
|
||||
}
|
||||
|
||||
fn is_enabled_for_fork(&self, _fork_name: ForkName) -> bool {
|
||||
// FIXME(altair): v1.1.0-alpha.3 doesn't mark the historical blocks test as
|
||||
// requiring real crypto, so only run these tests with real crypto for now.
|
||||
cfg!(not(feature = "fake_crypto"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct SanitySlotsHandler<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for SanitySlotsHandler<E> {
|
||||
@@ -184,11 +279,13 @@ impl<E: EthSpec + TypeName> Handler for SanitySlotsHandler<E> {
|
||||
"sanity"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
"slots".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct EpochProcessingHandler<E, T>(PhantomData<(E, T)>);
|
||||
|
||||
impl<E: EthSpec + TypeName, T: EpochTransition<E>> Handler for EpochProcessingHandler<E, T> {
|
||||
@@ -202,11 +299,83 @@ impl<E: EthSpec + TypeName, T: EpochTransition<E>> Handler for EpochProcessingHa
|
||||
"epoch_processing"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
T::name().into()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RewardsHandler<E: EthSpec> {
|
||||
handler_name: &'static str,
|
||||
_phantom: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> RewardsHandler<E> {
|
||||
pub fn new(handler_name: &'static str) -> Self {
|
||||
Self {
|
||||
handler_name,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for RewardsHandler<E> {
|
||||
type Case = cases::RewardsTest<E>;
|
||||
|
||||
fn config_name() -> &'static str {
|
||||
E::name()
|
||||
}
|
||||
|
||||
fn runner_name() -> &'static str {
|
||||
"rewards"
|
||||
}
|
||||
|
||||
fn handler_name(&self) -> String {
|
||||
self.handler_name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct ForkHandler<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for ForkHandler<E> {
|
||||
type Case = cases::ForkTest<E>;
|
||||
|
||||
fn config_name() -> &'static str {
|
||||
E::name()
|
||||
}
|
||||
|
||||
fn runner_name() -> &'static str {
|
||||
"fork"
|
||||
}
|
||||
|
||||
fn handler_name(&self) -> String {
|
||||
"fork".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct TransitionHandler<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for TransitionHandler<E> {
|
||||
type Case = cases::TransitionTest<E>;
|
||||
|
||||
fn config_name() -> &'static str {
|
||||
E::name()
|
||||
}
|
||||
|
||||
fn runner_name() -> &'static str {
|
||||
"transition"
|
||||
}
|
||||
|
||||
fn handler_name(&self) -> String {
|
||||
"core".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct FinalityHandler<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for FinalityHandler<E> {
|
||||
@@ -221,11 +390,13 @@ impl<E: EthSpec + TypeName> Handler for FinalityHandler<E> {
|
||||
"finality"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
"finality".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct GenesisValidityHandler<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for GenesisValidityHandler<E> {
|
||||
@@ -239,11 +410,13 @@ impl<E: EthSpec + TypeName> Handler for GenesisValidityHandler<E> {
|
||||
"genesis"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
"validity".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct GenesisInitializationHandler<E>(PhantomData<E>);
|
||||
|
||||
impl<E: EthSpec + TypeName> Handler for GenesisInitializationHandler<E> {
|
||||
@@ -257,11 +430,13 @@ impl<E: EthSpec + TypeName> Handler for GenesisInitializationHandler<E> {
|
||||
"genesis"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
"initialization".into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct OperationsHandler<E, O>(PhantomData<(E, O)>);
|
||||
|
||||
impl<E: EthSpec + TypeName, O: Operation<E>> Handler for OperationsHandler<E, O> {
|
||||
@@ -275,11 +450,13 @@ impl<E: EthSpec + TypeName, O: Operation<E>> Handler for OperationsHandler<E, O>
|
||||
"operations"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn handler_name(&self) -> String {
|
||||
O::handler_name()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
pub struct SszGenericHandler<H>(PhantomData<H>);
|
||||
|
||||
impl<H: TypeName> Handler for SszGenericHandler<H> {
|
||||
@@ -293,7 +470,12 @@ impl<H: TypeName> Handler for SszGenericHandler<H> {
|
||||
"ssz_generic"
|
||||
}
|
||||
|
||||
fn handler_name() -> String {
|
||||
fn is_enabled_for_fork(&self, fork_name: ForkName) -> bool {
|
||||
// SSZ generic tests are genesis only
|
||||
fork_name == ForkName::Base
|
||||
}
|
||||
|
||||
fn handler_name(&self) -> String {
|
||||
H::name().into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use types::EthSpec;
|
||||
|
||||
pub use case_result::CaseResult;
|
||||
pub use cases::Case;
|
||||
pub use cases::{
|
||||
FinalUpdates, JustificationAndFinalization, RegistryUpdates, RewardsAndPenalties, Slashings,
|
||||
EffectiveBalanceUpdates, Eth1DataReset, HistoricalRootsUpdate, InactivityUpdates,
|
||||
JustificationAndFinalization, ParticipationFlagUpdates, ParticipationRecordUpdates,
|
||||
RandaoMixesReset, RegistryUpdates, RewardsAndPenalties, Slashings, SlashingsReset,
|
||||
SyncCommitteeUpdates,
|
||||
};
|
||||
pub use decode::log_file_access;
|
||||
pub use error::Error;
|
||||
pub use handler::*;
|
||||
pub use type_name::TypeName;
|
||||
use types::{ChainSpec, EthSpec, ForkName};
|
||||
|
||||
mod bls_setting;
|
||||
mod case_result;
|
||||
@@ -17,3 +20,7 @@ mod error;
|
||||
mod handler;
|
||||
mod results;
|
||||
mod type_name;
|
||||
|
||||
pub fn testing_spec<E: EthSpec>(fork_name: ForkName) -> ChainSpec {
|
||||
fork_name.make_genesis_spec(E::default_spec())
|
||||
}
|
||||
|
||||
@@ -41,9 +41,15 @@ type_name_generic!(Attestation);
|
||||
type_name!(AttestationData);
|
||||
type_name_generic!(AttesterSlashing);
|
||||
type_name_generic!(BeaconBlock);
|
||||
type_name_generic!(BeaconBlockBase, "BeaconBlock");
|
||||
type_name_generic!(BeaconBlockAltair, "BeaconBlock");
|
||||
type_name_generic!(BeaconBlockBody);
|
||||
type_name_generic!(BeaconBlockBodyBase, "BeaconBlockBody");
|
||||
type_name_generic!(BeaconBlockBodyAltair, "BeaconBlockBody");
|
||||
type_name!(BeaconBlockHeader);
|
||||
type_name_generic!(BeaconState);
|
||||
type_name_generic!(BeaconStateBase, "BeaconState");
|
||||
type_name_generic!(BeaconStateAltair, "BeaconState");
|
||||
type_name!(Checkpoint);
|
||||
type_name!(Deposit);
|
||||
type_name!(DepositData);
|
||||
@@ -60,5 +66,7 @@ type_name_generic!(SignedBeaconBlock);
|
||||
type_name!(SignedBeaconBlockHeader);
|
||||
type_name!(SignedVoluntaryExit);
|
||||
type_name!(SigningData);
|
||||
type_name_generic!(SyncAggregate);
|
||||
type_name_generic!(SyncCommittee);
|
||||
type_name!(Validator);
|
||||
type_name!(VoluntaryExit);
|
||||
|
||||
@@ -1,40 +1,8 @@
|
||||
#![cfg(feature = "ef_tests")]
|
||||
|
||||
use ef_tests::*;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use types::*;
|
||||
|
||||
// Check that the config from the Eth2.0 spec tests matches our minimal/mainnet config.
|
||||
fn config_test<E: EthSpec + TypeName>() {
|
||||
let config_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("eth2.0-spec-tests")
|
||||
.join("tests")
|
||||
.join(E::name())
|
||||
.join("config")
|
||||
.join("phase0.yaml");
|
||||
let yaml_config = YamlConfig::from_file(&config_path).expect("config file loads OK");
|
||||
let spec = E::default_spec();
|
||||
let yaml_from_spec = YamlConfig::from_spec::<E>(&spec);
|
||||
assert_eq!(yaml_config.apply_to_chain_spec::<E>(&spec), Some(spec));
|
||||
assert_eq!(yaml_from_spec, yaml_config);
|
||||
assert_eq!(
|
||||
yaml_config.extra_fields,
|
||||
HashMap::new(),
|
||||
"not all config fields read"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mainnet_config_ok() {
|
||||
config_test::<MainnetEthSpec>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minimal_config_ok() {
|
||||
config_test::<MinimalEthSpec>();
|
||||
}
|
||||
|
||||
// Check that the hand-computed multiplications on EthSpec are correctly computed.
|
||||
// This test lives here because one is most likely to muck these up during a spec update.
|
||||
fn check_typenum_values<E: EthSpec>() {
|
||||
@@ -56,118 +24,141 @@ fn derived_typenum_values() {
|
||||
|
||||
#[test]
|
||||
fn shuffling() {
|
||||
ShufflingHandler::<MinimalEthSpec>::run();
|
||||
ShufflingHandler::<MainnetEthSpec>::run();
|
||||
ShufflingHandler::<MinimalEthSpec>::default().run();
|
||||
ShufflingHandler::<MainnetEthSpec>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations_deposit() {
|
||||
OperationsHandler::<MinimalEthSpec, Deposit>::run();
|
||||
OperationsHandler::<MainnetEthSpec, Deposit>::run();
|
||||
OperationsHandler::<MinimalEthSpec, Deposit>::default().run();
|
||||
OperationsHandler::<MainnetEthSpec, Deposit>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations_exit() {
|
||||
OperationsHandler::<MinimalEthSpec, SignedVoluntaryExit>::run();
|
||||
OperationsHandler::<MainnetEthSpec, SignedVoluntaryExit>::run();
|
||||
OperationsHandler::<MinimalEthSpec, SignedVoluntaryExit>::default().run();
|
||||
OperationsHandler::<MainnetEthSpec, SignedVoluntaryExit>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations_proposer_slashing() {
|
||||
OperationsHandler::<MinimalEthSpec, ProposerSlashing>::run();
|
||||
OperationsHandler::<MainnetEthSpec, ProposerSlashing>::run();
|
||||
OperationsHandler::<MinimalEthSpec, ProposerSlashing>::default().run();
|
||||
OperationsHandler::<MainnetEthSpec, ProposerSlashing>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations_attester_slashing() {
|
||||
OperationsHandler::<MinimalEthSpec, AttesterSlashing<_>>::run();
|
||||
OperationsHandler::<MainnetEthSpec, AttesterSlashing<_>>::run();
|
||||
OperationsHandler::<MinimalEthSpec, AttesterSlashing<_>>::default().run();
|
||||
OperationsHandler::<MainnetEthSpec, AttesterSlashing<_>>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations_attestation() {
|
||||
OperationsHandler::<MinimalEthSpec, Attestation<_>>::run();
|
||||
OperationsHandler::<MainnetEthSpec, Attestation<_>>::run();
|
||||
OperationsHandler::<MinimalEthSpec, Attestation<_>>::default().run();
|
||||
OperationsHandler::<MainnetEthSpec, Attestation<_>>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations_block_header() {
|
||||
OperationsHandler::<MinimalEthSpec, BeaconBlock<_>>::run();
|
||||
OperationsHandler::<MainnetEthSpec, BeaconBlock<_>>::run();
|
||||
OperationsHandler::<MinimalEthSpec, BeaconBlock<_>>::default().run();
|
||||
OperationsHandler::<MainnetEthSpec, BeaconBlock<_>>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations_sync_aggregate() {
|
||||
OperationsHandler::<MinimalEthSpec, SyncAggregate<_>>::default().run();
|
||||
OperationsHandler::<MainnetEthSpec, SyncAggregate<_>>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_blocks() {
|
||||
SanityBlocksHandler::<MinimalEthSpec>::run();
|
||||
SanityBlocksHandler::<MainnetEthSpec>::run();
|
||||
SanityBlocksHandler::<MinimalEthSpec>::default().run();
|
||||
SanityBlocksHandler::<MainnetEthSpec>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_slots() {
|
||||
SanitySlotsHandler::<MinimalEthSpec>::run();
|
||||
SanitySlotsHandler::<MainnetEthSpec>::run();
|
||||
SanitySlotsHandler::<MinimalEthSpec>::default().run();
|
||||
SanitySlotsHandler::<MainnetEthSpec>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
fn bls_aggregate() {
|
||||
BlsAggregateSigsHandler::run();
|
||||
BlsAggregateSigsHandler::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
fn bls_sign() {
|
||||
BlsSignMsgHandler::run();
|
||||
BlsSignMsgHandler::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
fn bls_verify() {
|
||||
BlsVerifyMsgHandler::run();
|
||||
BlsVerifyMsgHandler::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
fn bls_aggregate_verify() {
|
||||
BlsAggregateVerifyHandler::run();
|
||||
BlsAggregateVerifyHandler::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "fake_crypto"))]
|
||||
fn bls_fast_aggregate_verify() {
|
||||
BlsFastAggregateVerifyHandler::run();
|
||||
BlsFastAggregateVerifyHandler::default().run();
|
||||
}
|
||||
|
||||
/// As for `ssz_static_test_no_run` (below), but also executes the function as a test.
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
macro_rules! ssz_static_test {
|
||||
// Non-tree hash caching
|
||||
($test_name:ident, $typ:ident$(<$generics:tt>)?) => {
|
||||
ssz_static_test!($test_name, SszStaticHandler, $typ$(<$generics>)?);
|
||||
($($args:tt)*) => {
|
||||
ssz_static_test_no_run!(#[test] $($args)*);
|
||||
};
|
||||
}
|
||||
|
||||
/// Generate a function to run the SSZ static tests for a type.
|
||||
///
|
||||
/// Quite complex in order to support an optional #[test] attrib, generics, and the two EthSpecs.
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
macro_rules! ssz_static_test_no_run {
|
||||
// Top-level
|
||||
($(#[$test:meta])? $test_name:ident, $typ:ident$(<$generics:tt>)?) => {
|
||||
ssz_static_test_no_run!($(#[$test])? $test_name, SszStaticHandler, $typ$(<$generics>)?);
|
||||
};
|
||||
// Generic
|
||||
($test_name:ident, $handler:ident, $typ:ident<_>) => {
|
||||
ssz_static_test!(
|
||||
$test_name, $handler, {
|
||||
($(#[$test:meta])? $test_name:ident, $handler:ident, $typ:ident<_>) => {
|
||||
ssz_static_test_no_run!(
|
||||
$(#[$test])?
|
||||
$test_name,
|
||||
$handler,
|
||||
{
|
||||
($typ<MinimalEthSpec>, MinimalEthSpec),
|
||||
($typ<MainnetEthSpec>, MainnetEthSpec)
|
||||
}
|
||||
);
|
||||
};
|
||||
// Non-generic
|
||||
($test_name:ident, $handler:ident, $typ:ident) => {
|
||||
ssz_static_test!(
|
||||
$test_name, $handler, {
|
||||
($(#[$test:meta])? $test_name:ident, $handler:ident, $typ:ident) => {
|
||||
ssz_static_test_no_run!(
|
||||
$(#[$test])?
|
||||
$test_name,
|
||||
$handler,
|
||||
{
|
||||
($typ, MinimalEthSpec),
|
||||
($typ, MainnetEthSpec)
|
||||
}
|
||||
);
|
||||
};
|
||||
// Base case
|
||||
($test_name:ident, $handler:ident, { $(($($typ:ty),+)),+ }) => {
|
||||
#[test]
|
||||
($(#[$test:meta])? $test_name:ident, $handler:ident, { $(($($typ:ty),+)),+ }) => {
|
||||
$(#[$test])?
|
||||
fn $test_name() {
|
||||
$(
|
||||
$handler::<$($typ),+>::run();
|
||||
$handler::<$($typ),+>::default().run();
|
||||
)+
|
||||
}
|
||||
};
|
||||
@@ -175,101 +166,190 @@ macro_rules! ssz_static_test {
|
||||
|
||||
#[cfg(feature = "fake_crypto")]
|
||||
mod ssz_static {
|
||||
use ef_tests::{Handler, SszStaticHandler, SszStaticTHCHandler};
|
||||
use ef_tests::{Handler, SszStaticHandler, SszStaticTHCHandler, SszStaticWithSpecHandler};
|
||||
use types::*;
|
||||
|
||||
ssz_static_test!(aggregate_and_proof, AggregateAndProof<_>);
|
||||
ssz_static_test!(attestation, Attestation<_>);
|
||||
ssz_static_test!(attestation_data, AttestationData);
|
||||
ssz_static_test!(attester_slashing, AttesterSlashing<_>);
|
||||
ssz_static_test!(beacon_block, BeaconBlock<_>);
|
||||
ssz_static_test!(beacon_block_body, BeaconBlockBody<_>);
|
||||
ssz_static_test!(beacon_block, SszStaticWithSpecHandler, BeaconBlock<_>);
|
||||
ssz_static_test!(beacon_block_header, BeaconBlockHeader);
|
||||
ssz_static_test!(
|
||||
beacon_state,
|
||||
SszStaticTHCHandler, {
|
||||
(BeaconState<MinimalEthSpec>, BeaconTreeHashCache<_>, MinimalEthSpec),
|
||||
(BeaconState<MainnetEthSpec>, BeaconTreeHashCache<_>, MainnetEthSpec)
|
||||
}
|
||||
);
|
||||
ssz_static_test!(beacon_state, SszStaticTHCHandler, BeaconState<_>);
|
||||
ssz_static_test!(checkpoint, Checkpoint);
|
||||
// FIXME(altair): add ContributionAndProof
|
||||
ssz_static_test!(deposit, Deposit);
|
||||
ssz_static_test!(deposit_data, DepositData);
|
||||
ssz_static_test!(deposit_message, DepositMessage);
|
||||
// FIXME(sproul): move Eth1Block to consensus/types
|
||||
//
|
||||
// Tracked at: https://github.com/sigp/lighthouse/issues/1835
|
||||
//
|
||||
// ssz_static_test!(eth1_block, Eth1Block);
|
||||
// NOTE: Eth1Block intentionally omitted, see: https://github.com/sigp/lighthouse/issues/1835
|
||||
ssz_static_test!(eth1_data, Eth1Data);
|
||||
ssz_static_test!(fork, Fork);
|
||||
ssz_static_test!(fork_data, ForkData);
|
||||
ssz_static_test!(historical_batch, HistoricalBatch<_>);
|
||||
ssz_static_test!(indexed_attestation, IndexedAttestation<_>);
|
||||
// NOTE: LightClient* intentionally omitted
|
||||
ssz_static_test!(pending_attestation, PendingAttestation<_>);
|
||||
ssz_static_test!(proposer_slashing, ProposerSlashing);
|
||||
ssz_static_test!(signed_aggregate_and_proof, SignedAggregateAndProof<_>);
|
||||
ssz_static_test!(signed_beacon_block, SignedBeaconBlock<_>);
|
||||
ssz_static_test!(
|
||||
signed_beacon_block,
|
||||
SszStaticWithSpecHandler,
|
||||
SignedBeaconBlock<_>
|
||||
);
|
||||
ssz_static_test!(signed_beacon_block_header, SignedBeaconBlockHeader);
|
||||
// FIXME(altair): add SignedContributionAndProof
|
||||
ssz_static_test!(signed_voluntary_exit, SignedVoluntaryExit);
|
||||
ssz_static_test!(signing_data, SigningData);
|
||||
// FIXME(altair): add SyncCommitteeContribution/Signature/SigningData
|
||||
ssz_static_test!(validator, Validator);
|
||||
ssz_static_test!(voluntary_exit, VoluntaryExit);
|
||||
|
||||
// BeaconBlockBody has no internal indicator of which fork it is for, so we test it separately.
|
||||
#[test]
|
||||
fn beacon_block_body() {
|
||||
SszStaticHandler::<BeaconBlockBodyBase<MinimalEthSpec>, MinimalEthSpec>::base_only().run();
|
||||
SszStaticHandler::<BeaconBlockBodyBase<MainnetEthSpec>, MainnetEthSpec>::base_only().run();
|
||||
SszStaticHandler::<BeaconBlockBodyAltair<MinimalEthSpec>, MinimalEthSpec>::altair_only()
|
||||
.run();
|
||||
SszStaticHandler::<BeaconBlockBodyAltair<MainnetEthSpec>, MainnetEthSpec>::altair_only()
|
||||
.run();
|
||||
}
|
||||
|
||||
// Altair-only
|
||||
#[test]
|
||||
fn sync_aggregate() {
|
||||
SszStaticHandler::<SyncAggregate<MinimalEthSpec>, MinimalEthSpec>::altair_only().run();
|
||||
SszStaticHandler::<SyncAggregate<MainnetEthSpec>, MainnetEthSpec>::altair_only().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sync_committee() {
|
||||
SszStaticHandler::<SyncCommittee<MinimalEthSpec>, MinimalEthSpec>::altair_only().run();
|
||||
SszStaticHandler::<SyncCommittee<MainnetEthSpec>, MainnetEthSpec>::altair_only().run();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ssz_generic() {
|
||||
SszGenericHandler::<BasicVector>::run();
|
||||
SszGenericHandler::<Bitlist>::run();
|
||||
SszGenericHandler::<Bitvector>::run();
|
||||
SszGenericHandler::<Boolean>::run();
|
||||
SszGenericHandler::<Uints>::run();
|
||||
SszGenericHandler::<Containers>::run();
|
||||
SszGenericHandler::<BasicVector>::default().run();
|
||||
SszGenericHandler::<Bitlist>::default().run();
|
||||
SszGenericHandler::<Bitvector>::default().run();
|
||||
SszGenericHandler::<Boolean>::default().run();
|
||||
SszGenericHandler::<Uints>::default().run();
|
||||
SszGenericHandler::<Containers>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_justification_and_finalization() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, JustificationAndFinalization>::run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, JustificationAndFinalization>::run();
|
||||
EpochProcessingHandler::<MinimalEthSpec, JustificationAndFinalization>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, JustificationAndFinalization>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_rewards_and_penalties() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, RewardsAndPenalties>::run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, RewardsAndPenalties>::run();
|
||||
EpochProcessingHandler::<MinimalEthSpec, RewardsAndPenalties>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, RewardsAndPenalties>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_registry_updates() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, RegistryUpdates>::run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, RegistryUpdates>::run();
|
||||
EpochProcessingHandler::<MinimalEthSpec, RegistryUpdates>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, RegistryUpdates>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_slashings() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, Slashings>::run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, Slashings>::run();
|
||||
EpochProcessingHandler::<MinimalEthSpec, Slashings>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, Slashings>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_final_updates() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, FinalUpdates>::run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, FinalUpdates>::run();
|
||||
fn epoch_processing_eth1_data_reset() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, Eth1DataReset>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, Eth1DataReset>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_effective_balance_updates() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, EffectiveBalanceUpdates>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, EffectiveBalanceUpdates>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_slashings_reset() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, SlashingsReset>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, SlashingsReset>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_randao_mixes_reset() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, RandaoMixesReset>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, RandaoMixesReset>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_historical_roots_update() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, HistoricalRootsUpdate>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, HistoricalRootsUpdate>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_participation_record_updates() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, ParticipationRecordUpdates>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, ParticipationRecordUpdates>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_sync_committee_updates() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, SyncCommitteeUpdates>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, SyncCommitteeUpdates>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_inactivity_updates() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, InactivityUpdates>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, InactivityUpdates>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_participation_flag_updates() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, ParticipationFlagUpdates>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, ParticipationFlagUpdates>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fork_upgrade() {
|
||||
ForkHandler::<MinimalEthSpec>::default().run();
|
||||
ForkHandler::<MainnetEthSpec>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transition() {
|
||||
TransitionHandler::<MinimalEthSpec>::default().run();
|
||||
TransitionHandler::<MainnetEthSpec>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn finality() {
|
||||
FinalityHandler::<MinimalEthSpec>::run();
|
||||
FinalityHandler::<MainnetEthSpec>::run();
|
||||
FinalityHandler::<MinimalEthSpec>::default().run();
|
||||
FinalityHandler::<MainnetEthSpec>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_initialization() {
|
||||
GenesisInitializationHandler::<MinimalEthSpec>::run();
|
||||
GenesisInitializationHandler::<MinimalEthSpec>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn genesis_validity() {
|
||||
GenesisValidityHandler::<MinimalEthSpec>::run();
|
||||
GenesisValidityHandler::<MinimalEthSpec>::default().run();
|
||||
// Note: there are no genesis validity tests for mainnet
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rewards() {
|
||||
for handler in &["basic", "leak", "random"] {
|
||||
RewardsHandler::<MinimalEthSpec>::new(handler).run();
|
||||
RewardsHandler::<MainnetEthSpec>::new(handler).run();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ impl LocalSignerTestData<BeaconBlock<E>> {
|
||||
&self.spec,
|
||||
);
|
||||
|
||||
signed_block.signature.to_string()
|
||||
signed_block.signature().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -239,31 +239,39 @@ pub fn get_block<E: EthSpec>(seed: u64) -> BeaconBlock<E> {
|
||||
let mut block: BeaconBlock<E> = BeaconBlock::empty(spec);
|
||||
for _ in 0..E::MaxProposerSlashings::to_usize() {
|
||||
block
|
||||
.body
|
||||
.proposer_slashings
|
||||
.body_mut()
|
||||
.proposer_slashings_mut()
|
||||
.push(proposer_slashing.clone())
|
||||
.unwrap();
|
||||
}
|
||||
for _ in 0..E::MaxDeposits::to_usize() {
|
||||
block.body.deposits.push(deposit.clone()).unwrap();
|
||||
block
|
||||
.body_mut()
|
||||
.deposits_mut()
|
||||
.push(deposit.clone())
|
||||
.unwrap();
|
||||
}
|
||||
for _ in 0..E::MaxVoluntaryExits::to_usize() {
|
||||
block
|
||||
.body
|
||||
.voluntary_exits
|
||||
.body_mut()
|
||||
.voluntary_exits_mut()
|
||||
.push(signed_voluntary_exit.clone())
|
||||
.unwrap();
|
||||
}
|
||||
for _ in 0..E::MaxAttesterSlashings::to_usize() {
|
||||
block
|
||||
.body
|
||||
.attester_slashings
|
||||
.body_mut()
|
||||
.attester_slashings_mut()
|
||||
.push(attester_slashing.clone())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
for _ in 0..E::MaxAttestations::to_usize() {
|
||||
block.body.attestations.push(attestation.clone()).unwrap();
|
||||
block
|
||||
.body_mut()
|
||||
.attestations_mut()
|
||||
.push(attestation.clone())
|
||||
.unwrap();
|
||||
}
|
||||
block
|
||||
}
|
||||
|
||||
@@ -99,7 +99,7 @@ async fn verify_validator_count<E: EthSpec>(
|
||||
.await
|
||||
.map(|body| body.unwrap().data)
|
||||
.map_err(|e| format!("Get state root via http failed: {:?}", e))?
|
||||
.validators
|
||||
.validators()
|
||||
.len();
|
||||
validator_counts.push(vc);
|
||||
}
|
||||
|
||||
@@ -10,3 +10,5 @@ edition = "2018"
|
||||
state_processing = { path = "../../consensus/state_processing" }
|
||||
types = { path = "../../consensus/types" }
|
||||
eth2_ssz = "0.1.2"
|
||||
beacon_chain = { path = "../../beacon_node/beacon_chain" }
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use super::*;
|
||||
use beacon_chain::test_utils::{BeaconChainHarness, EphemeralHarnessType};
|
||||
use state_processing::{
|
||||
per_block_processing, per_block_processing::errors::ExitInvalid,
|
||||
test_utils::BlockProcessingBuilder, BlockProcessingError, BlockSignatureStrategy,
|
||||
per_block_processing, per_block_processing::errors::ExitInvalid, BlockProcessingError,
|
||||
BlockSignatureStrategy,
|
||||
};
|
||||
use types::{BeaconBlock, BeaconState, Epoch, EthSpec, SignedBeaconBlock};
|
||||
|
||||
@@ -14,8 +15,10 @@ struct ExitTest {
|
||||
validator_index: u64,
|
||||
exit_epoch: Epoch,
|
||||
state_epoch: Epoch,
|
||||
block_modifier: Box<dyn FnOnce(&mut BeaconBlock<E>)>,
|
||||
builder_modifier: Box<dyn FnOnce(BlockProcessingBuilder<E>) -> BlockProcessingBuilder<E>>,
|
||||
state_modifier: Box<dyn FnOnce(&mut BeaconState<E>)>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
block_modifier:
|
||||
Box<dyn FnOnce(&BeaconChainHarness<EphemeralHarnessType<E>>, &mut BeaconBlock<E>)>,
|
||||
#[allow(dead_code)]
|
||||
expected: Result<(), BlockProcessingError>,
|
||||
}
|
||||
@@ -26,8 +29,8 @@ impl Default for ExitTest {
|
||||
validator_index: VALIDATOR_INDEX,
|
||||
exit_epoch: STATE_EPOCH,
|
||||
state_epoch: STATE_EPOCH,
|
||||
block_modifier: Box::new(|_| ()),
|
||||
builder_modifier: Box::new(|x| x),
|
||||
state_modifier: Box::new(|_| ()),
|
||||
block_modifier: Box::new(|_, _| ()),
|
||||
expected: Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -35,14 +38,23 @@ impl Default for ExitTest {
|
||||
|
||||
impl ExitTest {
|
||||
fn block_and_pre_state(self) -> (SignedBeaconBlock<E>, BeaconState<E>) {
|
||||
let spec = &E::default_spec();
|
||||
let harness = get_harness::<E>(
|
||||
self.state_epoch.start_slot(E::slots_per_epoch()),
|
||||
VALIDATOR_COUNT,
|
||||
);
|
||||
let mut state = harness.get_current_state();
|
||||
(self.state_modifier)(&mut state);
|
||||
|
||||
(self.builder_modifier)(
|
||||
get_builder(spec, self.state_epoch.as_u64(), VALIDATOR_COUNT)
|
||||
.insert_exit(self.validator_index, self.exit_epoch)
|
||||
.modify(self.block_modifier),
|
||||
)
|
||||
.build(None, None)
|
||||
let block_modifier = self.block_modifier;
|
||||
let validator_index = self.validator_index;
|
||||
let exit_epoch = self.exit_epoch;
|
||||
|
||||
let (signed_block, state) =
|
||||
harness.make_block_with_modifier(state.clone(), state.slot() + 1, |block| {
|
||||
harness.add_voluntary_exit(block, validator_index, exit_epoch);
|
||||
block_modifier(&harness, block);
|
||||
});
|
||||
(signed_block, state)
|
||||
}
|
||||
|
||||
fn process(
|
||||
@@ -58,7 +70,7 @@ impl ExitTest {
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, not(debug_assertions)))]
|
||||
fn run(self) -> BeaconState<E> {
|
||||
let spec = &E::default_spec();
|
||||
let expected = self.expected.clone();
|
||||
@@ -95,23 +107,22 @@ vectors_and_tests!(
|
||||
// Ensures we can process a valid exit,
|
||||
valid_single_exit,
|
||||
ExitTest::default(),
|
||||
// Tests three exists in the same block.
|
||||
// Tests three exits in the same block.
|
||||
valid_three_exits,
|
||||
ExitTest {
|
||||
builder_modifier: Box::new(|builder| {
|
||||
builder
|
||||
.insert_exit(1, STATE_EPOCH)
|
||||
.insert_exit(2, STATE_EPOCH)
|
||||
block_modifier: Box::new(|harness, block| {
|
||||
harness.add_voluntary_exit(block, 1, STATE_EPOCH);
|
||||
harness.add_voluntary_exit(block, 2, STATE_EPOCH);
|
||||
}),
|
||||
..ExitTest::default()
|
||||
},
|
||||
// Ensures that a validator cannot be exited twice in the same block.
|
||||
invalid_duplicate,
|
||||
ExitTest {
|
||||
block_modifier: Box::new(|block| {
|
||||
block_modifier: Box::new(|_, block| {
|
||||
// Duplicate the exit
|
||||
let exit = block.body.voluntary_exits[0].clone();
|
||||
block.body.voluntary_exits.push(exit).unwrap();
|
||||
let exit = block.body().voluntary_exits()[0].clone();
|
||||
block.body_mut().voluntary_exits_mut().push(exit).unwrap();
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 1,
|
||||
@@ -128,8 +139,10 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_validator_unknown,
|
||||
ExitTest {
|
||||
block_modifier: Box::new(|block| {
|
||||
block.body.voluntary_exits[0].message.validator_index = VALIDATOR_COUNT as u64;
|
||||
block_modifier: Box::new(|_, block| {
|
||||
block.body_mut().voluntary_exits_mut()[0]
|
||||
.message
|
||||
.validator_index = VALIDATOR_COUNT as u64;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -147,9 +160,8 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_exit_already_initiated,
|
||||
ExitTest {
|
||||
builder_modifier: Box::new(|mut builder| {
|
||||
builder.state.validators[0].exit_epoch = STATE_EPOCH + 1;
|
||||
builder
|
||||
state_modifier: Box::new(|state| {
|
||||
state.validators_mut()[0].exit_epoch = STATE_EPOCH + 1;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -167,9 +179,8 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_not_active_before_activation_epoch,
|
||||
ExitTest {
|
||||
builder_modifier: Box::new(|mut builder| {
|
||||
builder.state.validators[0].activation_epoch = builder.spec.far_future_epoch;
|
||||
builder
|
||||
state_modifier: Box::new(|state| {
|
||||
state.validators_mut()[0].activation_epoch = E::default_spec().far_future_epoch;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -187,9 +198,8 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_not_active_after_exit_epoch,
|
||||
ExitTest {
|
||||
builder_modifier: Box::new(|mut builder| {
|
||||
builder.state.validators[0].exit_epoch = STATE_EPOCH;
|
||||
builder
|
||||
state_modifier: Box::new(|state| {
|
||||
state.validators_mut()[0].exit_epoch = STATE_EPOCH;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -286,10 +296,12 @@ vectors_and_tests!(
|
||||
// ```
|
||||
invalid_bad_signature,
|
||||
ExitTest {
|
||||
block_modifier: Box::new(|block| {
|
||||
block_modifier: Box::new(|_, block| {
|
||||
// Shift the validator index by 1 so that it's mismatched from the key that was
|
||||
// used to sign.
|
||||
block.body.voluntary_exits[0].message.validator_index = VALIDATOR_INDEX + 1;
|
||||
block.body_mut().voluntary_exits_mut()[0]
|
||||
.message
|
||||
.validator_index = VALIDATOR_INDEX + 1;
|
||||
}),
|
||||
expected: Err(BlockProcessingError::ExitInvalid {
|
||||
index: 0,
|
||||
@@ -299,14 +311,14 @@ vectors_and_tests!(
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, not(debug_assertions)))]
|
||||
mod custom_tests {
|
||||
use super::*;
|
||||
|
||||
fn assert_exited(state: &BeaconState<E>, validator_index: usize) {
|
||||
let spec = E::default_spec();
|
||||
|
||||
let validator = &state.validators[validator_index];
|
||||
let validator = &state.validators()[validator_index];
|
||||
assert_eq!(
|
||||
validator.exit_epoch,
|
||||
// This is correct until we exceed the churn limit. If that happens, we
|
||||
@@ -330,10 +342,9 @@ mod custom_tests {
|
||||
#[test]
|
||||
fn valid_three() {
|
||||
let state = ExitTest {
|
||||
builder_modifier: Box::new(|builder| {
|
||||
builder
|
||||
.insert_exit(1, STATE_EPOCH)
|
||||
.insert_exit(2, STATE_EPOCH)
|
||||
block_modifier: Box::new(|harness, block| {
|
||||
harness.add_voluntary_exit(block, 1, STATE_EPOCH);
|
||||
harness.add_voluntary_exit(block, 2, STATE_EPOCH);
|
||||
}),
|
||||
..ExitTest::default()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ macro_rules! vectors_and_tests {
|
||||
vec
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(all(test, not(debug_assertions)))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
$(
|
||||
|
||||
@@ -2,15 +2,21 @@
|
||||
mod macros;
|
||||
mod exit;
|
||||
|
||||
use beacon_chain::{
|
||||
store::StoreConfig,
|
||||
test_utils::{BeaconChainHarness, EphemeralHarnessType},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use ssz::Encode;
|
||||
use state_processing::test_utils::BlockProcessingBuilder;
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::exit;
|
||||
use types::MainnetEthSpec;
|
||||
use types::{BeaconState, ChainSpec, EthSpec, SignedBeaconBlock};
|
||||
use types::{
|
||||
test_utils::generate_deterministic_keypairs, BeaconState, EthSpec, Keypair, SignedBeaconBlock,
|
||||
};
|
||||
use types::{Hash256, MainnetEthSpec, Slot};
|
||||
|
||||
type E = MainnetEthSpec;
|
||||
|
||||
@@ -19,6 +25,8 @@ pub const VALIDATOR_COUNT: usize = 64;
|
||||
/// The base output directory for test vectors.
|
||||
pub const BASE_VECTOR_DIR: &str = "vectors";
|
||||
|
||||
pub const SLOT_OFFSET: u64 = 1;
|
||||
|
||||
/// Writes all known test vectors to `CARGO_MANIFEST_DIR/vectors`.
|
||||
fn main() {
|
||||
match write_all_vectors() {
|
||||
@@ -39,16 +47,36 @@ pub struct TestVector {
|
||||
pub error: Option<String>,
|
||||
}
|
||||
|
||||
/// Gets a `BlockProcessingBuilder` to be used in testing.
|
||||
fn get_builder(
|
||||
spec: &ChainSpec,
|
||||
epoch_offset: u64,
|
||||
num_validators: usize,
|
||||
) -> BlockProcessingBuilder<MainnetEthSpec> {
|
||||
// Set the state and block to be in the last slot of the `epoch_offset`th epoch.
|
||||
let last_slot_of_epoch = (MainnetEthSpec::genesis_epoch() + epoch_offset)
|
||||
.end_slot(MainnetEthSpec::slots_per_epoch());
|
||||
BlockProcessingBuilder::new(num_validators, last_slot_of_epoch, &spec).build_caches()
|
||||
lazy_static! {
|
||||
/// A cached set of keys.
|
||||
static ref KEYPAIRS: Vec<Keypair> = generate_deterministic_keypairs(VALIDATOR_COUNT);
|
||||
}
|
||||
|
||||
fn get_harness<E: EthSpec>(
|
||||
slot: Slot,
|
||||
validator_count: usize,
|
||||
) -> BeaconChainHarness<EphemeralHarnessType<E>> {
|
||||
let harness = BeaconChainHarness::new_with_store_config(
|
||||
E::default(),
|
||||
None,
|
||||
KEYPAIRS[0..validator_count].to_vec(),
|
||||
StoreConfig::default(),
|
||||
);
|
||||
let skip_to_slot = slot - SLOT_OFFSET;
|
||||
if skip_to_slot > Slot::new(0) {
|
||||
let state = harness.get_current_state();
|
||||
harness.add_attested_blocks_at_slots(
|
||||
state,
|
||||
Hash256::zero(),
|
||||
(skip_to_slot.as_u64()..slot.as_u64())
|
||||
.map(Slot::new)
|
||||
.collect::<Vec<_>>()
|
||||
.as_slice(),
|
||||
(0..validator_count).collect::<Vec<_>>().as_slice(),
|
||||
);
|
||||
}
|
||||
|
||||
harness
|
||||
}
|
||||
|
||||
/// Writes all vectors to file.
|
||||
|
||||
Reference in New Issue
Block a user