Epoch processing tests

This commit is contained in:
Michael Sproul 2019-08-30 13:29:26 +10:00
parent 81cafdc804
commit 6cf9b3c1a4
No known key found for this signature in database
GPG Key ID: 77B1309D2E54E914
13 changed files with 225 additions and 291 deletions

View File

@ -1,8 +1,5 @@
use crate::common::get_compact_committees_root; use crate::common::get_compact_committees_root;
use apply_rewards::process_rewards_and_penalties;
use errors::EpochProcessingError as Error; use errors::EpochProcessingError as Error;
use process_slashings::process_slashings;
use registry_updates::process_registry_updates;
use std::collections::HashMap; use std::collections::HashMap;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::*; use types::*;
@ -17,6 +14,10 @@ pub mod tests;
pub mod validator_statuses; pub mod validator_statuses;
pub mod winning_root; pub mod winning_root;
pub use apply_rewards::process_rewards_and_penalties;
pub use process_slashings::process_slashings;
pub use registry_updates::process_registry_updates;
/// Maps a shard to a winning root. /// Maps a shard to a winning root.
/// ///
/// It is generated during crosslink processing and later used to reward/penalize validators. /// It is generated during crosslink processing and later used to reward/penalize validators.

View File

@ -8,11 +8,7 @@ mod bls_g2_compressed;
mod bls_g2_uncompressed; mod bls_g2_uncompressed;
mod bls_priv_to_pub; mod bls_priv_to_pub;
mod bls_sign_msg; mod bls_sign_msg;
mod epoch_processing_crosslinks; mod epoch_processing;
mod epoch_processing_final_updates;
mod epoch_processing_justification_and_finalization;
mod epoch_processing_registry_updates;
mod epoch_processing_slashings;
mod genesis_initialization; mod genesis_initialization;
mod genesis_validity; mod genesis_validity;
mod operations_attestation; mod operations_attestation;
@ -34,11 +30,7 @@ pub use bls_g2_compressed::*;
pub use bls_g2_uncompressed::*; pub use bls_g2_uncompressed::*;
pub use bls_priv_to_pub::*; pub use bls_priv_to_pub::*;
pub use bls_sign_msg::*; pub use bls_sign_msg::*;
pub use epoch_processing_crosslinks::*; pub use epoch_processing::*;
pub use epoch_processing_final_updates::*;
pub use epoch_processing_justification_and_finalization::*;
pub use epoch_processing_registry_updates::*;
pub use epoch_processing_slashings::*;
pub use genesis_initialization::*; pub use genesis_initialization::*;
pub use genesis_validity::*; pub use genesis_validity::*;
pub use operations_attestation::*; pub use operations_attestation::*;
@ -69,6 +61,7 @@ pub trait Case: Debug {
/// Path to the directory for this test case. /// Path to the directory for this test case.
fn path(&self) -> &Path { fn path(&self) -> &Path {
// FIXME(michael): remove default impl
Path::new("") Path::new("")
} }

View File

@ -0,0 +1,147 @@
use super::*;
use crate::bls_setting::BlsSetting;
use crate::case_result::compare_beacon_state_results_without_caches;
use crate::type_name;
use crate::type_name::TypeName;
use crate::yaml_decode::{ssz_decode_file, yaml_decode_file};
use serde_derive::Deserialize;
use state_processing::per_epoch_processing::{
errors::EpochProcessingError, process_crosslinks, process_final_updates,
process_justification_and_finalization, process_registry_updates, process_slashings,
validator_statuses::ValidatorStatuses,
};
use std::marker::PhantomData;
use std::path::{Path, PathBuf};
use types::{BeaconState, ChainSpec, EthSpec};
#[derive(Debug, Clone, Default, Deserialize)]
pub struct Metadata {
pub description: Option<String>,
pub bls_setting: Option<BlsSetting>,
}
#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct EpochProcessing<E: EthSpec, T: EpochTransition<E>> {
pub path: PathBuf,
pub metadata: Metadata,
pub pre: BeaconState<E>,
pub post: Option<BeaconState<E>>,
#[serde(skip_deserializing)]
_phantom: PhantomData<T>,
}
pub trait EpochTransition<E: EthSpec>: TypeName + Debug {
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError>;
}
#[derive(Debug)]
pub struct JustificationAndFinalization;
#[derive(Debug)]
pub struct Crosslinks;
#[derive(Debug)]
pub struct RegistryUpdates;
#[derive(Debug)]
pub struct Slashings;
#[derive(Debug)]
pub struct FinalUpdates;
type_name!(
JustificationAndFinalization,
"justification_and_finalization"
);
type_name!(Crosslinks, "crosslinks");
type_name!(RegistryUpdates, "registry_updates");
type_name!(Slashings, "slashings");
type_name!(FinalUpdates, "final_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)
}
}
impl<E: EthSpec> EpochTransition<E> for Crosslinks {
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
process_crosslinks(state, spec)?;
Ok(())
}
}
impl<E: EthSpec> EpochTransition<E> for RegistryUpdates {
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
process_registry_updates(state, spec)
}
}
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)?;
Ok(())
}
}
impl<E: EthSpec> EpochTransition<E> for FinalUpdates {
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
process_final_updates(state, spec)
}
}
impl<E: EthSpec, T: EpochTransition<E>> LoadCase for EpochProcessing<E, T> {
fn load_from_dir(path: &Path) -> Result<Self, Error> {
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 post = if post_file.is_file() {
Some(ssz_decode_file(&post_file)?)
} else {
None
};
Ok(Self {
path: path.into(),
metadata,
pre,
post,
_phantom: PhantomData,
})
}
}
impl<E: EthSpec, T: EpochTransition<E>> Case for EpochProcessing<E, T> {
fn description(&self) -> String {
self.metadata
.description
.clone()
.unwrap_or_else(String::new)
}
fn path(&self) -> &Path {
&self.path
}
fn result(&self, _case_index: usize) -> Result<(), Error> {
let mut state = self.pre.clone();
let mut expected = self.post.clone();
let spec = &E::default_spec();
let mut result = (|| {
// Processing requires the epoch cache.
state.build_all_caches(spec)?;
T::run(&mut state, spec).map(|_| state)
})();
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

@ -1,37 +0,0 @@
use super::*;
use crate::case_result::compare_beacon_state_results_without_caches;
use serde_derive::Deserialize;
use state_processing::per_epoch_processing::process_crosslinks;
use types::{BeaconState, EthSpec};
#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct EpochProcessingCrosslinks<E: EthSpec> {
pub description: String,
pub pre: BeaconState<E>,
pub post: Option<BeaconState<E>>,
}
impl<E: EthSpec> YamlDecode for EpochProcessingCrosslinks<E> {
fn yaml_decode(yaml: &str) -> Result<Self, Error> {
Ok(serde_yaml::from_str(yaml).unwrap())
}
}
impl<E: EthSpec> Case for EpochProcessingCrosslinks<E> {
fn description(&self) -> String {
self.description.clone()
}
fn result(&self, _case_index: usize) -> Result<(), Error> {
let mut state = self.pre.clone();
let mut expected = self.post.clone();
// Processing requires the epoch cache.
state.build_all_caches(&E::default_spec()).unwrap();
let mut result = process_crosslinks(&mut state, &E::default_spec()).map(|_| state);
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

@ -1,41 +0,0 @@
use super::*;
use crate::case_result::compare_beacon_state_results_without_caches;
use serde_derive::Deserialize;
use state_processing::per_epoch_processing::process_final_updates;
use types::{BeaconState, EthSpec};
#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct EpochProcessingFinalUpdates<E: EthSpec> {
pub description: String,
pub pre: BeaconState<E>,
pub post: Option<BeaconState<E>>,
}
impl<E: EthSpec> YamlDecode for EpochProcessingFinalUpdates<E> {
fn yaml_decode(yaml: &str) -> Result<Self, Error> {
Ok(serde_yaml::from_str(yaml).unwrap())
}
}
impl<E: EthSpec> Case for EpochProcessingFinalUpdates<E> {
fn description(&self) -> String {
self.description.clone()
}
fn result(&self, _case_index: usize) -> Result<(), Error> {
let mut state = self.pre.clone();
let mut expected = self.post.clone();
let spec = &E::default_spec();
let mut result = (|| {
// Processing requires the epoch cache.
state.build_all_caches(spec)?;
process_final_updates(&mut state, spec).map(|_| state)
})();
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

@ -1,46 +0,0 @@
use super::*;
use crate::case_result::compare_beacon_state_results_without_caches;
use serde_derive::Deserialize;
use state_processing::per_epoch_processing::{
process_justification_and_finalization, validator_statuses::ValidatorStatuses,
};
use types::{BeaconState, EthSpec};
#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct EpochProcessingJustificationAndFinalization<E: EthSpec> {
pub description: String,
pub pre: BeaconState<E>,
pub post: Option<BeaconState<E>>,
}
impl<E: EthSpec> YamlDecode for EpochProcessingJustificationAndFinalization<E> {
fn yaml_decode(yaml: &str) -> Result<Self, Error> {
Ok(serde_yaml::from_str(yaml).unwrap())
}
}
impl<E: EthSpec> Case for EpochProcessingJustificationAndFinalization<E> {
fn description(&self) -> String {
self.description.clone()
}
fn result(&self, _case_index: usize) -> Result<(), Error> {
let mut state = self.pre.clone();
let mut expected = self.post.clone();
let spec = &E::default_spec();
// Processing requires the epoch cache.
state.build_all_caches(spec).unwrap();
let mut result = (|| {
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
validator_statuses.process_attestations(&state, spec)?;
process_justification_and_finalization(&mut state, &validator_statuses.total_balances)
.map(|_| state)
})();
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

@ -1,38 +0,0 @@
use super::*;
use crate::case_result::compare_beacon_state_results_without_caches;
use serde_derive::Deserialize;
use state_processing::per_epoch_processing::registry_updates::process_registry_updates;
use types::{BeaconState, EthSpec};
#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct EpochProcessingRegistryUpdates<E: EthSpec> {
pub description: String,
pub pre: BeaconState<E>,
pub post: Option<BeaconState<E>>,
}
impl<E: EthSpec> YamlDecode for EpochProcessingRegistryUpdates<E> {
fn yaml_decode(yaml: &str) -> Result<Self, Error> {
Ok(serde_yaml::from_str(yaml).unwrap())
}
}
impl<E: EthSpec> Case for EpochProcessingRegistryUpdates<E> {
fn description(&self) -> String {
self.description.clone()
}
fn result(&self, _case_index: usize) -> Result<(), Error> {
let mut state = self.pre.clone();
let mut expected = self.post.clone();
let spec = &E::default_spec();
// Processing requires the epoch cache.
state.build_all_caches(spec).unwrap();
let mut result = process_registry_updates(&mut state, spec).map(|_| state);
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

@ -1,50 +0,0 @@
use super::*;
use crate::case_result::compare_beacon_state_results_without_caches;
use serde_derive::Deserialize;
use state_processing::per_epoch_processing::{
process_slashings::process_slashings, validator_statuses::ValidatorStatuses,
};
use types::{BeaconState, EthSpec};
#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct EpochProcessingSlashings<E: EthSpec> {
pub description: String,
pub pre: BeaconState<E>,
pub post: Option<BeaconState<E>>,
}
impl<E: EthSpec> YamlDecode for EpochProcessingSlashings<E> {
fn yaml_decode(yaml: &str) -> Result<Self, Error> {
Ok(serde_yaml::from_str(yaml).unwrap())
}
}
impl<E: EthSpec> Case for EpochProcessingSlashings<E> {
fn description(&self) -> String {
self.description.clone()
}
fn result(&self, _case_index: usize) -> Result<(), Error> {
let mut state = self.pre.clone();
let mut expected = self.post.clone();
let spec = &E::default_spec();
let mut result = (|| {
// Processing requires the epoch cache.
state.build_all_caches(spec)?;
let mut validator_statuses = ValidatorStatuses::new(&state, spec)?;
validator_statuses.process_attestations(&state, spec)?;
process_slashings(
&mut state,
validator_statuses.total_balances.current_epoch,
spec,
)
.map(|_| state)
})();
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

@ -27,7 +27,7 @@ impl<E: EthSpec> LoadCase for SanitySlots<E> {
fn load_from_dir(path: &Path) -> Result<Self, Error> { fn load_from_dir(path: &Path) -> Result<Self, Error> {
let metadata_path = path.join("meta.yaml"); let metadata_path = path.join("meta.yaml");
let metadata: Metadata = if metadata_path.is_file() { let metadata: Metadata = if metadata_path.is_file() {
yaml_decode_file(&path.join("meta.yaml"))? yaml_decode_file(&metadata_path)?
} else { } else {
Metadata::default() Metadata::default()
}; };

View File

@ -1,4 +1,4 @@
use crate::cases::{self, Case, Cases, LoadCase}; use crate::cases::{self, Case, Cases, EpochTransition, LoadCase};
use crate::type_name::TypeName; use crate::type_name::TypeName;
use crate::EfTest; use crate::EfTest;
use std::fs; use std::fs;
@ -184,3 +184,21 @@ impl<E: EthSpec + TypeName> Handler for SanitySlotsHandler<E> {
"slots" "slots"
} }
} }
pub struct EpochProcessingHandler<E, T>(PhantomData<(E, T)>);
impl<E: EthSpec + TypeName, T: EpochTransition<E>> Handler for EpochProcessingHandler<E, T> {
type Case = cases::EpochProcessing<E, T>;
fn config_name() -> &'static str {
E::name()
}
fn runner_name() -> &'static str {
"epoch_processing"
}
fn handler_name() -> &'static str {
T::name()
}
}

View File

@ -2,6 +2,9 @@ use types::EthSpec;
pub use case_result::CaseResult; pub use case_result::CaseResult;
pub use cases::Case; pub use cases::Case;
pub use cases::{
Crosslinks, FinalUpdates, JustificationAndFinalization, RegistryUpdates, Slashings,
};
pub use error::Error; pub use error::Error;
pub use handler::*; pub use handler::*;
pub use yaml_decode::YamlDecode; pub use yaml_decode::YamlDecode;

View File

@ -5,57 +5,56 @@ pub trait TypeName {
fn name() -> &'static str; fn name() -> &'static str;
} }
impl TypeName for MinimalEthSpec { #[macro_export]
fn name() -> &'static str { macro_rules! type_name {
"minimal"
}
}
impl TypeName for MainnetEthSpec {
fn name() -> &'static str {
"mainnet"
}
}
macro_rules! impl_name {
($typ:ident) => { ($typ:ident) => {
type_name!($typ, stringify!($typ));
};
($typ:ident, $name:expr) => {
impl TypeName for $typ { impl TypeName for $typ {
fn name() -> &'static str { fn name() -> &'static str {
stringify!($typ) $name
} }
} }
}; };
} }
macro_rules! impl_name_generic { #[macro_export]
macro_rules! type_name_generic {
($typ:ident) => { ($typ:ident) => {
type_name_generic!($typ, stringify!($typ));
};
($typ:ident, $name:expr) => {
impl<E: EthSpec> TypeName for $typ<E> { impl<E: EthSpec> TypeName for $typ<E> {
fn name() -> &'static str { fn name() -> &'static str {
stringify!($typ) $name
} }
} }
}; };
} }
impl_name_generic!(Attestation); type_name!(MinimalEthSpec, "minimal");
impl_name!(AttestationData); type_name!(MainnetEthSpec, "mainnet");
impl_name!(AttestationDataAndCustodyBit);
impl_name_generic!(AttesterSlashing); type_name_generic!(Attestation);
impl_name_generic!(BeaconBlock); type_name!(AttestationData);
impl_name_generic!(BeaconBlockBody); type_name!(AttestationDataAndCustodyBit);
impl_name!(BeaconBlockHeader); type_name_generic!(AttesterSlashing);
impl_name_generic!(BeaconState); type_name_generic!(BeaconBlock);
impl_name!(Checkpoint); type_name_generic!(BeaconBlockBody);
impl_name_generic!(CompactCommittee); type_name!(BeaconBlockHeader);
impl_name!(Crosslink); type_name_generic!(BeaconState);
impl_name!(Deposit); type_name!(Checkpoint);
impl_name!(DepositData); type_name_generic!(CompactCommittee);
impl_name!(Eth1Data); type_name!(Crosslink);
impl_name!(Fork); type_name!(Deposit);
impl_name_generic!(HistoricalBatch); type_name!(DepositData);
impl_name_generic!(IndexedAttestation); type_name!(Eth1Data);
impl_name_generic!(PendingAttestation); type_name!(Fork);
impl_name!(ProposerSlashing); type_name_generic!(HistoricalBatch);
impl_name!(Transfer); type_name_generic!(IndexedAttestation);
impl_name!(Validator); type_name_generic!(PendingAttestation);
impl_name!(VoluntaryExit); type_name!(ProposerSlashing);
type_name!(Transfer);
type_name!(Validator);
type_name!(VoluntaryExit);

