Operations tests

This commit is contained in:
Michael Sproul 2019-08-30 16:16:38 +10:00
parent c5a22b57d2
commit fcf16faad3
No known key found for this signature in database
GPG Key ID: 77B1309D2E54E914
11 changed files with 248 additions and 405 deletions

View File

@ -11,13 +11,7 @@ mod bls_sign_msg;
mod epoch_processing;
mod genesis_initialization;
mod genesis_validity;
mod operations_attestation;
mod operations_attester_slashing;
mod operations_block_header;
mod operations_deposit;
mod operations_exit;
mod operations_proposer_slashing;
mod operations_transfer;
mod operations;
mod sanity_blocks;
mod sanity_slots;
mod shuffling;
@ -33,13 +27,7 @@ pub use bls_sign_msg::*;
pub use epoch_processing::*;
pub use genesis_initialization::*;
pub use genesis_validity::*;
pub use operations_attestation::*;
pub use operations_attester_slashing::*;
pub use operations_block_header::*;
pub use operations_deposit::*;
pub use operations_exit::*;
pub use operations_proposer_slashing::*;
pub use operations_transfer::*;
pub use operations::*;
pub use sanity_blocks::*;
pub use sanity_slots::*;
pub use shuffling::*;

View File

@ -0,0 +1,193 @@
use super::*;
use crate::bls_setting::BlsSetting;
use crate::case_result::compare_beacon_state_results_without_caches;
use crate::type_name::TypeName;
use crate::yaml_decode::{ssz_decode_file, yaml_decode_file};
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,
process_transfers,
};
use std::path::{Path, PathBuf};
use types::{
Attestation, AttesterSlashing, BeaconBlock, BeaconState, ChainSpec, Deposit, EthSpec,
ProposerSlashing, Transfer, VoluntaryExit,
};
#[derive(Debug, Clone, Default, Deserialize)]
struct Metadata {
description: Option<String>,
bls_setting: Option<BlsSetting>,
}
#[derive(Debug, Clone)]
pub struct Operations<E: EthSpec, O: Operation<E>> {
pub path: PathBuf,
metadata: Metadata,
pub pre: BeaconState<E>,
pub operation: O,
pub post: Option<BeaconState<E>>,
}
pub trait Operation<E: EthSpec>: Decode + TypeName + std::fmt::Debug {
fn handler_name() -> String {
Self::name().to_lowercase()
}
fn filename() -> String {
format!("{}.ssz", Self::handler_name())
}
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError>;
}
impl<E: EthSpec> Operation<E> for Attestation<E> {
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
process_attestations(state, &[self.clone()], spec)
}
}
impl<E: EthSpec> Operation<E> for AttesterSlashing<E> {
fn handler_name() -> String {
"attester_slashing".into()
}
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
process_attester_slashings(state, &[self.clone()], spec)
}
}
impl<E: EthSpec> Operation<E> for Deposit {
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
process_deposits(state, &[self.clone()], spec)
}
}
impl<E: EthSpec> Operation<E> for ProposerSlashing {
fn handler_name() -> String {
"proposer_slashing".into()
}
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
process_proposer_slashings(state, &[self.clone()], spec)
}
}
impl<E: EthSpec> Operation<E> for Transfer {
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
process_transfers(state, &[self.clone()], spec)
}
}
impl<E: EthSpec> Operation<E> for VoluntaryExit {
fn handler_name() -> String {
"voluntary_exit".into()
}
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
process_exits(state, &[self.clone()], spec)
}
}
impl<E: EthSpec> Operation<E> for BeaconBlock<E> {
fn handler_name() -> String {
"block_header".into()
}
fn filename() -> String {
"block.ssz".into()
}
fn apply_to(
&self,
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
process_block_header(state, self, spec, true)
}
}
impl<E: EthSpec, O: Operation<E>> LoadCase for Operations<E, O> {
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 operation = ssz_decode_file(&path.join(O::filename()))?;
let post_filename = path.join("post.ssz");
let post = if post_filename.is_file() {
Some(ssz_decode_file(&post_filename)?)
} else {
None
};
Ok(Self {
path: path.into(),
metadata,
pre,
operation,
post,
})
}
}
impl<E: EthSpec, O: Operation<E>> Case for Operations<E, O> {
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> {
self.metadata.bls_setting.unwrap_or_default().check()?;
let spec = &E::default_spec();
let mut state = self.pre.clone();
let mut expected = self.post.clone();
// Processing requires the epoch cache.
state.build_all_caches(spec).unwrap();
let mut result = self.operation.apply_to(&mut state, spec).map(|()| state);
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

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

View File

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

View File

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

View File

@ -1,42 +0,0 @@
use super::*;
use crate::bls_setting::BlsSetting;
use crate::case_result::compare_beacon_state_results_without_caches;
use serde_derive::Deserialize;
use state_processing::per_block_processing::process_deposits;
use types::{BeaconState, Deposit, EthSpec};
#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct OperationsDeposit<E: EthSpec> {
pub description: String,
pub bls_setting: Option<BlsSetting>,
pub pre: BeaconState<E>,
pub deposit: Deposit,
pub post: Option<BeaconState<E>>,
}
impl<E: EthSpec> YamlDecode for OperationsDeposit<E> {
fn yaml_decode(yaml: &str) -> Result<Self, Error> {
Ok(serde_yaml::from_str(yaml).unwrap())
}
}
impl<E: EthSpec> Case for OperationsDeposit<E> {
fn description(&self) -> String {
self.description.clone()
}
fn result(&self, _case_index: usize) -> Result<(), Error> {
self.bls_setting.unwrap_or_default().check()?;
let mut state = self.pre.clone();
let deposit = self.deposit.clone();
let mut expected = self.post.clone();
let result = process_deposits(&mut state, &[deposit], &E::default_spec());
let mut result = result.and_then(|_| Ok(state));
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

@ -1,45 +0,0 @@
use super::*;
use crate::bls_setting::BlsSetting;
use crate::case_result::compare_beacon_state_results_without_caches;
use serde_derive::Deserialize;
use state_processing::per_block_processing::process_exits;
use types::{BeaconState, EthSpec, VoluntaryExit};
#[derive(Debug, Clone, Deserialize)]
#[serde(bound = "E: EthSpec")]
pub struct OperationsExit<E: EthSpec> {
pub description: String,
pub bls_setting: Option<BlsSetting>,
pub pre: BeaconState<E>,
pub voluntary_exit: VoluntaryExit,
pub post: Option<BeaconState<E>>,
}
impl<E: EthSpec> YamlDecode for OperationsExit<E> {
fn yaml_decode(yaml: &str) -> Result<Self, Error> {
Ok(serde_yaml::from_str(yaml).unwrap())
}
}
impl<E: EthSpec> Case for OperationsExit<E> {
fn description(&self) -> String {
self.description.clone()
}
fn result(&self, _case_index: usize) -> Result<(), Error> {
self.bls_setting.unwrap_or_default().check()?;
let mut state = self.pre.clone();
let exit = self.voluntary_exit.clone();
let mut expected = self.post.clone();
// Exit processing requires the epoch cache.
state.build_all_caches(&E::default_spec()).unwrap();
let result = process_exits(&mut state, &[exit], &E::default_spec());
let mut result = result.and_then(|_| Ok(state));
compare_beacon_state_results_without_caches(&mut result, &mut expected)
}
}

View File

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

View File

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

View File

@ -1,4 +1,4 @@
use crate::cases::{self, Case, Cases, EpochTransition, LoadCase};
use crate::cases::{self, Case, Cases, EpochTransition, LoadCase, Operation};
use crate::type_name::TypeName;
use crate::EfTest;
use std::fs;
@ -20,7 +20,7 @@ pub trait Handler {
fn runner_name() -> &'static str;
fn handler_name() -> &'static str;
fn handler_name() -> String;
fn run() {
let handler_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
@ -64,8 +64,8 @@ macro_rules! bls_handler {
"bls"
}
fn handler_name() -> &'static str {
$handler_name
fn handler_name() -> String {
$handler_name.into()
}
}
};
@ -106,8 +106,8 @@ where
"ssz_static"
}
fn handler_name() -> &'static str {
T::name()
fn handler_name() -> String {
T::name().into()
}
}
@ -126,8 +126,8 @@ where
"ssz_static"
}
fn handler_name() -> &'static str {
T::name()
fn handler_name() -> String {
T::name().into()
}
}
@ -144,8 +144,8 @@ impl<E: EthSpec + TypeName> Handler for ShufflingHandler<E> {
"shuffling"
}
fn handler_name() -> &'static str {
"core"
fn handler_name() -> String {
"core".into()
}
}
@ -162,8 +162,8 @@ impl<E: EthSpec + TypeName> Handler for SanityBlocksHandler<E> {
"sanity"
}
fn handler_name() -> &'static str {
"blocks"
fn handler_name() -> String {
"blocks".into()
}
}
@ -180,8 +180,8 @@ impl<E: EthSpec + TypeName> Handler for SanitySlotsHandler<E> {
"sanity"
}
fn handler_name() -> &'static str {
"slots"
fn handler_name() -> String {
"slots".into()
}
}
@ -198,8 +198,8 @@ impl<E: EthSpec + TypeName, T: EpochTransition<E>> Handler for EpochProcessingHa
"epoch_processing"
}
fn handler_name() -> &'static str {
T::name()
fn handler_name() -> String {
T::name().into()
}
}
@ -216,8 +216,8 @@ impl<E: EthSpec + TypeName> Handler for GenesisValidityHandler<E> {
"genesis"
}
fn handler_name() -> &'static str {
"validity"
fn handler_name() -> String {
"validity".into()
}
}
@ -234,7 +234,25 @@ impl<E: EthSpec + TypeName> Handler for GenesisInitializationHandler<E> {
"genesis"
}
fn handler_name() -> &'static str {
"initialization"
fn handler_name() -> String {
"initialization".into()
}
}
pub struct OperationsHandler<E, O>(PhantomData<(E, O)>);
impl<E: EthSpec + TypeName, O: Operation<E>> Handler for OperationsHandler<E, O> {
type Case = cases::Operations<E, O>;
fn config_name() -> &'static str {
E::name()
}
fn runner_name() -> &'static str {
"operations"
}
fn handler_name() -> String {
O::handler_name()
}
}

