Re-organise test_harness binary
Moves manifest and components into separate files.
This commit is contained in:
parent
db28cc1b92
commit
f5614381e1
@ -1,20 +1,15 @@
|
|||||||
use self::beacon_chain_harness::BeaconChainHarness;
|
|
||||||
use self::validator_harness::ValidatorHarness;
|
|
||||||
use beacon_chain::CheckPoint;
|
|
||||||
use bls::create_proof_of_possession;
|
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use env_logger::{Builder, Env};
|
use env_logger::{Builder, Env};
|
||||||
use log::{info, warn};
|
use manifest::Manifest;
|
||||||
use std::{fs::File, io::prelude::*};
|
use std::{fs::File, io::prelude::*};
|
||||||
use types::*;
|
use yaml_rust::YamlLoader;
|
||||||
use types::{
|
|
||||||
attester_slashing::AttesterSlashingBuilder, proposer_slashing::ProposerSlashingBuilder,
|
|
||||||
};
|
|
||||||
use yaml_rust::{Yaml, YamlLoader};
|
|
||||||
|
|
||||||
mod beacon_chain_harness;
|
mod beacon_chain_harness;
|
||||||
|
mod manifest;
|
||||||
mod validator_harness;
|
mod validator_harness;
|
||||||
|
|
||||||
|
use validator_harness::ValidatorHarness;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = App::new("Lighthouse Test Harness Runner")
|
let matches = App::new("Lighthouse Test Harness Runner")
|
||||||
.version("0.0.1")
|
.version("0.0.1")
|
||||||
@ -49,293 +44,3 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Manifest {
|
|
||||||
pub results: Results,
|
|
||||||
pub config: Config,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Manifest {
|
|
||||||
pub fn from_yaml(test_case: &Yaml) -> Self {
|
|
||||||
Self {
|
|
||||||
results: Results::from_yaml(&test_case["results"]),
|
|
||||||
config: Config::from_yaml(&test_case["config"]),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn spec(&self) -> ChainSpec {
|
|
||||||
let mut spec = ChainSpec::foundation();
|
|
||||||
|
|
||||||
if let Some(n) = self.config.epoch_length {
|
|
||||||
spec.epoch_length = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
spec
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn execute(&self) -> ExecutionResult {
|
|
||||||
let spec = self.spec();
|
|
||||||
let validator_count = self.config.deposits_for_chain_start;
|
|
||||||
let slots = self.config.num_slots;
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"Building BeaconChainHarness with {} validators...",
|
|
||||||
validator_count
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut harness = BeaconChainHarness::new(spec, validator_count);
|
|
||||||
|
|
||||||
info!("Starting simulation across {} slots...", slots);
|
|
||||||
|
|
||||||
for slot_height in 0..slots {
|
|
||||||
// Feed deposits to the BeaconChain.
|
|
||||||
if let Some(ref deposits) = self.config.deposits {
|
|
||||||
for (slot, deposit, keypair) in deposits {
|
|
||||||
if *slot == slot_height {
|
|
||||||
info!("Including deposit at slot height {}.", slot_height);
|
|
||||||
harness.add_deposit(deposit.clone(), Some(keypair.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Feed proposer slashings to the BeaconChain.
|
|
||||||
if let Some(ref slashings) = self.config.proposer_slashings {
|
|
||||||
for (slot, validator_index) in slashings {
|
|
||||||
if *slot == slot_height {
|
|
||||||
info!(
|
|
||||||
"Including proposer slashing at slot height {} for validator #{}.",
|
|
||||||
slot_height, validator_index
|
|
||||||
);
|
|
||||||
let slashing = build_proposer_slashing(&harness, *validator_index);
|
|
||||||
harness.add_proposer_slashing(slashing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Feed attester slashings to the BeaconChain.
|
|
||||||
if let Some(ref slashings) = self.config.attester_slashings {
|
|
||||||
for (slot, validator_indices) in slashings {
|
|
||||||
if *slot == slot_height {
|
|
||||||
info!(
|
|
||||||
"Including attester slashing at slot height {} for validators {:?}.",
|
|
||||||
slot_height, validator_indices
|
|
||||||
);
|
|
||||||
let slashing =
|
|
||||||
build_double_vote_attester_slashing(&harness, &validator_indices[..]);
|
|
||||||
harness.add_attester_slashing(slashing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a block or skip a slot.
|
|
||||||
match self.config.skip_slots {
|
|
||||||
Some(ref skip_slots) if skip_slots.contains(&slot_height) => {
|
|
||||||
warn!("Skipping slot at height {}.", slot_height);
|
|
||||||
harness.increment_beacon_chain_slot();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
info!("Producing block at slot height {}.", slot_height);
|
|
||||||
harness.advance_chain_with_block();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
harness.run_fork_choice();
|
|
||||||
|
|
||||||
info!("Test execution complete!");
|
|
||||||
|
|
||||||
ExecutionResult {
|
|
||||||
chain: harness.chain_dump().expect("Chain dump failed."),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn assert_result_valid(&self, result: ExecutionResult) {
|
|
||||||
info!("Verifying test results...");
|
|
||||||
|
|
||||||
let skipped_slots = self
|
|
||||||
.config
|
|
||||||
.skip_slots
|
|
||||||
.clone()
|
|
||||||
.and_then(|slots| Some(slots.len()))
|
|
||||||
.unwrap_or_else(|| 0);
|
|
||||||
let expected_blocks = self.config.num_slots as usize + 1 - skipped_slots;
|
|
||||||
|
|
||||||
assert_eq!(result.chain.len(), expected_blocks);
|
|
||||||
|
|
||||||
info!(
|
|
||||||
"OK: Chain length is {} ({} skipped slots).",
|
|
||||||
result.chain.len(),
|
|
||||||
skipped_slots
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(ref skip_slots) = self.config.skip_slots {
|
|
||||||
for checkpoint in &result.chain {
|
|
||||||
let block_slot = checkpoint.beacon_block.slot.as_u64();
|
|
||||||
assert!(
|
|
||||||
!skip_slots.contains(&block_slot),
|
|
||||||
"Slot {} was not skipped.",
|
|
||||||
block_slot
|
|
||||||
);
|
|
||||||
}
|
|
||||||
info!("OK: Skipped slots not present in chain.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(ref deposits) = self.config.deposits {
|
|
||||||
let latest_state = &result.chain.last().expect("Empty chain.").beacon_state;
|
|
||||||
assert_eq!(
|
|
||||||
latest_state.validator_registry.len(),
|
|
||||||
self.config.deposits_for_chain_start + deposits.len()
|
|
||||||
);
|
|
||||||
info!(
|
|
||||||
"OK: Validator registry has {} more validators.",
|
|
||||||
deposits.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_double_vote_attester_slashing(
|
|
||||||
harness: &BeaconChainHarness,
|
|
||||||
validator_indices: &[u64],
|
|
||||||
) -> AttesterSlashing {
|
|
||||||
let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: u64| {
|
|
||||||
harness
|
|
||||||
.validator_sign(validator_index as usize, message, epoch, domain)
|
|
||||||
.expect("Unable to sign AttesterSlashing")
|
|
||||||
};
|
|
||||||
|
|
||||||
AttesterSlashingBuilder::double_vote(validator_indices, signer, &harness.spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_proposer_slashing(harness: &BeaconChainHarness, validator_index: u64) -> ProposerSlashing {
|
|
||||||
let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: u64| {
|
|
||||||
harness
|
|
||||||
.validator_sign(validator_index as usize, message, epoch, domain)
|
|
||||||
.expect("Unable to sign AttesterSlashing")
|
|
||||||
};
|
|
||||||
|
|
||||||
ProposerSlashingBuilder::double_vote(validator_index, signer, &harness.spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type DepositTuple = (u64, Deposit, Keypair);
|
|
||||||
pub type ProposerSlashingTuple = (u64, u64);
|
|
||||||
pub type AttesterSlashingTuple = (u64, Vec<u64>);
|
|
||||||
|
|
||||||
struct ExecutionResult {
|
|
||||||
pub chain: Vec<CheckPoint>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Results {
|
|
||||||
pub num_validators: Option<usize>,
|
|
||||||
pub slashed_validators: Option<Vec<u64>>,
|
|
||||||
pub exited_validators: Option<Vec<u64>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Results {
|
|
||||||
pub fn from_yaml(yaml: &Yaml) -> Self {
|
|
||||||
Self {
|
|
||||||
num_validators: as_usize(&yaml, "num_validators"),
|
|
||||||
slashed_validators: as_vec_u64(&yaml, "slashed_validators"),
|
|
||||||
exited_validators: as_vec_u64(&yaml, "exited_validators"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Config {
|
|
||||||
pub deposits_for_chain_start: usize,
|
|
||||||
pub epoch_length: Option<u64>,
|
|
||||||
pub num_slots: u64,
|
|
||||||
pub skip_slots: Option<Vec<u64>>,
|
|
||||||
pub deposits: Option<Vec<DepositTuple>>,
|
|
||||||
pub proposer_slashings: Option<Vec<ProposerSlashingTuple>>,
|
|
||||||
pub attester_slashings: Option<Vec<AttesterSlashingTuple>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Config {
|
|
||||||
pub fn from_yaml(yaml: &Yaml) -> Self {
|
|
||||||
Self {
|
|
||||||
deposits_for_chain_start: as_usize(&yaml, "deposits_for_chain_start")
|
|
||||||
.expect("Must specify validator count"),
|
|
||||||
epoch_length: as_u64(&yaml, "epoch_length"),
|
|
||||||
num_slots: as_u64(&yaml, "num_slots").expect("Must specify `config.num_slots`"),
|
|
||||||
skip_slots: as_vec_u64(yaml, "skip_slots"),
|
|
||||||
deposits: parse_deposits(&yaml),
|
|
||||||
proposer_slashings: parse_proposer_slashings(&yaml),
|
|
||||||
attester_slashings: parse_attester_slashings(&yaml),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_attester_slashings(yaml: &Yaml) -> Option<Vec<AttesterSlashingTuple>> {
|
|
||||||
let mut slashings = vec![];
|
|
||||||
|
|
||||||
for slashing in yaml["attester_slashings"].as_vec()? {
|
|
||||||
let slot = as_u64(slashing, "slot").expect("Incomplete attester_slashing (slot)");
|
|
||||||
let validator_indices = as_vec_u64(slashing, "validator_indices")
|
|
||||||
.expect("Incomplete attester_slashing (validator_indices)");
|
|
||||||
|
|
||||||
slashings.push((slot, validator_indices));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(slashings)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_proposer_slashings(yaml: &Yaml) -> Option<Vec<ProposerSlashingTuple>> {
|
|
||||||
let mut slashings = vec![];
|
|
||||||
|
|
||||||
for slashing in yaml["proposer_slashings"].as_vec()? {
|
|
||||||
let slot = as_u64(slashing, "slot").expect("Incomplete proposer slashing (slot)_");
|
|
||||||
let validator_index = as_u64(slashing, "validator_index")
|
|
||||||
.expect("Incomplete proposer slashing (validator_index)");
|
|
||||||
|
|
||||||
slashings.push((slot, validator_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(slashings)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_deposits(yaml: &Yaml) -> Option<Vec<DepositTuple>> {
|
|
||||||
let mut deposits = vec![];
|
|
||||||
|
|
||||||
for deposit in yaml["deposits"].as_vec()? {
|
|
||||||
let keypair = Keypair::random();
|
|
||||||
let proof_of_possession = create_proof_of_possession(&keypair);
|
|
||||||
|
|
||||||
let slot = as_u64(deposit, "slot").expect("Incomplete deposit");
|
|
||||||
let deposit = Deposit {
|
|
||||||
branch: vec![],
|
|
||||||
index: as_u64(deposit, "merkle_index").unwrap(),
|
|
||||||
deposit_data: DepositData {
|
|
||||||
amount: 32_000_000_000,
|
|
||||||
timestamp: 1,
|
|
||||||
deposit_input: DepositInput {
|
|
||||||
pubkey: keypair.pk.clone(),
|
|
||||||
withdrawal_credentials: Hash256::zero(),
|
|
||||||
proof_of_possession,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
deposits.push((slot, deposit, keypair));
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(deposits)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_usize(yaml: &Yaml, key: &str) -> Option<usize> {
|
|
||||||
yaml[key].as_i64().and_then(|n| Some(n as usize))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_u64(yaml: &Yaml, key: &str) -> Option<u64> {
|
|
||||||
yaml[key].as_i64().and_then(|n| Some(n as u64))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_vec_u64(yaml: &Yaml, key: &str) -> Option<Vec<u64>> {
|
|
||||||
yaml[key].clone().into_vec().and_then(|vec| {
|
|
||||||
Some(
|
|
||||||
vec.iter()
|
|
||||||
.map(|item| item.as_i64().unwrap() as u64)
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
89
beacon_node/beacon_chain/test_harness/src/manifest/config.rs
Normal file
89
beacon_node/beacon_chain/test_harness/src/manifest/config.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use super::yaml_helpers::{as_u64, as_usize, as_vec_u64};
|
||||||
|
use bls::create_proof_of_possession;
|
||||||
|
use types::*;
|
||||||
|
use yaml_rust::Yaml;
|
||||||
|
|
||||||
|
pub type DepositTuple = (u64, Deposit, Keypair);
|
||||||
|
pub type ProposerSlashingTuple = (u64, u64);
|
||||||
|
pub type AttesterSlashingTuple = (u64, Vec<u64>);
|
||||||
|
|
||||||
|
pub struct Config {
|
||||||
|
pub deposits_for_chain_start: usize,
|
||||||
|
pub epoch_length: Option<u64>,
|
||||||
|
pub num_slots: u64,
|
||||||
|
pub skip_slots: Option<Vec<u64>>,
|
||||||
|
pub deposits: Option<Vec<DepositTuple>>,
|
||||||
|
pub proposer_slashings: Option<Vec<ProposerSlashingTuple>>,
|
||||||
|
pub attester_slashings: Option<Vec<AttesterSlashingTuple>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn from_yaml(yaml: &Yaml) -> Self {
|
||||||
|
Self {
|
||||||
|
deposits_for_chain_start: as_usize(&yaml, "deposits_for_chain_start")
|
||||||
|
.expect("Must specify validator count"),
|
||||||
|
epoch_length: as_u64(&yaml, "epoch_length"),
|
||||||
|
num_slots: as_u64(&yaml, "num_slots").expect("Must specify `config.num_slots`"),
|
||||||
|
skip_slots: as_vec_u64(yaml, "skip_slots"),
|
||||||
|
deposits: parse_deposits(&yaml),
|
||||||
|
proposer_slashings: parse_proposer_slashings(&yaml),
|
||||||
|
attester_slashings: parse_attester_slashings(&yaml),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_attester_slashings(yaml: &Yaml) -> Option<Vec<AttesterSlashingTuple>> {
|
||||||
|
let mut slashings = vec![];
|
||||||
|
|
||||||
|
for slashing in yaml["attester_slashings"].as_vec()? {
|
||||||
|
let slot = as_u64(slashing, "slot").expect("Incomplete attester_slashing (slot)");
|
||||||
|
let validator_indices = as_vec_u64(slashing, "validator_indices")
|
||||||
|
.expect("Incomplete attester_slashing (validator_indices)");
|
||||||
|
|
||||||
|
slashings.push((slot, validator_indices));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(slashings)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_proposer_slashings(yaml: &Yaml) -> Option<Vec<ProposerSlashingTuple>> {
|
||||||
|
let mut slashings = vec![];
|
||||||
|
|
||||||
|
for slashing in yaml["proposer_slashings"].as_vec()? {
|
||||||
|
let slot = as_u64(slashing, "slot").expect("Incomplete proposer slashing (slot)_");
|
||||||
|
let validator_index = as_u64(slashing, "validator_index")
|
||||||
|
.expect("Incomplete proposer slashing (validator_index)");
|
||||||
|
|
||||||
|
slashings.push((slot, validator_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(slashings)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_deposits(yaml: &Yaml) -> Option<Vec<DepositTuple>> {
|
||||||
|
let mut deposits = vec![];
|
||||||
|
|
||||||
|
for deposit in yaml["deposits"].as_vec()? {
|
||||||
|
let keypair = Keypair::random();
|
||||||
|
let proof_of_possession = create_proof_of_possession(&keypair);
|
||||||
|
|
||||||
|
let slot = as_u64(deposit, "slot").expect("Incomplete deposit");
|
||||||
|
let deposit = Deposit {
|
||||||
|
branch: vec![],
|
||||||
|
index: as_u64(deposit, "merkle_index").unwrap(),
|
||||||
|
deposit_data: DepositData {
|
||||||
|
amount: 32_000_000_000,
|
||||||
|
timestamp: 1,
|
||||||
|
deposit_input: DepositInput {
|
||||||
|
pubkey: keypair.pk.clone(),
|
||||||
|
withdrawal_credentials: Hash256::zero(),
|
||||||
|
proof_of_possession,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
deposits.push((slot, deposit, keypair));
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(deposits)
|
||||||
|
}
|
185
beacon_node/beacon_chain/test_harness/src/manifest/mod.rs
Normal file
185
beacon_node/beacon_chain/test_harness/src/manifest/mod.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
use self::config::Config;
|
||||||
|
use self::results::Results;
|
||||||
|
use crate::beacon_chain_harness::BeaconChainHarness;
|
||||||
|
use beacon_chain::CheckPoint;
|
||||||
|
use log::{info, warn};
|
||||||
|
use types::*;
|
||||||
|
use types::{
|
||||||
|
attester_slashing::AttesterSlashingBuilder, proposer_slashing::ProposerSlashingBuilder,
|
||||||
|
};
|
||||||
|
use yaml_rust::Yaml;
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
mod results;
|
||||||
|
mod yaml_helpers;
|
||||||
|
|
||||||
|
pub struct Manifest {
|
||||||
|
pub results: Results,
|
||||||
|
pub config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ExecutionResult {
|
||||||
|
pub chain: Vec<CheckPoint>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Manifest {
|
||||||
|
pub fn from_yaml(test_case: &Yaml) -> Self {
|
||||||
|
Self {
|
||||||
|
results: Results::from_yaml(&test_case["results"]),
|
||||||
|
config: Config::from_yaml(&test_case["config"]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spec(&self) -> ChainSpec {
|
||||||
|
let mut spec = ChainSpec::foundation();
|
||||||
|
|
||||||
|
if let Some(n) = self.config.epoch_length {
|
||||||
|
spec.epoch_length = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
spec
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn execute(&self) -> ExecutionResult {
|
||||||
|
let spec = self.spec();
|
||||||
|
let validator_count = self.config.deposits_for_chain_start;
|
||||||
|
let slots = self.config.num_slots;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Building BeaconChainHarness with {} validators...",
|
||||||
|
validator_count
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut harness = BeaconChainHarness::new(spec, validator_count);
|
||||||
|
|
||||||
|
info!("Starting simulation across {} slots...", slots);
|
||||||
|
|
||||||
|
for slot_height in 0..slots {
|
||||||
|
// Feed deposits to the BeaconChain.
|
||||||
|
if let Some(ref deposits) = self.config.deposits {
|
||||||
|
for (slot, deposit, keypair) in deposits {
|
||||||
|
if *slot == slot_height {
|
||||||
|
info!("Including deposit at slot height {}.", slot_height);
|
||||||
|
harness.add_deposit(deposit.clone(), Some(keypair.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed proposer slashings to the BeaconChain.
|
||||||
|
if let Some(ref slashings) = self.config.proposer_slashings {
|
||||||
|
for (slot, validator_index) in slashings {
|
||||||
|
if *slot == slot_height {
|
||||||
|
info!(
|
||||||
|
"Including proposer slashing at slot height {} for validator #{}.",
|
||||||
|
slot_height, validator_index
|
||||||
|
);
|
||||||
|
let slashing = build_proposer_slashing(&harness, *validator_index);
|
||||||
|
harness.add_proposer_slashing(slashing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feed attester slashings to the BeaconChain.
|
||||||
|
if let Some(ref slashings) = self.config.attester_slashings {
|
||||||
|
for (slot, validator_indices) in slashings {
|
||||||
|
if *slot == slot_height {
|
||||||
|
info!(
|
||||||
|
"Including attester slashing at slot height {} for validators {:?}.",
|
||||||
|
slot_height, validator_indices
|
||||||
|
);
|
||||||
|
let slashing =
|
||||||
|
build_double_vote_attester_slashing(&harness, &validator_indices[..]);
|
||||||
|
harness.add_attester_slashing(slashing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a block or skip a slot.
|
||||||
|
match self.config.skip_slots {
|
||||||
|
Some(ref skip_slots) if skip_slots.contains(&slot_height) => {
|
||||||
|
warn!("Skipping slot at height {}.", slot_height);
|
||||||
|
harness.increment_beacon_chain_slot();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
info!("Producing block at slot height {}.", slot_height);
|
||||||
|
harness.advance_chain_with_block();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
harness.run_fork_choice();
|
||||||
|
|
||||||
|
info!("Test execution complete!");
|
||||||
|
|
||||||
|
ExecutionResult {
|
||||||
|
chain: harness.chain_dump().expect("Chain dump failed."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assert_result_valid(&self, result: ExecutionResult) {
|
||||||
|
info!("Verifying test results...");
|
||||||
|
|
||||||
|
let skipped_slots = self
|
||||||
|
.config
|
||||||
|
.skip_slots
|
||||||
|
.clone()
|
||||||
|
.and_then(|slots| Some(slots.len()))
|
||||||
|
.unwrap_or_else(|| 0);
|
||||||
|
let expected_blocks = self.config.num_slots as usize + 1 - skipped_slots;
|
||||||
|
|
||||||
|
assert_eq!(result.chain.len(), expected_blocks);
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"OK: Chain length is {} ({} skipped slots).",
|
||||||
|
result.chain.len(),
|
||||||
|
skipped_slots
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(ref skip_slots) = self.config.skip_slots {
|
||||||
|
for checkpoint in &result.chain {
|
||||||
|
let block_slot = checkpoint.beacon_block.slot.as_u64();
|
||||||
|
assert!(
|
||||||
|
!skip_slots.contains(&block_slot),
|
||||||
|
"Slot {} was not skipped.",
|
||||||
|
block_slot
|
||||||
|
);
|
||||||
|
}
|
||||||
|
info!("OK: Skipped slots not present in chain.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ref deposits) = self.config.deposits {
|
||||||
|
let latest_state = &result.chain.last().expect("Empty chain.").beacon_state;
|
||||||
|
assert_eq!(
|
||||||
|
latest_state.validator_registry.len(),
|
||||||
|
self.config.deposits_for_chain_start + deposits.len()
|
||||||
|
);
|
||||||
|
info!(
|
||||||
|
"OK: Validator registry has {} more validators.",
|
||||||
|
deposits.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_double_vote_attester_slashing(
|
||||||
|
harness: &BeaconChainHarness,
|
||||||
|
validator_indices: &[u64],
|
||||||
|
) -> AttesterSlashing {
|
||||||
|
let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: u64| {
|
||||||
|
harness
|
||||||
|
.validator_sign(validator_index as usize, message, epoch, domain)
|
||||||
|
.expect("Unable to sign AttesterSlashing")
|
||||||
|
};
|
||||||
|
|
||||||
|
AttesterSlashingBuilder::double_vote(validator_indices, signer, &harness.spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_proposer_slashing(harness: &BeaconChainHarness, validator_index: u64) -> ProposerSlashing {
|
||||||
|
let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: u64| {
|
||||||
|
harness
|
||||||
|
.validator_sign(validator_index as usize, message, epoch, domain)
|
||||||
|
.expect("Unable to sign AttesterSlashing")
|
||||||
|
};
|
||||||
|
|
||||||
|
ProposerSlashingBuilder::double_vote(validator_index, signer, &harness.spec)
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
use super::yaml_helpers::{as_usize, as_vec_u64};
|
||||||
|
use yaml_rust::Yaml;
|
||||||
|
|
||||||
|
pub struct Results {
|
||||||
|
pub num_validators: Option<usize>,
|
||||||
|
pub slashed_validators: Option<Vec<u64>>,
|
||||||
|
pub exited_validators: Option<Vec<u64>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Results {
|
||||||
|
pub fn from_yaml(yaml: &Yaml) -> Self {
|
||||||
|
Self {
|
||||||
|
num_validators: as_usize(&yaml, "num_validators"),
|
||||||
|
slashed_validators: as_vec_u64(&yaml, "slashed_validators"),
|
||||||
|
exited_validators: as_vec_u64(&yaml, "exited_validators"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
use yaml_rust::Yaml;
|
||||||
|
|
||||||
|
pub fn as_usize(yaml: &Yaml, key: &str) -> Option<usize> {
|
||||||
|
yaml[key].as_i64().and_then(|n| Some(n as usize))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_u64(yaml: &Yaml, key: &str) -> Option<u64> {
|
||||||
|
yaml[key].as_i64().and_then(|n| Some(n as u64))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_vec_u64(yaml: &Yaml, key: &str) -> Option<Vec<u64>> {
|
||||||
|
yaml[key].clone().into_vec().and_then(|vec| {
|
||||||
|
Some(
|
||||||
|
vec.iter()
|
||||||
|
.map(|item| item.as_i64().unwrap() as u64)
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user