View File

@ -262,52 +262,37 @@ ssz_static_test!(ssz_static_transfer, Transfer, SR);
ssz_static_test!(ssz_static_validator, Validator); ssz_static_test!(ssz_static_validator, Validator);
ssz_static_test!(ssz_static_voluntary_exit, VoluntaryExit, SR); ssz_static_test!(ssz_static_voluntary_exit, VoluntaryExit, SR);
/*
#[test] #[test]
fn epoch_processing_justification_and_finalization() { fn epoch_processing_justification_and_finalization() {
yaml_files_in_test_dir(&Path::new("epoch_processing").join("justification_and_finalization")) EpochProcessingHandler::<MinimalEthSpec, JustificationAndFinalization>::run();
.into_par_iter() EpochProcessingHandler::<MainnetEthSpec, JustificationAndFinalization>::run();
.for_each(|file| {
Doc::assert_tests_pass(file);
});
} }
#[test] #[test]
fn epoch_processing_crosslinks() { fn epoch_processing_crosslinks() {
yaml_files_in_test_dir(&Path::new("epoch_processing").join("crosslinks")) EpochProcessingHandler::<MinimalEthSpec, Crosslinks>::run();
.into_par_iter() EpochProcessingHandler::<MainnetEthSpec, Crosslinks>::run();
.for_each(|file| {
Doc::assert_tests_pass(file);
});
} }
#[test] #[test]
fn epoch_processing_registry_updates() { fn epoch_processing_registry_updates() {
yaml_files_in_test_dir(&Path::new("epoch_processing").join("registry_updates")) EpochProcessingHandler::<MinimalEthSpec, RegistryUpdates>::run();
.into_par_iter() EpochProcessingHandler::<MainnetEthSpec, RegistryUpdates>::run();
.for_each(|file| {
Doc::assert_tests_pass(file);
});
} }
#[test] #[test]
fn epoch_processing_slashings() { fn epoch_processing_slashings() {
yaml_files_in_test_dir(&Path::new("epoch_processing").join("slashings")) EpochProcessingHandler::<MinimalEthSpec, Slashings>::run();
.into_par_iter() EpochProcessingHandler::<MainnetEthSpec, Slashings>::run();
.for_each(|file| {
Doc::assert_tests_pass(file);
});
} }
#[test] #[test]
fn epoch_processing_final_updates() { fn epoch_processing_final_updates() {
yaml_files_in_test_dir(&Path::new("epoch_processing").join("final_updates")) EpochProcessingHandler::<MainnetEthSpec, FinalUpdates>::run();
.into_par_iter() EpochProcessingHandler::<MainnetEthSpec, FinalUpdates>::run();
.for_each(|file| {
Doc::assert_tests_pass(file);
});
} }
/*
#[test] #[test]
fn genesis_initialization() { fn genesis_initialization() {
yaml_files_in_test_dir(&Path::new("genesis").join("initialization")) yaml_files_in_test_dir(&Path::new("genesis").join("initialization"))