From 23a308e595cad8438aef47a1be95af2516839448 Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Wed, 28 Aug 2019 18:46:16 +1000 Subject: [PATCH] BLS and SSZ static tests --- eth2/types/src/attestation_data.rs | 16 +- eth2/types/src/checkpoint.rs | 3 +- tests/ef_tests/src/cases.rs | 27 +++ .../src/cases/bls_aggregate_pubkeys.rs | 6 +- .../ef_tests/src/cases/bls_aggregate_sigs.rs | 6 +- tests/ef_tests/src/cases/bls_g2_compressed.rs | 6 +- tests/ef_tests/src/cases/bls_priv_to_pub.rs | 6 +- tests/ef_tests/src/cases/bls_sign_msg.rs | 6 +- tests/ef_tests/src/cases/ssz_static.rs | 207 +++++++++--------- tests/ef_tests/src/doc.rs | 53 +++-- tests/ef_tests/src/handler.rs | 130 +++++++++++ tests/ef_tests/src/lib.rs | 3 + tests/ef_tests/src/type_name.rs | 61 ++++++ tests/ef_tests/src/yaml_decode.rs | 14 +- tests/ef_tests/src/yaml_decode/utils.rs | 10 - tests/ef_tests/tests/tests.rs | 105 ++++++++- 16 files changed, 481 insertions(+), 178 deletions(-) create mode 100644 tests/ef_tests/src/handler.rs create mode 100644 tests/ef_tests/src/type_name.rs delete mode 100644 tests/ef_tests/src/yaml_decode/utils.rs diff --git a/eth2/types/src/attestation_data.rs b/eth2/types/src/attestation_data.rs index f2e63598f..4d82ce126 100644 --- a/eth2/types/src/attestation_data.rs +++ b/eth2/types/src/attestation_data.rs @@ -4,25 +4,13 @@ use crate::{Checkpoint, Crosslink, Hash256}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; -use tree_hash::TreeHash; -use tree_hash_derive::{SignedRoot, TreeHash}; +use tree_hash_derive::TreeHash; /// The data upon which an attestation is based. /// /// Spec v0.8.0 #[derive( - Debug, - Clone, - PartialEq, - Eq, - Serialize, - Deserialize, - Hash, - Encode, - Decode, - TreeHash, - TestRandom, - SignedRoot, + Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, Encode, Decode, TreeHash, TestRandom, )] pub struct AttestationData { // LMD GHOST vote diff --git a/eth2/types/src/checkpoint.rs b/eth2/types/src/checkpoint.rs index dc40b336f..0c7001921 100644 --- a/eth2/types/src/checkpoint.rs +++ b/eth2/types/src/checkpoint.rs @@ -4,7 +4,7 @@ use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash::TreeHash; -use tree_hash_derive::{SignedRoot, TreeHash}; +use tree_hash_derive::TreeHash; /// Casper FFG checkpoint, used in attestations. /// @@ -22,7 +22,6 @@ use tree_hash_derive::{SignedRoot, TreeHash}; Decode, TreeHash, TestRandom, - SignedRoot, )] pub struct Checkpoint { pub epoch: Epoch, diff --git a/tests/ef_tests/src/cases.rs b/tests/ef_tests/src/cases.rs index 1ae4ea1d8..7f6ffb0c4 100644 --- a/tests/ef_tests/src/cases.rs +++ b/tests/ef_tests/src/cases.rs @@ -1,5 +1,6 @@ use super::*; use std::fmt::Debug; +use std::path::Path; mod bls_aggregate_pubkeys; mod bls_aggregate_sigs; @@ -53,6 +54,11 @@ pub use shuffling::*; pub use ssz_generic::*; pub use ssz_static::*; +pub trait LoadCase: Sized { + /// Load the test case from a test case directory. + fn load_from_dir(_path: &Path) -> Result; +} + pub trait Case: Debug { /// An optional field for implementing a custom description. /// @@ -68,6 +74,26 @@ pub trait Case: Debug { fn result(&self, case_index: usize) -> Result<(), Error>; } +pub trait BlsCase: serde::de::DeserializeOwned {} + +impl YamlDecode for T +where + T: BlsCase, +{ + fn yaml_decode(string: &str) -> Result { + serde_yaml::from_str(string).map_err(|e| Error::FailedToParseTest(format!("{:?}", e))) + } +} + +impl LoadCase for T +where + T: BlsCase, +{ + fn load_from_dir(path: &Path) -> Result { + Self::yaml_decode_file(&path.join("data.yaml")) + } +} + #[derive(Debug)] pub struct Cases { pub test_cases: Vec, @@ -86,6 +112,7 @@ where } } +// FIXME(michael): delete this impl YamlDecode for Cases { /// Decodes a YAML list of test cases fn yaml_decode(yaml: &str) -> Result { diff --git a/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs b/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs index 6e38743f2..c94e14495 100644 --- a/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs +++ b/tests/ef_tests/src/cases/bls_aggregate_pubkeys.rs @@ -9,11 +9,7 @@ pub struct BlsAggregatePubkeys { pub output: String, } -impl YamlDecode for BlsAggregatePubkeys { - fn yaml_decode(yaml: &str) -> Result { - Ok(serde_yaml::from_str(yaml).unwrap()) - } -} +impl BlsCase for BlsAggregatePubkeys {} impl Case for BlsAggregatePubkeys { fn result(&self, _case_index: usize) -> Result<(), Error> { diff --git a/tests/ef_tests/src/cases/bls_aggregate_sigs.rs b/tests/ef_tests/src/cases/bls_aggregate_sigs.rs index eeecab82c..882ad7220 100644 --- a/tests/ef_tests/src/cases/bls_aggregate_sigs.rs +++ b/tests/ef_tests/src/cases/bls_aggregate_sigs.rs @@ -9,11 +9,7 @@ pub struct BlsAggregateSigs { pub output: String, } -impl YamlDecode for BlsAggregateSigs { - fn yaml_decode(yaml: &str) -> Result { - Ok(serde_yaml::from_str(yaml).unwrap()) - } -} +impl BlsCase for BlsAggregateSigs {} impl Case for BlsAggregateSigs { fn result(&self, _case_index: usize) -> Result<(), Error> { diff --git a/tests/ef_tests/src/cases/bls_g2_compressed.rs b/tests/ef_tests/src/cases/bls_g2_compressed.rs index 185cb58f3..547d8d03a 100644 --- a/tests/ef_tests/src/cases/bls_g2_compressed.rs +++ b/tests/ef_tests/src/cases/bls_g2_compressed.rs @@ -15,11 +15,7 @@ pub struct BlsG2Compressed { pub output: Vec, } -impl YamlDecode for BlsG2Compressed { - fn yaml_decode(yaml: &str) -> Result { - Ok(serde_yaml::from_str(yaml).unwrap()) - } -} +impl BlsCase for BlsG2Compressed {} impl Case for BlsG2Compressed { fn result(&self, _case_index: usize) -> Result<(), Error> { diff --git a/tests/ef_tests/src/cases/bls_priv_to_pub.rs b/tests/ef_tests/src/cases/bls_priv_to_pub.rs index d72a43bbb..869a0891c 100644 --- a/tests/ef_tests/src/cases/bls_priv_to_pub.rs +++ b/tests/ef_tests/src/cases/bls_priv_to_pub.rs @@ -9,11 +9,7 @@ pub struct BlsPrivToPub { pub output: String, } -impl YamlDecode for BlsPrivToPub { - fn yaml_decode(yaml: &str) -> Result { - Ok(serde_yaml::from_str(yaml).unwrap()) - } -} +impl BlsCase for BlsPrivToPub {} impl Case for BlsPrivToPub { fn result(&self, _case_index: usize) -> Result<(), Error> { diff --git a/tests/ef_tests/src/cases/bls_sign_msg.rs b/tests/ef_tests/src/cases/bls_sign_msg.rs index e62c3550f..476ecdefb 100644 --- a/tests/ef_tests/src/cases/bls_sign_msg.rs +++ b/tests/ef_tests/src/cases/bls_sign_msg.rs @@ -16,11 +16,7 @@ pub struct BlsSign { pub output: String, } -impl YamlDecode for BlsSign { - fn yaml_decode(yaml: &str) -> Result { - Ok(serde_yaml::from_str(yaml).unwrap()) - } -} +impl BlsCase for BlsSign {} impl Case for BlsSign { fn result(&self, _case_index: usize) -> Result<(), Error> { diff --git a/tests/ef_tests/src/cases/ssz_static.rs b/tests/ef_tests/src/cases/ssz_static.rs index 96ba38b6a..6a949073d 100644 --- a/tests/ef_tests/src/cases/ssz_static.rs +++ b/tests/ef_tests/src/cases/ssz_static.rs @@ -3,125 +3,120 @@ use crate::case_result::compare_result; use serde_derive::Deserialize; use ssz::{Decode, Encode}; use std::fmt::Debug; -use std::marker::PhantomData; -use tree_hash::TreeHash; -use types::{ - test_utils::TestRandom, Attestation, AttestationData, AttestationDataAndCustodyBit, - AttesterSlashing, BeaconBlock, BeaconBlockBody, BeaconBlockHeader, BeaconState, Checkpoint, - CompactCommittee, Crosslink, Deposit, DepositData, Eth1Data, EthSpec, Fork, Hash256, - HistoricalBatch, IndexedAttestation, PendingAttestation, ProposerSlashing, Transfer, Validator, - VoluntaryExit, -}; - -// Enum variant names are used by Serde when deserializing the test YAML -#[allow(clippy::large_enum_variant)] -#[derive(Debug, Clone, Deserialize)] -pub enum SszStatic -where - E: EthSpec, -{ - Fork(SszStaticInner), - Crosslink(SszStaticInner), - Checkpoint(SszStaticInner), - CompactCommittee(SszStaticInner, E>), - Eth1Data(SszStaticInner), - AttestationData(SszStaticInner), - AttestationDataAndCustodyBit(SszStaticInner), - IndexedAttestation(SszStaticInner, E>), - DepositData(SszStaticInner), - BeaconBlockHeader(SszStaticInner), - Validator(SszStaticInner), - PendingAttestation(SszStaticInner, E>), - HistoricalBatch(SszStaticInner, E>), - ProposerSlashing(SszStaticInner), - AttesterSlashing(SszStaticInner, E>), - Attestation(SszStaticInner, E>), - Deposit(SszStaticInner), - VoluntaryExit(SszStaticInner), - Transfer(SszStaticInner), - BeaconBlockBody(SszStaticInner, E>), - BeaconBlock(SszStaticInner, E>), - BeaconState(SszStaticInner, E>), -} +use std::fs; +use tree_hash::{SignedRoot, TreeHash}; +use types::Hash256; #[derive(Debug, Clone, Deserialize)] -pub struct SszStaticInner -where - E: EthSpec, -{ - pub value: T, - pub serialized: String, - pub root: String, - #[serde(skip, default)] - _phantom: PhantomData, +struct SszStaticRoots { + root: String, + signing_root: Option, } -impl YamlDecode for SszStatic { +impl YamlDecode for SszStaticRoots { fn yaml_decode(yaml: &str) -> Result { - serde_yaml::from_str(yaml).map_err(|e| Error::FailedToParseTest(format!("{:?}", e))) + Ok(serde_yaml::from_str(yaml).unwrap()) } } -impl Case for SszStatic { - fn result(&self, _case_index: usize) -> Result<(), Error> { - use self::SszStatic::*; - - match *self { - Fork(ref val) => ssz_static_test(val), - Crosslink(ref val) => ssz_static_test(val), - Checkpoint(ref val) => ssz_static_test(val), - CompactCommittee(ref val) => ssz_static_test(val), - Eth1Data(ref val) => ssz_static_test(val), - AttestationData(ref val) => ssz_static_test(val), - AttestationDataAndCustodyBit(ref val) => ssz_static_test(val), - IndexedAttestation(ref val) => ssz_static_test(val), - DepositData(ref val) => ssz_static_test(val), - BeaconBlockHeader(ref val) => ssz_static_test(val), - Validator(ref val) => ssz_static_test(val), - PendingAttestation(ref val) => ssz_static_test(val), - HistoricalBatch(ref val) => ssz_static_test(val), - ProposerSlashing(ref val) => ssz_static_test(val), - AttesterSlashing(ref val) => ssz_static_test(val), - Attestation(ref val) => ssz_static_test(val), - Deposit(ref val) => ssz_static_test(val), - VoluntaryExit(ref val) => ssz_static_test(val), - Transfer(ref val) => ssz_static_test(val), - BeaconBlockBody(ref val) => ssz_static_test(val), - BeaconBlock(ref val) => ssz_static_test(val), - BeaconState(ref val) => ssz_static_test(val), - } - } +#[derive(Debug, Clone)] +pub struct SszStatic { + roots: SszStaticRoots, + serialized: Vec, + value: T, } -fn ssz_static_test(tc: &SszStaticInner) -> Result<(), Error> -where - T: Clone - + Decode - + Debug - + Encode - + PartialEq - + serde::de::DeserializeOwned - + TreeHash - + TestRandom, +#[derive(Debug, Clone)] +pub struct SszStaticSR { + roots: SszStaticRoots, + serialized: Vec, + value: T, +} + +// Trait alias for all deez bounds +pub trait SszStaticType: + serde::de::DeserializeOwned + Decode + Encode + TreeHash + Clone + PartialEq + Debug { - // Verify we can decode SSZ in the same way we can decode YAML. - let ssz = hex::decode(&tc.serialized[2..]) - .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let expected = tc.value.clone(); - let decode_result = T::from_ssz_bytes(&ssz); - compare_result(&decode_result, &Some(expected))?; +} - // Verify we can encode the result back into original ssz bytes - let decoded = decode_result.unwrap(); - let encoded_result = decoded.as_ssz_bytes(); - compare_result::, Error>(&Ok(encoded_result), &Some(ssz))?; +impl SszStaticType for T where + T: serde::de::DeserializeOwned + Decode + Encode + TreeHash + Clone + PartialEq + Debug +{ +} - // Verify the TreeHash root of the decoded struct matches the test. - let expected_root = - &hex::decode(&tc.root[2..]).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; - let expected_root = Hash256::from_slice(&expected_root); - let tree_hash_root = Hash256::from_slice(&decoded.tree_hash_root()); - compare_result::(&Ok(tree_hash_root), &Some(expected_root))?; +fn load_from_dir(path: &Path) -> Result<(SszStaticRoots, Vec, T), Error> { + // FIXME: set description/name + let roots = SszStaticRoots::yaml_decode_file(&path.join("roots.yaml"))?; + + let serialized = fs::read(&path.join("serialized.ssz")).expect("serialized.ssz exists"); + + let yaml = fs::read_to_string(&path.join("value.yaml")).expect("value.yaml exists"); + let value = + serde_yaml::from_str(&yaml).map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + + Ok((roots, serialized, value)) +} + +impl LoadCase for SszStatic { + fn load_from_dir(path: &Path) -> Result { + load_from_dir(path).map(|(roots, serialized, value)| Self { + roots, + serialized, + value, + }) + } +} + +impl LoadCase for SszStaticSR { + fn load_from_dir(path: &Path) -> Result { + load_from_dir(path).map(|(roots, serialized, value)| Self { + roots, + serialized, + value, + }) + } +} + +fn check_serialization(value: &T, serialized: &[u8]) -> Result<(), Error> { + // Check serialization + let serialized_result = value.as_ssz_bytes(); + compare_result::, Error>(&Ok(serialized_result), &Some(serialized.to_vec()))?; + + // Check deserialization + let deserialized_result = T::from_ssz_bytes(serialized); + compare_result(&deserialized_result, &Some(value.clone()))?; Ok(()) } + +fn check_tree_hash(expected_str: &str, actual_root: Vec) -> Result<(), Error> { + let expected_root = hex::decode(&expected_str[2..]) + .map_err(|e| Error::FailedToParseTest(format!("{:?}", e)))?; + let expected_root = Hash256::from_slice(&expected_root); + let tree_hash_root = Hash256::from_slice(&actual_root); + compare_result::(&Ok(tree_hash_root), &Some(expected_root)) +} + +impl Case for SszStatic { + fn result(&self, _case_index: usize) -> Result<(), Error> { + check_serialization(&self.value, &self.serialized)?; + check_tree_hash(&self.roots.root, self.value.tree_hash_root())?; + Ok(()) + } +} + +impl Case for SszStaticSR { + fn result(&self, _case_index: usize) -> Result<(), Error> { + check_serialization(&self.value, &self.serialized)?; + check_tree_hash(&self.roots.root, self.value.tree_hash_root())?; + check_tree_hash( + &self + .roots + .signing_root + .as_ref() + .expect("signed root exists"), + self.value.signed_root(), + )?; + Ok(()) + } +} diff --git a/tests/ef_tests/src/doc.rs b/tests/ef_tests/src/doc.rs index 7dfe9954c..f3a41697e 100644 --- a/tests/ef_tests/src/doc.rs +++ b/tests/ef_tests/src/doc.rs @@ -2,10 +2,14 @@ use crate::case_result::CaseResult; use crate::cases::*; use crate::doc_header::DocHeader; use crate::error::Error; -use crate::yaml_decode::{yaml_split_header_and_cases, YamlDecode}; +use crate::yaml_decode::YamlDecode; use crate::EfTest; use serde_derive::Deserialize; -use std::{fs::File, io::prelude::*, path::PathBuf}; +use std::{ + fs::File, + io::prelude::*, + path::{Path, PathBuf}, +}; use types::{MainnetEthSpec, MinimalEthSpec}; #[derive(Debug, Deserialize)] @@ -19,15 +23,13 @@ impl Doc { fn from_path(path: PathBuf) -> Self { let mut file = File::open(path.clone()).unwrap(); - let mut yaml = String::new(); - file.read_to_string(&mut yaml).unwrap(); - - let (header_yaml, cases_yaml) = yaml_split_header_and_cases(yaml.clone()); + let mut cases_yaml = String::new(); + file.read_to_string(&mut cases_yaml).unwrap(); Self { - header_yaml, cases_yaml, path, + header_yaml: String::new(), } } @@ -40,8 +42,6 @@ impl Doc { header.config.as_ref(), ) { ("ssz", "uint", _) => run_test::(self), - ("ssz", "static", "minimal") => run_test::>(self), - ("ssz", "static", "mainnet") => run_test::>(self), ("sanity", "slots", "minimal") => run_test::>(self), // FIXME: skipped due to compact committees issue ("sanity", "slots", "mainnet") => vec![], // run_test::>(self), @@ -172,14 +172,36 @@ impl Doc { } } -pub fn run_test(doc: &Doc) -> Vec +pub fn assert_tests_pass(path: &Path, results: &[CaseResult]) { + let doc = Doc { + header_yaml: String::new(), + cases_yaml: String::new(), + path: path.into(), + }; + + let (failed, skipped_bls, skipped_known_failures) = categorize_results(results); + + if failed.len() + skipped_known_failures.len() > 0 { + print_results( + &doc, + &failed, + &skipped_bls, + &skipped_known_failures, + &results, + ); + if !failed.is_empty() { + panic!("Tests failed (see above)"); + } + } else { + println!("Passed {} tests in {}", results.len(), path.display()); + } +} + +pub fn run_test(_: &Doc) -> Vec where Cases: EfTest + YamlDecode, { - // Pass only the "test_cases" YAML string to `yaml_decode`. - let test_cases: Cases = Cases::yaml_decode(&doc.cases_yaml).unwrap(); - - test_cases.test_results() + panic!("FIXME(michael): delete this") } pub fn categorize_results( @@ -208,7 +230,6 @@ pub fn print_results( skipped_known_failures: &[&CaseResult], results: &[CaseResult], ) { - let header: DocHeader = serde_yaml::from_str(&doc.header_yaml).unwrap(); println!("--------------------------------------------------"); println!( "Test {}", @@ -218,7 +239,7 @@ pub fn print_results( "Failure" } ); - println!("Title: {}", header.title); + println!("Title: TODO"); println!("File: {:?}", doc.path); println!( "{} tests, {} failed, {} skipped (known failure), {} skipped (bls), {} passed. (See below for errors)", diff --git a/tests/ef_tests/src/handler.rs b/tests/ef_tests/src/handler.rs new file mode 100644 index 000000000..1dac988ac --- /dev/null +++ b/tests/ef_tests/src/handler.rs @@ -0,0 +1,130 @@ +use crate::cases::{self, Case, Cases, LoadCase}; +use crate::type_name::TypeName; +use crate::EfTest; +use std::fs; +use std::marker::PhantomData; +use std::path::PathBuf; +use tree_hash::SignedRoot; + +pub trait Handler { + type Case: Case + LoadCase; + + fn config_name() -> &'static str { + "general" + } + + fn fork_name() -> &'static str { + "phase0" + } + + fn runner_name() -> &'static str; + + fn handler_name() -> &'static str; + + 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(Self::runner_name()) + .join(Self::handler_name()); + + // Iterate through test suites + // TODO: parallelism + // TODO: error handling? + let test_cases = fs::read_dir(&handler_path) + .expect("open main directory") + .flat_map(|entry| { + entry + .ok() + .filter(|e| e.file_type().map(|ty| ty.is_dir()).unwrap_or(false)) + }) + .flat_map(|suite| fs::read_dir(suite.path()).expect("open suite dir")) + .flat_map(Result::ok) + .map(|test_case_dir| Self::Case::load_from_dir(&test_case_dir.path()).expect("loads")) + .collect::>(); + + let results = Cases { test_cases }.test_results(); + + crate::doc::assert_tests_pass(&handler_path, &results); + } +} + +macro_rules! bls_handler { + ($runner_name: ident, $case_name:ident, $handler_name:expr) => { + pub struct $runner_name; + + impl Handler for $runner_name { + type Case = cases::$case_name; + + fn runner_name() -> &'static str { + "bls" + } + + fn handler_name() -> &'static str { + $handler_name + } + } + }; +} + +bls_handler!( + BlsAggregatePubkeysHandler, + BlsAggregatePubkeys, + "aggregate_pubkeys" +); +bls_handler!(BlsAggregateSigsHandler, BlsAggregateSigs, "aggregate_sigs"); +bls_handler!( + BlsG2CompressedHandler, + BlsG2Compressed, + "msg_hash_compressed" +); +bls_handler!(BlsPrivToPubHandler, BlsPrivToPub, "priv_to_pub"); +bls_handler!(BlsSignMsgHandler, BlsSign, "sign_msg"); + +/// Handler for SSZ types that do not implement `SignedRoot`. +pub struct SszStaticHandler(PhantomData<(T, E)>); + +/// Handler for SSZ types that do implement `SignedRoot`. +pub struct SszStaticSRHandler(PhantomData<(T, E)>); + +impl Handler for SszStaticHandler +where + T: cases::SszStaticType + TypeName, + E: TypeName, +{ + type Case = cases::SszStatic; + + fn config_name() -> &'static str { + E::name() + } + + fn runner_name() -> &'static str { + "ssz_static" + } + + fn handler_name() -> &'static str { + T::name() + } +} + +impl Handler for SszStaticSRHandler +where + T: cases::SszStaticType + SignedRoot + TypeName, + E: TypeName, +{ + type Case = cases::SszStaticSR; + + fn config_name() -> &'static str { + E::name() + } + + fn runner_name() -> &'static str { + "ssz_static" + } + + fn handler_name() -> &'static str { + T::name() + } +} diff --git a/tests/ef_tests/src/lib.rs b/tests/ef_tests/src/lib.rs index fdd4e7b85..cc17c3ea4 100644 --- a/tests/ef_tests/src/lib.rs +++ b/tests/ef_tests/src/lib.rs @@ -4,6 +4,7 @@ pub use case_result::CaseResult; pub use cases::Case; pub use doc::Doc; pub use error::Error; +pub use handler::*; pub use yaml_decode::YamlDecode; mod bls_setting; @@ -12,6 +13,8 @@ mod cases; mod doc; mod doc_header; mod error; +mod handler; +mod type_name; mod yaml_decode; /// Defined where an object can return the results of some test(s) adhering to the Ethereum diff --git a/tests/ef_tests/src/type_name.rs b/tests/ef_tests/src/type_name.rs new file mode 100644 index 000000000..fe55a7f5f --- /dev/null +++ b/tests/ef_tests/src/type_name.rs @@ -0,0 +1,61 @@ +//! Mapping from types to canonical string identifiers used in testing. +use types::*; + +pub trait TypeName { + fn name() -> &'static str; +} + +impl TypeName for MinimalEthSpec { + fn name() -> &'static str { + "minimal" + } +} + +impl TypeName for MainnetEthSpec { + fn name() -> &'static str { + "mainnet" + } +} + +macro_rules! impl_name { + ($typ:ident) => { + impl TypeName for $typ { + fn name() -> &'static str { + stringify!($typ) + } + } + }; +} + +macro_rules! impl_name_generic { + ($typ:ident) => { + impl TypeName for $typ { + fn name() -> &'static str { + stringify!($typ) + } + } + }; +} + +impl_name_generic!(Attestation); +impl_name!(AttestationData); +impl_name!(AttestationDataAndCustodyBit); +impl_name_generic!(AttesterSlashing); +impl_name_generic!(BeaconBlock); +impl_name_generic!(BeaconBlockBody); +impl_name!(BeaconBlockHeader); +impl_name_generic!(BeaconState); +impl_name!(Checkpoint); +impl_name_generic!(CompactCommittee); +impl_name!(Crosslink); +impl_name!(Deposit); +impl_name!(DepositData); +impl_name!(Eth1Data); +impl_name!(Fork); +impl_name_generic!(HistoricalBatch); +impl_name_generic!(IndexedAttestation); +impl_name_generic!(PendingAttestation); +impl_name!(ProposerSlashing); +impl_name!(Transfer); +impl_name!(Validator); +impl_name!(VoluntaryExit); diff --git a/tests/ef_tests/src/yaml_decode.rs b/tests/ef_tests/src/yaml_decode.rs index c89dd92a9..af122fb0c 100644 --- a/tests/ef_tests/src/yaml_decode.rs +++ b/tests/ef_tests/src/yaml_decode.rs @@ -1,14 +1,20 @@ use super::*; use ethereum_types::{U128, U256}; +use std::fs; +use std::path::Path; use types::Fork; -mod utils; - -pub use utils::*; - pub trait YamlDecode: Sized { /// Decode an object from the test specification YAML. fn yaml_decode(string: &str) -> Result; + + fn yaml_decode_file(path: &Path) -> Result { + fs::read_to_string(path) + .map_err(|e| { + Error::FailedToParseTest(format!("Unable to load {}: {:?}", path.display(), e)) + }) + .and_then(|s| Self::yaml_decode(&s)) + } } /// Basic types can general be decoded with the `parse` fn if they implement `str::FromStr`. diff --git a/tests/ef_tests/src/yaml_decode/utils.rs b/tests/ef_tests/src/yaml_decode/utils.rs deleted file mode 100644 index 7b6caac72..000000000 --- a/tests/ef_tests/src/yaml_decode/utils.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub fn yaml_split_header_and_cases(mut yaml: String) -> (String, String) { - let test_cases_start = yaml.find("\ntest_cases:\n").unwrap(); - // + 1 to skip the \n we used for matching. - let mut test_cases = yaml.split_off(test_cases_start + 1); - - let end_of_first_line = test_cases.find('\n').unwrap(); - let test_cases = test_cases.split_off(end_of_first_line + 1); - - (yaml, test_cases) -} diff --git a/tests/ef_tests/tests/tests.rs b/tests/ef_tests/tests/tests.rs index deb699e78..000c53330 100644 --- a/tests/ef_tests/tests/tests.rs +++ b/tests/ef_tests/tests/tests.rs @@ -1,12 +1,20 @@ use ef_tests::*; use rayon::prelude::*; use std::path::{Path, PathBuf}; +use types::{ + Attestation, AttestationData, AttestationDataAndCustodyBit, AttesterSlashing, BeaconBlock, + BeaconBlockBody, BeaconBlockHeader, BeaconState, Checkpoint, CompactCommittee, Crosslink, + Deposit, DepositData, Eth1Data, Fork, HistoricalBatch, IndexedAttestation, MainnetEthSpec, + MinimalEthSpec, PendingAttestation, ProposerSlashing, Transfer, Validator, VoluntaryExit, +}; use walkdir::WalkDir; fn yaml_files_in_test_dir(dir: &Path) -> Vec { let base_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")) .join("eth2.0-spec-tests") .join("tests") + .join("general") + .join("phase0") .join(dir); assert!( @@ -155,12 +163,107 @@ fn sanity_slots() { #[cfg(not(feature = "fake_crypto"))] fn bls() { yaml_files_in_test_dir(&Path::new("bls")) - .into_par_iter() + .into_iter() .for_each(|file| { Doc::assert_tests_pass(file); }); } +#[test] +#[cfg(not(feature = "fake_crypto"))] +fn bls_aggregate_pubkeys() { + BlsAggregatePubkeysHandler::run(); +} + +#[test] +#[cfg(not(feature = "fake_crypto"))] +fn bls_aggregate_sigs() { + BlsAggregateSigsHandler::run(); +} + +#[test] +#[cfg(not(feature = "fake_crypto"))] +fn bls_msg_hash_g2_compressed() { + BlsG2CompressedHandler::run(); +} + +#[test] +#[cfg(not(feature = "fake_crypto"))] +fn bls_priv_to_pub() { + BlsPrivToPubHandler::run(); +} + +#[test] +#[cfg(not(feature = "fake_crypto"))] +fn bls_sign_msg() { + BlsSignMsgHandler::run(); +} + +macro_rules! ssz_static_test { + // Signed-root + ($test_name:ident, $typ:ident$(<$generics:tt>)?, SR) => { + ssz_static_test!($test_name, SszStaticSRHandler, $typ$(<$generics>)?); + }; + // Non-signed root + ($test_name:ident, $typ:ident$(<$generics:tt>)?) => { + ssz_static_test!($test_name, SszStaticHandler, $typ$(<$generics>)?); + }; + // Generic + ($test_name:ident, $handler:ident, $typ:ident<_>) => { + ssz_static_test!( + $test_name, $handler, { + ($typ, MinimalEthSpec), + ($typ, MainnetEthSpec) + } + ); + }; + // Non-generic + ($test_name:ident, $handler:ident, $typ:ident) => { + ssz_static_test!( + $test_name, $handler, { + ($typ, MinimalEthSpec), + ($typ, MainnetEthSpec) + } + ); + }; + // Base case + ($test_name:ident, $handler:ident, { $(($typ:ty, $spec:ident)),+ }) => { + #[test] + #[cfg(feature = "fake_crypto")] + fn $test_name() { + $( + $handler::<$typ, $spec>::run(); + )+ + } + }; +} + +ssz_static_test!(ssz_static_attestation, Attestation<_>, SR); +ssz_static_test!(ssz_static_attestation_data, AttestationData); +ssz_static_test!( + ssz_static_attestation_data_and_custody_bit, + AttestationDataAndCustodyBit +); +ssz_static_test!(ssz_static_attester_slashing, AttesterSlashing<_>); +ssz_static_test!(ssz_static_beacon_block, BeaconBlock<_>, SR); +ssz_static_test!(ssz_static_beacon_block_body, BeaconBlockBody<_>); +ssz_static_test!(ssz_static_beacon_block_header, BeaconBlockHeader, SR); +ssz_static_test!(ssz_static_beacon_state, BeaconState<_>); +ssz_static_test!(ssz_static_checkpoint, Checkpoint); +ssz_static_test!(ssz_static_compact_committee, CompactCommittee<_>); +ssz_static_test!(ssz_static_crosslink, Crosslink); +ssz_static_test!(ssz_static_deposit, Deposit); +ssz_static_test!(ssz_static_deposit_data, DepositData, SR); +ssz_static_test!(ssz_static_eth1_data, Eth1Data); +ssz_static_test!(ssz_static_fork, Fork); +ssz_static_test!(ssz_static_historical_batch, HistoricalBatch<_>); +ssz_static_test!(ssz_static_indexed_attestation, IndexedAttestation<_>, SR); +ssz_static_test!(ssz_static_pending_attestation, PendingAttestation<_>); +ssz_static_test!(ssz_static_proposer_slashing, ProposerSlashing); +ssz_static_test!(ssz_static_transfer, Transfer, SR); +ssz_static_test!(ssz_static_validator, Validator); +ssz_static_test!(ssz_static_voluntary_exit, VoluntaryExit, SR); + #[test] fn epoch_processing_justification_and_finalization() { yaml_files_in_test_dir(&Path::new("epoch_processing").join("justification_and_finalization"))