2018-11-04 14:35:00 +00:00
|
|
|
use super::attestation_parent_hashes::{attestation_parent_hashes, ParentHashesError};
|
|
|
|
use super::db::stores::{BeaconBlockAtSlotError, BeaconBlockStore, ValidatorStore};
|
|
|
|
use super::db::{ClientDB, DBError};
|
2018-10-02 07:35:03 +00:00
|
|
|
use super::message_generation::generate_signed_message;
|
|
|
|
use super::signature_verification::{
|
2018-11-04 14:35:00 +00:00
|
|
|
verify_aggregate_signature_for_indices, SignatureVerificationError,
|
2018-10-02 07:35:03 +00:00
|
|
|
};
|
2018-11-04 14:35:00 +00:00
|
|
|
use super::types::Hash256;
|
|
|
|
use super::types::{AttestationRecord, AttesterMap};
|
|
|
|
use std::collections::HashSet;
|
|
|
|
use std::sync::Arc;
|
2018-10-02 07:35:03 +00:00
|
|
|
|
2018-11-04 14:35:00 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
2018-10-02 07:35:03 +00:00
|
|
|
pub enum AttestationValidationError {
|
2018-10-12 09:41:18 +00:00
|
|
|
ParentSlotTooHigh,
|
2018-10-12 22:39:10 +00:00
|
|
|
ParentSlotTooLow,
|
2018-10-12 09:41:18 +00:00
|
|
|
BlockSlotTooHigh,
|
|
|
|
BlockSlotTooLow,
|
2018-10-02 07:35:03 +00:00
|
|
|
JustifiedSlotIncorrect,
|
2018-10-11 13:41:47 +00:00
|
|
|
InvalidJustifiedBlockHash,
|
2018-10-02 07:35:03 +00:00
|
|
|
TooManyObliqueHashes,
|
|
|
|
BadCurrentHashes,
|
|
|
|
BadObliqueHashes,
|
|
|
|
BadAttesterMap,
|
|
|
|
IntWrapping,
|
|
|
|
PublicKeyCorrupt,
|
|
|
|
NoPublicKeyForValidator,
|
|
|
|
BadBitfieldLength,
|
|
|
|
InvalidBitfield,
|
|
|
|
InvalidBitfieldEndBits,
|
|
|
|
NoSignatures,
|
|
|
|
NonZeroTrailingBits,
|
|
|
|
BadAggregateSignature,
|
|
|
|
DBError(String),
|
2018-11-07 22:32:33 +00:00
|
|
|
OutOfBoundsBitfieldIndex,
|
2018-10-02 07:35:03 +00:00
|
|
|
}
|
|
|
|
|
2018-10-02 07:58:33 +00:00
|
|
|
/// The context against which some attestation should be validated.
|
2018-10-02 07:35:03 +00:00
|
|
|
pub struct AttestationValidationContext<T>
|
2018-11-04 14:35:00 +00:00
|
|
|
where
|
|
|
|
T: ClientDB + Sized,
|
2018-10-02 07:35:03 +00:00
|
|
|
{
|
2018-10-02 07:58:33 +00:00
|
|
|
/// The slot as determined by the system time.
|
2018-10-02 07:35:03 +00:00
|
|
|
pub block_slot: u64,
|
2018-10-12 09:41:18 +00:00
|
|
|
/// The slot of the parent of the block that contained this attestation.
|
|
|
|
pub parent_block_slot: u64,
|
2018-10-02 07:58:33 +00:00
|
|
|
/// The cycle_length as determined by the chain configuration.
|
2018-10-02 07:35:03 +00:00
|
|
|
pub cycle_length: u8,
|
2018-10-02 07:58:33 +00:00
|
|
|
/// The last justified slot as per the client's view of the canonical chain.
|
2018-10-02 07:35:03 +00:00
|
|
|
pub last_justified_slot: u64,
|
2018-10-02 07:58:33 +00:00
|
|
|
/// A vec of the hashes of the blocks preceeding the present slot.
|
2018-10-23 11:15:08 +00:00
|
|
|
pub recent_block_hashes: Arc<Vec<Hash256>>,
|
2018-10-11 13:41:47 +00:00
|
|
|
/// The store containing block information.
|
2018-10-16 02:59:45 +00:00
|
|
|
pub block_store: Arc<BeaconBlockStore<T>>,
|
2018-10-02 07:58:33 +00:00
|
|
|
/// The store containing validator information.
|
2018-10-02 07:35:03 +00:00
|
|
|
pub validator_store: Arc<ValidatorStore<T>>,
|
2018-10-02 07:58:33 +00:00
|
|
|
/// A map of (slot, shard_id) to the attestation set of validation indices.
|
2018-10-02 07:35:03 +00:00
|
|
|
pub attester_map: Arc<AttesterMap>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> AttestationValidationContext<T>
|
2018-11-04 14:35:00 +00:00
|
|
|
where
|
|
|
|
T: ClientDB,
|
2018-10-02 07:35:03 +00:00
|
|
|
{
|
2018-10-02 07:58:33 +00:00
|
|
|
/// Validate a (fully deserialized) AttestationRecord against this context.
|
|
|
|
///
|
|
|
|
/// The function will return a HashSet of validator indices (canonical validator indices not
|
|
|
|
/// attestation indices) if the validation passed successfully, or an error otherwise.
|
|
|
|
///
|
|
|
|
/// The attestation's aggregate signature will be verified, therefore the function must able to
|
|
|
|
/// access all required validation public keys via the `validator_store`.
|
2018-11-04 14:35:00 +00:00
|
|
|
pub fn validate_attestation(
|
|
|
|
&self,
|
|
|
|
a: &AttestationRecord,
|
|
|
|
) -> Result<HashSet<usize>, AttestationValidationError> {
|
2018-10-02 07:35:03 +00:00
|
|
|
/*
|
2018-10-12 09:41:18 +00:00
|
|
|
* The attesation slot must be less than or equal to the parent of the slot of the block
|
|
|
|
* that contained the attestation.
|
2018-10-02 07:35:03 +00:00
|
|
|
*/
|
2018-10-12 09:41:18 +00:00
|
|
|
if a.slot > self.parent_block_slot {
|
|
|
|
return Err(AttestationValidationError::ParentSlotTooHigh);
|
2018-10-02 07:35:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The slot of this attestation must not be more than cycle_length + 1 distance
|
2018-10-12 22:39:10 +00:00
|
|
|
* from the parent_slot of block that contained it.
|
2018-10-02 07:35:03 +00:00
|
|
|
*/
|
2018-11-04 14:35:00 +00:00
|
|
|
if a.slot < self
|
|
|
|
.parent_block_slot
|
|
|
|
.saturating_sub(u64::from(self.cycle_length).saturating_add(1))
|
|
|
|
{
|
2018-10-12 22:39:10 +00:00
|
|
|
return Err(AttestationValidationError::ParentSlotTooLow);
|
2018-10-02 07:35:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-10-15 04:08:43 +00:00
|
|
|
* The attestation justified slot must not be higher than the last_justified_slot of the
|
|
|
|
* context.
|
2018-10-02 07:35:03 +00:00
|
|
|
*/
|
2018-10-11 13:41:47 +00:00
|
|
|
if a.justified_slot > self.last_justified_slot {
|
2018-10-02 07:35:03 +00:00
|
|
|
return Err(AttestationValidationError::JustifiedSlotIncorrect);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There is no need to include more oblique parents hashes than there are blocks
|
|
|
|
* in a cycle.
|
|
|
|
*/
|
|
|
|
if a.oblique_parent_hashes.len() > usize::from(self.cycle_length) {
|
|
|
|
return Err(AttestationValidationError::TooManyObliqueHashes);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Retrieve the set of attestation indices for this slot and shard id.
|
|
|
|
*
|
|
|
|
* This is an array mapping the order that validators will appear in the bitfield to the
|
|
|
|
* canonincal index of a validator.
|
|
|
|
*/
|
2018-11-04 14:35:00 +00:00
|
|
|
let attestation_indices = self
|
|
|
|
.attester_map
|
|
|
|
.get(&(a.slot, a.shard_id))
|
2018-10-02 07:35:03 +00:00
|
|
|
.ok_or(AttestationValidationError::BadAttesterMap)?;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The bitfield must be no longer than the minimum required to represent each validator in the
|
2018-10-11 13:55:29 +00:00
|
|
|
* attestation indices for this slot and shard id.
|
2018-10-02 07:35:03 +00:00
|
|
|
*/
|
2018-11-04 14:35:00 +00:00
|
|
|
if a.attester_bitfield.num_bytes() != bytes_for_bits(attestation_indices.len()) {
|
2018-10-02 07:35:03 +00:00
|
|
|
return Err(AttestationValidationError::BadBitfieldLength);
|
2018-11-04 14:35:00 +00:00
|
|
|
}
|
2018-10-02 07:35:03 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If there are excess bits in the bitfield because the number of a validators in not a
|
|
|
|
* multiple of 8, reject this attestation record.
|
|
|
|
*
|
|
|
|
* Allow extra set bits would permit mutliple different byte layouts (and therefore hashes) to
|
|
|
|
* refer to the same AttesationRecord.
|
|
|
|
*/
|
|
|
|
if a.attester_bitfield.len() > attestation_indices.len() {
|
2018-11-04 14:35:00 +00:00
|
|
|
return Err(AttestationValidationError::InvalidBitfieldEndBits);
|
2018-10-02 07:35:03 +00:00
|
|
|
}
|
|
|
|
|
2018-10-11 13:41:47 +00:00
|
|
|
/*
|
|
|
|
* Generate the parent hashes for this attestation
|
|
|
|
*/
|
|
|
|
let parent_hashes = attestation_parent_hashes(
|
|
|
|
self.cycle_length,
|
|
|
|
self.block_slot,
|
|
|
|
a.slot,
|
2018-10-23 11:15:08 +00:00
|
|
|
&self.recent_block_hashes,
|
2018-11-04 14:35:00 +00:00
|
|
|
&a.oblique_parent_hashes,
|
|
|
|
)?;
|
2018-10-11 13:41:47 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The specified justified block hash supplied in the attestation must be in the chain at
|
|
|
|
* the given slot number.
|
|
|
|
*
|
|
|
|
* First, we find the latest parent hash from the parent_hashes array. Then, using the
|
|
|
|
* block store (database) we iterate back through the blocks until we find (or fail to
|
|
|
|
* find) the justified block hash referenced in the attestation record.
|
|
|
|
*/
|
2018-11-04 14:35:00 +00:00
|
|
|
let latest_parent_hash = parent_hashes
|
|
|
|
.last()
|
2018-10-11 13:41:47 +00:00
|
|
|
.ok_or(AttestationValidationError::BadCurrentHashes)?;
|
2018-11-04 14:35:00 +00:00
|
|
|
match self
|
|
|
|
.block_store
|
|
|
|
.block_at_slot(&latest_parent_hash, a.justified_slot)?
|
|
|
|
{
|
2018-10-11 13:41:47 +00:00
|
|
|
Some((ref hash, _)) if *hash == a.justified_block_hash.to_vec() => (),
|
2018-11-04 14:35:00 +00:00
|
|
|
_ => return Err(AttestationValidationError::InvalidJustifiedBlockHash),
|
2018-10-11 13:41:47 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Generate the message that this attestation aggregate signature must sign across.
|
|
|
|
*/
|
2018-10-02 07:35:03 +00:00
|
|
|
let signed_message = {
|
|
|
|
generate_signed_message(
|
|
|
|
a.slot,
|
|
|
|
&parent_hashes,
|
|
|
|
a.shard_id,
|
|
|
|
&a.shard_block_hash,
|
2018-11-04 14:35:00 +00:00
|
|
|
a.justified_slot,
|
|
|
|
)
|
2018-10-02 07:35:03 +00:00
|
|
|
};
|
|
|
|
|
2018-11-04 14:35:00 +00:00
|
|
|
let voted_hashset = verify_aggregate_signature_for_indices(
|
|
|
|
&signed_message,
|
|
|
|
&a.aggregate_sig,
|
|
|
|
&attestation_indices,
|
|
|
|
&a.attester_bitfield,
|
|
|
|
&self.validator_store,
|
|
|
|
)?;
|
2018-10-02 07:35:03 +00:00
|
|
|
|
|
|
|
/*
|
2018-10-11 13:53:56 +00:00
|
|
|
* If the hashset of voters is None, the signature verification failed.
|
2018-10-02 07:35:03 +00:00
|
|
|
*/
|
2018-10-09 00:08:09 +00:00
|
|
|
match voted_hashset {
|
2018-10-02 07:35:03 +00:00
|
|
|
None => Err(AttestationValidationError::BadAggregateSignature),
|
2018-10-11 13:53:56 +00:00
|
|
|
Some(hashset) => Ok(hashset),
|
2018-10-02 07:35:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-20 18:12:49 +00:00
|
|
|
fn bytes_for_bits(bits: usize) -> usize {
|
|
|
|
(bits.saturating_sub(1) / 8) + 1
|
|
|
|
}
|
|
|
|
|
2018-10-02 07:35:03 +00:00
|
|
|
impl From<ParentHashesError> for AttestationValidationError {
|
|
|
|
fn from(e: ParentHashesError) -> Self {
|
|
|
|
match e {
|
2018-11-04 14:35:00 +00:00
|
|
|
ParentHashesError::BadCurrentHashes => AttestationValidationError::BadCurrentHashes,
|
|
|
|
ParentHashesError::BadObliqueHashes => AttestationValidationError::BadObliqueHashes,
|
|
|
|
ParentHashesError::SlotTooLow => AttestationValidationError::BlockSlotTooLow,
|
|
|
|
ParentHashesError::SlotTooHigh => AttestationValidationError::BlockSlotTooHigh,
|
|
|
|
ParentHashesError::IntWrapping => AttestationValidationError::IntWrapping,
|
2018-10-02 07:35:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-16 02:59:45 +00:00
|
|
|
impl From<BeaconBlockAtSlotError> for AttestationValidationError {
|
|
|
|
fn from(e: BeaconBlockAtSlotError) -> Self {
|
2018-10-11 13:41:47 +00:00
|
|
|
match e {
|
2018-10-16 02:59:45 +00:00
|
|
|
BeaconBlockAtSlotError::DBError(s) => AttestationValidationError::DBError(s),
|
2018-11-04 14:35:00 +00:00
|
|
|
_ => AttestationValidationError::InvalidJustifiedBlockHash,
|
2018-10-11 13:41:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-02 07:35:03 +00:00
|
|
|
impl From<DBError> for AttestationValidationError {
|
|
|
|
fn from(e: DBError) -> Self {
|
|
|
|
AttestationValidationError::DBError(e.message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<SignatureVerificationError> for AttestationValidationError {
|
|
|
|
fn from(e: SignatureVerificationError) -> Self {
|
|
|
|
match e {
|
2018-11-04 14:35:00 +00:00
|
|
|
SignatureVerificationError::BadValidatorIndex => {
|
|
|
|
AttestationValidationError::BadAttesterMap
|
|
|
|
}
|
|
|
|
SignatureVerificationError::PublicKeyCorrupt => {
|
|
|
|
AttestationValidationError::PublicKeyCorrupt
|
|
|
|
}
|
|
|
|
SignatureVerificationError::NoPublicKeyForValidator => {
|
|
|
|
AttestationValidationError::NoPublicKeyForValidator
|
|
|
|
}
|
|
|
|
SignatureVerificationError::DBError(s) => AttestationValidationError::DBError(s),
|
2018-11-07 22:32:33 +00:00
|
|
|
SignatureVerificationError::OutOfBoundsBitfieldIndex
|
|
|
|
=> AttestationValidationError::OutOfBoundsBitfieldIndex,
|
2018-10-02 07:35:03 +00:00
|
|
|
}
|
|
|
|
}
|
2018-11-20 18:12:49 +00:00
|
|
|
}
|