Add slashings and attestations to per block benching
This commit is contained in:
		
							parent
							
								
									73ebb4bc2e
								
							
						
					
					
						commit
						f8ec1e0cfa
					
				| @ -20,6 +20,39 @@ pub fn block_processing_16k_validators(c: &mut Criterion) { | ||||
|     let (state, keypairs) = build_state(validator_count, &spec); | ||||
|     let block = build_block(&state, &keypairs, &spec); | ||||
| 
 | ||||
|     assert_eq!( | ||||
|         block.body.proposer_slashings.len(), | ||||
|         spec.max_proposer_slashings as usize, | ||||
|         "The block should have the maximum possible proposer slashings" | ||||
|     ); | ||||
| 
 | ||||
|     assert_eq!( | ||||
|         block.body.attester_slashings.len(), | ||||
|         spec.max_attester_slashings as usize, | ||||
|         "The block should have the maximum possible attester slashings" | ||||
|     ); | ||||
| 
 | ||||
|     for attester_slashing in &block.body.attester_slashings { | ||||
|         let len_1 = attester_slashing | ||||
|             .slashable_attestation_1 | ||||
|             .validator_indices | ||||
|             .len(); | ||||
|         let len_2 = attester_slashing | ||||
|             .slashable_attestation_1 | ||||
|             .validator_indices | ||||
|             .len(); | ||||
|         assert!( | ||||
|             (len_1 == len_2) && (len_2 == spec.max_indices_per_slashable_vote as usize), | ||||
|             "Each attester slashing should have the maximum possible validator indices" | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     assert_eq!( | ||||
|         block.body.attestations.len(), | ||||
|         spec.max_attestations as usize, | ||||
|         "The block should have the maximum possible attestations." | ||||
|     ); | ||||
| 
 | ||||
|     bench_block_processing( | ||||
|         c, | ||||
|         &block, | ||||
| @ -52,6 +85,45 @@ fn build_block(state: &BeaconState, keypairs: &[Keypair], spec: &ChainSpec) -> B | ||||
| 
 | ||||
|     builder.set_randao_reveal(&keypair.sk, &state.fork, spec); | ||||
| 
 | ||||
|     // Insert the maximum possible number of `ProposerSlashing` objects.
 | ||||
|     for validator_index in 0..spec.max_proposer_slashings { | ||||
|         builder.insert_proposer_slashing( | ||||
|             validator_index, | ||||
|             &keypairs[validator_index as usize].sk, | ||||
|             &state.fork, | ||||
|             spec, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // 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<u64> = (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(); | ||||
| 
 | ||||
|         builder.insert_attester_slashing( | ||||
|             &attester_slashing_group, | ||||
|             &attester_slashing_keypairs, | ||||
|             &state.fork, | ||||
|             spec, | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     // Insert the maximum possible number of `Attestation` objects.
 | ||||
|     let all_secret_keys: Vec<&SecretKey> = keypairs.iter().map(|keypair| &keypair.sk).collect(); | ||||
|     builder | ||||
|         .fill_with_attestations(state, &all_secret_keys, spec) | ||||
|         .unwrap(); | ||||
| 
 | ||||
|     builder.build(&keypair.sk, &state.fork, spec) | ||||
| } | ||||
| 
 | ||||
| @ -153,7 +225,10 @@ fn bench_block_processing( | ||||
|     ); | ||||
| 
 | ||||
|     let state = initial_state.clone(); | ||||
|     let block = initial_block.clone(); | ||||
|     let mut block = initial_block.clone(); | ||||
|     // Slashings will invalidate the attestations.
 | ||||
|     block.body.proposer_slashings = vec![]; | ||||
|     block.body.attester_slashings = vec![]; | ||||
|     let spec = initial_spec.clone(); | ||||
|     c.bench( | ||||
|         &format!("block_processing_{}", desc), | ||||
| @ -221,11 +296,14 @@ fn bench_block_processing( | ||||
|     ); | ||||
| 
 | ||||
|     let state = initial_state.clone(); | ||||
|     let block = initial_block.clone(); | ||||
|     let mut block = initial_block.clone(); | ||||
|     // Slashings will invalidate the attestations.
 | ||||
|     block.body.proposer_slashings = vec![]; | ||||
|     block.body.attester_slashings = vec![]; | ||||
|     let spec = initial_spec.clone(); | ||||
|     c.bench( | ||||
|         &format!("block_processing_{}", desc), | ||||
|         Benchmark::new("per_block_processing", move |b| { | ||||
|         Benchmark::new("per_block_processing_no_slashings", move |b| { | ||||
|             b.iter_with_setup( | ||||
|                 || state.clone(), | ||||
|                 |mut state| black_box(per_block_processing(&mut state, &block, &spec).unwrap()), | ||||
|  | ||||
| @ -1,5 +1,8 @@ | ||||
| use rayon::prelude::*; | ||||
| use ssz::{SignedRoot, TreeHash}; | ||||
| use types::*; | ||||
| use types::{ | ||||
|     attester_slashing::AttesterSlashingBuilder, proposer_slashing::ProposerSlashingBuilder, *, | ||||
| }; | ||||
| 
 | ||||
| pub struct BeaconBlockBencher { | ||||
|     block: BeaconBlock, | ||||
| @ -33,6 +36,114 @@ impl BeaconBlockBencher { | ||||
|         self.block.randao_reveal = Signature::new(&message, domain, sk); | ||||
|     } | ||||
| 
 | ||||
|     /// Inserts a signed, valid `ProposerSlashing` for the validator.
 | ||||
|     pub fn insert_proposer_slashing( | ||||
|         &mut self, | ||||
|         validator_index: u64, | ||||
|         secret_key: &SecretKey, | ||||
|         fork: &Fork, | ||||
|         spec: &ChainSpec, | ||||
|     ) { | ||||
|         let proposer_slashing = build_proposer_slashing(validator_index, secret_key, fork, spec); | ||||
|         self.block.body.proposer_slashings.push(proposer_slashing); | ||||
|     } | ||||
| 
 | ||||
|     /// Inserts a signed, valid `AttesterSlashing` for each validator index in `validator_indices`.
 | ||||
|     pub fn insert_attester_slashing( | ||||
|         &mut self, | ||||
|         validator_indices: &[u64], | ||||
|         secret_keys: &[&SecretKey], | ||||
|         fork: &Fork, | ||||
|         spec: &ChainSpec, | ||||
|     ) { | ||||
|         let attester_slashing = | ||||
|             build_double_vote_attester_slashing(validator_indices, secret_keys, fork, spec); | ||||
|         self.block.body.attester_slashings.push(attester_slashing); | ||||
|     } | ||||
| 
 | ||||
|     /// Fills the block with as many attestations as possible.
 | ||||
|     ///
 | ||||
|     /// Note: this will not perform well when `jepoch_committees_count % slots_per_epoch != 0`
 | ||||
|     pub fn fill_with_attestations( | ||||
|         &mut self, | ||||
|         state: &BeaconState, | ||||
|         secret_keys: &[&SecretKey], | ||||
|         spec: &ChainSpec, | ||||
|     ) -> Result<(), BeaconStateError> { | ||||
|         let mut slot = self.block.slot - spec.min_attestation_inclusion_delay; | ||||
|         let mut attestations_added = 0; | ||||
| 
 | ||||
|         // Stores the following (in order):
 | ||||
|         //
 | ||||
|         // - The slot of the committee.
 | ||||
|         // - A list of all validators in the committee.
 | ||||
|         // - A list of all validators in the committee that should sign the attestation.
 | ||||
|         // - The shard of the committee.
 | ||||
|         let mut committees: Vec<(Slot, Vec<usize>, Vec<usize>, u64)> = vec![]; | ||||
| 
 | ||||
|         // Loop backwards through slots gathering each committee, until:
 | ||||
|         //
 | ||||
|         // - The slot is too old to be included in a block at this slot.
 | ||||
|         // - The `MAX_ATTESTATIONS`.
 | ||||
|         loop { | ||||
|             if attestations_added == spec.max_attestations { | ||||
|                 break; | ||||
|             } | ||||
|             if state.slot >= slot + spec.slots_per_epoch { | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             for (committee, shard) in state.get_crosslink_committees_at_slot(slot, spec)? { | ||||
|                 committees.push((slot, committee.clone(), committee.clone(), *shard)) | ||||
|             } | ||||
| 
 | ||||
|             attestations_added += 1; | ||||
|             slot -= 1; | ||||
|         } | ||||
| 
 | ||||
|         // Loop through all the committees, splitting each one in half until we have
 | ||||
|         // `MAX_ATTESTATIONS` committees.
 | ||||
|         loop { | ||||
|             if committees.len() >= spec.max_attestations as usize { | ||||
|                 break; | ||||
|             } | ||||
| 
 | ||||
|             for index in 0..committees.len() { | ||||
|                 if committees.len() >= spec.max_attestations as usize { | ||||
|                     break; | ||||
|                 } | ||||
| 
 | ||||
|                 let (slot, committee, mut signing_validators, shard) = committees[index].clone(); | ||||
| 
 | ||||
|                 let new_signing_validators = | ||||
|                     signing_validators.split_off(signing_validators.len() / 2); | ||||
| 
 | ||||
|                 committees[index] = (slot, committee.clone(), signing_validators, shard); | ||||
|                 committees.push((slot, committee, new_signing_validators, shard)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         let mut attestations: Vec<Attestation> = committees | ||||
|             .par_iter() | ||||
|             .map(|(slot, committee, signing_validators, shard)| { | ||||
|                 committee_to_attestation( | ||||
|                     state, | ||||
|                     &committee, | ||||
|                     signing_validators, | ||||
|                     secret_keys, | ||||
|                     *shard, | ||||
|                     *slot, | ||||
|                     &state.fork, | ||||
|                     spec, | ||||
|                 ) | ||||
|             }) | ||||
|             .collect(); | ||||
| 
 | ||||
|         self.block.body.attestations.append(&mut attestations); | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| 
 | ||||
|     /// 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); | ||||
| @ -44,3 +155,131 @@ impl BeaconBlockBencher { | ||||
|         self.block | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Builds an `ProposerSlashing` for some `validator_index`.
 | ||||
| ///
 | ||||
| /// Signs the message using a `BeaconChainHarness`.
 | ||||
| fn build_proposer_slashing( | ||||
|     validator_index: u64, | ||||
|     secret_key: &SecretKey, | ||||
|     fork: &Fork, | ||||
|     spec: &ChainSpec, | ||||
| ) -> ProposerSlashing { | ||||
|     let signer = |_validator_index: u64, message: &[u8], epoch: Epoch, domain: Domain| { | ||||
|         let domain = spec.get_domain(epoch, domain, fork); | ||||
|         Signature::new(message, domain, secret_key) | ||||
|     }; | ||||
| 
 | ||||
|     ProposerSlashingBuilder::double_vote(validator_index, signer, spec) | ||||
| } | ||||
| 
 | ||||
| /// Builds an `AttesterSlashing` for some `validator_indices`.
 | ||||
| ///
 | ||||
| /// Signs the message using a `BeaconChainHarness`.
 | ||||
| fn build_double_vote_attester_slashing( | ||||
|     validator_indices: &[u64], | ||||
|     secret_keys: &[&SecretKey], | ||||
|     fork: &Fork, | ||||
|     spec: &ChainSpec, | ||||
| ) -> AttesterSlashing { | ||||
|     let signer = |validator_index: u64, message: &[u8], epoch: Epoch, domain: Domain| { | ||||
|         let key_index = validator_indices | ||||
|             .iter() | ||||
|             .position(|&i| i == validator_index) | ||||
|             .expect("Unable to find attester slashing key"); | ||||
|         let domain = spec.get_domain(epoch, domain, fork); | ||||
|         Signature::new(message, domain, secret_keys[key_index]) | ||||
|     }; | ||||
| 
 | ||||
|     AttesterSlashingBuilder::double_vote(validator_indices, signer) | ||||
| } | ||||
| 
 | ||||
| /// Convert some committee into a valid `Attestation`.
 | ||||
| ///
 | ||||
| /// Note: `committee` must be the full committee for the attestation. `signing_validators` is a
 | ||||
| /// list of validator indices that should sign the attestation.
 | ||||
| fn committee_to_attestation( | ||||
|     state: &BeaconState, | ||||
|     committee: &[usize], | ||||
|     signing_validators: &[usize], | ||||
|     secret_keys: &[&SecretKey], | ||||
|     shard: u64, | ||||
|     slot: Slot, | ||||
|     fork: &Fork, | ||||
|     spec: &ChainSpec, | ||||
| ) -> Attestation { | ||||
|     let current_epoch = state.current_epoch(spec); | ||||
|     let previous_epoch = state.previous_epoch(spec); | ||||
| 
 | ||||
|     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(); | ||||
| 
 | ||||
|     let data = AttestationData { | ||||
|         slot, | ||||
|         shard, | ||||
|         beacon_block_root: *state.get_block_root(slot, spec).unwrap(), | ||||
|         epoch_boundary_root, | ||||
|         crosslink_data_root: Hash256::zero(), | ||||
|         latest_crosslink: state.latest_crosslinks[shard as usize].clone(), | ||||
|         justified_epoch, | ||||
|         justified_block_root, | ||||
|     }; | ||||
| 
 | ||||
|     let mut aggregate_signature = AggregateSignature::new(); | ||||
|     let mut aggregation_bitfield = Bitfield::new(); | ||||
|     let mut custody_bitfield = Bitfield::new(); | ||||
| 
 | ||||
|     let message = AttestationDataAndCustodyBit { | ||||
|         data: data.clone(), | ||||
|         custody_bit: false, | ||||
|     } | ||||
|     .hash_tree_root(); | ||||
| 
 | ||||
|     let domain = spec.get_domain( | ||||
|         data.slot.epoch(spec.slots_per_epoch), | ||||
|         Domain::Attestation, | ||||
|         fork, | ||||
|     ); | ||||
| 
 | ||||
|     for (i, validator_index) in committee.iter().enumerate() { | ||||
|         custody_bitfield.set(i, false); | ||||
| 
 | ||||
|         if signing_validators | ||||
|             .iter() | ||||
|             .any(|&signer| *validator_index == signer) | ||||
|         { | ||||
|             aggregation_bitfield.set(i, true); | ||||
|             let signature = Signature::new(&message, domain, secret_keys[*validator_index]); | ||||
|             aggregate_signature.add(&signature); | ||||
|         } else { | ||||
|             aggregation_bitfield.set(i, false); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     Attestation { | ||||
|         aggregation_bitfield, | ||||
|         data, | ||||
|         custody_bitfield, | ||||
|         aggregate_signature, | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -66,6 +66,7 @@ impl AttesterSlashingBuilder { | ||||
| 
 | ||||
|         let add_signatures = |attestation: &mut SlashableAttestation| { | ||||
|             for (i, validator_index) in validator_indices.iter().enumerate() { | ||||
|                 attestation.custody_bitfield.set(i, false); | ||||
|                 let attestation_data_and_custody_bit = AttestationDataAndCustodyBit { | ||||
|                     data: attestation.data.clone(), | ||||
|                     custody_bit: attestation.custody_bitfield.get(i).unwrap(), | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user