From 460d711e672c0f74ae42cc6ff430ee40df4d5068 Mon Sep 17 00:00:00 2001 From: Wei Tang <accounts@that.world> Date: Mon, 25 Mar 2019 13:35:08 +0100 Subject: [PATCH] Integrate state test to CI (#109) * Ignore keys, data* and nix shell config * Add eth2.0-tests submodule * Move structs to yamltests * Integrate small config state test to CI * Update submodule in init script * Use GIT_SUBMODULE_STRATEGY --- .gitignore | 3 + .gitlab-ci.yml | 2 +- .gitmodules | 3 + yamltests/res/eth2.0-tests | 1 + yamltests/src/lib.rs | 159 +++++++++++++++++++++++++++++++++++++ yamltests/src/main.rs | 147 +--------------------------------- 6 files changed, 170 insertions(+), 145 deletions(-) create mode 100644 .gitmodules create mode 160000 yamltests/res/eth2.0-tests create mode 100644 yamltests/src/lib.rs diff --git a/.gitignore b/.gitignore index a1f5457e..73ff894c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ /**/target **/*.rs.bk +/keys +/data* +/shell.nix diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8be61ba9..17803163 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,7 @@ variables: CI_SERVER_NAME: "GitLab CI" CARGO_HOME: "${CI_PROJECT_DIR}/.cargo" RUST_TOOLCHAIN: "stable" + GIT_SUBMODULE_STRATEGY: "recursive" cache: key: "${CI_JOB_NAME}" @@ -51,4 +52,3 @@ build-linux: - master - tags - web - diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..b01e31d3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "yamltests/res/eth2.0-tests"] + path = yamltests/res/eth2.0-tests + url = https://github.com/ethereum/eth2.0-tests diff --git a/yamltests/res/eth2.0-tests b/yamltests/res/eth2.0-tests new file mode 160000 index 00000000..3ec28295 --- /dev/null +++ b/yamltests/res/eth2.0-tests @@ -0,0 +1 @@ +Subproject commit 3ec28295b0c8365f0ec7ad79cfe933755021ee1b diff --git a/yamltests/src/lib.rs b/yamltests/src/lib.rs new file mode 100644 index 00000000..2f8db035 --- /dev/null +++ b/yamltests/src/lib.rs @@ -0,0 +1,159 @@ +use std::io::{self, Write}; +use std::collections::HashMap; + +use serde_derive::{Serialize, Deserialize}; +use ssz::FixedVec; +use primitives::H256; +use serenity::{BeaconState, BeaconBlock, Slot, Fork, Timestamp, Validator, Epoch, Shard, Eth1Data, Eth1DataVote, PendingAttestation, Crosslink, BeaconBlockHeader}; + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct ExpectedBeaconState { + // Misc + pub slot: Option<Slot>, + pub genesis_time: Option<Timestamp>, + pub fork: Option<Fork>, + + // Validator registry + pub validator_registry: Option<Vec<Validator>>, + pub validator_balances: Option<Vec<u64>>, + pub validator_registry_update_epoch: Option<Epoch>, + + // Randomness and committees + pub latest_randao_mixes: Option<FixedVec<H256>>, + pub previous_shuffling_start_shard: Option<Shard>, + pub current_shuffling_start_shard: Option<Shard>, + pub previous_shuffling_epoch: Option<Epoch>, + pub current_shuffling_epoch: Option<Epoch>, + pub previous_shuffling_seed: Option<H256>, + pub current_shuffling_seed: Option<H256>, + + // Finality + pub previous_epoch_attestations: Option<Vec<PendingAttestation>>, + pub current_epoch_attestations: Option<Vec<PendingAttestation>>, + pub previous_justified_epoch: Option<Epoch>, + pub current_justified_epoch: Option<Epoch>, + pub previous_justified_root: Option<H256>, + pub current_justified_root: Option<H256>, + pub justification_bitfield: Option<u64>, + pub finalized_epoch: Option<Epoch>, + pub finalized_root: Option<H256>, + + // Recent state + pub latest_crosslinks: Option<FixedVec<Crosslink>>, + pub latest_block_roots: Option<FixedVec<H256>>, + pub latest_state_roots: Option<FixedVec<H256>>, + pub latest_active_index_roots: Option<FixedVec<H256>>, + pub latest_slashed_balances: Option<FixedVec<u64>>, + pub latest_block_header: Option<BeaconBlockHeader>, + pub historical_roots: Option<Vec<H256>>, + + // Ethereum 1.0 chain data + pub latest_eth1_data: Option<Eth1Data>, + pub eth1_data_votes: Option<Vec<Eth1DataVote>>, + pub deposit_index: Option<u64>, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct Collection { + pub title: String, + pub summary: String, + pub test_suite: String, + pub fork: String, + pub test_cases: Vec<Test>, +} + +#[derive(Serialize, Deserialize, Debug)] +#[serde(deny_unknown_fields)] +pub struct Test { + pub name: String, + pub config: HashMap<String, String>, + pub verify_signatures: bool, + pub initial_state: BeaconState, + pub blocks: Vec<BeaconBlock>, + pub expected_state: ExpectedBeaconState, +} + +pub fn run_collection(coll: Collection, only: Option<&str>) { + for test in coll.test_cases { + if let Some(only) = only { + if test.name != only { + continue + } + } + run_test(test); + } +} + +pub fn run_test(test: Test) { + print!("Running test: {} ...", test.name); + io::stdout().flush().ok().expect("Could not flush stdout"); + let mut state = test.initial_state; + + for block in test.blocks { + match serenity::execute_block(&block, &mut state) { + Ok(()) => { + println!(" done"); + }, + Err(err) => { + println!(" failed\n"); + println!("Error: {:?}", err); + panic!(); + } + } + } + + check_expected(&state, test.expected_state); +} + +pub fn check_expected(state: &BeaconState, expected: ExpectedBeaconState) { + macro_rules! check { + ( $($field:tt,)+ ) => { + $( + if let Some($field) = expected.$field { + if $field != state.$field { + println!("\nExpected state check failed for {}", stringify!($field)); + println!("Expected: {:?}", $field); + println!("Actual: {:?}", state.$field); + panic!(); + } + } + )+ + } + } + + check!( + // Misc + slot, genesis_time, fork, + // Validator registry + validator_registry, validator_balances, validator_registry_update_epoch, + // Randomness and committees + latest_randao_mixes, previous_shuffling_start_shard, + current_shuffling_start_shard, previous_shuffling_epoch, + current_shuffling_epoch, previous_shuffling_seed, + current_shuffling_seed, + // Finality + previous_epoch_attestations, current_epoch_attestations, + previous_justified_epoch, current_justified_epoch, + previous_justified_root, current_justified_root, + justification_bitfield, finalized_epoch, finalized_root, + // Recent state + latest_crosslinks, latest_block_roots, latest_state_roots, + latest_active_index_roots, latest_slashed_balances, + latest_block_header, historical_roots, + // Ethereum 1.0 chain data + latest_eth1_data, eth1_data_votes, deposit_index, + ); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sanity_check_small_config_32_vals() { + let coll = serde_yaml::from_str(&include_str!("../res/eth2.0-tests/state/sanity-check_small-config_32-vals.yaml")).unwrap(); + run_collection(coll, None); + } +} diff --git a/yamltests/src/main.rs b/yamltests/src/main.rs index 438d65d0..0861eab6 100644 --- a/yamltests/src/main.rs +++ b/yamltests/src/main.rs @@ -1,81 +1,8 @@ -use std::collections::HashMap; use std::fs::File; -use std::io::{self, BufReader, Write}; +use std::io::BufReader; use clap::{App, Arg}; -use serde_derive::{Serialize, Deserialize}; -use ssz::FixedVec; -use primitives::H256; -use serenity::{BeaconState, BeaconBlock, Slot, Fork, Timestamp, Validator, Epoch, Shard, Eth1Data, Eth1DataVote, PendingAttestation, Crosslink, BeaconBlockHeader}; - -#[derive(Serialize, Deserialize, Debug)] -#[serde(deny_unknown_fields)] -pub struct ExpectedBeaconState { - // Misc - pub slot: Option<Slot>, - pub genesis_time: Option<Timestamp>, - pub fork: Option<Fork>, - - // Validator registry - pub validator_registry: Option<Vec<Validator>>, - pub validator_balances: Option<Vec<u64>>, - pub validator_registry_update_epoch: Option<Epoch>, - - // Randomness and committees - pub latest_randao_mixes: Option<FixedVec<H256>>, - pub previous_shuffling_start_shard: Option<Shard>, - pub current_shuffling_start_shard: Option<Shard>, - pub previous_shuffling_epoch: Option<Epoch>, - pub current_shuffling_epoch: Option<Epoch>, - pub previous_shuffling_seed: Option<H256>, - pub current_shuffling_seed: Option<H256>, - - // Finality - pub previous_epoch_attestations: Option<Vec<PendingAttestation>>, - pub current_epoch_attestations: Option<Vec<PendingAttestation>>, - pub previous_justified_epoch: Option<Epoch>, - pub current_justified_epoch: Option<Epoch>, - pub previous_justified_root: Option<H256>, - pub current_justified_root: Option<H256>, - pub justification_bitfield: Option<u64>, - pub finalized_epoch: Option<Epoch>, - pub finalized_root: Option<H256>, - - // Recent state - pub latest_crosslinks: Option<FixedVec<Crosslink>>, - pub latest_block_roots: Option<FixedVec<H256>>, - pub latest_state_roots: Option<FixedVec<H256>>, - pub latest_active_index_roots: Option<FixedVec<H256>>, - pub latest_slashed_balances: Option<FixedVec<u64>>, - pub latest_block_header: Option<BeaconBlockHeader>, - pub historical_roots: Option<Vec<H256>>, - - // Ethereum 1.0 chain data - pub latest_eth1_data: Option<Eth1Data>, - pub eth1_data_votes: Option<Vec<Eth1DataVote>>, - pub deposit_index: Option<u64>, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(deny_unknown_fields)] -pub struct Collection { - pub title: String, - pub summary: String, - pub test_suite: String, - pub fork: String, - pub test_cases: Vec<Test>, -} - -#[derive(Serialize, Deserialize, Debug)] -#[serde(deny_unknown_fields)] -pub struct Test { - pub name: String, - pub config: HashMap<String, String>, - pub verify_signatures: bool, - pub initial_state: BeaconState, - pub blocks: Vec<BeaconBlock>, - pub expected_state: ExpectedBeaconState, -} +use yamltests::{Collection, run_collection}; fn main() { let matches = App::new("yamltests") @@ -95,73 +22,5 @@ fn main() { let only = matches.value_of("only"); let coll = serde_yaml::from_reader::<_, Collection>(BufReader::new(file)).expect("Parse test cases failed"); - for test in coll.test_cases { - if let Some(only) = only { - if test.name != only { - continue - } - } - run_test(test); - } -} - -fn run_test(test: Test) { - print!("Running test: {} ...", test.name); - io::stdout().flush().ok().expect("Could not flush stdout"); - let mut state = test.initial_state; - - for block in test.blocks { - match serenity::execute_block(&block, &mut state) { - Ok(()) => { - println!(" done"); - }, - Err(err) => { - println!(" failed\n"); - println!("Error: {:?}", err); - panic!(); - } - } - } - - check_expected(&state, test.expected_state); -} - -fn check_expected(state: &BeaconState, expected: ExpectedBeaconState) { - macro_rules! check { - ( $($field:tt,)+ ) => { - $( - if let Some($field) = expected.$field { - if $field != state.$field { - println!("\nExpected state check failed for {}", stringify!($field)); - println!("Expected: {:?}", $field); - println!("Actual: {:?}", state.$field); - panic!(); - } - } - )+ - } - } - - check!( - // Misc - slot, genesis_time, fork, - // Validator registry - validator_registry, validator_balances, validator_registry_update_epoch, - // Randomness and committees - latest_randao_mixes, previous_shuffling_start_shard, - current_shuffling_start_shard, previous_shuffling_epoch, - current_shuffling_epoch, previous_shuffling_seed, - current_shuffling_seed, - // Finality - previous_epoch_attestations, current_epoch_attestations, - previous_justified_epoch, current_justified_epoch, - previous_justified_root, current_justified_root, - justification_bitfield, finalized_epoch, finalized_root, - // Recent state - latest_crosslinks, latest_block_roots, latest_state_roots, - latest_active_index_roots, latest_slashed_balances, - latest_block_header, historical_roots, - // Ethereum 1.0 chain data - latest_eth1_data, eth1_data_votes, deposit_index, - ); + run_collection(coll, only); } -- GitLab