diff --git a/lighthouse/state/validation/attestation_validation.rs b/lighthouse/state/validation/attestation_validation.rs index 470432063..9dfa943fb 100644 --- a/lighthouse/state/validation/attestation_validation.rs +++ b/lighthouse/state/validation/attestation_validation.rs @@ -1,14 +1,178 @@ -use super::CrystallizedState; -use super::ActiveState; use super::AttestationRecord; -use super::Block; -use super::ChainConfig; +use super::attestation_parent_hashes::{ + attestation_parent_hashes, + ParentHashesError, +}; +use super::db::ClientDB; +use super::db::stores::BlockStore; +use super::ssz::SszStream; +use super::utils::bls::{ + AggregateSignature, + PublicKey, +}; +use super::utils::hash::canonical_hash; +use super::utils::types::{ + Hash256, + Bitfield, +}; +use std::collections::HashMap; +use std::sync::Arc; -use ::utils::errors::AttestationValidationError; +#[derive(Debug,PartialEq)] +pub enum AttestationValidationError { + SlotTooHigh, + SlotTooLow, + JustifiedSlotTooHigh, + TooManyObliqueHashes, + BadCurrentHashes, + BadObliqueHashes, + BadAttesterMap, + IntWrapping, + IncorrectBitField, + NoSignatures, + NonZeroTrailingBits, + AggregateSignatureFail +} -// implementation of validate_attestation in the v2.1 python reference implementation -// see: https://github.com/ethereum/beacon_chain/blob/a79ab2c6f03cbdabf2b6d9d435c26e2b216e09a5/beacon_chain/state/state_transition.py#L61 -pub fn validate_attestation( +type Slot = u64; +type ShardId = u64; +type AttesterMap = HashMap<(Slot, ShardId), Vec>; + +fn bytes_for_bits(bits: usize) -> usize { + (bits.saturating_sub(1) / 8) + 1 +} + +pub fn validate_attestation(a: &AttestationRecord, + block_slot: u64, + cycle_length: u8, + known_last_justified_slot: u64, + known_parent_hashes: Arc>, + block_store: BlockStore, + attester_map: Arc) + -> Result + where T: ClientDB + Sized +{ + /* + * The attesation slot must not be higher than the block that contained it. + */ + if a.slot > block_slot { + return Err(AttestationValidationError::SlotTooHigh); + } + + /* + * The slot of this attestation must not be more than cycle_length + 1 distance + * from the block that contained it. + * + * The below code stays overflow-safe as long as cycle length is a < 64 bit integer. + */ + if a.slot < block_slot.saturating_sub(u64::from(cycle_length) + 1) { + return Err(AttestationValidationError::SlotTooLow); + } + + /* + * The attestation must indicate that its last justified slot is the same as the last justified + * slot known to us. + */ + if a.justified_slot > known_last_justified_slot { + return Err(AttestationValidationError::JustifiedSlotTooHigh); + } + + /* + * 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(cycle_length) { + return Err(AttestationValidationError::TooManyObliqueHashes); + } + + let attestation_indices = attester_map.get(&(a.slot, a.shard_id.into())) + .ok_or(AttestationValidationError::BadAttesterMap)?; + + if a.attester_bitfield.num_bytes() != + bytes_for_bits(attestation_indices.len()) + { + return Err(AttestationValidationError::IncorrectBitField); + } + + let signed_message = { + let parent_hashes = attestation_parent_hashes( + cycle_length, + block_slot, + a.slot, + &known_parent_hashes, + &a.oblique_parent_hashes)?; + generate_signed_message( + a.slot, + &parent_hashes, + a.shard_id, + &a.shard_block_hash, + a.justified_slot) + }; + + Ok(false) +} + +fn collect_pub_keys(attestation_indices: &Vec, + bitfield: &Bitfield) + -> Option> +{ + // cats + None +} + +/// Generates the message used to validate the signature provided with an AttestationRecord. +/// +/// Ensures that the signer of the message has a view of the chain that is compatible with ours. +fn generate_signed_message(slot: u64, + parent_hashes: &[Hash256], + shard_id: u16, + shard_block_hash: &Hash256, + justified_slot: u64) + -> Vec +{ + /* + * Note: it's a little risky here to use SSZ, because the encoding is not necessarily SSZ + * (for example, SSZ might change whilst this doesn't). + * + * I have suggested switching this to ssz here: + * https://github.com/ethereum/eth2.0-specs/issues/5 + * + * If this doesn't happen, it would be safer to not use SSZ at all. + */ + let mut ssz_stream = SszStream::new(); + ssz_stream.append(&slot); + for h in parent_hashes { + ssz_stream.append_encoded_raw(&h.to_vec()) + } + ssz_stream.append(&shard_id); + ssz_stream.append(shard_block_hash); + ssz_stream.append(&justified_slot); + let bytes = ssz_stream.drain(); + canonical_hash(&bytes) +} + +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 + } + } +} + +/* +// Implementation of validate_attestation in the v2.1 python reference implementation see: +// +// github.com/ethereum/beacon_chain/beacon_chain/state/state_transition.py +pub fn validate_attestation_2( crystallized_state: &CrystallizedState, active_state: &ActiveState, attestation: &AttestationRecord, @@ -79,3 +243,4 @@ mod tests { //assert_eq!(result, Err(AttestationValidationError::SlotTooLow)); } } +*/ diff --git a/lighthouse/state/validation/mod.rs b/lighthouse/state/validation/mod.rs index 5630a96e9..39663dba1 100644 --- a/lighthouse/state/validation/mod.rs +++ b/lighthouse/state/validation/mod.rs @@ -8,6 +8,10 @@ use super::chain_config::ChainConfig; use super::block; use super::Logger; use super::db; +use super::attestation_record::AttestationRecord; +use super::ssz; +use super::transition::attestation_parent_hashes; +use super::utils; -// mod attestation_validation; +mod attestation_validation; mod ssz_block; diff --git a/lighthouse/state/validation/ssz_block.rs b/lighthouse/state/validation/ssz_block.rs index 5d3d9b59a..c94f25294 100644 --- a/lighthouse/state/validation/ssz_block.rs +++ b/lighthouse/state/validation/ssz_block.rs @@ -35,7 +35,7 @@ pub fn validate_ssz_block(b: &SszBlock, _validator_store: &ValidatorStore, _log: &Logger) -> Result - where T: Sized + ClientDB + where T: ClientDB + Sized { if block_store.block_exists(&b.block_hash())? { return Ok(BlockStatus::KnownBlock);