From 0b2aa26f2d884e404eb6ac87527fc8b78befaa77 Mon Sep 17 00:00:00 2001 From: Michael Sproul Date: Tue, 21 May 2019 16:38:16 +1000 Subject: [PATCH] spec v0.6.1: attestation processing/verif --- .../src/common/convert_to_indexed.rs | 29 ++ eth2/state_processing/src/common/mod.rs | 2 + .../src/per_block_processing.rs | 21 +- .../src/per_block_processing/errors.rs | 27 +- .../validate_attestation.rs | 258 ++++-------------- .../verify_indexed_attestation.rs | 170 +++++++----- eth2/types/src/pending_attestation.rs | 18 +- 7 files changed, 227 insertions(+), 298 deletions(-) create mode 100644 eth2/state_processing/src/common/convert_to_indexed.rs diff --git a/eth2/state_processing/src/common/convert_to_indexed.rs b/eth2/state_processing/src/common/convert_to_indexed.rs new file mode 100644 index 0000000000..5492252f7c --- /dev/null +++ b/eth2/state_processing/src/common/convert_to_indexed.rs @@ -0,0 +1,29 @@ +use super::get_attesting_indices; +use itertools::{Either, Itertools}; +use types::*; + +/// Convert `attestation` to (almost) indexed-verifiable form. +/// +/// Spec v0.6.1 +pub fn convert_to_indexed( + state: &BeaconState, + attestation: &Attestation, +) -> Result { + let attesting_indices = + get_attesting_indices(state, &attestation.data, &attestation.aggregation_bitfield)?; + + let (custody_bit_0_indices, custody_bit_1_indices) = + attesting_indices.into_iter().enumerate().partition_map( + |(committee_idx, validator_idx)| match attestation.custody_bitfield.get(committee_idx) { + Ok(true) => Either::Right(validator_idx as u64), + _ => Either::Left(validator_idx as u64), + }, + ); + + Ok(IndexedAttestation { + custody_bit_0_indices, + custody_bit_1_indices, + data: attestation.data.clone(), + signature: attestation.signature.clone(), + }) +} diff --git a/eth2/state_processing/src/common/mod.rs b/eth2/state_processing/src/common/mod.rs index 09c9d265f2..3f9745688c 100644 --- a/eth2/state_processing/src/common/mod.rs +++ b/eth2/state_processing/src/common/mod.rs @@ -1,8 +1,10 @@ +mod convert_to_indexed; mod exit; mod get_attesting_indices; mod slash_validator; mod verify_bitfield; +pub use convert_to_indexed::convert_to_indexed; pub use exit::initiate_validator_exit; pub use get_attesting_indices::{get_attesting_indices, get_attesting_indices_unsorted}; pub use slash_validator::slash_validator; diff --git a/eth2/state_processing/src/per_block_processing.rs b/eth2/state_processing/src/per_block_processing.rs index 6a4cc56436..555f58c8ea 100644 --- a/eth2/state_processing/src/per_block_processing.rs +++ b/eth2/state_processing/src/per_block_processing.rs @@ -14,7 +14,9 @@ pub use validate_attestation::{ }; pub use verify_deposit::{get_existing_validator_index, verify_deposit, verify_deposit_index}; pub use verify_exit::{verify_exit, verify_exit_time_independent_only}; -pub use verify_indexed_attestation::verify_indexed_attestation; +pub use verify_indexed_attestation::{ + verify_indexed_attestation, verify_indexed_attestation_without_signature, +}; pub use verify_transfer::{ execute_transfer, verify_transfer, verify_transfer_time_independent_only, }; @@ -304,7 +306,7 @@ pub fn process_attester_slashings( /// Returns `Ok(())` if the validation and state updates completed successfully, otherwise returns /// an `Err` describing the invalid object or cause of failure. /// -/// Spec v0.5.1 +/// Spec v0.6.1 pub fn process_attestations( state: &mut BeaconState, attestations: &[Attestation], @@ -327,13 +329,20 @@ pub fn process_attestations( })?; // Update the state in series. + let proposer_index = + state.get_beacon_proposer_index(state.slot, RelativeEpoch::Current, spec)? as u64; for attestation in attestations { - let pending_attestation = PendingAttestation::from_attestation(attestation, state.slot); - let attestation_epoch = attestation.data.slot.epoch(spec.slots_per_epoch); + let attestation_slot = state.get_attestation_slot(&attestation.data)?; + let pending_attestation = PendingAttestation { + aggregation_bitfield: attestation.aggregation_bitfield.clone(), + data: attestation.data.clone(), + inclusion_delay: (state.slot - attestation_slot).as_u64(), + proposer_index, + }; - if attestation_epoch == state.current_epoch() { + if attestation.data.target_epoch == state.current_epoch() { state.current_epoch_attestations.push(pending_attestation) - } else if attestation_epoch == state.previous_epoch(spec) { + } else { state.previous_epoch_attestations.push(pending_attestation) } } diff --git a/eth2/state_processing/src/per_block_processing/errors.rs b/eth2/state_processing/src/per_block_processing/errors.rs index a922b01518..b7a5cab2c1 100644 --- a/eth2/state_processing/src/per_block_processing/errors.rs +++ b/eth2/state_processing/src/per_block_processing/errors.rs @@ -126,6 +126,8 @@ pub enum AttestationInvalid { }, /// Attestation slot is too far in the past to be included in a block. IncludedTooLate { state: Slot, attestation: Slot }, + /// Attestation target epoch does not match the current or previous epoch. + BadTargetEpoch, /// Attestation justified epoch does not match the states current or previous justified epoch. /// /// `is_current` is `true` if the attestation was compared to the @@ -170,11 +172,20 @@ pub enum AttestationInvalid { BadSignature, /// The shard block root was not set to zero. This is a phase 0 requirement. ShardBlockRootNotZero, + /// The indexed attestation created from this attestation was found to be invalid. + BadIndexedAttestation(IndexedAttestationInvalid), } impl_from_beacon_state_error!(AttestationValidationError); impl_into_with_index_with_beacon_error!(AttestationValidationError, AttestationInvalid); +impl From for AttestationValidationError { + fn from(err: IndexedAttestationValidationError) -> Self { + let IndexedAttestationValidationError::Invalid(e) = err; + AttestationValidationError::Invalid(AttestationInvalid::BadIndexedAttestation(e)) + } +} + /* * `AttesterSlashing` Validation */ @@ -224,25 +235,23 @@ pub enum IndexedAttestationValidationError { /// Describes why an object is invalid. #[derive(Debug, PartialEq)] pub enum IndexedAttestationInvalid { + /// The custody bit 0 validators intersect with the bit 1 validators. + CustodyBitValidatorsIntersect, /// The custody bitfield has some bits set `true`. This is not allowed in phase 0. CustodyBitfieldHasSetBits, /// No validator indices were specified. NoValidatorIndices, + /// The number of indices exceeds the global maximum. + /// + /// (max_indices, indices_given) + MaxIndicesExceed(u64, usize), /// The validator indices were not in increasing order. /// /// The error occured between the given `index` and `index + 1` BadValidatorIndicesOrdering(usize), - /// The custody bitfield length is not the smallest possible size to represent the validators. - /// - /// (validators_len, bitfield_len) - BadCustodyBitfieldLength(usize, usize), - /// The number of slashable indices exceed the global maximum. - /// - /// (max_indices, indices_given) - MaxIndicesExceed(usize, usize), /// The validator index is unknown. One cannot slash one who does not exist. UnknownValidator(u64), - /// The slashable attestation aggregate signature was not valid. + /// The indexed attestation aggregate signature was not valid. BadSignature, } diff --git a/eth2/state_processing/src/per_block_processing/validate_attestation.rs b/eth2/state_processing/src/per_block_processing/validate_attestation.rs index cb26389df5..44eb026165 100644 --- a/eth2/state_processing/src/per_block_processing/validate_attestation.rs +++ b/eth2/state_processing/src/per_block_processing/validate_attestation.rs @@ -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( state: &BeaconState, attestation: &Attestation, @@ -31,7 +34,7 @@ pub fn validate_attestation_time_independent_only( /// /// 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( state: &BeaconState, attestation: &Attestation, @@ -44,7 +47,7 @@ pub fn validate_attestation_without_signature( /// given state, optionally validating the aggregate signature. /// /// -/// Spec v0.5.1 +/// Spec v0.6.1 fn validate_attestation_parametric( state: &BeaconState, attestation: &Attestation, @@ -52,107 +55,29 @@ fn validate_attestation_parametric( 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( 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( +/// Spec v0.6.1 +fn verify_casper_ffg_vote( attestation: &Attestation, state: &BeaconState, 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( - state: &BeaconState, - 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(()) -} diff --git a/eth2/state_processing/src/per_block_processing/verify_indexed_attestation.rs b/eth2/state_processing/src/per_block_processing/verify_indexed_attestation.rs index abb88f5811..50a943b53e 100644 --- a/eth2/state_processing/src/per_block_processing/verify_indexed_attestation.rs +++ b/eth2/state_processing/src/per_block_processing/verify_indexed_attestation.rs @@ -1,76 +1,119 @@ use super::errors::{ IndexedAttestationInvalid as Invalid, IndexedAttestationValidationError as Error, }; -use crate::common::verify_bitfield_length; +use std::collections::HashSet; +use std::iter::FromIterator; use tree_hash::TreeHash; use types::*; -/// Indicates if a `IndexedAttestation` is valid to be included in a block in the current epoch of the given -/// state. +/// Verify an `IndexedAttestation`. /// -/// Returns `Ok(())` if the `IndexedAttestation` is valid, otherwise indicates the reason for invalidity. -/// -/// Spec v0.5.1 +/// Spec v0.6.1 pub fn verify_indexed_attestation( state: &BeaconState, indexed_attestation: &IndexedAttestation, spec: &ChainSpec, ) -> Result<(), Error> { - if indexed_attestation.custody_bitfield.num_set_bits() > 0 { + verify_indexed_attestation_parametric(state, indexed_attestation, spec, true) +} + +/// Verify but don't check the signature. +/// +/// Spec v0.6.1 +pub fn verify_indexed_attestation_without_signature( + state: &BeaconState, + indexed_attestation: &IndexedAttestation, + spec: &ChainSpec, +) -> Result<(), Error> { + verify_indexed_attestation_parametric(state, indexed_attestation, spec, false) +} + +/// Optionally check the signature. +/// +/// Spec v0.6.1 +fn verify_indexed_attestation_parametric( + state: &BeaconState, + indexed_attestation: &IndexedAttestation, + spec: &ChainSpec, + verify_signature: bool, +) -> Result<(), Error> { + let custody_bit_0_indices = &indexed_attestation.custody_bit_0_indices; + let custody_bit_1_indices = &indexed_attestation.custody_bit_1_indices; + + // Ensure no duplicate indices across custody bits + let custody_bit_intersection = + &HashSet::from_iter(custody_bit_0_indices) & &HashSet::from_iter(custody_bit_1_indices); + verify!( + custody_bit_intersection.is_empty(), + Invalid::CustodyBitValidatorsIntersect + ); + + // Check that nobody signed with custody bit 1 (to be removed in phase 1) + if custody_bit_1_indices.len() > 0 { invalid!(Invalid::CustodyBitfieldHasSetBits); } - if indexed_attestation.validator_indices.is_empty() { - invalid!(Invalid::NoValidatorIndices); - } + let total_indices = custody_bit_0_indices.len() + custody_bit_1_indices.len(); + verify!(1 <= total_indices, Invalid::NoValidatorIndices); + verify!( + total_indices as u64 <= spec.max_indices_per_attestation, + Invalid::MaxIndicesExceed(spec.max_indices_per_attestation, total_indices) + ); - for i in 0..(indexed_attestation.validator_indices.len() - 1) { - if indexed_attestation.validator_indices[i] >= indexed_attestation.validator_indices[i + 1] - { - invalid!(Invalid::BadValidatorIndicesOrdering(i)); - } - } - - if !verify_bitfield_length( - &indexed_attestation.custody_bitfield, - indexed_attestation.validator_indices.len(), - ) { - invalid!(Invalid::BadCustodyBitfieldLength( - indexed_attestation.validator_indices.len(), - indexed_attestation.custody_bitfield.len() - )); - } - - if indexed_attestation.validator_indices.len() > spec.max_indices_per_indexed_vote as usize { - invalid!(Invalid::MaxIndicesExceed( - spec.max_indices_per_indexed_vote as usize, - indexed_attestation.validator_indices.len() - )); - } - - // TODO: this signature verification could likely be replaced with: - // - // super::validate_attestation::validate_attestation_signature(..) - - let mut aggregate_pubs = vec![AggregatePublicKey::new(); 2]; - let mut message_exists = vec![false; 2]; - - for (i, v) in indexed_attestation.validator_indices.iter().enumerate() { - let custody_bit = match indexed_attestation.custody_bitfield.get(i) { - Ok(bit) => bit, - Err(_) => unreachable!(), - }; - - 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); + // Check that both vectors of indices are sorted + let check_sorted = |list: &Vec| { + for i in 0..list.len() - 1 { + if list[i] >= list[i + 1] { + invalid!(Invalid::BadValidatorIndicesOrdering(i)); } - None => invalid!(Invalid::UnknownValidator(*v)), - }; + } + Ok(()) + }; + check_sorted(custody_bit_0_indices)?; + check_sorted(custody_bit_1_indices)?; + + if verify_signature { + verify_indexed_attestation_signature(state, indexed_attestation, spec)?; } + Ok(()) +} + +/// Create an aggregate public key for a list of validators, failing if any key can't be found. +fn create_aggregate_pubkey<'a, T, I>( + state: &BeaconState, + validator_indices: I, +) -> Result +where + I: IntoIterator, + T: EthSpec, +{ + validator_indices.into_iter().try_fold( + AggregatePublicKey::new(), + |mut aggregate_pubkey, &validator_idx| { + state + .validator_registry + .get(validator_idx as usize) + .ok_or(Error::Invalid(Invalid::UnknownValidator(validator_idx))) + .map(|validator| { + aggregate_pubkey.add(&validator.pubkey); + aggregate_pubkey + }) + }, + ) +} + +/// Verify the signature of an IndexedAttestation. +/// +/// Spec v0.6.1 +fn verify_indexed_attestation_signature( + state: &BeaconState, + indexed_attestation: &IndexedAttestation, + spec: &ChainSpec, +) -> Result<(), Error> { + let bit_0_pubkey = create_aggregate_pubkey(state, &indexed_attestation.custody_bit_0_indices)?; + let bit_1_pubkey = create_aggregate_pubkey(state, &indexed_attestation.custody_bit_1_indices)?; + let message_0 = AttestationDataAndCustodyBit { data: indexed_attestation.data.clone(), custody_bit: false, @@ -85,23 +128,24 @@ pub fn verify_indexed_attestation( let mut messages = vec![]; let mut keys = vec![]; - if message_exists[0] { + if !indexed_attestation.custody_bit_0_indices.is_empty() { messages.push(&message_0[..]); - keys.push(&aggregate_pubs[0]); + keys.push(&bit_0_pubkey); } - if message_exists[1] { + if !indexed_attestation.custody_bit_1_indices.is_empty() { messages.push(&message_1[..]); - keys.push(&aggregate_pubs[1]); + keys.push(&bit_1_pubkey); } - let domain = { - let epoch = indexed_attestation.data.slot.epoch(spec.slots_per_epoch); - spec.get_domain(epoch, Domain::Attestation, &state.fork) - }; + let domain = spec.get_domain( + indexed_attestation.data.target_epoch, + Domain::Attestation, + &state.fork, + ); verify!( indexed_attestation - .aggregate_signature + .signature .verify_multiple(&messages[..], domain, &keys[..]), Invalid::BadSignature ); diff --git a/eth2/types/src/pending_attestation.rs b/eth2/types/src/pending_attestation.rs index 1da69d2057..2c3b04a429 100644 --- a/eth2/types/src/pending_attestation.rs +++ b/eth2/types/src/pending_attestation.rs @@ -1,5 +1,5 @@ use crate::test_utils::TestRandom; -use crate::{Attestation, AttestationData, Bitfield}; +use crate::{AttestationData, Bitfield}; use serde_derive::{Deserialize, Serialize}; use ssz_derive::{Decode, Encode}; @@ -28,22 +28,6 @@ pub struct PendingAttestation { pub proposer_index: u64, } -impl PendingAttestation { - /// Create a `PendingAttestation` from an `Attestation`. - pub fn from_attestation( - attestation: &Attestation, - inclusion_delay: u64, - proposer_index: u64, - ) -> Self { - PendingAttestation { - data: attestation.data.clone(), - aggregation_bitfield: attestation.aggregation_bitfield.clone(), - inclusion_delay, - proposer_index, - } - } -} - #[cfg(test)] mod tests { use super::*;