diff --git a/eth2/state_processing/benches/block_processing_benches.rs b/eth2/state_processing/benches/block_processing_benches.rs index 75943b1ad..2ff2e7413 100644 --- a/eth2/state_processing/benches/block_processing_benches.rs +++ b/eth2/state_processing/benches/block_processing_benches.rs @@ -17,8 +17,8 @@ pub fn block_processing_16k_validators(c: &mut Criterion) { let validator_count = 16_384; - let (state, keypairs) = build_state(validator_count, &spec); - let block = build_block(&state, &keypairs, &spec); + let (mut state, keypairs) = build_state(validator_count, &spec); + let block = build_block(&mut state, &keypairs, &spec); assert_eq!( block.body.proposer_slashings.len(), @@ -53,6 +53,24 @@ pub fn block_processing_16k_validators(c: &mut Criterion) { "The block should have the maximum possible attestations." ); + assert_eq!( + block.body.deposits.len(), + spec.max_deposits as usize, + "The block should have the maximum possible deposits." + ); + + assert_eq!( + block.body.voluntary_exits.len(), + spec.max_voluntary_exits as usize, + "The block should have the maximum possible voluntary exits." + ); + + assert_eq!( + block.body.transfers.len(), + spec.max_transfers as usize, + "The block should have the maximum possible transfers." + ); + bench_block_processing( c, &block, @@ -75,7 +93,7 @@ fn build_state(validator_count: usize, spec: &ChainSpec) -> (BeaconState, Vec BeaconBlock { +fn build_block(state: &mut BeaconState, keypairs: &[Keypair], spec: &ChainSpec) -> BeaconBlock { let mut builder = BeaconBlockBencher::new(spec); builder.set_slot(state.slot); @@ -85,8 +103,13 @@ fn build_block(state: &BeaconState, keypairs: &[Keypair], spec: &ChainSpec) -> B builder.set_randao_reveal(&keypair.sk, &state.fork, spec); + // Used as a stream of validator indices for use in slashings, exits, etc. + let mut validators_iter = (0..keypairs.len() as u64).into_iter(); + // Insert the maximum possible number of `ProposerSlashing` objects. - for validator_index in 0..spec.max_proposer_slashings { + for _ in 0..spec.max_proposer_slashings { + let validator_index = validators_iter.next().expect("Insufficient validators."); + builder.insert_proposer_slashing( validator_index, &keypairs[validator_index as usize].sk, @@ -96,26 +119,18 @@ fn build_block(state: &BeaconState, keypairs: &[Keypair], spec: &ChainSpec) -> B } // Insert the maximum possible number of `AttesterSlashing` objects - let number_of_slashable_attesters = - spec.max_indices_per_slashable_vote * spec.max_attester_slashings; - let all_attester_slashing_indices: Vec = (spec.max_proposer_slashings - ..(spec.max_proposer_slashings + number_of_slashable_attesters)) - .collect(); - let attester_slashing_groups: Vec<&[u64]> = all_attester_slashing_indices - .chunks(spec.max_indices_per_slashable_vote as usize) - .collect(); - for attester_slashing_group in attester_slashing_groups { - let attester_slashing_keypairs: Vec<&SecretKey> = attester_slashing_group - .iter() - .map(|&validator_index| &keypairs[validator_index as usize].sk) - .collect(); + for _ in 0..spec.max_attester_slashings { + let mut attesters: Vec = vec![]; + let mut secret_keys: Vec<&SecretKey> = vec![]; - builder.insert_attester_slashing( - &attester_slashing_group, - &attester_slashing_keypairs, - &state.fork, - spec, - ); + for _ in 0..spec.max_indices_per_slashable_vote { + let validator_index = validators_iter.next().expect("Insufficient validators."); + + attesters.push(validator_index); + secret_keys.push(&keypairs[validator_index as usize].sk); + } + + builder.insert_attester_slashing(&attesters, &secret_keys, &state.fork, spec); } // Insert the maximum possible number of `Attestation` objects. @@ -124,6 +139,41 @@ fn build_block(state: &BeaconState, keypairs: &[Keypair], spec: &ChainSpec) -> B .fill_with_attestations(state, &all_secret_keys, spec) .unwrap(); + // Insert the maximum possible number of `Deposit` objects. + for i in 0..spec.max_deposits { + builder.insert_deposit(32_000_000_000, state.deposit_index + i, spec); + } + + // Insert the maximum possible number of `Exit` objects. + for _ in 0..spec.max_voluntary_exits { + let validator_index = validators_iter.next().expect("Insufficient validators."); + + builder.insert_exit( + state, + validator_index, + &keypairs[validator_index as usize].sk, + spec, + ); + } + + // Insert the maximum possible number of `Transfer` objects. + for _ in 0..spec.max_transfers { + let validator_index = validators_iter.next().expect("Insufficient validators."); + + // Manually set the validator to be withdrawn. + state.validator_registry[validator_index as usize].withdrawable_epoch = + state.previous_epoch(spec); + + builder.insert_transfer( + state, + validator_index, + validator_index, + 1, + keypairs[validator_index as usize].clone(), + spec, + ); + } + builder.build(&keypair.sk, &state.fork, spec) } diff --git a/eth2/state_processing/benching_utils/src/beacon_block_bencher.rs b/eth2/state_processing/benching_utils/src/beacon_block_bencher.rs index 5e7fddb55..3eafdc0c9 100644 --- a/eth2/state_processing/benching_utils/src/beacon_block_bencher.rs +++ b/eth2/state_processing/benching_utils/src/beacon_block_bencher.rs @@ -1,8 +1,13 @@ use rayon::prelude::*; use ssz::{SignedRoot, TreeHash}; use types::{ - attester_slashing::AttesterSlashingBuilder, proposer_slashing::ProposerSlashingBuilder, - test_utils::TestingAttestationBuilder, *, + attester_slashing::AttesterSlashingBuilder, + proposer_slashing::ProposerSlashingBuilder, + test_utils::{ + TestingAttestationBuilder, TestingDepositBuilder, TestingTransferBuilder, + TestingVoluntaryExitBuilder, + }, + *, }; pub struct BeaconBlockBencher { @@ -145,6 +150,54 @@ impl BeaconBlockBencher { Ok(()) } + /// Insert a `Valid` deposit into the state. + pub fn insert_deposit(&mut self, amount: u64, index: u64, spec: &ChainSpec) { + let keypair = Keypair::random(); + + let mut builder = TestingDepositBuilder::new(amount); + builder.set_index(index); + builder.sign(&keypair, spec); + + self.block.body.deposits.push(builder.build()) + } + + /// Insert a `Valid` exit into the state. + pub fn insert_exit( + &mut self, + state: &BeaconState, + validator_index: u64, + secret_key: &SecretKey, + spec: &ChainSpec, + ) { + let mut builder = TestingVoluntaryExitBuilder::new( + state.slot.epoch(spec.slots_per_epoch), + validator_index, + ); + + builder.sign(secret_key, &state.fork, spec); + + self.block.body.voluntary_exits.push(builder.build()) + } + + /// Insert a `Valid` transfer into the state. + /// + /// Note: this will set the validator to be withdrawable by directly modifying the state + /// validator registry. This _may_ cause problems historic hashes, etc. + pub fn insert_transfer( + &mut self, + state: &BeaconState, + from: u64, + to: u64, + amount: u64, + keypair: Keypair, + spec: &ChainSpec, + ) { + let mut builder = TestingTransferBuilder::new(from, to, amount, state.slot); + builder.sign(keypair, &state.fork, spec); + + self.block.body.transfers.push(builder.build()) + } + /// 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); diff --git a/eth2/types/src/test_utils/mod.rs b/eth2/types/src/test_utils/mod.rs index 6138940a2..2145f684a 100644 --- a/eth2/types/src/test_utils/mod.rs +++ b/eth2/types/src/test_utils/mod.rs @@ -1,6 +1,12 @@ mod test_random; mod testing_attestation_builder; +mod testing_deposit_builder; +mod testing_transfer_builder; +mod testing_voluntary_exit_builder; pub use rand::{prng::XorShiftRng, SeedableRng}; pub use test_random::TestRandom; pub use testing_attestation_builder::TestingAttestationBuilder; +pub use testing_deposit_builder::TestingDepositBuilder; +pub use testing_transfer_builder::TestingTransferBuilder; +pub use testing_voluntary_exit_builder::TestingVoluntaryExitBuilder; diff --git a/eth2/types/src/test_utils/testing_deposit_builder.rs b/eth2/types/src/test_utils/testing_deposit_builder.rs new file mode 100644 index 000000000..c7eadcfd1 --- /dev/null +++ b/eth2/types/src/test_utils/testing_deposit_builder.rs @@ -0,0 +1,48 @@ +use crate::*; +use bls::{create_proof_of_possession, get_withdrawal_credentials}; + +pub struct TestingDepositBuilder { + deposit: Deposit, +} + +impl TestingDepositBuilder { + pub fn new(amount: u64) -> Self { + let keypair = Keypair::random(); + + let deposit = Deposit { + branch: vec![], + index: 0, + deposit_data: DepositData { + amount, + timestamp: 1, + deposit_input: DepositInput { + pubkey: keypair.pk, + withdrawal_credentials: Hash256::zero(), + proof_of_possession: Signature::empty_signature(), + }, + }, + }; + + Self { deposit } + } + + pub fn set_index(&mut self, index: u64) { + self.deposit.index = index; + } + + pub fn sign(&mut self, keypair: &Keypair, spec: &ChainSpec) { + self.deposit.deposit_data.deposit_input.pubkey = keypair.pk.clone(); + self.deposit.deposit_data.deposit_input.proof_of_possession = + create_proof_of_possession(&keypair); + self.deposit + .deposit_data + .deposit_input + .withdrawal_credentials = Hash256::from_slice( + &get_withdrawal_credentials(&keypair.pk, spec.bls_withdrawal_prefix_byte)[..], + ); + } + + pub fn build(self) -> Deposit { + self.deposit + } +} diff --git a/eth2/types/src/test_utils/testing_transfer_builder.rs b/eth2/types/src/test_utils/testing_transfer_builder.rs new file mode 100644 index 000000000..a8a6d7a17 --- /dev/null +++ b/eth2/types/src/test_utils/testing_transfer_builder.rs @@ -0,0 +1,37 @@ +use crate::*; +use ssz::SignedRoot; + +pub struct TestingTransferBuilder { + transfer: Transfer, +} + +impl TestingTransferBuilder { + pub fn new(from: u64, to: u64, amount: u64, slot: Slot) -> Self { + let keypair = Keypair::random(); + + let mut transfer = Transfer { + from, + to, + amount, + fee: 0, + slot, + pubkey: keypair.pk, + signature: Signature::empty_signature(), + }; + + Self { transfer } + } + + pub fn sign(&mut self, keypair: Keypair, fork: &Fork, spec: &ChainSpec) { + self.transfer.pubkey = keypair.pk; + let message = self.transfer.signed_root(); + let epoch = self.transfer.slot.epoch(spec.slots_per_epoch); + let domain = spec.get_domain(epoch, Domain::Transfer, fork); + + self.transfer.signature = Signature::new(&message, domain, &keypair.sk); + } + + pub fn build(self) -> Transfer { + self.transfer + } +} diff --git a/eth2/types/src/test_utils/testing_voluntary_exit_builder.rs b/eth2/types/src/test_utils/testing_voluntary_exit_builder.rs new file mode 100644 index 000000000..92ef4484e --- /dev/null +++ b/eth2/types/src/test_utils/testing_voluntary_exit_builder.rs @@ -0,0 +1,29 @@ +use crate::*; +use ssz::SignedRoot; + +pub struct TestingVoluntaryExitBuilder { + exit: VoluntaryExit, +} + +impl TestingVoluntaryExitBuilder { + pub fn new(epoch: Epoch, validator_index: u64) -> Self { + let exit = VoluntaryExit { + epoch, + validator_index, + signature: Signature::empty_signature(), + }; + + Self { exit } + } + + pub fn sign(&mut self, secret_key: &SecretKey, fork: &Fork, spec: &ChainSpec) { + let message = self.exit.signed_root(); + let domain = spec.get_domain(self.exit.epoch, Domain::Exit, fork); + + self.exit.signature = Signature::new(&message, domain, secret_key); + } + + pub fn build(self) -> VoluntaryExit { + self.exit + } +}