op_pool: update for v0.6.x
This commit is contained in:
parent
4287891026
commit
7fbcdd54d7
@ -6,10 +6,12 @@ use state_processing::per_block_processing::errors::{
|
|||||||
AttestationValidationError, AttesterSlashingValidationError, DepositValidationError,
|
AttestationValidationError, AttesterSlashingValidationError, DepositValidationError,
|
||||||
ExitValidationError, ProposerSlashingValidationError, TransferValidationError,
|
ExitValidationError, ProposerSlashingValidationError, TransferValidationError,
|
||||||
};
|
};
|
||||||
|
#[cfg(not(test))]
|
||||||
|
use state_processing::per_block_processing::verify_deposit_merkle_proof;
|
||||||
use state_processing::per_block_processing::{
|
use state_processing::per_block_processing::{
|
||||||
gather_attester_slashing_indices_modular, validate_attestation,
|
get_slashable_indices_modular, validate_attestation,
|
||||||
validate_attestation_time_independent_only, verify_attester_slashing, verify_deposit,
|
validate_attestation_time_independent_only, verify_attester_slashing, verify_exit,
|
||||||
verify_exit, verify_exit_time_independent_only, verify_proposer_slashing, verify_transfer,
|
verify_exit_time_independent_only, verify_proposer_slashing, verify_transfer,
|
||||||
verify_transfer_time_independent_only,
|
verify_transfer_time_independent_only,
|
||||||
};
|
};
|
||||||
use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet};
|
use std::collections::{btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet};
|
||||||
@ -20,11 +22,6 @@ use types::{
|
|||||||
EthSpec, ProposerSlashing, Transfer, Validator, VoluntaryExit,
|
EthSpec, ProposerSlashing, Transfer, Validator, VoluntaryExit,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
const VERIFY_DEPOSIT_PROOFS: bool = false;
|
|
||||||
#[cfg(not(test))]
|
|
||||||
const VERIFY_DEPOSIT_PROOFS: bool = false; // TODO: enable this
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct OperationPool<T: EthSpec + Default> {
|
pub struct OperationPool<T: EthSpec + Default> {
|
||||||
/// Map from attestation ID (see below) to vectors of attestations.
|
/// Map from attestation ID (see below) to vectors of attestations.
|
||||||
@ -60,7 +57,7 @@ impl AttestationId {
|
|||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut bytes = ssz_encode(attestation);
|
let mut bytes = ssz_encode(attestation);
|
||||||
let epoch = attestation.slot.epoch(spec.slots_per_epoch);
|
let epoch = attestation.target_epoch;
|
||||||
bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, state, spec));
|
bytes.extend_from_slice(&AttestationId::compute_domain_bytes(epoch, state, spec));
|
||||||
AttestationId(bytes)
|
AttestationId(bytes)
|
||||||
}
|
}
|
||||||
@ -85,19 +82,13 @@ impl AttestationId {
|
|||||||
/// receive for including it in a block.
|
/// receive for including it in a block.
|
||||||
// TODO: this could be optimised with a map from validator index to whether that validator has
|
// TODO: this could be optimised with a map from validator index to whether that validator has
|
||||||
// attested in each of the current and previous epochs. Currently quadractic in number of validators.
|
// attested in each of the current and previous epochs. Currently quadractic in number of validators.
|
||||||
fn attestation_score<T: EthSpec>(
|
fn attestation_score<T: EthSpec>(attestation: &Attestation, state: &BeaconState<T>) -> usize {
|
||||||
attestation: &Attestation,
|
|
||||||
state: &BeaconState<T>,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> usize {
|
|
||||||
// Bitfield of validators whose attestations are new/fresh.
|
// Bitfield of validators whose attestations are new/fresh.
|
||||||
let mut new_validators = attestation.aggregation_bitfield.clone();
|
let mut new_validators = attestation.aggregation_bitfield.clone();
|
||||||
|
|
||||||
let attestation_epoch = attestation.data.slot.epoch(spec.slots_per_epoch);
|
let state_attestations = if attestation.data.target_epoch == state.current_epoch() {
|
||||||
|
|
||||||
let state_attestations = if attestation_epoch == state.current_epoch() {
|
|
||||||
&state.current_epoch_attestations
|
&state.current_epoch_attestations
|
||||||
} else if attestation_epoch == state.previous_epoch() {
|
} else if attestation.data.target_epoch == state.previous_epoch() {
|
||||||
&state.previous_epoch_attestations
|
&state.previous_epoch_attestations
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@ -199,7 +190,7 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
.filter(|attestation| validate_attestation(state, attestation, spec).is_ok())
|
.filter(|attestation| validate_attestation(state, attestation, spec).is_ok())
|
||||||
// Scored by the number of new attestations they introduce (descending)
|
// Scored by the number of new attestations they introduce (descending)
|
||||||
// TODO: need to consider attestations introduced in THIS block
|
// TODO: need to consider attestations introduced in THIS block
|
||||||
.map(|att| (att, attestation_score(att, state, spec)))
|
.map(|att| (att, attestation_score(att, state)))
|
||||||
// Don't include any useless attestations (score 0)
|
// Don't include any useless attestations (score 0)
|
||||||
.filter(|&(_, score)| score != 0)
|
.filter(|&(_, score)| score != 0)
|
||||||
.sorted_by_key(|&(_, score)| std::cmp::Reverse(score))
|
.sorted_by_key(|&(_, score)| std::cmp::Reverse(score))
|
||||||
@ -211,15 +202,16 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Remove attestations which are too old to be included in a block.
|
/// Remove attestations which are too old to be included in a block.
|
||||||
// TODO: we could probably prune other attestations here:
|
pub fn prune_attestations(&self, finalized_state: &BeaconState<T>) {
|
||||||
// - ones that are completely covered by attestations included in the state
|
// We know we can include an attestation if:
|
||||||
// - maybe ones invalidated by the confirmation of one fork over another
|
// state.slot <= attestation_slot + SLOTS_PER_EPOCH
|
||||||
pub fn prune_attestations(&self, finalized_state: &BeaconState<T>, spec: &ChainSpec) {
|
// We approximate this check using the attestation's epoch, to avoid computing
|
||||||
|
// the slot or relying on the committee cache of the finalized state.
|
||||||
self.attestations.write().retain(|_, attestations| {
|
self.attestations.write().retain(|_, attestations| {
|
||||||
// All the attestations in this bucket have the same data, so we only need to
|
// All the attestations in this bucket have the same data, so we only need to
|
||||||
// check the first one.
|
// check the first one.
|
||||||
attestations.first().map_or(false, |att| {
|
attestations.first().map_or(false, |att| {
|
||||||
finalized_state.slot < att.data.slot + spec.slots_per_epoch
|
finalized_state.current_epoch() <= att.data.target_epoch + 1
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -227,6 +219,7 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
/// Add a deposit to the pool.
|
/// Add a deposit to the pool.
|
||||||
///
|
///
|
||||||
/// No two distinct deposits should be added with the same index.
|
/// No two distinct deposits should be added with the same index.
|
||||||
|
#[cfg_attr(test, allow(unused_variables))]
|
||||||
pub fn insert_deposit(
|
pub fn insert_deposit(
|
||||||
&self,
|
&self,
|
||||||
deposit: Deposit,
|
deposit: Deposit,
|
||||||
@ -237,7 +230,9 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
|
|
||||||
match self.deposits.write().entry(deposit.index) {
|
match self.deposits.write().entry(deposit.index) {
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
verify_deposit(state, &deposit, VERIFY_DEPOSIT_PROOFS, spec)?;
|
// TODO: fix tests to generate valid merkle proofs
|
||||||
|
#[cfg(not(test))]
|
||||||
|
verify_deposit_merkle_proof(state, &deposit, spec)?;
|
||||||
entry.insert(deposit);
|
entry.insert(deposit);
|
||||||
Ok(Fresh)
|
Ok(Fresh)
|
||||||
}
|
}
|
||||||
@ -245,7 +240,9 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
if entry.get() == &deposit {
|
if entry.get() == &deposit {
|
||||||
Ok(Duplicate)
|
Ok(Duplicate)
|
||||||
} else {
|
} else {
|
||||||
verify_deposit(state, &deposit, VERIFY_DEPOSIT_PROOFS, spec)?;
|
// TODO: fix tests to generate valid merkle proofs
|
||||||
|
#[cfg(not(test))]
|
||||||
|
verify_deposit_merkle_proof(state, &deposit, spec)?;
|
||||||
Ok(Replaced(Box::new(entry.insert(deposit))))
|
Ok(Replaced(Box::new(entry.insert(deposit))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -256,6 +253,7 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
///
|
///
|
||||||
/// Take at most the maximum number of deposits, beginning from the current deposit index.
|
/// Take at most the maximum number of deposits, beginning from the current deposit index.
|
||||||
pub fn get_deposits(&self, state: &BeaconState<T>, spec: &ChainSpec) -> Vec<Deposit> {
|
pub fn get_deposits(&self, state: &BeaconState<T>, spec: &ChainSpec) -> Vec<Deposit> {
|
||||||
|
// TODO: might want to re-check the Merkle proof to account for Eth1 forking
|
||||||
let start_idx = state.deposit_index;
|
let start_idx = state.deposit_index;
|
||||||
(start_idx..start_idx + spec.max_deposits)
|
(start_idx..start_idx + spec.max_deposits)
|
||||||
.map(|idx| self.deposits.read().get(&idx).cloned())
|
.map(|idx| self.deposits.read().get(&idx).cloned())
|
||||||
@ -300,8 +298,8 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> (AttestationId, AttestationId) {
|
) -> (AttestationId, AttestationId) {
|
||||||
(
|
(
|
||||||
AttestationId::from_data(&slashing.slashable_attestation_1.data, state, spec),
|
AttestationId::from_data(&slashing.attestation_1.data, state, spec),
|
||||||
AttestationId::from_data(&slashing.slashable_attestation_2.data, state, spec),
|
AttestationId::from_data(&slashing.attestation_2.data, state, spec),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -356,12 +354,10 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
})
|
})
|
||||||
.filter(|(_, slashing)| {
|
.filter(|(_, slashing)| {
|
||||||
// Take all slashings that will slash 1 or more validators.
|
// Take all slashings that will slash 1 or more validators.
|
||||||
let slashed_validators = gather_attester_slashing_indices_modular(
|
let slashed_validators =
|
||||||
state,
|
get_slashable_indices_modular(state, slashing, |index, validator| {
|
||||||
slashing,
|
validator.slashed || to_be_slashed.contains(&index)
|
||||||
|index, validator| validator.slashed || to_be_slashed.contains(&index),
|
});
|
||||||
spec,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Extend the `to_be_slashed` set so subsequent iterations don't try to include
|
// Extend the `to_be_slashed` set so subsequent iterations don't try to include
|
||||||
// useless slashings.
|
// useless slashings.
|
||||||
@ -380,7 +376,7 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Prune proposer slashings for all slashed or withdrawn validators.
|
/// Prune proposer slashings for all slashed or withdrawn validators.
|
||||||
pub fn prune_proposer_slashings(&self, finalized_state: &BeaconState<T>, spec: &ChainSpec) {
|
pub fn prune_proposer_slashings(&self, finalized_state: &BeaconState<T>) {
|
||||||
prune_validator_hash_map(
|
prune_validator_hash_map(
|
||||||
&mut self.proposer_slashings.write(),
|
&mut self.proposer_slashings.write(),
|
||||||
|validator| {
|
|validator| {
|
||||||
@ -396,13 +392,11 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
self.attester_slashings.write().retain(|id, slashing| {
|
self.attester_slashings.write().retain(|id, slashing| {
|
||||||
let fork_ok = &Self::attester_slashing_id(slashing, finalized_state, spec) == id;
|
let fork_ok = &Self::attester_slashing_id(slashing, finalized_state, spec) == id;
|
||||||
let curr_epoch = finalized_state.current_epoch();
|
let curr_epoch = finalized_state.current_epoch();
|
||||||
let slashing_ok = gather_attester_slashing_indices_modular(
|
let slashing_ok =
|
||||||
finalized_state,
|
get_slashable_indices_modular(finalized_state, slashing, |_, validator| {
|
||||||
slashing,
|
validator.slashed || validator.is_withdrawable_at(curr_epoch)
|
||||||
|_, validator| validator.slashed || validator.is_withdrawable_at(curr_epoch),
|
})
|
||||||
spec,
|
.is_ok();
|
||||||
)
|
|
||||||
.is_ok();
|
|
||||||
fork_ok && slashing_ok
|
fork_ok && slashing_ok
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -435,7 +429,7 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Prune if validator has already exited at the last finalized state.
|
/// Prune if validator has already exited at the last finalized state.
|
||||||
pub fn prune_voluntary_exits(&self, finalized_state: &BeaconState<T>, spec: &ChainSpec) {
|
pub fn prune_voluntary_exits(&self, finalized_state: &BeaconState<T>) {
|
||||||
prune_validator_hash_map(
|
prune_validator_hash_map(
|
||||||
&mut self.voluntary_exits.write(),
|
&mut self.voluntary_exits.write(),
|
||||||
|validator| validator.is_exited_at(finalized_state.current_epoch()),
|
|validator| validator.is_exited_at(finalized_state.current_epoch()),
|
||||||
@ -481,11 +475,11 @@ impl<T: EthSpec> OperationPool<T> {
|
|||||||
|
|
||||||
/// Prune all types of transactions given the latest finalized state.
|
/// Prune all types of transactions given the latest finalized state.
|
||||||
pub fn prune_all(&self, finalized_state: &BeaconState<T>, spec: &ChainSpec) {
|
pub fn prune_all(&self, finalized_state: &BeaconState<T>, spec: &ChainSpec) {
|
||||||
self.prune_attestations(finalized_state, spec);
|
self.prune_attestations(finalized_state);
|
||||||
self.prune_deposits(finalized_state);
|
self.prune_deposits(finalized_state);
|
||||||
self.prune_proposer_slashings(finalized_state, spec);
|
self.prune_proposer_slashings(finalized_state);
|
||||||
self.prune_attester_slashings(finalized_state, spec);
|
self.prune_attester_slashings(finalized_state, spec);
|
||||||
self.prune_voluntary_exits(finalized_state, spec);
|
self.prune_voluntary_exits(finalized_state);
|
||||||
self.prune_transfers(finalized_state);
|
self.prune_transfers(finalized_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -565,8 +559,8 @@ mod tests {
|
|||||||
let rng = &mut XorShiftRng::from_seed([42; 16]);
|
let rng = &mut XorShiftRng::from_seed([42; 16]);
|
||||||
let (ref spec, ref state) = test_state(rng);
|
let (ref spec, ref state) = test_state(rng);
|
||||||
let op_pool = OperationPool::new();
|
let op_pool = OperationPool::new();
|
||||||
let deposit1 = make_deposit(rng, state, spec);
|
let deposit1 = make_deposit(rng);
|
||||||
let mut deposit2 = make_deposit(rng, state, spec);
|
let mut deposit2 = make_deposit(rng);
|
||||||
deposit2.index = deposit1.index;
|
deposit2.index = deposit1.index;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -594,7 +588,7 @@ mod tests {
|
|||||||
let offset = 1;
|
let offset = 1;
|
||||||
assert!(offset <= extra);
|
assert!(offset <= extra);
|
||||||
|
|
||||||
let deposits = dummy_deposits(rng, &state, &spec, start, max_deposits + extra);
|
let deposits = dummy_deposits(rng, start, max_deposits + extra);
|
||||||
|
|
||||||
for deposit in &deposits {
|
for deposit in &deposits {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -625,8 +619,8 @@ mod tests {
|
|||||||
let gap = 25;
|
let gap = 25;
|
||||||
let start2 = start1 + count + gap;
|
let start2 = start1 + count + gap;
|
||||||
|
|
||||||
let deposits1 = dummy_deposits(rng, &state, &spec, start1, count);
|
let deposits1 = dummy_deposits(rng, start1, count);
|
||||||
let deposits2 = dummy_deposits(rng, &state, &spec, start2, count);
|
let deposits2 = dummy_deposits(rng, start2, count);
|
||||||
|
|
||||||
for d in deposits1.into_iter().chain(deposits2) {
|
for d in deposits1.into_iter().chain(deposits2) {
|
||||||
assert!(op_pool.insert_deposit(d, &state, &spec).is_ok());
|
assert!(op_pool.insert_deposit(d, &state, &spec).is_ok());
|
||||||
@ -664,38 +658,14 @@ mod tests {
|
|||||||
assert_eq!(op_pool.num_deposits(), 0);
|
assert_eq!(op_pool.num_deposits(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a random deposit (with a valid proof of posession)
|
// Create a random deposit
|
||||||
fn make_deposit<T: EthSpec>(
|
fn make_deposit(rng: &mut XorShiftRng) -> Deposit {
|
||||||
rng: &mut XorShiftRng,
|
Deposit::random_for_test(rng)
|
||||||
state: &BeaconState<T>,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) -> Deposit {
|
|
||||||
let keypair = Keypair::random();
|
|
||||||
let mut deposit = Deposit::random_for_test(rng);
|
|
||||||
let mut deposit_input = DepositInput {
|
|
||||||
pubkey: keypair.pk.clone(),
|
|
||||||
withdrawal_credentials: Hash256::zero(),
|
|
||||||
proof_of_possession: Signature::empty_signature(),
|
|
||||||
};
|
|
||||||
deposit_input.proof_of_possession = deposit_input.create_proof_of_possession(
|
|
||||||
&keypair.sk,
|
|
||||||
state.slot.epoch(spec.slots_per_epoch),
|
|
||||||
&state.fork,
|
|
||||||
spec,
|
|
||||||
);
|
|
||||||
deposit.deposit_data.deposit_input = deposit_input;
|
|
||||||
deposit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create `count` dummy deposits with sequential deposit IDs beginning from `start`.
|
// Create `count` dummy deposits with sequential deposit IDs beginning from `start`.
|
||||||
fn dummy_deposits<T: EthSpec>(
|
fn dummy_deposits(rng: &mut XorShiftRng, start: u64, count: u64) -> Vec<Deposit> {
|
||||||
rng: &mut XorShiftRng,
|
let proto_deposit = make_deposit(rng);
|
||||||
state: &BeaconState<T>,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
start: u64,
|
|
||||||
count: u64,
|
|
||||||
) -> Vec<Deposit> {
|
|
||||||
let proto_deposit = make_deposit(rng, state, spec);
|
|
||||||
(start..start + count)
|
(start..start + count)
|
||||||
.map(|index| {
|
.map(|index| {
|
||||||
let mut deposit = proto_deposit.clone();
|
let mut deposit = proto_deposit.clone();
|
||||||
@ -722,7 +692,8 @@ mod tests {
|
|||||||
/// Create a signed attestation for use in tests.
|
/// Create a signed attestation for use in tests.
|
||||||
/// Signed by all validators in `committee[signing_range]` and `committee[extra_signer]`.
|
/// Signed by all validators in `committee[signing_range]` and `committee[extra_signer]`.
|
||||||
fn signed_attestation<R: std::slice::SliceIndex<[usize], Output = [usize]>, E: EthSpec>(
|
fn signed_attestation<R: std::slice::SliceIndex<[usize], Output = [usize]>, E: EthSpec>(
|
||||||
committee: &CrosslinkCommittee,
|
committee: &[usize],
|
||||||
|
shard: u64,
|
||||||
keypairs: &[Keypair],
|
keypairs: &[Keypair],
|
||||||
signing_range: R,
|
signing_range: R,
|
||||||
slot: Slot,
|
slot: Slot,
|
||||||
@ -730,18 +701,12 @@ mod tests {
|
|||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
extra_signer: Option<usize>,
|
extra_signer: Option<usize>,
|
||||||
) -> Attestation {
|
) -> Attestation {
|
||||||
let mut builder = TestingAttestationBuilder::new(
|
let mut builder = TestingAttestationBuilder::new(state, committee, slot, shard, spec);
|
||||||
state,
|
let signers = &committee[signing_range];
|
||||||
&committee.committee,
|
|
||||||
slot,
|
|
||||||
committee.shard,
|
|
||||||
spec,
|
|
||||||
);
|
|
||||||
let signers = &committee.committee[signing_range];
|
|
||||||
let committee_keys = signers.iter().map(|&i| &keypairs[i].sk).collect::<Vec<_>>();
|
let committee_keys = signers.iter().map(|&i| &keypairs[i].sk).collect::<Vec<_>>();
|
||||||
builder.sign(signers, &committee_keys, &state.fork, spec);
|
builder.sign(signers, &committee_keys, &state.fork, spec);
|
||||||
extra_signer.map(|c_idx| {
|
extra_signer.map(|c_idx| {
|
||||||
let validator_index = committee.committee[c_idx];
|
let validator_index = committee[c_idx];
|
||||||
builder.sign(
|
builder.sign(
|
||||||
&[validator_index],
|
&[validator_index],
|
||||||
&[&keypairs[validator_index].sk],
|
&[&keypairs[validator_index].sk],
|
||||||
@ -759,7 +724,7 @@ mod tests {
|
|||||||
let spec = E::spec();
|
let spec = E::spec();
|
||||||
|
|
||||||
let num_validators =
|
let num_validators =
|
||||||
num_committees * (spec.slots_per_epoch * spec.target_committee_size) as usize;
|
num_committees * spec.slots_per_epoch as usize * spec.target_committee_size;
|
||||||
let mut state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(
|
let mut state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(
|
||||||
num_validators,
|
num_validators,
|
||||||
&spec,
|
&spec,
|
||||||
@ -773,18 +738,6 @@ mod tests {
|
|||||||
(state, keypairs, FoundationEthSpec::spec())
|
(state, keypairs, FoundationEthSpec::spec())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the latest crosslink in the state to match the attestation.
|
|
||||||
fn fake_latest_crosslink<E: EthSpec>(
|
|
||||||
att: &Attestation,
|
|
||||||
state: &mut BeaconState<E>,
|
|
||||||
spec: &ChainSpec,
|
|
||||||
) {
|
|
||||||
state.latest_crosslinks[att.data.shard as usize] = Crosslink {
|
|
||||||
crosslink_data_root: att.data.crosslink_data_root,
|
|
||||||
epoch: att.data.slot.epoch(spec.slots_per_epoch),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_attestation_score() {
|
fn test_attestation_score() {
|
||||||
let (ref mut state, ref keypairs, ref spec) =
|
let (ref mut state, ref keypairs, ref spec) =
|
||||||
@ -792,27 +745,47 @@ mod tests {
|
|||||||
|
|
||||||
let slot = state.slot - 1;
|
let slot = state.slot - 1;
|
||||||
let committees = state
|
let committees = state
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
.get_crosslink_committees_at_slot(slot)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.into_iter()
|
||||||
|
.map(CrosslinkCommittee::into_owned)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for committee in committees {
|
for cc in committees {
|
||||||
let att1 = signed_attestation(&committee, keypairs, ..2, slot, state, spec, None);
|
let att1 = signed_attestation(
|
||||||
let att2 = signed_attestation(&committee, keypairs, .., slot, state, spec, None);
|
&cc.committee,
|
||||||
|
cc.shard,
|
||||||
|
keypairs,
|
||||||
|
..2,
|
||||||
|
slot,
|
||||||
|
state,
|
||||||
|
spec,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
let att2 = signed_attestation(
|
||||||
|
&cc.committee,
|
||||||
|
cc.shard,
|
||||||
|
keypairs,
|
||||||
|
..,
|
||||||
|
slot,
|
||||||
|
state,
|
||||||
|
spec,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
att1.aggregation_bitfield.num_set_bits(),
|
att1.aggregation_bitfield.num_set_bits(),
|
||||||
attestation_score(&att1, state, spec)
|
attestation_score(&att1, state)
|
||||||
);
|
);
|
||||||
|
|
||||||
state
|
state.current_epoch_attestations.push(PendingAttestation {
|
||||||
.current_epoch_attestations
|
aggregation_bitfield: att1.aggregation_bitfield.clone(),
|
||||||
.push(PendingAttestation::from_attestation(&att1, state.slot));
|
data: att1.data.clone(),
|
||||||
|
inclusion_delay: 0,
|
||||||
|
proposer_index: 0,
|
||||||
|
});
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(cc.committee.len() - 2, attestation_score(&att2, state));
|
||||||
committee.committee.len() - 2,
|
|
||||||
attestation_score(&att2, state, spec)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -826,9 +799,11 @@ mod tests {
|
|||||||
|
|
||||||
let slot = state.slot - 1;
|
let slot = state.slot - 1;
|
||||||
let committees = state
|
let committees = state
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
.get_crosslink_committees_at_slot(slot)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.into_iter()
|
||||||
|
.map(CrosslinkCommittee::into_owned)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
committees.len(),
|
committees.len(),
|
||||||
@ -836,11 +811,12 @@ mod tests {
|
|||||||
"we expect just one committee with this many validators"
|
"we expect just one committee with this many validators"
|
||||||
);
|
);
|
||||||
|
|
||||||
for committee in &committees {
|
for cc in &committees {
|
||||||
let step_size = 2;
|
let step_size = 2;
|
||||||
for i in (0..committee.committee.len()).step_by(step_size) {
|
for i in (0..cc.committee.len()).step_by(step_size) {
|
||||||
let att = signed_attestation(
|
let att = signed_attestation(
|
||||||
committee,
|
&cc.committee,
|
||||||
|
cc.shard,
|
||||||
keypairs,
|
keypairs,
|
||||||
i..i + step_size,
|
i..i + step_size,
|
||||||
slot,
|
slot,
|
||||||
@ -848,7 +824,6 @@ mod tests {
|
|||||||
spec,
|
spec,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
fake_latest_crosslink(&att, state, spec);
|
|
||||||
op_pool.insert_attestation(att, state, spec).unwrap();
|
op_pool.insert_attestation(att, state, spec).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -872,13 +847,13 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Prune attestations shouldn't do anything at this point.
|
// Prune attestations shouldn't do anything at this point.
|
||||||
op_pool.prune_attestations(state, spec);
|
op_pool.prune_attestations(state);
|
||||||
assert_eq!(op_pool.num_attestations(), committees.len());
|
assert_eq!(op_pool.num_attestations(), committees.len());
|
||||||
|
|
||||||
// But once we advance to an epoch after the attestation, it should prune it out of
|
// But once we advance to more than an epoch after the attestation, it should prune it
|
||||||
// existence.
|
// out of existence.
|
||||||
state.slot = slot + spec.slots_per_epoch;
|
state.slot += 2 * spec.slots_per_epoch;
|
||||||
op_pool.prune_attestations(state, spec);
|
op_pool.prune_attestations(state);
|
||||||
assert_eq!(op_pool.num_attestations(), 0);
|
assert_eq!(op_pool.num_attestations(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -892,13 +867,23 @@ mod tests {
|
|||||||
|
|
||||||
let slot = state.slot - 1;
|
let slot = state.slot - 1;
|
||||||
let committees = state
|
let committees = state
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
.get_crosslink_committees_at_slot(slot)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.into_iter()
|
||||||
|
.map(CrosslinkCommittee::into_owned)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
for committee in &committees {
|
for cc in &committees {
|
||||||
let att = signed_attestation(committee, keypairs, .., slot, state, spec, None);
|
let att = signed_attestation(
|
||||||
fake_latest_crosslink(&att, state, spec);
|
&cc.committee,
|
||||||
|
cc.shard,
|
||||||
|
keypairs,
|
||||||
|
..,
|
||||||
|
slot,
|
||||||
|
state,
|
||||||
|
spec,
|
||||||
|
None,
|
||||||
|
);
|
||||||
op_pool
|
op_pool
|
||||||
.insert_attestation(att.clone(), state, spec)
|
.insert_attestation(att.clone(), state, spec)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -919,17 +904,20 @@ mod tests {
|
|||||||
|
|
||||||
let slot = state.slot - 1;
|
let slot = state.slot - 1;
|
||||||
let committees = state
|
let committees = state
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
.get_crosslink_committees_at_slot(slot)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.into_iter()
|
||||||
|
.map(CrosslinkCommittee::into_owned)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let step_size = 2;
|
let step_size = 2;
|
||||||
for committee in &committees {
|
for cc in &committees {
|
||||||
// Create attestations that overlap on `step_size` validators, like:
|
// Create attestations that overlap on `step_size` validators, like:
|
||||||
// {0,1,2,3}, {2,3,4,5}, {4,5,6,7}, ...
|
// {0,1,2,3}, {2,3,4,5}, {4,5,6,7}, ...
|
||||||
for i in (0..committee.committee.len() - step_size).step_by(step_size) {
|
for i in (0..cc.committee.len() - step_size).step_by(step_size) {
|
||||||
let att = signed_attestation(
|
let att = signed_attestation(
|
||||||
committee,
|
&cc.committee,
|
||||||
|
cc.shard,
|
||||||
keypairs,
|
keypairs,
|
||||||
i..i + 2 * step_size,
|
i..i + 2 * step_size,
|
||||||
slot,
|
slot,
|
||||||
@ -937,7 +925,6 @@ mod tests {
|
|||||||
spec,
|
spec,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
fake_latest_crosslink(&att, state, spec);
|
|
||||||
op_pool.insert_attestation(att, state, spec).unwrap();
|
op_pool.insert_attestation(att, state, spec).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -965,17 +952,20 @@ mod tests {
|
|||||||
|
|
||||||
let slot = state.slot - 1;
|
let slot = state.slot - 1;
|
||||||
let committees = state
|
let committees = state
|
||||||
.get_crosslink_committees_at_slot(slot, spec)
|
.get_crosslink_committees_at_slot(slot)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.clone();
|
.into_iter()
|
||||||
|
.map(CrosslinkCommittee::into_owned)
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
let max_attestations = spec.max_attestations as usize;
|
let max_attestations = spec.max_attestations as usize;
|
||||||
let target_committee_size = spec.target_committee_size as usize;
|
let target_committee_size = spec.target_committee_size as usize;
|
||||||
|
|
||||||
let mut insert_attestations = |committee, step_size| {
|
let insert_attestations = |cc: &OwnedCrosslinkCommittee, step_size| {
|
||||||
for i in (0..target_committee_size).step_by(step_size) {
|
for i in (0..target_committee_size).step_by(step_size) {
|
||||||
let att = signed_attestation(
|
let att = signed_attestation(
|
||||||
committee,
|
&cc.committee,
|
||||||
|
cc.shard,
|
||||||
keypairs,
|
keypairs,
|
||||||
i..i + step_size,
|
i..i + step_size,
|
||||||
slot,
|
slot,
|
||||||
@ -983,7 +973,6 @@ mod tests {
|
|||||||
spec,
|
spec,
|
||||||
if i == 0 { None } else { Some(0) },
|
if i == 0 { None } else { Some(0) },
|
||||||
);
|
);
|
||||||
fake_latest_crosslink(&att, state, spec);
|
|
||||||
op_pool.insert_attestation(att, state, spec).unwrap();
|
op_pool.insert_attestation(att, state, spec).unwrap();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -4,7 +4,9 @@ use rayon::prelude::*;
|
|||||||
use tree_hash::{SignedRoot, TreeHash};
|
use tree_hash::{SignedRoot, TreeHash};
|
||||||
use types::*;
|
use types::*;
|
||||||
|
|
||||||
pub use self::verify_attester_slashing::{get_slashable_indices, verify_attester_slashing};
|
pub use self::verify_attester_slashing::{
|
||||||
|
get_slashable_indices, get_slashable_indices_modular, verify_attester_slashing,
|
||||||
|
};
|
||||||
pub use self::verify_proposer_slashing::verify_proposer_slashing;
|
pub use self::verify_proposer_slashing::verify_proposer_slashing;
|
||||||
pub use validate_attestation::{
|
pub use validate_attestation::{
|
||||||
validate_attestation, validate_attestation_time_independent_only,
|
validate_attestation, validate_attestation_time_independent_only,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::*;
|
use crate::*;
|
||||||
|
use tree_hash::TreeHash;
|
||||||
|
|
||||||
/// Builds an `AttestationData` to be used for testing purposes.
|
/// Builds an `AttestationData` to be used for testing purposes.
|
||||||
///
|
///
|
||||||
@ -44,6 +45,17 @@ impl TestingAttestationDataBuilder {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let previous_crosslink_root = if is_previous_epoch {
|
||||||
|
Hash256::from_slice(
|
||||||
|
&state
|
||||||
|
.get_previous_crosslink(shard)
|
||||||
|
.unwrap()
|
||||||
|
.tree_hash_root(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Hash256::from_slice(&state.get_current_crosslink(shard).unwrap().tree_hash_root())
|
||||||
|
};
|
||||||
|
|
||||||
let source_root = *state
|
let source_root = *state
|
||||||
.get_block_root(source_epoch.start_slot(spec.slots_per_epoch))
|
.get_block_root(source_epoch.start_slot(spec.slots_per_epoch))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -60,7 +72,7 @@ impl TestingAttestationDataBuilder {
|
|||||||
|
|
||||||
// Crosslink vote
|
// Crosslink vote
|
||||||
shard,
|
shard,
|
||||||
previous_crosslink_root: spec.zero_hash,
|
previous_crosslink_root,
|
||||||
crosslink_data_root: spec.zero_hash,
|
crosslink_data_root: spec.zero_hash,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user