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 (state, keypairs) = build_state(validator_count, &spec);
|
||||||
let block = build_block(&state, &keypairs, &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(
|
bench_block_processing(
|
||||||
c,
|
c,
|
||||||
&block,
|
&block,
|
||||||
@ -52,6 +85,45 @@ fn build_block(state: &BeaconState, keypairs: &[Keypair], spec: &ChainSpec) -> B
|
|||||||
|
|
||||||
builder.set_randao_reveal(&keypair.sk, &state.fork, spec);
|
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)
|
builder.build(&keypair.sk, &state.fork, spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +225,10 @@ fn bench_block_processing(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let state = initial_state.clone();
|
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();
|
let spec = initial_spec.clone();
|
||||||
c.bench(
|
c.bench(
|
||||||
&format!("block_processing_{}", desc),
|
&format!("block_processing_{}", desc),
|
||||||
@ -221,11 +296,14 @@ fn bench_block_processing(
|
|||||||
);
|
);
|
||||||
|
|
||||||
let state = initial_state.clone();
|
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();
|
let spec = initial_spec.clone();
|
||||||
c.bench(
|
c.bench(
|
||||||
&format!("block_processing_{}", desc),
|
&format!("block_processing_{}", desc),
|
||||||
Benchmark::new("per_block_processing", move |b| {
|
Benchmark::new("per_block_processing_no_slashings", move |b| {
|
||||||
b.iter_with_setup(
|
b.iter_with_setup(
|
||||||
|| state.clone(),
|
|| state.clone(),
|
||||||
|mut state| black_box(per_block_processing(&mut state, &block, &spec).unwrap()),
|
|mut state| black_box(per_block_processing(&mut state, &block, &spec).unwrap()),
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
use rayon::prelude::*;
|
||||||
use ssz::{SignedRoot, TreeHash};
|
use ssz::{SignedRoot, TreeHash};
|
||||||
use types::*;
|
use types::{
|
||||||
|
attester_slashing::AttesterSlashingBuilder, proposer_slashing::ProposerSlashingBuilder, *,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct BeaconBlockBencher {
|
pub struct BeaconBlockBencher {
|
||||||
block: BeaconBlock,
|
block: BeaconBlock,
|
||||||
@ -33,6 +36,114 @@ impl BeaconBlockBencher {
|
|||||||
self.block.randao_reveal = Signature::new(&message, domain, sk);
|
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.
|
/// Signs and returns the block, consuming the builder.
|
||||||
pub fn build(mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> BeaconBlock {
|
pub fn build(mut self, sk: &SecretKey, fork: &Fork, spec: &ChainSpec) -> BeaconBlock {
|
||||||
self.sign(sk, fork, spec);
|
self.sign(sk, fork, spec);
|
||||||
@ -44,3 +155,131 @@ impl BeaconBlockBencher {
|
|||||||
self.block
|
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| {
|
let add_signatures = |attestation: &mut SlashableAttestation| {
|
||||||
for (i, validator_index) in validator_indices.iter().enumerate() {
|
for (i, validator_index) in validator_indices.iter().enumerate() {
|
||||||
|
attestation.custody_bitfield.set(i, false);
|
||||||
let attestation_data_and_custody_bit = AttestationDataAndCustodyBit {
|
let attestation_data_and_custody_bit = AttestationDataAndCustodyBit {
|
||||||
data: attestation.data.clone(),
|
data: attestation.data.clone(),
|
||||||
custody_bit: attestation.custody_bitfield.get(i).unwrap(),
|
custody_bit: attestation.custody_bitfield.get(i).unwrap(),
|
||||||
|
Loading…
Reference in New Issue
Block a user