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