From 26537c118466d303ccfc708695dca5c744bb1e8e Mon Sep 17 00:00:00 2001 From: Paul Hauner Date: Mon, 24 Sep 2018 18:08:48 +1000 Subject: [PATCH] Progress further on attestation validation --- .../validation/attestation_validation.rs | 111 ++++++++++++++---- 1 file changed, 86 insertions(+), 25 deletions(-) diff --git a/lighthouse/state/validation/attestation_validation.rs b/lighthouse/state/validation/attestation_validation.rs index 694b897ab..aa94a91bc 100644 --- a/lighthouse/state/validation/attestation_validation.rs +++ b/lighthouse/state/validation/attestation_validation.rs @@ -1,3 +1,4 @@ +use std::collections::HashSet; use super::AttestationRecord; use super::attestation_parent_hashes::{ attestation_parent_hashes, @@ -12,33 +13,35 @@ use super::db::stores::{ ValidatorStore, }; use super::ssz::SszStream; -use super::bls::{ - AggregateSignature, - AggregatePublicKey, - PublicKey, -}; use super::utils::hash::canonical_hash; use super::utils::types::{ Hash256, - Bitfield, }; use std::collections::HashMap; use std::sync::Arc; +use super::signatures::{ + verify_aggregate_signature_for_indices, + SignatureVerificationError, +}; #[derive(Debug,PartialEq)] pub enum AttestationValidationError { SlotTooHigh, SlotTooLow, JustifiedSlotTooHigh, + UnknownJustifiedBlock, TooManyObliqueHashes, BadCurrentHashes, BadObliqueHashes, BadAttesterMap, IntWrapping, + PublicKeyCorrupt, + NoPublicKeyForValidator, IncorrectBitField, NoSignatures, NonZeroTrailingBits, - AggregateSignatureFail + AggregateSignatureFail, + DBError(String), } type Slot = u64; @@ -55,8 +58,9 @@ pub fn validate_attestation(a: &AttestationRecord, known_last_justified_slot: u64, known_parent_hashes: Arc>, block_store: BlockStore, + validator_store: ValidatorStore, attester_map: Arc) - -> Result + -> Result<(bool, Option>), AttestationValidationError> where T: ClientDB + Sized { /* @@ -92,15 +96,46 @@ pub fn validate_attestation(a: &AttestationRecord, 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. + */ let attestation_indices = attester_map.get(&(a.slot, a.shard_id.into())) .ok_or(AttestationValidationError::BadAttesterMap)?; + /* + * The bitfield must be no longer than the minimum required to represent each validator in the + * attestation indicies for this slot and shard id. + */ if a.attester_bitfield.num_bytes() != bytes_for_bits(attestation_indices.len()) { return Err(AttestationValidationError::IncorrectBitField); } + /* + * 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. + */ + let last_byte = + a.attester_bitfield.get_byte(a.attester_bitfield.num_bytes()) + .ok_or(AttestationValidationError::IncorrectBitField)?; + if any_of_last_n_bits_are_set(last_byte, a.attester_bitfield.len() % 8) { + return Err(AttestationValidationError::IncorrectBitField) + } + + /* + * The specified justified block hash must be known to us + */ + if !block_store.block_exists(&a.justified_block_hash)? { + return Err(AttestationValidationError::UnknownJustifiedBlock) + } + let signed_message = { let parent_hashes = attestation_parent_hashes( cycle_length, @@ -116,15 +151,20 @@ pub fn validate_attestation(a: &AttestationRecord, a.justified_slot) }; - Ok(false) + let (signature_valid, voted_hashmap) = + verify_aggregate_signature_for_indices( + &signed_message, + &a.aggregate_sig, + &attestation_indices, + &a.attester_bitfield, + &validator_store)?; + + Ok((signature_valid, voted_hashmap)) } -fn collect_pub_keys(attestation_indices: &Vec, - bitfield: &Bitfield) - -> Option> -{ - // cats - None +fn any_of_last_n_bits_are_set(byte: &u8, n: usize) -> bool { + let shift = 8_u8.saturating_sub(n as u8); + ((!0 >> shift) & byte) > 0 } /// Generates the message used to validate the signature provided with an AttestationRecord. @@ -161,16 +201,37 @@ fn generate_signed_message(slot: u64, impl From for AttestationValidationError { fn from(e: ParentHashesError) -> Self { match e { - ParentHashesError::BadCurrentHashes => - AttestationValidationError::BadCurrentHashes, - ParentHashesError::BadObliqueHashes => - AttestationValidationError::BadObliqueHashes, - ParentHashesError::SlotTooLow => - AttestationValidationError::SlotTooLow, - ParentHashesError::SlotTooHigh => - AttestationValidationError::SlotTooHigh, - ParentHashesError::IntWrapping => - AttestationValidationError::IntWrapping + ParentHashesError::BadCurrentHashes + => AttestationValidationError::BadCurrentHashes, + ParentHashesError::BadObliqueHashes + => AttestationValidationError::BadObliqueHashes, + ParentHashesError::SlotTooLow + => AttestationValidationError::SlotTooLow, + ParentHashesError::SlotTooHigh + => AttestationValidationError::SlotTooHigh, + ParentHashesError::IntWrapping + => AttestationValidationError::IntWrapping + } + } +} + +impl From for AttestationValidationError { + fn from(e: DBError) -> Self { + AttestationValidationError::DBError(e.message) + } +} + +impl From for AttestationValidationError { + fn from(e: SignatureVerificationError) -> Self { + match e { + SignatureVerificationError::BadValidatorIndex + => AttestationValidationError::BadAttesterMap, + SignatureVerificationError::PublicKeyCorrupt + => AttestationValidationError::PublicKeyCorrupt, + SignatureVerificationError::NoPublicKeyForValidator + => AttestationValidationError::NoPublicKeyForValidator, + SignatureVerificationError::DBError(s) + => AttestationValidationError::DBError(s), } } }