Add incomplete per-block processing benchmarks
Still needs to fill block with operations
This commit is contained in:
parent
ca5d9658ce
commit
73ebb4bc2e
@ -1,8 +1,11 @@
|
|||||||
use criterion::{criterion_group, criterion_main};
|
use criterion::{criterion_group, criterion_main};
|
||||||
|
|
||||||
|
mod block_processing_benches;
|
||||||
mod epoch_processing_benches;
|
mod epoch_processing_benches;
|
||||||
|
|
||||||
use epoch_processing_benches::epoch_processing_16k_validators;
|
criterion_group!(
|
||||||
|
benches,
|
||||||
criterion_group!(benches, epoch_processing_16k_validators);
|
// epoch_processing_benches::epoch_processing_16k_validators,
|
||||||
|
block_processing_benches::block_processing_16k_validators,
|
||||||
|
);
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
276
eth2/state_processing/benches/block_processing_benches.rs
Normal file
276
eth2/state_processing/benches/block_processing_benches.rs
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
use benching_utils::{BeaconBlockBencher, BeaconStateBencher};
|
||||||
|
use criterion::Criterion;
|
||||||
|
use criterion::{black_box, Benchmark};
|
||||||
|
use state_processing::{
|
||||||
|
per_block_processing,
|
||||||
|
per_block_processing::{
|
||||||
|
process_attestations, process_attester_slashings, process_deposits, process_eth1_data,
|
||||||
|
process_exits, process_proposer_slashings, process_randao, process_transfers,
|
||||||
|
verify_block_signature,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use types::*;
|
||||||
|
|
||||||
|
/// Run the benchmarking suite on a foundation spec with 16,384 validators.
|
||||||
|
pub fn block_processing_16k_validators(c: &mut Criterion) {
|
||||||
|
let spec = ChainSpec::foundation();
|
||||||
|
|
||||||
|
let validator_count = 16_384;
|
||||||
|
|
||||||
|
let (state, keypairs) = build_state(validator_count, &spec);
|
||||||
|
let block = build_block(&state, &keypairs, &spec);
|
||||||
|
|
||||||
|
bench_block_processing(
|
||||||
|
c,
|
||||||
|
&block,
|
||||||
|
&state,
|
||||||
|
&spec,
|
||||||
|
&format!("{}_validators", validator_count),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_state(validator_count: usize, spec: &ChainSpec) -> (BeaconState, Vec<Keypair>) {
|
||||||
|
let mut builder = BeaconStateBencher::new(validator_count, &spec);
|
||||||
|
|
||||||
|
// Set the state to be just before an epoch transition.
|
||||||
|
let target_slot = (spec.genesis_epoch + 4).end_slot(spec.slots_per_epoch);
|
||||||
|
builder.teleport_to_slot(target_slot, &spec);
|
||||||
|
|
||||||
|
// Builds all caches; benches will not contain shuffling/committee building times.
|
||||||
|
builder.build_caches(&spec).unwrap();
|
||||||
|
|
||||||
|
builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_block(state: &BeaconState, keypairs: &[Keypair], spec: &ChainSpec) -> BeaconBlock {
|
||||||
|
let mut builder = BeaconBlockBencher::new(spec);
|
||||||
|
|
||||||
|
builder.set_slot(state.slot);
|
||||||
|
|
||||||
|
let proposer_index = state.get_beacon_proposer_index(state.slot, spec).unwrap();
|
||||||
|
let keypair = &keypairs[proposer_index];
|
||||||
|
|
||||||
|
builder.set_randao_reveal(&keypair.sk, &state.fork, spec);
|
||||||
|
|
||||||
|
builder.build(&keypair.sk, &state.fork, spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Run the detailed benchmarking suite on the given `BeaconState`.
|
||||||
|
///
|
||||||
|
/// `desc` will be added to the title of each bench.
|
||||||
|
fn bench_block_processing(
|
||||||
|
c: &mut Criterion,
|
||||||
|
initial_block: &BeaconBlock,
|
||||||
|
initial_state: &BeaconState,
|
||||||
|
initial_spec: &ChainSpec,
|
||||||
|
desc: &str,
|
||||||
|
) {
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("verify_block_signature", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| black_box(verify_block_signature(&mut state, &block, &spec).unwrap()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("process_randao", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| black_box(process_randao(&mut state, &block, &spec).unwrap()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("process_eth1_data", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| black_box(process_eth1_data(&mut state, &block.eth1_data).unwrap()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("process_proposer_slashings", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| {
|
||||||
|
black_box(
|
||||||
|
process_proposer_slashings(
|
||||||
|
&mut state,
|
||||||
|
&block.body.proposer_slashings,
|
||||||
|
&spec,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("process_attester_slashings", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| {
|
||||||
|
black_box(
|
||||||
|
process_attester_slashings(
|
||||||
|
&mut state,
|
||||||
|
&block.body.attester_slashings,
|
||||||
|
&spec,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("process_attestations", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| {
|
||||||
|
black_box(
|
||||||
|
process_attestations(&mut state, &block.body.attestations, &spec).unwrap(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("process_deposits", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| {
|
||||||
|
black_box(process_deposits(&mut state, &block.body.deposits, &spec).unwrap())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("process_exits", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| {
|
||||||
|
black_box(
|
||||||
|
process_exits(&mut state, &block.body.voluntary_exits, &spec).unwrap(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("process_transfers", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| {
|
||||||
|
black_box(process_transfers(&mut state, &block.body.transfers, &spec).unwrap())
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let state = initial_state.clone();
|
||||||
|
let block = initial_block.clone();
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("per_block_processing", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| black_box(per_block_processing(&mut state, &block, &spec).unwrap()),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut state = initial_state.clone();
|
||||||
|
state.drop_cache(RelativeEpoch::Previous);
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("build_previous_state_epoch_cache", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| {
|
||||||
|
black_box(
|
||||||
|
state
|
||||||
|
.build_epoch_cache(RelativeEpoch::Previous, &spec)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut state = initial_state.clone();
|
||||||
|
state.drop_cache(RelativeEpoch::Current);
|
||||||
|
let spec = initial_spec.clone();
|
||||||
|
c.bench(
|
||||||
|
&format!("block_processing_{}", desc),
|
||||||
|
Benchmark::new("build_current_state_epoch_cache", move |b| {
|
||||||
|
b.iter_with_setup(
|
||||||
|
|| state.clone(),
|
||||||
|
|mut state| {
|
||||||
|
black_box(
|
||||||
|
state
|
||||||
|
.build_epoch_cache(RelativeEpoch::Current, &spec)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.sample_size(10),
|
||||||
|
);
|
||||||
|
}
|
@ -19,18 +19,19 @@ pub fn epoch_processing_16k_validators(c: &mut Criterion) {
|
|||||||
let validator_count = 16_384;
|
let validator_count = 16_384;
|
||||||
|
|
||||||
let mut builder = BeaconStateBencher::new(validator_count, &spec);
|
let mut builder = BeaconStateBencher::new(validator_count, &spec);
|
||||||
builder.teleport_to_end_of_epoch(spec.genesis_epoch + 4, &spec);
|
|
||||||
builder.insert_attestations(&spec);
|
|
||||||
let mut state = builder.build();
|
|
||||||
|
|
||||||
// Build all the caches so the following state does _not_ include the cache-building time.
|
// Set the state to be just before an epoch transition.
|
||||||
state
|
let target_slot = (spec.genesis_epoch + 4).end_slot(spec.slots_per_epoch);
|
||||||
.build_epoch_cache(RelativeEpoch::Previous, &spec)
|
builder.teleport_to_slot(target_slot, &spec);
|
||||||
.unwrap();
|
|
||||||
state
|
// Builds all caches; benches will not contain shuffling/committee building times.
|
||||||
.build_epoch_cache(RelativeEpoch::Current, &spec)
|
builder.build_caches(&spec).unwrap();
|
||||||
.unwrap();
|
|
||||||
state.build_epoch_cache(RelativeEpoch::Next, &spec).unwrap();
|
// Inserts one attestation with full participation for each committee able to include an
|
||||||
|
// attestation in this state.
|
||||||
|
builder.insert_attestations(&spec);
|
||||||
|
|
||||||
|
let (state, _keypairs) = builder.build();
|
||||||
|
|
||||||
// Assert that the state has the maximum possible attestations.
|
// Assert that the state has the maximum possible attestations.
|
||||||
let committees_per_epoch = spec.get_epoch_committee_count(validator_count);
|
let committees_per_epoch = spec.get_epoch_committee_count(validator_count);
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
use ssz::{SignedRoot, TreeHash};
|
||||||
|
use types::*;
|
||||||
|
|
||||||
|
pub struct BeaconBlockBencher {
|
||||||
|
block: BeaconBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BeaconBlockBencher {
|
||||||
|
pub fn new(spec: &ChainSpec) -> Self {
|
||||||
|
Self {
|
||||||
|
block: BeaconBlock::genesis(spec.zero_hash, spec),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_slot(&mut self, slot: Slot) {
|
||||||
|
self.block.slot = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signs the block.
|
||||||
|
pub fn sign(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) {
|
||||||
|
let proposal = self.block.proposal(spec);
|
||||||
|
let message = proposal.signed_root();
|
||||||
|
let epoch = self.block.slot.epoch(spec.slots_per_epoch);
|
||||||
|
let domain = spec.get_domain(epoch, Domain::Proposal, fork);
|
||||||
|
self.block.signature = Signature::new(&message, domain, sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the randao to be a signature across the blocks epoch.
|
||||||
|
pub fn set_randao_reveal(&mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) {
|
||||||
|
let epoch = self.block.slot.epoch(spec.slots_per_epoch);
|
||||||
|
let message = epoch.hash_tree_root();
|
||||||
|
let domain = spec.get_domain(epoch, Domain::Randao, fork);
|
||||||
|
self.block.randao_reveal = Signature::new(&message, domain, sk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signs and returns the block, consuming the builder.
|
||||||
|
pub fn build(mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> BeaconBlock {
|
||||||
|
self.sign(sk, fork, spec);
|
||||||
|
self.block
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the block, consuming the builder.
|
||||||
|
pub fn build_without_signing(self) -> BeaconBlock {
|
||||||
|
self.block
|
||||||
|
}
|
||||||
|
}
|
213
eth2/state_processing/benching_utils/src/beacon_state_bencher.rs
Normal file
213
eth2/state_processing/benching_utils/src/beacon_state_bencher.rs
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
use bls::get_withdrawal_credentials;
|
||||||
|
use int_to_bytes::int_to_bytes48;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
use types::beacon_state::BeaconStateBuilder;
|
||||||
|
use types::*;
|
||||||
|
|
||||||
|
pub struct BeaconStateBencher {
|
||||||
|
state: BeaconState,
|
||||||
|
keypairs: Vec<Keypair>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BeaconStateBencher {
|
||||||
|
pub fn new(validator_count: usize, spec: &ChainSpec) -> Self {
|
||||||
|
let keypairs: Vec<Keypair> = (0..validator_count)
|
||||||
|
.collect::<Vec<usize>>()
|
||||||
|
.par_iter()
|
||||||
|
.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();
|
||||||
|
|
||||||
|
let validators = keypairs
|
||||||
|
.iter()
|
||||||
|
.map(|keypair| {
|
||||||
|
let withdrawal_credentials = Hash256::from_slice(&get_withdrawal_credentials(
|
||||||
|
&keypair.pk,
|
||||||
|
spec.bls_withdrawal_prefix_byte,
|
||||||
|
));
|
||||||
|
|
||||||
|
Validator {
|
||||||
|
pubkey: keypair.pk.clone(),
|
||||||
|
withdrawal_credentials,
|
||||||
|
activation_epoch: spec.far_future_epoch,
|
||||||
|
exit_epoch: spec.far_future_epoch,
|
||||||
|
withdrawable_epoch: spec.far_future_epoch,
|
||||||
|
initiated_exit: false,
|
||||||
|
slashed: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut state_builder = BeaconStateBuilder::new(
|
||||||
|
0,
|
||||||
|
Eth1Data {
|
||||||
|
deposit_root: Hash256::zero(),
|
||||||
|
block_hash: Hash256::zero(),
|
||||||
|
},
|
||||||
|
spec,
|
||||||
|
);
|
||||||
|
|
||||||
|
let balances = vec![32_000_000_000; validator_count];
|
||||||
|
|
||||||
|
state_builder.import_existing_validators(
|
||||||
|
validators,
|
||||||
|
balances,
|
||||||
|
validator_count as u64,
|
||||||
|
spec,
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
state: state_builder.build(spec).unwrap(),
|
||||||
|
keypairs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> (BeaconState, Vec<Keypair>) {
|
||||||
|
(self.state, self.keypairs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_caches(&mut self, spec: &ChainSpec) -> Result<(), BeaconStateError> {
|
||||||
|
let state = &mut self.state;
|
||||||
|
|
||||||
|
state.build_epoch_cache(RelativeEpoch::Previous, &spec)?;
|
||||||
|
state.build_epoch_cache(RelativeEpoch::Current, &spec)?;
|
||||||
|
state.build_epoch_cache(RelativeEpoch::Next, &spec)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `BeaconState` to be in a slot, calling `teleport_to_epoch` to update the epoch.
|
||||||
|
pub fn teleport_to_slot(&mut self, slot: Slot, spec: &ChainSpec) {
|
||||||
|
self.teleport_to_epoch(slot.epoch(spec.slots_per_epoch), spec);
|
||||||
|
self.state.slot = slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the `BeaconState` to be in the first 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).
|
||||||
|
fn teleport_to_epoch(&mut self, epoch: Epoch, spec: &ChainSpec) {
|
||||||
|
let state = &mut self.state;
|
||||||
|
|
||||||
|
let slot = epoch.start_slot(spec.slots_per_epoch);
|
||||||
|
|
||||||
|
state.slot = slot;
|
||||||
|
state.validator_registry_update_epoch = epoch - 1;
|
||||||
|
|
||||||
|
state.previous_shuffling_epoch = epoch - 1;
|
||||||
|
state.current_shuffling_epoch = epoch;
|
||||||
|
|
||||||
|
state.previous_shuffling_seed = Hash256::from_low_u64_le(0);
|
||||||
|
state.current_shuffling_seed = Hash256::from_low_u64_le(1);
|
||||||
|
|
||||||
|
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, spec: &ChainSpec) {
|
||||||
|
let state = &mut self.state;
|
||||||
|
|
||||||
|
state
|
||||||
|
.build_epoch_cache(RelativeEpoch::Previous, spec)
|
||||||
|
.unwrap();
|
||||||
|
state
|
||||||
|
.build_epoch_cache(RelativeEpoch::Current, spec)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let current_epoch = state.current_epoch(spec);
|
||||||
|
let previous_epoch = state.previous_epoch(spec);
|
||||||
|
|
||||||
|
let first_slot = previous_epoch.start_slot(spec.slots_per_epoch).as_u64();
|
||||||
|
let last_slot = current_epoch.end_slot(spec.slots_per_epoch).as_u64()
|
||||||
|
- spec.min_attestation_inclusion_delay;
|
||||||
|
let last_slot = std::cmp::min(state.slot.as_u64(), last_slot);
|
||||||
|
|
||||||
|
for slot in first_slot..last_slot + 1 {
|
||||||
|
let slot = Slot::from(slot);
|
||||||
|
|
||||||
|
let committees = state
|
||||||
|
.get_crosslink_committees_at_slot(slot, spec)
|
||||||
|
.unwrap()
|
||||||
|
.clone();
|
||||||
|
|
||||||
|
for (committee, shard) in committees {
|
||||||
|
state
|
||||||
|
.latest_attestations
|
||||||
|
.push(committee_to_pending_attestation(
|
||||||
|
state, &committee, shard, slot, spec,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.slots_per_epoch) != slot.epoch(spec.slots_per_epoch);
|
||||||
|
|
||||||
|
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.slots_per_epoch), spec)
|
||||||
|
.unwrap()
|
||||||
|
} else {
|
||||||
|
*state
|
||||||
|
.get_block_root(current_epoch.start_slot(spec.slots_per_epoch), spec)
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let justified_block_root = *state
|
||||||
|
.get_block_root(justified_epoch.start_slot(spec.slots_per_epoch), spec)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
PendingAttestation {
|
||||||
|
aggregation_bitfield,
|
||||||
|
data: AttestationData {
|
||||||
|
slot,
|
||||||
|
shard,
|
||||||
|
beacon_block_root: *state.get_block_root(slot, spec).unwrap(),
|
||||||
|
epoch_boundary_root,
|
||||||
|
crosslink_data_root: Hash256::zero(),
|
||||||
|
latest_crosslink: Crosslink {
|
||||||
|
epoch: slot.epoch(spec.slots_per_epoch),
|
||||||
|
crosslink_data_root: Hash256::zero(),
|
||||||
|
},
|
||||||
|
justified_epoch,
|
||||||
|
justified_block_root,
|
||||||
|
},
|
||||||
|
custody_bitfield,
|
||||||
|
inclusion_slot: slot + spec.min_attestation_inclusion_delay,
|
||||||
|
}
|
||||||
|
}
|
@ -1,195 +1,5 @@
|
|||||||
use bls::get_withdrawal_credentials;
|
mod beacon_block_bencher;
|
||||||
use int_to_bytes::int_to_bytes48;
|
mod beacon_state_bencher;
|
||||||
use rayon::prelude::*;
|
|
||||||
use types::beacon_state::BeaconStateBuilder;
|
|
||||||
use types::*;
|
|
||||||
|
|
||||||
pub struct BeaconStateBencher {
|
pub use beacon_block_bencher::BeaconBlockBencher;
|
||||||
state: BeaconState,
|
pub use beacon_state_bencher::BeaconStateBencher;
|
||||||
}
|
|
||||||
|
|
||||||
impl BeaconStateBencher {
|
|
||||||
pub fn new(validator_count: usize, spec: &ChainSpec) -> Self {
|
|
||||||
let keypairs: Vec<Keypair> = (0..validator_count)
|
|
||||||
.collect::<Vec<usize>>()
|
|
||||||
.par_iter()
|
|
||||||
.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();
|
|
||||||
|
|
||||||
let validators = keypairs
|
|
||||||
.iter()
|
|
||||||
.map(|keypair| {
|
|
||||||
let withdrawal_credentials = Hash256::from_slice(&get_withdrawal_credentials(
|
|
||||||
&keypair.pk,
|
|
||||||
spec.bls_withdrawal_prefix_byte,
|
|
||||||
));
|
|
||||||
|
|
||||||
Validator {
|
|
||||||
pubkey: keypair.pk.clone(),
|
|
||||||
withdrawal_credentials,
|
|
||||||
activation_epoch: spec.far_future_epoch,
|
|
||||||
exit_epoch: spec.far_future_epoch,
|
|
||||||
withdrawable_epoch: spec.far_future_epoch,
|
|
||||||
initiated_exit: false,
|
|
||||||
slashed: false,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut state_builder = BeaconStateBuilder::new(
|
|
||||||
0,
|
|
||||||
Eth1Data {
|
|
||||||
deposit_root: Hash256::zero(),
|
|
||||||
block_hash: Hash256::zero(),
|
|
||||||
},
|
|
||||||
spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
let balances = vec![32_000_000_000; validator_count];
|
|
||||||
|
|
||||||
state_builder.import_existing_validators(
|
|
||||||
validators,
|
|
||||||
balances,
|
|
||||||
validator_count as u64,
|
|
||||||
spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
state: state_builder.build(spec).unwrap(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build(self) -> BeaconState {
|
|
||||||
self.state
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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, spec: &ChainSpec) {
|
|
||||||
let state = &mut self.state;
|
|
||||||
|
|
||||||
let slot = epoch.end_slot(spec.slots_per_epoch);
|
|
||||||
|
|
||||||
state.slot = slot;
|
|
||||||
state.validator_registry_update_epoch = epoch - 1;
|
|
||||||
|
|
||||||
state.previous_shuffling_epoch = epoch - 1;
|
|
||||||
state.current_shuffling_epoch = epoch;
|
|
||||||
|
|
||||||
state.previous_shuffling_seed = Hash256::from_low_u64_le(0);
|
|
||||||
state.current_shuffling_seed = Hash256::from_low_u64_le(1);
|
|
||||||
|
|
||||||
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, spec: &ChainSpec) {
|
|
||||||
let state = &mut self.state;
|
|
||||||
|
|
||||||
state
|
|
||||||
.build_epoch_cache(RelativeEpoch::Previous, spec)
|
|
||||||
.unwrap();
|
|
||||||
state
|
|
||||||
.build_epoch_cache(RelativeEpoch::Current, spec)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let current_epoch = state.current_epoch(spec);
|
|
||||||
let previous_epoch = state.previous_epoch(spec);
|
|
||||||
|
|
||||||
let first_slot = previous_epoch.start_slot(spec.slots_per_epoch).as_u64();
|
|
||||||
let last_slot = current_epoch.end_slot(spec.slots_per_epoch).as_u64()
|
|
||||||
- spec.min_attestation_inclusion_delay;
|
|
||||||
let last_slot = std::cmp::min(state.slot.as_u64(), last_slot);
|
|
||||||
|
|
||||||
for slot in first_slot..last_slot + 1 {
|
|
||||||
let slot = Slot::from(slot);
|
|
||||||
|
|
||||||
let committees = state
|
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
|
||||||
.unwrap()
|
|
||||||
.clone();
|
|
||||||
|
|
||||||
for (committee, shard) in committees {
|
|
||||||
state
|
|
||||||
.latest_attestations
|
|
||||||
.push(committee_to_pending_attestation(
|
|
||||||
state, &committee, shard, slot, spec,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.slots_per_epoch) != slot.epoch(spec.slots_per_epoch);
|
|
||||||
|
|
||||||
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.slots_per_epoch), spec)
|
|
||||||
.unwrap()
|
|
||||||
} else {
|
|
||||||
*state
|
|
||||||
.get_block_root(current_epoch.start_slot(spec.slots_per_epoch), spec)
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let justified_block_root = *state
|
|
||||||
.get_block_root(justified_epoch.start_slot(spec.slots_per_epoch), spec)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
PendingAttestation {
|
|
||||||
aggregation_bitfield,
|
|
||||||
data: AttestationData {
|
|
||||||
slot,
|
|
||||||
shard,
|
|
||||||
beacon_block_root: *state.get_block_root(slot, spec).unwrap(),
|
|
||||||
epoch_boundary_root,
|
|
||||||
crosslink_data_root: Hash256::zero(),
|
|
||||||
latest_crosslink: Crosslink {
|
|
||||||
epoch: slot.epoch(spec.slots_per_epoch),
|
|
||||||
crosslink_data_root: Hash256::zero(),
|
|
||||||
},
|
|
||||||
justified_epoch,
|
|
||||||
justified_block_root,
|
|
||||||
},
|
|
||||||
custody_bitfield,
|
|
||||||
inclusion_slot: slot + spec.min_attestation_inclusion_delay,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
use self::verify_proposer_slashing::verify_proposer_slashing;
|
use self::verify_proposer_slashing::verify_proposer_slashing;
|
||||||
use errors::{BlockInvalid as Invalid, BlockProcessingError as Error, IntoWithIndex};
|
use errors::{BlockInvalid as Invalid, BlockProcessingError as Error, IntoWithIndex};
|
||||||
use hashing::hash;
|
use hashing::hash;
|
||||||
use log::debug;
|
|
||||||
use ssz::{ssz_encode, SignedRoot, TreeHash};
|
use ssz::{ssz_encode, SignedRoot, TreeHash};
|
||||||
use types::*;
|
use types::*;
|
||||||
|
|
||||||
@ -70,22 +69,21 @@ fn per_block_processing_signature_optional(
|
|||||||
// Verify that `block.slot == state.slot`.
|
// Verify that `block.slot == state.slot`.
|
||||||
verify!(block.slot == state.slot, Invalid::StateSlotMismatch);
|
verify!(block.slot == state.slot, Invalid::StateSlotMismatch);
|
||||||
|
|
||||||
// Ensure the current epoch cache is built.
|
// Ensure the current and previous epoch cache is built.
|
||||||
state.build_epoch_cache(RelativeEpoch::Current, spec)?;
|
state.build_epoch_cache(RelativeEpoch::Current, spec)?;
|
||||||
|
state.build_epoch_cache(RelativeEpoch::Previous, spec)?;
|
||||||
|
|
||||||
if should_verify_block_signature {
|
if should_verify_block_signature {
|
||||||
verify_block_signature(&state, &block, &spec)?;
|
verify_block_signature(&state, &block, &spec)?;
|
||||||
}
|
}
|
||||||
process_randao(&mut state, &block, &spec)?;
|
process_randao(&mut state, &block, &spec)?;
|
||||||
process_eth1_data(&mut state, &block.eth1_data)?;
|
process_eth1_data(&mut state, &block.eth1_data)?;
|
||||||
process_proposer_slashings(&mut state, &block.body.proposer_slashings[..], spec)?;
|
process_proposer_slashings(&mut state, &block.body.proposer_slashings, spec)?;
|
||||||
process_attester_slashings(&mut state, &block.body.attester_slashings[..], spec)?;
|
process_attester_slashings(&mut state, &block.body.attester_slashings, spec)?;
|
||||||
process_attestations(&mut state, &block.body.attestations[..], spec)?;
|
process_attestations(&mut state, &block.body.attestations, spec)?;
|
||||||
process_deposits(&mut state, &block.body.deposits[..], spec)?;
|
process_deposits(&mut state, &block.body.deposits, spec)?;
|
||||||
process_exits(&mut state, &block.body.voluntary_exits[..], spec)?;
|
process_exits(&mut state, &block.body.voluntary_exits, spec)?;
|
||||||
process_transfers(&mut state, &block.body.transfers[..], spec)?;
|
process_transfers(&mut state, &block.body.transfers, spec)?;
|
||||||
|
|
||||||
debug!("per_block_processing complete.");
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -11,8 +11,11 @@ fn runs_without_error() {
|
|||||||
let spec = ChainSpec::few_validators();
|
let spec = ChainSpec::few_validators();
|
||||||
|
|
||||||
let mut builder = BeaconStateBencher::new(8, &spec);
|
let mut builder = BeaconStateBencher::new(8, &spec);
|
||||||
builder.teleport_to_end_of_epoch(spec.genesis_epoch + 4, &spec);
|
|
||||||
let mut state = builder.build();
|
let target_slot = (spec.genesis_epoch + 4).end_slot(spec.slots_per_epoch);
|
||||||
|
builder.teleport_to_slot(target_slot, &spec);
|
||||||
|
|
||||||
|
let (mut state, _keypairs) = builder.build();
|
||||||
|
|
||||||
per_epoch_processing(&mut state, &spec).unwrap();
|
per_epoch_processing(&mut state, &spec).unwrap();
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::test_utils::TestRandom;
|
use crate::test_utils::TestRandom;
|
||||||
use crate::{BeaconBlockBody, ChainSpec, Eth1Data, Hash256, Slot};
|
use crate::{BeaconBlockBody, ChainSpec, Eth1Data, Hash256, Proposal, Slot};
|
||||||
use bls::Signature;
|
use bls::Signature;
|
||||||
use rand::RngCore;
|
use rand::RngCore;
|
||||||
use serde_derive::Serialize;
|
use serde_derive::Serialize;
|
||||||
use ssz::TreeHash;
|
use ssz::{SignedRoot, TreeHash};
|
||||||
use ssz_derive::{Decode, Encode, SignedRoot, TreeHash};
|
use ssz_derive::{Decode, Encode, SignedRoot, TreeHash};
|
||||||
use test_random_derive::TestRandom;
|
use test_random_derive::TestRandom;
|
||||||
|
|
||||||
@ -33,7 +33,6 @@ impl BeaconBlock {
|
|||||||
deposit_root: spec.zero_hash,
|
deposit_root: spec.zero_hash,
|
||||||
block_hash: spec.zero_hash,
|
block_hash: spec.zero_hash,
|
||||||
},
|
},
|
||||||
signature: spec.empty_signature.clone(),
|
|
||||||
body: BeaconBlockBody {
|
body: BeaconBlockBody {
|
||||||
proposer_slashings: vec![],
|
proposer_slashings: vec![],
|
||||||
attester_slashings: vec![],
|
attester_slashings: vec![],
|
||||||
@ -42,6 +41,7 @@ impl BeaconBlock {
|
|||||||
voluntary_exits: vec![],
|
voluntary_exits: vec![],
|
||||||
transfers: vec![],
|
transfers: vec![],
|
||||||
},
|
},
|
||||||
|
signature: spec.empty_signature.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +49,16 @@ impl BeaconBlock {
|
|||||||
pub fn canonical_root(&self) -> Hash256 {
|
pub fn canonical_root(&self) -> Hash256 {
|
||||||
Hash256::from_slice(&self.hash_tree_root()[..])
|
Hash256::from_slice(&self.hash_tree_root()[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an unsigned proposal for block.
|
||||||
|
pub fn proposal(&self, spec: &ChainSpec) -> Proposal {
|
||||||
|
Proposal {
|
||||||
|
slot: self.slot,
|
||||||
|
shard: spec.beacon_chain_shard_number,
|
||||||
|
block_root: Hash256::from_slice(&self.signed_root()),
|
||||||
|
signature: spec.empty_signature.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
Loading…
Reference in New Issue
Block a user