Add comments to test_harness::Manifest

This commit is contained in:
Paul Hauner 2019-03-03 15:07:54 +11:00
parent 1703508385
commit 7b72934943
No known key found for this signature in database
GPG Key ID: D362883A9218FCC6
6 changed files with 111 additions and 3 deletions

View File

@ -37,6 +37,18 @@ fn main() {
}; };
for doc in &docs { for doc in &docs {
// For each `test_cases` YAML in the document, build a `Manifest`, execute it and
// assert that the execution result matches the manifest description.
//
// In effect, for each `test_case` a new `BeaconChainHarness` is created from genesis
// and a new `BeaconChain` is built as per the manifest.
//
// After the `BeaconChain` has been built out as per the manifest, a dump of all blocks
// and states in the chain is obtained and checked against the `results` specified in
// the `test_case`.
//
// If any of the expectations in the results are not met, the process
// panics with a message.
for test_case in doc["test_cases"].as_vec().unwrap() { for test_case in doc["test_cases"].as_vec().unwrap() {
let manifest = Manifest::from_yaml(test_case); let manifest = Manifest::from_yaml(test_case);
manifest.assert_result_valid(manifest.execute()) manifest.assert_result_valid(manifest.execute())

View File

@ -1,4 +1,32 @@
//! Provides a testing environment for the `BeaconChain`, `Attester` and `BlockProposer` objects.
//!
//! This environment bypasses networking client runtimes and connects the `Attester` and `Proposer`
//! directly to the `BeaconChain` via an `Arc`.
//!
//! The `BeaconChainHarness` contains a single `BeaconChain` instance and many `ValidatorHarness`
//! instances. All of the `ValidatorHarness` instances work to advance the `BeaconChain` by
//! producing blocks and attestations.
//!
//! Example:
//! ```
//! use test_harness::BeaconChainHarness;
//! use types::ChainSpec;
//!
//! let validator_count = 8;
//! let spec = ChainSpec::few_validators();
//!
//! let mut harness = BeaconChainHarness::new(spec, validator_count);
//!
//! harness.advance_chain_with_block();
//!
//! let chain = harness.chain_dump().unwrap();
//!
//! // One block should have been built on top of the genesis block.
//! assert_eq!(chain.len(), 2);
//! ```
mod beacon_chain_harness; mod beacon_chain_harness;
pub mod manifest;
mod validator_harness; mod validator_harness;
pub use self::beacon_chain_harness::BeaconChainHarness; pub use self::beacon_chain_harness::BeaconChainHarness;

View File

@ -7,18 +7,29 @@ pub type DepositTuple = (u64, Deposit, Keypair);
pub type ProposerSlashingTuple = (u64, u64); pub type ProposerSlashingTuple = (u64, u64);
pub type AttesterSlashingTuple = (u64, Vec<u64>); pub type AttesterSlashingTuple = (u64, Vec<u64>);
/// Defines the execution of a `BeaconStateHarness` across a series of slots.
#[derive(Debug)] #[derive(Debug)]
pub struct Config { pub struct Config {
/// Initial validators.
pub deposits_for_chain_start: usize, pub deposits_for_chain_start: usize,
/// Number of slots in an epoch.
pub epoch_length: Option<u64>, pub epoch_length: Option<u64>,
/// Number of slots to build before ending execution.
pub num_slots: u64, pub num_slots: u64,
/// Number of slots that should be skipped due to inactive validator.
pub skip_slots: Option<Vec<u64>>, pub skip_slots: Option<Vec<u64>>,
/// Deposits to be included during execution.
pub deposits: Option<Vec<DepositTuple>>, pub deposits: Option<Vec<DepositTuple>>,
/// Proposer slashings to be included during execution.
pub proposer_slashings: Option<Vec<ProposerSlashingTuple>>, pub proposer_slashings: Option<Vec<ProposerSlashingTuple>>,
/// Attester slashings to be including during execution.
pub attester_slashings: Option<Vec<AttesterSlashingTuple>>, pub attester_slashings: Option<Vec<AttesterSlashingTuple>>,
} }
impl Config { impl Config {
/// Load from a YAML document.
///
/// Expects to receive the `config` section of the document.
pub fn from_yaml(yaml: &Yaml) -> Self { pub fn from_yaml(yaml: &Yaml) -> Self {
Self { Self {
deposits_for_chain_start: as_usize(&yaml, "deposits_for_chain_start") deposits_for_chain_start: as_usize(&yaml, "deposits_for_chain_start")
@ -33,6 +44,7 @@ impl Config {
} }
} }
/// Parse the `attester_slashings` section of the YAML document.
fn parse_attester_slashings(yaml: &Yaml) -> Option<Vec<AttesterSlashingTuple>> { fn parse_attester_slashings(yaml: &Yaml) -> Option<Vec<AttesterSlashingTuple>> {
let mut slashings = vec![]; let mut slashings = vec![];
@ -47,6 +59,7 @@ fn parse_attester_slashings(yaml: &Yaml) -> Option<Vec<AttesterSlashingTuple>> {
Some(slashings) Some(slashings)
} }
/// Parse the `proposer_slashings` section of the YAML document.
fn parse_proposer_slashings(yaml: &Yaml) -> Option<Vec<ProposerSlashingTuple>> { fn parse_proposer_slashings(yaml: &Yaml) -> Option<Vec<ProposerSlashingTuple>> {
let mut slashings = vec![]; let mut slashings = vec![];
@ -61,6 +74,7 @@ fn parse_proposer_slashings(yaml: &Yaml) -> Option<Vec<ProposerSlashingTuple>> {
Some(slashings) Some(slashings)
} }
/// Parse the `deposits` section of the YAML document.
fn parse_deposits(yaml: &Yaml) -> Option<Vec<DepositTuple>> { fn parse_deposits(yaml: &Yaml) -> Option<Vec<DepositTuple>> {
let mut deposits = vec![]; let mut deposits = vec![];

View File

@ -1,5 +1,6 @@
use self::config::Config; //! Defines execution and testing specs for a `BeaconChainHarness` instance. Supports loading from
use self::results::Results; //! a YAML file.
use crate::beacon_chain_harness::BeaconChainHarness; use crate::beacon_chain_harness::BeaconChainHarness;
use beacon_chain::CheckPoint; use beacon_chain::CheckPoint;
use log::{info, warn}; use log::{info, warn};
@ -14,18 +15,36 @@ mod results;
mod state_check; mod state_check;
mod yaml_helpers; mod yaml_helpers;
pub use config::Config;
pub use results::Results;
pub use state_check::StateCheck;
/// Defines the execution and testing of a `BeaconChainHarness` instantiation.
///
/// Typical workflow is:
///
/// 1. Instantiate the `Manifest` from YAML: `let manifest = Manifest::from_yaml(&my_yaml);`
/// 2. Execute the manifest: `let result = manifest.execute();`
/// 3. Test the results against the manifest: `manifest.assert_result_valid(result);`
#[derive(Debug)] #[derive(Debug)]
pub struct Manifest { pub struct Manifest {
pub results: Results, /// Defines the execution.
pub config: Config, pub config: Config,
/// Defines tests to run against the execution result.
pub results: Results,
} }
/// The result of executing a `Manifest`.
///
pub struct ExecutionResult { pub struct ExecutionResult {
/// The canonical beacon chain generated from the execution.
pub chain: Vec<CheckPoint>, pub chain: Vec<CheckPoint>,
/// The spec used for execution.
pub spec: ChainSpec, pub spec: ChainSpec,
} }
impl Manifest { impl Manifest {
/// Load the manifest from a YAML document.
pub fn from_yaml(test_case: &Yaml) -> Self { pub fn from_yaml(test_case: &Yaml) -> Self {
Self { Self {
results: Results::from_yaml(&test_case["results"]), results: Results::from_yaml(&test_case["results"]),
@ -33,6 +52,9 @@ impl Manifest {
} }
} }
/// Return a `ChainSpec::foundation()`.
///
/// If specified in `config`, returns it with a modified `epoch_length`.
fn spec(&self) -> ChainSpec { fn spec(&self) -> ChainSpec {
let mut spec = ChainSpec::foundation(); let mut spec = ChainSpec::foundation();
@ -43,6 +65,7 @@ impl Manifest {
spec spec
} }
/// Executes the manifest, returning an `ExecutionResult`.
pub fn execute(&self) -> ExecutionResult { pub fn execute(&self) -> ExecutionResult {
let spec = self.spec(); let spec = self.spec();
let validator_count = self.config.deposits_for_chain_start; let validator_count = self.config.deposits_for_chain_start;
@ -123,6 +146,11 @@ impl Manifest {
} }
} }
/// Checks that the `ExecutionResult` is consistent with the specifications in `self.results`.
///
/// # Panics
///
/// Panics with a message if any result does not match exepectations.
pub fn assert_result_valid(&self, execution_result: ExecutionResult) { pub fn assert_result_valid(&self, execution_result: ExecutionResult) {
info!("Verifying test results..."); info!("Verifying test results...");
let spec = &execution_result.spec; let spec = &execution_result.spec;
@ -157,6 +185,9 @@ impl Manifest {
} }
} }
/// Builds an `AttesterSlashing` for some `validator_indices`.
///
/// Signs the message using a `BeaconChainHarness`.
fn build_double_vote_attester_slashing( fn build_double_vote_attester_slashing(
harness: &BeaconChainHarness, harness: &BeaconChainHarness,
validator_indices: &[u64], validator_indices: &[u64],
@ -170,6 +201,9 @@ fn build_double_vote_attester_slashing(
AttesterSlashingBuilder::double_vote(validator_indices, signer, &harness.spec) AttesterSlashingBuilder::double_vote(validator_indices, signer, &harness.spec)
} }
/// Builds an `ProposerSlashing` for some `validator_index`.
///
/// Signs the message using a `BeaconChainHarness`.
fn build_proposer_slashing(harness: &BeaconChainHarness, validator_index: u64) -> ProposerSlashing { fn build_proposer_slashing(harness: &BeaconChainHarness, validator_index: u64) -> ProposerSlashing {
let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: u64| { let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: u64| {
harness harness

View File

@ -2,6 +2,8 @@ use super::state_check::StateCheck;
use super::yaml_helpers::as_usize; use super::yaml_helpers::as_usize;
use yaml_rust::Yaml; use yaml_rust::Yaml;
/// A series of tests to be carried out upon an `ExecutionResult`, returned from executing a
/// `Manifest`.
#[derive(Debug)] #[derive(Debug)]
pub struct Results { pub struct Results {
pub num_skipped_slots: Option<usize>, pub num_skipped_slots: Option<usize>,
@ -9,6 +11,9 @@ pub struct Results {
} }
impl Results { impl Results {
/// Load from a YAML document.
///
/// Expects the `results` section of the YAML document.
pub fn from_yaml(yaml: &Yaml) -> Self { pub fn from_yaml(yaml: &Yaml) -> Self {
Self { Self {
num_skipped_slots: as_usize(yaml, "num_skipped_slots"), num_skipped_slots: as_usize(yaml, "num_skipped_slots"),
@ -17,6 +22,7 @@ impl Results {
} }
} }
/// Parse the `state_checks` section of the YAML document.
fn parse_state_checks(yaml: &Yaml) -> Option<Vec<StateCheck>> { fn parse_state_checks(yaml: &Yaml) -> Option<Vec<StateCheck>> {
let mut states = vec![]; let mut states = vec![];

View File

@ -3,15 +3,24 @@ use log::info;
use types::*; use types::*;
use yaml_rust::Yaml; use yaml_rust::Yaml;
/// Tests to be conducted upon a `BeaconState` object generated during the execution of a
/// `Manifest`.
#[derive(Debug)] #[derive(Debug)]
pub struct StateCheck { pub struct StateCheck {
/// Checked against `beacon_state.slot`.
pub slot: Slot, pub slot: Slot,
/// Checked against `beacon_state.validator_registry.len()`.
pub num_validators: Option<usize>, pub num_validators: Option<usize>,
/// A list of validator indices which have been penalized. Must be in ascending order.
pub slashed_validators: Option<Vec<u64>>, pub slashed_validators: Option<Vec<u64>>,
/// A list of validator indices which have been exited. Must be in ascending order.
pub exited_validators: Option<Vec<u64>>, pub exited_validators: Option<Vec<u64>>,
} }
impl StateCheck { impl StateCheck {
/// Load from a YAML document.
///
/// Expects the `state_check` section of the YAML document.
pub fn from_yaml(yaml: &Yaml) -> Self { pub fn from_yaml(yaml: &Yaml) -> Self {
Self { Self {
slot: Slot::from(as_u64(&yaml, "slot").expect("State must specify slot")), slot: Slot::from(as_u64(&yaml, "slot").expect("State must specify slot")),
@ -21,6 +30,11 @@ impl StateCheck {
} }
} }
/// Performs all checks against a `BeaconState`
///
/// # Panics
///
/// Panics with an error message if any test fails.
pub fn assert_valid(&self, state: &BeaconState, spec: &ChainSpec) { pub fn assert_valid(&self, state: &BeaconState, spec: &ChainSpec) {
let state_epoch = state.slot.epoch(spec.epoch_length); let state_epoch = state.slot.epoch(spec.epoch_length);