View File

@ -36,71 +36,47 @@ fn shuffling() {
ShufflingHandler::<MainnetEthSpec>::run();
}
/*
#[test]
fn operations_deposit() {
yaml_files_in_test_dir(&Path::new("operations").join("deposit"))
.into_par_iter()
.for_each(|file| {
Doc::assert_tests_pass(file);
});
OperationsHandler::<MinimalEthSpec, Deposit>::run();
OperationsHandler::<MainnetEthSpec, Deposit>::run();
}
#[test]
fn operations_transfer() {
yaml_files_in_test_dir(&Path::new("operations").join("transfer"))
.into_par_iter()
.rev()
.for_each(|file| {
Doc::assert_tests_pass(file);
});
OperationsHandler::<MinimalEthSpec, Transfer>::run();
// Note: there are no transfer tests for mainnet
}
#[test]
fn operations_exit() {
yaml_files_in_test_dir(&Path::new("operations").join("voluntary_exit"))
.into_par_iter()
.for_each(|file| {
Doc::assert_tests_pass(file);
});
OperationsHandler::<MinimalEthSpec, VoluntaryExit>::run();
OperationsHandler::<MainnetEthSpec, VoluntaryExit>::run();
}
#[test]
fn operations_proposer_slashing() {
yaml_files_in_test_dir(&Path::new("operations").join("proposer_slashing"))
.into_par_iter()
.for_each(|file| {
Doc::assert_tests_pass(file);
});
OperationsHandler::<MinimalEthSpec, ProposerSlashing>::run();
OperationsHandler::<MainnetEthSpec, ProposerSlashing>::run();
}
#[test]
fn operations_attester_slashing() {
yaml_files_in_test_dir(&Path::new("operations").join("attester_slashing"))
.into_par_iter()
.for_each(|file| {
Doc::assert_tests_pass(file);
});
OperationsHandler::<MinimalEthSpec, AttesterSlashing<_>>::run();
OperationsHandler::<MainnetEthSpec, AttesterSlashing<_>>::run();
}
#[test]
fn operations_attestation() {
yaml_files_in_test_dir(&Path::new("operations").join("attestation"))
.into_par_iter()
.for_each(|file| {
Doc::assert_tests_pass(file);
});
OperationsHandler::<MinimalEthSpec, Attestation<_>>::run();
OperationsHandler::<MainnetEthSpec, Attestation<_>>::run();
}
#[test]
fn operations_block_header() {
yaml_files_in_test_dir(&Path::new("operations").join("block_header"))
.into_par_iter()
.for_each(|file| {
Doc::assert_tests_pass(file);
});
OperationsHandler::<MinimalEthSpec, BeaconBlock<_>>::run();
OperationsHandler::<MainnetEthSpec, BeaconBlock<_>>::run();
}
*/
#[test]
fn sanity_blocks() {
@ -114,18 +90,6 @@ fn sanity_slots() {
SanitySlotsHandler::<MainnetEthSpec>::run();
}
/*
#[test]
#[cfg(not(feature = "fake_crypto"))]
fn bls() {
yaml_files_in_test_dir(&Path::new("bls"))
.into_iter()
.for_each(|file| {
Doc::assert_tests_pass(file);
});
}
*/
#[test]
#[cfg(not(feature = "fake_crypto"))]
fn bls_aggregate_pubkeys() {
@ -259,6 +223,5 @@ fn genesis_initialization() {
#[test]
fn genesis_validity() {
GenesisValidityHandler::<MinimalEthSpec>::run();
// TODO: mainnet tests don't exist yet
// GenesisValidityHandler::<MainnetEthSpec>::run();
// Note: there are no genesis validity tests for mainnet
}