Merge pull request #259 from sigp/bench-epoch-trans
Add benching and test for epoch processing
This commit is contained in:
commit
d140563545
@ -4,6 +4,15 @@ version = "0.1.0"
|
|||||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "benches"
|
||||||
|
harness = false
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = "0.2"
|
||||||
|
test_harness = { path = "../../beacon_node/beacon_chain/test_harness" }
|
||||||
|
env_logger = "0.6.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hashing = { path = "../utils/hashing" }
|
hashing = { path = "../utils/hashing" }
|
||||||
int_to_bytes = { path = "../utils/int_to_bytes" }
|
int_to_bytes = { path = "../utils/int_to_bytes" }
|
||||||
|
28
eth2/state_processing/benches/benches.rs
Normal file
28
eth2/state_processing/benches/benches.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use criterion::Criterion;
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main};
|
||||||
|
// use env_logger::{Builder, Env};
|
||||||
|
use state_processing::SlotProcessable;
|
||||||
|
use types::{beacon_state::BeaconStateBuilder, ChainSpec, Hash256};
|
||||||
|
|
||||||
|
fn epoch_processing(c: &mut Criterion) {
|
||||||
|
// Builder::from_env(Env::default().default_filter_or("debug")).init();
|
||||||
|
|
||||||
|
let mut builder = BeaconStateBuilder::with_random_validators(8);
|
||||||
|
builder.spec = ChainSpec::few_validators();
|
||||||
|
|
||||||
|
builder.genesis().unwrap();
|
||||||
|
builder.teleport_to_end_of_epoch(builder.spec.genesis_epoch + 4);
|
||||||
|
|
||||||
|
let state = builder.build().unwrap();
|
||||||
|
|
||||||
|
c.bench_function("epoch processing", move |b| {
|
||||||
|
let spec = &builder.spec;
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| black_box(state.per_slot_processing(Hash256::zero(), spec).unwrap()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, epoch_processing,);
|
||||||
|
criterion_main!(benches);
|
@ -9,6 +9,8 @@ use types::{
|
|||||||
Crosslink, Epoch, Hash256, InclusionError, PendingAttestation, RelativeEpoch,
|
Crosslink, Epoch, Hash256, InclusionError, PendingAttestation, RelativeEpoch,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod tests;
|
||||||
|
|
||||||
macro_rules! safe_add_assign {
|
macro_rules! safe_add_assign {
|
||||||
($a: expr, $b: expr) => {
|
($a: expr, $b: expr) => {
|
||||||
$a = $a.saturating_add($b);
|
$a = $a.saturating_add($b);
|
||||||
@ -725,11 +727,3 @@ impl From<BeaconStateError> for WinningRootError {
|
|||||||
WinningRootError::BeaconStateError(e)
|
WinningRootError::BeaconStateError(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
21
eth2/state_processing/src/epoch_processable/tests.rs
Normal file
21
eth2/state_processing/src/epoch_processable/tests.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#![cfg(test)]
|
||||||
|
use crate::EpochProcessable;
|
||||||
|
use env_logger::{Builder, Env};
|
||||||
|
use types::beacon_state::BeaconStateBuilder;
|
||||||
|
use types::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn runs_without_error() {
|
||||||
|
Builder::from_env(Env::default().default_filter_or("error")).init();
|
||||||
|
|
||||||
|
let mut builder = BeaconStateBuilder::with_random_validators(8);
|
||||||
|
builder.spec = ChainSpec::few_validators();
|
||||||
|
|
||||||
|
builder.genesis().unwrap();
|
||||||
|
builder.teleport_to_end_of_epoch(builder.spec.genesis_epoch + 4);
|
||||||
|
|
||||||
|
let mut state = builder.build().unwrap();
|
||||||
|
|
||||||
|
let spec = &builder.spec;
|
||||||
|
state.per_epoch_processing(spec).unwrap();
|
||||||
|
}
|
@ -7,13 +7,16 @@ use crate::{
|
|||||||
};
|
};
|
||||||
use bls::verify_proof_of_possession;
|
use bls::verify_proof_of_possession;
|
||||||
use honey_badger_split::SplitExt;
|
use honey_badger_split::SplitExt;
|
||||||
use log::{debug, trace};
|
use log::{debug, error, trace};
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash};
|
use ssz::{hash, Decodable, DecodeError, Encodable, SszStream, TreeHash};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use swap_or_not_shuffle::get_permutated_index;
|
use swap_or_not_shuffle::get_permutated_index;
|
||||||
|
|
||||||
|
pub use builder::BeaconStateBuilder;
|
||||||
|
|
||||||
|
mod builder;
|
||||||
mod epoch_cache;
|
mod epoch_cache;
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
@ -384,12 +387,13 @@ impl BeaconState {
|
|||||||
seed: Hash256,
|
seed: Hash256,
|
||||||
epoch: Epoch,
|
epoch: Epoch,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Option<Vec<Vec<usize>>> {
|
) -> Result<Vec<Vec<usize>>, Error> {
|
||||||
let active_validator_indices =
|
let active_validator_indices =
|
||||||
get_active_validator_indices(&self.validator_registry, epoch);
|
get_active_validator_indices(&self.validator_registry, epoch);
|
||||||
|
|
||||||
if active_validator_indices.is_empty() {
|
if active_validator_indices.is_empty() {
|
||||||
return None;
|
error!("get_shuffling: no validators.");
|
||||||
|
return Err(Error::InsufficientValidators);
|
||||||
}
|
}
|
||||||
|
|
||||||
let committees_per_epoch =
|
let committees_per_epoch =
|
||||||
@ -402,22 +406,21 @@ impl BeaconState {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut shuffled_active_validator_indices = vec![0; active_validator_indices.len()];
|
let mut shuffled_active_validator_indices = vec![0; active_validator_indices.len()];
|
||||||
for &i in &active_validator_indices {
|
for (i, _) in active_validator_indices.iter().enumerate() {
|
||||||
let shuffled_i = get_permutated_index(
|
let shuffled_i = get_permutated_index(
|
||||||
i,
|
i,
|
||||||
active_validator_indices.len(),
|
active_validator_indices.len(),
|
||||||
&seed[..],
|
&seed[..],
|
||||||
spec.shuffle_round_count,
|
spec.shuffle_round_count,
|
||||||
)?;
|
)
|
||||||
|
.ok_or_else(|| Error::UnableToShuffle)?;
|
||||||
shuffled_active_validator_indices[i] = active_validator_indices[shuffled_i]
|
shuffled_active_validator_indices[i] = active_validator_indices[shuffled_i]
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(
|
Ok(shuffled_active_validator_indices
|
||||||
shuffled_active_validator_indices
|
|
||||||
.honey_badger_split(committees_per_epoch as usize)
|
.honey_badger_split(committees_per_epoch as usize)
|
||||||
.map(|slice: &[usize]| slice.to_vec())
|
.map(|slice: &[usize]| slice.to_vec())
|
||||||
.collect(),
|
.collect())
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the number of committees in the previous epoch.
|
/// Return the number of committees in the previous epoch.
|
||||||
@ -522,7 +525,6 @@ impl BeaconState {
|
|||||||
self.get_committee_params_at_slot(slot, registry_change, spec)?;
|
self.get_committee_params_at_slot(slot, registry_change, spec)?;
|
||||||
|
|
||||||
self.get_shuffling(seed, shuffling_epoch, spec)
|
self.get_shuffling(seed, shuffling_epoch, spec)
|
||||||
.ok_or_else(|| Error::UnableToShuffle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the following params for the given slot:
|
/// Returns the following params for the given slot:
|
||||||
|
215
eth2/types/src/beacon_state/builder.rs
Normal file
215
eth2/types/src/beacon_state/builder.rs
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
use crate::*;
|
||||||
|
use bls::create_proof_of_possession;
|
||||||
|
|
||||||
|
/// Builds a `BeaconState` for use in testing or benchmarking.
|
||||||
|
///
|
||||||
|
/// Building the `BeaconState` is a three step processes:
|
||||||
|
///
|
||||||
|
/// 1. Create a new `BeaconStateBuilder`.
|
||||||
|
/// 2. Run the `genesis` function to generate a new BeaconState.
|
||||||
|
/// 3. (Optional) Use builder functions to modify the `BeaconState`.
|
||||||
|
/// 4. Call `build()` to obtain a cloned `BeaconState`.
|
||||||
|
///
|
||||||
|
/// Step (2) is necessary because step (3) requires an existing `BeaconState` object. (2) is not
|
||||||
|
/// included in (1) to allow for modifying params before generating the `BeaconState` (e.g., the
|
||||||
|
/// spec).
|
||||||
|
///
|
||||||
|
/// Step (4) produces a clone of the BeaconState and doesn't consume the `BeaconStateBuilder` to
|
||||||
|
/// allow access to the `keypairs` and `spec`.
|
||||||
|
pub struct BeaconStateBuilder {
|
||||||
|
pub state: Option<BeaconState>,
|
||||||
|
pub genesis_time: u64,
|
||||||
|
pub initial_validator_deposits: Vec<Deposit>,
|
||||||
|
pub latest_eth1_data: Eth1Data,
|
||||||
|
pub spec: ChainSpec,
|
||||||
|
pub keypairs: Vec<Keypair>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BeaconStateBuilder {
|
||||||
|
/// Create a new builder with the given number of validators.
|
||||||
|
pub fn with_random_validators(validator_count: usize) -> Self {
|
||||||
|
let genesis_time = 10_000_000;
|
||||||
|
let keypairs: Vec<Keypair> = (0..validator_count)
|
||||||
|
.collect::<Vec<usize>>()
|
||||||
|
.iter()
|
||||||
|
.map(|_| Keypair::random())
|
||||||
|
.collect();
|
||||||
|
let initial_validator_deposits = keypairs
|
||||||
|
.iter()
|
||||||
|
.map(|keypair| Deposit {
|
||||||
|
branch: vec![], // branch verification is not specified.
|
||||||
|
index: 0, // index verification is not specified.
|
||||||
|
deposit_data: DepositData {
|
||||||
|
amount: 32_000_000_000, // 32 ETH (in Gwei)
|
||||||
|
timestamp: genesis_time - 1,
|
||||||
|
deposit_input: DepositInput {
|
||||||
|
pubkey: keypair.pk.clone(),
|
||||||
|
withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
|
||||||
|
proof_of_possession: create_proof_of_possession(&keypair),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let latest_eth1_data = Eth1Data {
|
||||||
|
deposit_root: Hash256::zero(),
|
||||||
|
block_hash: Hash256::zero(),
|
||||||
|
};
|
||||||
|
let spec = ChainSpec::foundation();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
state: None,
|
||||||
|
genesis_time,
|
||||||
|
initial_validator_deposits,
|
||||||
|
latest_eth1_data,
|
||||||
|
spec,
|
||||||
|
keypairs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the `BeaconState::genesis` function and produces a `BeaconState`.
|
||||||
|
pub fn genesis(&mut self) -> Result<(), BeaconStateError> {
|
||||||
|
let state = BeaconState::genesis(
|
||||||
|
self.genesis_time,
|
||||||
|
self.initial_validator_deposits.clone(),
|
||||||
|
self.latest_eth1_data.clone(),
|
||||||
|
&self.spec,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
self.state = Some(state);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `BeaconState` to be in the last slot of the given epoch.
|
||||||
|
///
|
||||||
|
/// Sets all justification/finalization parameters to be be as "perfect" as possible (i.e.,
|
||||||
|
/// highest justified and finalized slots, full justification bitfield, etc).
|
||||||
|
pub fn teleport_to_end_of_epoch(&mut self, epoch: Epoch) {
|
||||||
|
let state = self.state.as_mut().expect("Genesis required");
|
||||||
|
|
||||||
|
let slot = epoch.end_slot(self.spec.epoch_length);
|
||||||
|
|
||||||
|
state.slot = slot;
|
||||||
|
state.validator_registry_update_epoch = epoch - 1;
|
||||||
|
|
||||||
|
state.previous_calculation_epoch = epoch - 1;
|
||||||
|
state.current_calculation_epoch = epoch;
|
||||||
|
|
||||||
|
state.previous_epoch_seed = Hash256::from(&b"previous_seed"[..]);
|
||||||
|
state.current_epoch_seed = Hash256::from(&b"current_seed"[..]);
|
||||||
|
|
||||||
|
state.previous_justified_epoch = epoch - 2;
|
||||||
|
state.justified_epoch = epoch - 1;
|
||||||
|
state.justification_bitfield = u64::max_value();
|
||||||
|
state.finalized_epoch = epoch - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a full set of attestations for the `BeaconState`. Each attestation has full
|
||||||
|
/// participation from its committee and references the expected beacon_block hashes.
|
||||||
|
///
|
||||||
|
/// These attestations should be fully conducive to justification and finalization.
|
||||||
|
pub fn insert_attestations(&mut self) {
|
||||||
|
let state = self.state.as_mut().expect("Genesis required");
|
||||||
|
|
||||||
|
state
|
||||||
|
.build_epoch_cache(RelativeEpoch::Previous, &self.spec)
|
||||||
|
.unwrap();
|
||||||
|
state
|
||||||
|
.build_epoch_cache(RelativeEpoch::Current, &self.spec)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let current_epoch = state.current_epoch(&self.spec);
|
||||||
|
let previous_epoch = state.previous_epoch(&self.spec);
|
||||||
|
let current_epoch_depth =
|
||||||
|
(state.slot - current_epoch.end_slot(self.spec.epoch_length)).as_usize();
|
||||||
|
|
||||||
|
let previous_epoch_slots = previous_epoch.slot_iter(self.spec.epoch_length);
|
||||||
|
let current_epoch_slots = current_epoch
|
||||||
|
.slot_iter(self.spec.epoch_length)
|
||||||
|
.take(current_epoch_depth);
|
||||||
|
|
||||||
|
for slot in previous_epoch_slots.chain(current_epoch_slots) {
|
||||||
|
let committees = state
|
||||||
|
.get_crosslink_committees_at_slot(slot, &self.spec)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
for (committee, shard) in committees {
|
||||||
|
state
|
||||||
|
.latest_attestations
|
||||||
|
.push(committee_to_pending_attestation(
|
||||||
|
state, &committee, shard, slot, &self.spec,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a cloned `BeaconState`.
|
||||||
|
pub fn build(&self) -> Result<BeaconState, BeaconStateError> {
|
||||||
|
match &self.state {
|
||||||
|
Some(state) => Ok(state.clone()),
|
||||||
|
None => panic!("Genesis required"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a valid PendingAttestation with full participation for some committee.
|
||||||
|
fn committee_to_pending_attestation(
|
||||||
|
state: &BeaconState,
|
||||||
|
committee: &[usize],
|
||||||
|
shard: u64,
|
||||||
|
slot: Slot,
|
||||||
|
spec: &ChainSpec,
|
||||||
|
) -> PendingAttestation {
|
||||||
|
let current_epoch = state.current_epoch(spec);
|
||||||
|
let previous_epoch = state.previous_epoch(spec);
|
||||||
|
|
||||||
|
let mut aggregation_bitfield = Bitfield::new();
|
||||||
|
let mut custody_bitfield = Bitfield::new();
|
||||||
|
|
||||||
|
for (i, _) in committee.iter().enumerate() {
|
||||||
|
aggregation_bitfield.set(i, true);
|
||||||
|
custody_bitfield.set(i, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_previous_epoch = state.slot.epoch(spec.epoch_length) != slot.epoch(spec.epoch_length);
|
||||||
|
|
||||||
|
let justified_epoch = if is_previous_epoch {
|
||||||
|
state.previous_justified_epoch
|
||||||
|
} else {
|
||||||
|
state.justified_epoch
|
||||||
|
};
|
||||||
|
|
||||||
|
let epoch_boundary_root = if is_previous_epoch {
|
||||||
|
*state
|
||||||
|
.get_block_root(previous_epoch.start_slot(spec.epoch_length), spec)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
*state
|
||||||
|
.get_block_root(current_epoch.start_slot(spec.epoch_length), spec)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let justified_block_root = *state
|
||||||
|
.get_block_root(justified_epoch.start_slot(spec.epoch_length), &spec)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
PendingAttestation {
|
||||||
|
aggregation_bitfield,
|
||||||
|
data: AttestationData {
|
||||||
|
slot,
|
||||||
|
shard,
|
||||||
|
beacon_block_root: *state.get_block_root(slot, spec).unwrap(),
|
||||||
|
epoch_boundary_root,
|
||||||
|
shard_block_root: Hash256::zero(),
|
||||||
|
latest_crosslink: Crosslink {
|
||||||
|
epoch: slot.epoch(spec.epoch_length),
|
||||||
|
shard_block_root: Hash256::zero(),
|
||||||
|
},
|
||||||
|
justified_epoch,
|
||||||
|
justified_block_root,
|
||||||
|
},
|
||||||
|
custody_bitfield,
|
||||||
|
inclusion_slot: slot,
|
||||||
|
}
|
||||||
|
}
|
@ -2,73 +2,13 @@
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
use crate::test_utils::{SeedableRng, TestRandom, XorShiftRng};
|
||||||
use crate::{
|
use crate::{BeaconState, ChainSpec};
|
||||||
BeaconState, BeaconStateError, ChainSpec, Deposit, DepositData, DepositInput, Eth1Data,
|
|
||||||
Hash256, Keypair,
|
|
||||||
};
|
|
||||||
use bls::create_proof_of_possession;
|
|
||||||
use ssz::{ssz_encode, Decodable};
|
use ssz::{ssz_encode, Decodable};
|
||||||
|
|
||||||
struct BeaconStateTestBuilder {
|
|
||||||
pub genesis_time: u64,
|
|
||||||
pub initial_validator_deposits: Vec<Deposit>,
|
|
||||||
pub latest_eth1_data: Eth1Data,
|
|
||||||
pub spec: ChainSpec,
|
|
||||||
pub keypairs: Vec<Keypair>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BeaconStateTestBuilder {
|
|
||||||
pub fn with_random_validators(validator_count: usize) -> Self {
|
|
||||||
let genesis_time = 10_000_000;
|
|
||||||
let keypairs: Vec<Keypair> = (0..validator_count)
|
|
||||||
.collect::<Vec<usize>>()
|
|
||||||
.iter()
|
|
||||||
.map(|_| Keypair::random())
|
|
||||||
.collect();
|
|
||||||
let initial_validator_deposits = keypairs
|
|
||||||
.iter()
|
|
||||||
.map(|keypair| Deposit {
|
|
||||||
branch: vec![], // branch verification is not specified.
|
|
||||||
index: 0, // index verification is not specified.
|
|
||||||
deposit_data: DepositData {
|
|
||||||
amount: 32_000_000_000, // 32 ETH (in Gwei)
|
|
||||||
timestamp: genesis_time - 1,
|
|
||||||
deposit_input: DepositInput {
|
|
||||||
pubkey: keypair.pk.clone(),
|
|
||||||
withdrawal_credentials: Hash256::zero(), // Withdrawal not possible.
|
|
||||||
proof_of_possession: create_proof_of_possession(&keypair),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let latest_eth1_data = Eth1Data {
|
|
||||||
deposit_root: Hash256::zero(),
|
|
||||||
block_hash: Hash256::zero(),
|
|
||||||
};
|
|
||||||
let spec = ChainSpec::foundation();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
genesis_time,
|
|
||||||
initial_validator_deposits,
|
|
||||||
latest_eth1_data,
|
|
||||||
spec,
|
|
||||||
keypairs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(&self) -> Result<BeaconState, BeaconStateError> {
|
|
||||||
BeaconState::genesis(
|
|
||||||
self.genesis_time,
|
|
||||||
self.initial_validator_deposits.clone(),
|
|
||||||
self.latest_eth1_data.clone(),
|
|
||||||
&self.spec,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
pub fn can_produce_genesis_block() {
|
pub fn can_produce_genesis_block() {
|
||||||
let builder = BeaconStateTestBuilder::with_random_validators(2);
|
let mut builder = BeaconStateBuilder::with_random_validators(2);
|
||||||
|
builder.genesis().unwrap();
|
||||||
|
|
||||||
builder.build().unwrap();
|
builder.build().unwrap();
|
||||||
}
|
}
|
||||||
@ -79,9 +19,11 @@ pub fn can_produce_genesis_block() {
|
|||||||
pub fn get_attestation_participants_consistency() {
|
pub fn get_attestation_participants_consistency() {
|
||||||
let mut rng = XorShiftRng::from_seed([42; 16]);
|
let mut rng = XorShiftRng::from_seed([42; 16]);
|
||||||
|
|
||||||
let mut builder = BeaconStateTestBuilder::with_random_validators(8);
|
let mut builder = BeaconStateBuilder::with_random_validators(8);
|
||||||
builder.spec = ChainSpec::few_validators();
|
builder.spec = ChainSpec::few_validators();
|
||||||
|
|
||||||
|
builder.genesis().unwrap();
|
||||||
|
|
||||||
let mut state = builder.build().unwrap();
|
let mut state = builder.build().unwrap();
|
||||||
let spec = builder.spec.clone();
|
let spec = builder.spec.clone();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user