spec v0.6.1: attestation processing/verif

This commit is contained in:
Michael Sproul
2019-05-21 16:38:16 +10:00
parent f8d4e742ad
commit 0b2aa26f2d
7 changed files with 227 additions and 298 deletions

View File

@@ -1,5 +1,8 @@
use super::errors::{AttestationInvalid as Invalid, AttestationValidationError as Error};
use crate::common::verify_bitfield_length;
use crate::common::convert_to_indexed;
use crate::per_block_processing::{
verify_indexed_attestation, verify_indexed_attestation_without_signature,
};
use tree_hash::TreeHash;
use types::*;
@@ -8,7 +11,7 @@ use types::*;
///
/// Returns `Ok(())` if the `Attestation` is valid, otherwise indicates the reason for invalidity.
///
/// Spec v0.5.1
/// Spec v0.6.1
pub fn validate_attestation<T: EthSpec>(
state: &BeaconState<T>,
attestation: &Attestation,
@@ -31,7 +34,7 @@ pub fn validate_attestation_time_independent_only<T: EthSpec>(
///
/// Returns `Ok(())` if the `Attestation` is valid, otherwise indicates the reason for invalidity.
///
/// Spec v0.5.1
/// Spec v0.6.1
pub fn validate_attestation_without_signature<T: EthSpec>(
state: &BeaconState<T>,
attestation: &Attestation,
@@ -44,7 +47,7 @@ pub fn validate_attestation_without_signature<T: EthSpec>(
/// given state, optionally validating the aggregate signature.
///
///
/// Spec v0.5.1
/// Spec v0.6.1
fn validate_attestation_parametric<T: EthSpec>(
state: &BeaconState<T>,
attestation: &Attestation,
@@ -52,107 +55,29 @@ fn validate_attestation_parametric<T: EthSpec>(
verify_signature: bool,
time_independent_only: bool,
) -> Result<(), Error> {
// Can't submit pre-historic attestations.
verify!(
attestation.data.slot >= spec.genesis_slot,
Invalid::PreGenesis {
genesis: spec.genesis_slot,
attestation: attestation.data.slot
}
);
let attestation_slot = state.get_attestation_slot(&attestation.data)?;
// Can't submit attestations too far in history.
verify!(
state.slot <= attestation.data.slot + spec.slots_per_epoch,
Invalid::IncludedTooLate {
state: spec.genesis_slot,
attestation: attestation.data.slot
}
);
// Can't submit attestation too quickly.
// Check attestation slot.
verify!(
time_independent_only
|| attestation.data.slot + spec.min_attestation_inclusion_delay <= state.slot,
|| attestation_slot + spec.min_attestation_inclusion_delay <= state.slot,
Invalid::IncludedTooEarly {
state: state.slot,
delay: spec.min_attestation_inclusion_delay,
attestation: attestation.data.slot
attestation: attestation_slot
}
);
verify!(
state.slot <= attestation_slot + spec.slots_per_epoch,
Invalid::IncludedTooLate {
state: state.slot,
attestation: attestation_slot
}
);
// Verify the justified epoch and root is correct.
// Verify the Casper FFG vote.
if !time_independent_only {
verify_justified_epoch_and_root(attestation, state, spec)?;
}
// Check that the crosslink data is valid.
//
// Verify that either:
//
// (i)`state.latest_crosslinks[attestation.data.shard] == attestation.data.latest_crosslink`,
//
// (ii) `state.latest_crosslinks[attestation.data.shard] ==
// Crosslink(crosslink_data_root=attestation.data.crosslink_data_root,
// epoch=slot_to_epoch(attestation.data.slot))`.
let potential_crosslink = Crosslink {
crosslink_data_root: attestation.data.crosslink_data_root,
epoch: attestation.data.slot.epoch(spec.slots_per_epoch),
};
verify!(
(attestation.data.previous_crosslink
== state.latest_crosslinks[attestation.data.shard as usize])
| (state.latest_crosslinks[attestation.data.shard as usize] == potential_crosslink),
Invalid::BadPreviousCrosslink
);
// Attestation must be non-empty!
verify!(
attestation.aggregation_bitfield.num_set_bits() != 0,
Invalid::AggregationBitfieldIsEmpty
);
// Custody bitfield must be empty (be be removed in phase 1)
verify!(
attestation.custody_bitfield.num_set_bits() == 0,
Invalid::CustodyBitfieldHasSetBits
);
// Get the committee for the specific shard that this attestation is for.
let crosslink_committee = state
.get_crosslink_committees_at_slot(attestation.data.slot, spec)?
.iter()
.find(|c| c.shard == attestation.data.shard)
.ok_or_else(|| {
Error::Invalid(Invalid::NoCommitteeForShard {
shard: attestation.data.shard,
slot: attestation.data.slot,
})
})?;
let committee = &crosslink_committee.committee;
// Custody bitfield length is correct.
//
// This is not directly in the spec, but it is inferred.
verify!(
verify_bitfield_length(&attestation.custody_bitfield, committee.len()),
Invalid::BadCustodyBitfieldLength {
committee_len: committee.len(),
bitfield_len: attestation.custody_bitfield.len()
}
);
// Aggregation bitfield length is correct.
//
// This is not directly in the spec, but it is inferred.
verify!(
verify_bitfield_length(&attestation.aggregation_bitfield, committee.len()),
Invalid::BadAggregationBitfieldLength {
committee_len: committee.len(),
bitfield_len: attestation.custody_bitfield.len()
}
);
if verify_signature {
verify_attestation_signature(state, committee, attestation, spec)?;
verify_casper_ffg_vote(attestation, state, spec)?;
}
// Crosslink data root is zero (to be removed in phase 1).
@@ -161,145 +86,72 @@ fn validate_attestation_parametric<T: EthSpec>(
Invalid::ShardBlockRootNotZero
);
// Check signature and bitfields
let indexed_attestation = convert_to_indexed(state, attestation)?;
if verify_signature {
verify_indexed_attestation(state, &indexed_attestation, spec)?;
} else {
verify_indexed_attestation_without_signature(state, &indexed_attestation, spec)?;
}
Ok(())
}
/// Verify that the `source_epoch` and `source_root` of an `Attestation` correctly
/// match the current (or previous) justified epoch and root from the state.
/// Check target epoch, source epoch, source root, and source crosslink.
///
/// Spec v0.5.1
fn verify_justified_epoch_and_root<T: EthSpec>(
/// Spec v0.6.1
fn verify_casper_ffg_vote<T: EthSpec>(
attestation: &Attestation,
state: &BeaconState<T>,
spec: &ChainSpec,
) -> Result<(), Error> {
let state_epoch = state.slot.epoch(spec.slots_per_epoch);
let attestation_epoch = attestation.data.slot.epoch(spec.slots_per_epoch);
if attestation_epoch >= state_epoch {
let data = &attestation.data;
if data.target_epoch == state.current_epoch() {
verify!(
attestation.data.source_epoch == state.current_justified_epoch,
data.source_epoch == state.current_justified_epoch,
Invalid::WrongJustifiedEpoch {
state: state.current_justified_epoch,
attestation: attestation.data.source_epoch,
attestation: data.source_epoch,
is_current: true,
}
);
verify!(
attestation.data.source_root == state.current_justified_root,
data.source_root == state.current_justified_root,
Invalid::WrongJustifiedRoot {
state: state.current_justified_root,
attestation: attestation.data.source_root,
attestation: data.source_root,
is_current: true,
}
);
} else {
verify!(
attestation.data.source_epoch == state.previous_justified_epoch,
data.previous_crosslink_root
== Hash256::from_slice(&state.get_current_crosslink(data.shard)?.tree_hash_root()),
Invalid::BadPreviousCrosslink
);
} else if data.target_epoch == state.previous_epoch() {
verify!(
data.source_epoch == state.previous_justified_epoch,
Invalid::WrongJustifiedEpoch {
state: state.previous_justified_epoch,
attestation: attestation.data.source_epoch,
attestation: data.source_epoch,
is_current: false,
}
);
verify!(
attestation.data.source_root == state.previous_justified_root,
data.source_root == state.previous_justified_root,
Invalid::WrongJustifiedRoot {
state: state.previous_justified_root,
attestation: attestation.data.source_root,
is_current: true,
attestation: data.source_root,
is_current: false,
}
);
verify!(
data.previous_crosslink_root
== Hash256::from_slice(&state.get_previous_crosslink(data.shard)?.tree_hash_root()),
Invalid::BadPreviousCrosslink
);
} else {
invalid!(Invalid::BadTargetEpoch)
}
Ok(())
}
/// Verifies an aggregate signature for some given `AttestationData`, returning `true` if the
/// `aggregate_signature` is valid.
///
/// Returns `false` if:
/// - `aggregate_signature` was not signed correctly.
/// - `custody_bitfield` does not have a bit for each index of `committee`.
/// - A `validator_index` in `committee` is not in `state.validator_registry`.
///
/// Spec v0.5.1
fn verify_attestation_signature<T: EthSpec>(
state: &BeaconState<T>,
committee: &[usize],
a: &Attestation,
spec: &ChainSpec,
) -> Result<(), Error> {
let mut aggregate_pubs = vec![AggregatePublicKey::new(); 2];
let mut message_exists = vec![false; 2];
let attestation_epoch = a.data.slot.epoch(spec.slots_per_epoch);
for (i, v) in committee.iter().enumerate() {
let validator_signed = a.aggregation_bitfield.get(i).map_err(|_| {
Error::Invalid(Invalid::BadAggregationBitfieldLength {
committee_len: committee.len(),
bitfield_len: a.aggregation_bitfield.len(),
})
})?;
if validator_signed {
let custody_bit: bool = match a.custody_bitfield.get(i) {
Ok(bit) => bit,
// Invalidate signature if custody_bitfield.len() < committee
Err(_) => {
return Err(Error::Invalid(Invalid::BadCustodyBitfieldLength {
committee_len: committee.len(),
bitfield_len: a.aggregation_bitfield.len(),
}));
}
};
message_exists[custody_bit as usize] = true;
match state.validator_registry.get(*v as usize) {
Some(validator) => {
aggregate_pubs[custody_bit as usize].add(&validator.pubkey);
}
// Return error if validator index is unknown.
None => return Err(Error::BeaconStateError(BeaconStateError::UnknownValidator)),
};
}
}
// Message when custody bitfield is `false`
let message_0 = AttestationDataAndCustodyBit {
data: a.data.clone(),
custody_bit: false,
}
.tree_hash_root();
// Message when custody bitfield is `true`
let message_1 = AttestationDataAndCustodyBit {
data: a.data.clone(),
custody_bit: true,
}
.tree_hash_root();
let mut messages = vec![];
let mut keys = vec![];
// If any validator signed a message with a `false` custody bit.
if message_exists[0] {
messages.push(&message_0[..]);
keys.push(&aggregate_pubs[0]);
}
// If any validator signed a message with a `true` custody bit.
if message_exists[1] {
messages.push(&message_1[..]);
keys.push(&aggregate_pubs[1]);
}
let domain = spec.get_domain(attestation_epoch, Domain::Attestation, &state.fork);
verify!(
a.aggregate_signature
.verify_multiple(&messages[..], domain, &keys[..]),
Invalid::BadSignature
);
Ok(())
}