Allow test_harness to load validators from file
Also adds a command to test_harness binary to generate validators
This commit is contained in:
parent
2f484db82c
commit
ec9e0bbddf
1
beacon_node/beacon_chain/test_harness/.gitignore
vendored
Normal file
1
beacon_node/beacon_chain/test_harness/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
validators/
|
@ -33,12 +33,14 @@ failure = "0.1"
|
||||
failure_derive = "0.1"
|
||||
fork_choice = { path = "../../../eth2/fork_choice" }
|
||||
hashing = { path = "../../../eth2/utils/hashing" }
|
||||
int_to_bytes = { path = "../../../eth2/utils/int_to_bytes" }
|
||||
log = "0.4"
|
||||
env_logger = "0.6.0"
|
||||
rayon = "1.0"
|
||||
serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_yaml = "0.8"
|
||||
slot_clock = { path = "../../../eth2/utils/slot_clock" }
|
||||
ssz = { path = "../../../eth2/utils/ssz" }
|
||||
types = { path = "../../../eth2/types" }
|
||||
|
@ -6,16 +6,20 @@ use db::{
|
||||
MemoryDB,
|
||||
};
|
||||
use fork_choice::BitwiseLMDGhost;
|
||||
use generate_deposits::generate_deposits_with_random_keypairs;
|
||||
use log::debug;
|
||||
use rayon::prelude::*;
|
||||
use slot_clock::TestingSlotClock;
|
||||
use std::collections::HashSet;
|
||||
use std::iter::FromIterator;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use types::*;
|
||||
|
||||
mod generate_deposits;
|
||||
mod load_deposits_from_file;
|
||||
|
||||
pub use generate_deposits::generate_deposits_with_deterministic_keypairs;
|
||||
pub use load_deposits_from_file::load_deposits_from_file;
|
||||
|
||||
/// The beacon chain harness simulates a single beacon node with `validator_count` validators connected
|
||||
/// to it. Each validator is provided a borrow to the beacon chain, where it may read
|
||||
@ -37,7 +41,7 @@ impl BeaconChainHarness {
|
||||
///
|
||||
/// - A keypair, `BlockProducer` and `Attester` for each validator.
|
||||
/// - A new BeaconChain struct where the given validators are in the genesis.
|
||||
pub fn new(spec: ChainSpec, validator_count: usize) -> Self {
|
||||
pub fn new(spec: ChainSpec, validator_count: usize, validators_dir: Option<&Path>) -> Self {
|
||||
let db = Arc::new(MemoryDB::open());
|
||||
let block_store = Arc::new(BeaconBlockStore::new(db.clone()));
|
||||
let state_store = Arc::new(BeaconStateStore::new(db.clone()));
|
||||
@ -49,8 +53,17 @@ impl BeaconChainHarness {
|
||||
block_hash: Hash256::zero(),
|
||||
};
|
||||
|
||||
let (keypairs, initial_validator_deposits) =
|
||||
generate_deposits_with_random_keypairs(validator_count, genesis_time, &spec);
|
||||
let (keypairs, initial_validator_deposits) = if let Some(path) = validators_dir {
|
||||
let keypairs_path = path.join("keypairs.yaml");
|
||||
let deposits_path = path.join("deposits.yaml");
|
||||
load_deposits_from_file(
|
||||
validator_count,
|
||||
&keypairs_path.as_path(),
|
||||
&deposits_path.as_path(),
|
||||
)
|
||||
} else {
|
||||
generate_deposits_with_deterministic_keypairs(validator_count, genesis_time, &spec)
|
||||
};
|
||||
|
||||
// Create the Beacon Chain
|
||||
let beacon_chain = Arc::new(
|
||||
|
@ -1,11 +1,16 @@
|
||||
use bls::{create_proof_of_possession, get_withdrawal_credentials};
|
||||
use int_to_bytes::int_to_bytes48;
|
||||
use log::debug;
|
||||
use rayon::prelude::*;
|
||||
use types::*;
|
||||
|
||||
/// Generates `validator_count` deposits using randomly generated keypairs and some default specs
|
||||
/// for the deposits.
|
||||
pub fn generate_deposits_with_random_keypairs(
|
||||
/// Generates `validator_count` deposits using keypairs where the secret key is the index of the
|
||||
/// validator.
|
||||
///
|
||||
/// For example, the first validator has a secret key of `int_to_bytes48(1)`, the second has
|
||||
/// `int_to_bytes48(2)` and so on. (We skip `0` as it generates a weird looking public key and is
|
||||
/// probably invalid).
|
||||
pub fn generate_deposits_with_deterministic_keypairs(
|
||||
validator_count: usize,
|
||||
genesis_time: u64,
|
||||
spec: &ChainSpec,
|
||||
@ -18,7 +23,12 @@ pub fn generate_deposits_with_random_keypairs(
|
||||
let keypairs: Vec<Keypair> = (0..validator_count)
|
||||
.collect::<Vec<usize>>()
|
||||
.par_iter()
|
||||
.map(|_| Keypair::random())
|
||||
.map(|&i| {
|
||||
let secret = int_to_bytes48(i as u64 + 1);
|
||||
let sk = SecretKey::from_bytes(&secret).unwrap();
|
||||
let pk = PublicKey::from_secret_key(&sk);
|
||||
Keypair { sk, pk }
|
||||
})
|
||||
.collect();
|
||||
|
||||
debug!(
|
||||
|
@ -0,0 +1,38 @@
|
||||
use log::debug;
|
||||
use serde_yaml;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use types::*;
|
||||
|
||||
pub fn load_deposits_from_file(
|
||||
validator_count: usize,
|
||||
keypairs_path: &Path,
|
||||
deposits_path: &Path,
|
||||
) -> (Vec<Keypair>, Vec<Deposit>) {
|
||||
debug!("Loading keypairs from file...");
|
||||
let keypairs_file = File::open(keypairs_path).unwrap();
|
||||
let mut keypairs: Vec<Keypair> = serde_yaml::from_reader(&keypairs_file).unwrap();
|
||||
|
||||
debug!("Loading deposits from file...");
|
||||
let deposits_file = File::open(deposits_path).unwrap();
|
||||
let mut deposits: Vec<Deposit> = serde_yaml::from_reader(&deposits_file).unwrap();
|
||||
|
||||
assert!(
|
||||
keypairs.len() >= validator_count,
|
||||
"Unable to load {} keypairs from file ({} available)",
|
||||
validator_count,
|
||||
keypairs.len()
|
||||
);
|
||||
|
||||
assert!(
|
||||
deposits.len() >= validator_count,
|
||||
"Unable to load {} deposits from file ({} available)",
|
||||
validator_count,
|
||||
deposits.len()
|
||||
);
|
||||
|
||||
keypairs.truncate(validator_count);
|
||||
deposits.truncate(validator_count);
|
||||
|
||||
(keypairs, deposits)
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
use clap::{App, Arg, SubCommand};
|
||||
use env_logger::{Builder, Env};
|
||||
use prepare::prepare;
|
||||
use run_test::run_test;
|
||||
use types::ChainSpec;
|
||||
|
||||
mod beacon_chain_harness;
|
||||
mod prepare;
|
||||
mod run_test;
|
||||
mod test_case;
|
||||
mod validator_harness;
|
||||
@ -17,12 +20,22 @@ fn main() {
|
||||
.arg(
|
||||
Arg::with_name("log")
|
||||
.long("log-level")
|
||||
.short("l")
|
||||
.value_name("LOG_LEVEL")
|
||||
.help("Logging level.")
|
||||
.possible_values(&["error", "warn", "info", "debug", "trace"])
|
||||
.default_value("debug")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("spec")
|
||||
.long("spec")
|
||||
.short("s")
|
||||
.value_name("SPECIFICATION")
|
||||
.help("ChainSpec instantiation.")
|
||||
.possible_values(&["foundation", "few_validators"])
|
||||
.default_value("foundation"),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("run_test")
|
||||
.about("Executes a YAML test specification")
|
||||
@ -32,6 +45,41 @@ fn main() {
|
||||
.value_name("FILE")
|
||||
.help("YAML file test_case.")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("validators_dir")
|
||||
.long("validators-dir")
|
||||
.short("v")
|
||||
.value_name("VALIDATORS_DIR")
|
||||
.help("A directory with validator deposits and keypair YAML."),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
SubCommand::with_name("prepare")
|
||||
.about("Builds validator YAML files for faster tests.")
|
||||
.arg(
|
||||
Arg::with_name("validator_count")
|
||||
.long("validator_count")
|
||||
.short("n")
|
||||
.value_name("VALIDATOR_COUNT")
|
||||
.help("Number of validators to generate.")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("genesis_time")
|
||||
.long("genesis_time")
|
||||
.short("t")
|
||||
.value_name("GENESIS_TIME")
|
||||
.help("Time for validator deposits.")
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("output_dir")
|
||||
.long("output_dir")
|
||||
.short("d")
|
||||
.value_name("GENESIS_TIME")
|
||||
.help("Output directory for generated YAML.")
|
||||
.default_value("validators"),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
@ -40,7 +88,17 @@ fn main() {
|
||||
Builder::from_env(Env::default().default_filter_or(log_level)).init();
|
||||
}
|
||||
|
||||
let spec = match matches.value_of("spec") {
|
||||
Some("foundation") => ChainSpec::foundation(),
|
||||
Some("few_validators") => ChainSpec::few_validators(),
|
||||
_ => unreachable!(), // Has a default value, should always exist.
|
||||
};
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("run_test") {
|
||||
run_test(matches);
|
||||
}
|
||||
|
||||
if let Some(matches) = matches.subcommand_matches("prepare") {
|
||||
prepare(matches, &spec);
|
||||
}
|
||||
}
|
||||
|
35
beacon_node/beacon_chain/test_harness/src/prepare.rs
Normal file
35
beacon_node/beacon_chain/test_harness/src/prepare.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use crate::beacon_chain_harness::generate_deposits_with_deterministic_keypairs;
|
||||
use clap::{value_t, ArgMatches};
|
||||
use log::debug;
|
||||
use serde_yaml;
|
||||
use std::path::Path;
|
||||
use std::{fs, fs::File};
|
||||
use types::*;
|
||||
|
||||
const KEYPAIRS_FILE: &str = "keypairs.yaml";
|
||||
const DEPOSITS_FILE: &str = "deposits.yaml";
|
||||
|
||||
pub fn prepare(matches: &ArgMatches, spec: &ChainSpec) {
|
||||
let validator_count = value_t!(matches.value_of("validator_count"), usize)
|
||||
.expect("Validator count is required argument");
|
||||
let genesis_time =
|
||||
value_t!(matches.value_of("genesis_time"), u64).expect("Genesis time is required argument");
|
||||
let output_dir = matches
|
||||
.value_of("output_dir")
|
||||
.expect("Output dir has a default value.");
|
||||
|
||||
let (keypairs, deposits) =
|
||||
generate_deposits_with_deterministic_keypairs(validator_count, genesis_time, &spec);
|
||||
|
||||
debug!("Created keypairs and deposits, writing to file...");
|
||||
|
||||
fs::create_dir_all(Path::new(output_dir)).unwrap();
|
||||
|
||||
let keypairs_path = Path::new(output_dir).join(KEYPAIRS_FILE);
|
||||
let keypairs_file = File::create(keypairs_path).unwrap();
|
||||
serde_yaml::to_writer(keypairs_file, &keypairs).unwrap();
|
||||
|
||||
let deposits_path = Path::new(output_dir).join(DEPOSITS_FILE);
|
||||
let deposits_file = File::create(deposits_path).unwrap();
|
||||
serde_yaml::to_writer(deposits_file, &deposits).unwrap();
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
use crate::test_case::TestCase;
|
||||
use clap::ArgMatches;
|
||||
use std::path::Path;
|
||||
use std::{fs::File, io::prelude::*};
|
||||
use yaml_rust::YamlLoader;
|
||||
|
||||
@ -15,6 +16,10 @@ pub fn run_test(matches: &ArgMatches) {
|
||||
};
|
||||
|
||||
for doc in &docs {
|
||||
let validators_dir = matches
|
||||
.value_of("validators_dir")
|
||||
.and_then(|dir_str| Some(Path::new(dir_str)));
|
||||
|
||||
// For each `test_cases` YAML in the document, build a `TestCase`, execute it and
|
||||
// assert that the execution result matches the test_case description.
|
||||
//
|
||||
@ -29,7 +34,7 @@ pub fn run_test(matches: &ArgMatches) {
|
||||
// panics with a message.
|
||||
for test_case in doc["test_cases"].as_vec().unwrap() {
|
||||
let test_case = TestCase::from_yaml(test_case);
|
||||
test_case.assert_result_valid(test_case.execute())
|
||||
test_case.assert_result_valid(test_case.execute(validators_dir))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use beacon_chain::CheckPoint;
|
||||
use bls::{create_proof_of_possession, get_withdrawal_credentials};
|
||||
use log::{info, warn};
|
||||
use ssz::SignedRoot;
|
||||
use std::path::Path;
|
||||
use types::*;
|
||||
|
||||
use types::{
|
||||
@ -70,7 +71,7 @@ impl TestCase {
|
||||
|
||||
/// Executes the test case, returning an `ExecutionResult`.
|
||||
#[allow(clippy::cyclomatic_complexity)]
|
||||
pub fn execute(&self) -> ExecutionResult {
|
||||
pub fn execute(&self, validators_dir: Option<&Path>) -> ExecutionResult {
|
||||
let spec = self.spec();
|
||||
let validator_count = self.config.deposits_for_chain_start;
|
||||
let slots = self.config.num_slots;
|
||||
@ -80,7 +81,7 @@ impl TestCase {
|
||||
validator_count
|
||||
);
|
||||
|
||||
let mut harness = BeaconChainHarness::new(spec, validator_count);
|
||||
let mut harness = BeaconChainHarness::new(spec, validator_count, validators_dir);
|
||||
|
||||
info!("Starting simulation across {} slots...", slots);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user