Use all attestation subnets (#1257)

* Update `milagro_bls` to new release (#1183)

* Update milagro_bls to new release

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Tidy up fake cryptos

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* move SecretHash to bls and put plaintext back

Signed-off-by: Kirk Baird <baird.k@outlook.com>

* Update v0.12.0 to v0.12.1

* Add compute_subnet_for_attestation

* Replace CommitteeIndex topic with Attestation

* Fix warnings

* Fix attestation service tests

* fmt

* Appease clippy

* return error from validator_subscriptions

* move state out of loop

* Fix early break on error

* Get state from slot clock

* Fix beacon state in attestation tests

* Add failing test for lookahead > 1

* Minor change

* Address some review comments

* Add subnet verification to beacon chain

* Move subnet verification to processor

* Pass committee_count_at_slot to ValidatorDuty and ValidatorSubscription

* Pass subnet id for publishing attestations

* Fix attestation service tests

* Fix more tests

* Fix fork choice test

* Remove unused code

* Remove more unused and expensive code

Co-authored-by: Kirk Baird <baird.k@outlook.com>
Co-authored-by: Michael Sproul <michael@sigmaprime.io>
Co-authored-by: Age Manning <Age@AgeManning.com>
Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
Pawan Dhananjay 2020-06-18 14:41:03 +05:30 committed by GitHub
parent 81c9fe3817
commit 3199b1a6f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 444 additions and 199 deletions

View File

@ -52,7 +52,7 @@ use std::borrow::Cow;
use tree_hash::TreeHash; use tree_hash::TreeHash;
use types::{ use types::{
Attestation, BeaconCommittee, CommitteeIndex, Epoch, EthSpec, Hash256, IndexedAttestation, Attestation, BeaconCommittee, CommitteeIndex, Epoch, EthSpec, Hash256, IndexedAttestation,
RelativeEpoch, SelectionProof, SignedAggregateAndProof, Slot, RelativeEpoch, SelectionProof, SignedAggregateAndProof, Slot, SubnetId,
}; };
/// Returned when an attestation was not successfully verified. It might not have been verified for /// Returned when an attestation was not successfully verified. It might not have been verified for
@ -123,6 +123,11 @@ pub enum Error {
/// The attestation is attesting to a state that is later than itself. (Viz., attesting to the /// The attestation is attesting to a state that is later than itself. (Viz., attesting to the
/// future). /// future).
AttestsToFutureBlock { block: Slot, attestation: Slot }, AttestsToFutureBlock { block: Slot, attestation: Slot },
/// The attestation was received on an invalid attestation subnet.
InvalidSubnetId {
received: SubnetId,
expected: SubnetId,
},
/// The attestation failed the `state_processing` verification stage. /// The attestation failed the `state_processing` verification stage.
Invalid(AttestationValidationError), Invalid(AttestationValidationError),
/// There was an error whilst processing the attestation. It is not known if it is valid or invalid. /// There was an error whilst processing the attestation. It is not known if it is valid or invalid.
@ -234,28 +239,29 @@ impl<T: BeaconChainTypes> VerifiedAggregatedAttestation<T> {
return Err(Error::EmptyAggregationBitfield); return Err(Error::EmptyAggregationBitfield);
} }
let indexed_attestation = map_attestation_committee(chain, attestation, |committee| { let indexed_attestation =
// Note: this clones the signature which is known to be a relatively slow operation. map_attestation_committee(chain, attestation, |(committee, _)| {
// // Note: this clones the signature which is known to be a relatively slow operation.
// Future optimizations should remove this clone. //
let selection_proof = // Future optimizations should remove this clone.
SelectionProof::from(signed_aggregate.message.selection_proof.clone()); let selection_proof =
SelectionProof::from(signed_aggregate.message.selection_proof.clone());
if !selection_proof if !selection_proof
.is_aggregator(committee.committee.len(), &chain.spec) .is_aggregator(committee.committee.len(), &chain.spec)
.map_err(|e| Error::BeaconChainError(e.into()))? .map_err(|e| Error::BeaconChainError(e.into()))?
{ {
return Err(Error::InvalidSelectionProof { aggregator_index }); return Err(Error::InvalidSelectionProof { aggregator_index });
} }
// Ensure the aggregator is a member of the committee for which it is aggregating. // Ensure the aggregator is a member of the committee for which it is aggregating.
if !committee.committee.contains(&(aggregator_index as usize)) { if !committee.committee.contains(&(aggregator_index as usize)) {
return Err(Error::AggregatorNotInCommittee { aggregator_index }); return Err(Error::AggregatorNotInCommittee { aggregator_index });
} }
get_indexed_attestation(committee.committee, &attestation) get_indexed_attestation(committee.committee, &attestation)
.map_err(|e| BeaconChainError::from(e).into()) .map_err(|e| BeaconChainError::from(e).into())
})?; })?;
// Ensure that all signatures are valid. // Ensure that all signatures are valid.
if !verify_signed_aggregate_signatures(chain, &signed_aggregate, &indexed_attestation)? { if !verify_signed_aggregate_signatures(chain, &signed_aggregate, &indexed_attestation)? {
@ -309,8 +315,12 @@ impl<T: BeaconChainTypes> VerifiedAggregatedAttestation<T> {
impl<T: BeaconChainTypes> VerifiedUnaggregatedAttestation<T> { impl<T: BeaconChainTypes> VerifiedUnaggregatedAttestation<T> {
/// Returns `Ok(Self)` if the `attestation` is valid to be (re)published on the gossip /// Returns `Ok(Self)` if the `attestation` is valid to be (re)published on the gossip
/// network. /// network.
///
/// `subnet_id` is the subnet from which we received this attestation. This function will
/// verify that it was received on the correct subnet.
pub fn verify( pub fn verify(
attestation: Attestation<T::EthSpec>, attestation: Attestation<T::EthSpec>,
subnet_id: SubnetId,
chain: &BeaconChain<T>, chain: &BeaconChain<T>,
) -> Result<Self, Error> { ) -> Result<Self, Error> {
// Ensure attestation is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots (within a // Ensure attestation is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots (within a
@ -330,7 +340,23 @@ impl<T: BeaconChainTypes> VerifiedUnaggregatedAttestation<T> {
// attestation and do not delay consideration for later. // attestation and do not delay consideration for later.
verify_head_block_is_known(chain, &attestation)?; verify_head_block_is_known(chain, &attestation)?;
let indexed_attestation = obtain_indexed_attestation(chain, &attestation)?; let (indexed_attestation, committees_per_slot) =
obtain_indexed_attestation_and_committees_per_slot(chain, &attestation)?;
let expected_subnet_id = SubnetId::compute_subnet_for_attestation_data::<T::EthSpec>(
&indexed_attestation.data,
committees_per_slot,
&chain.spec,
)
.map_err(BeaconChainError::from)?;
// Ensure the attestation is from the correct subnet.
if subnet_id != expected_subnet_id {
return Err(Error::InvalidSubnetId {
received: subnet_id,
expected: expected_subnet_id,
});
}
let validator_index = *indexed_attestation let validator_index = *indexed_attestation
.attesting_indices .attesting_indices
@ -566,19 +592,23 @@ pub fn verify_signed_aggregate_signatures<T: BeaconChainTypes>(
Ok(verify_signature_sets(signature_sets)) Ok(verify_signature_sets(signature_sets))
} }
/// Returns the `indexed_attestation` for the `attestation` using the public keys cached in the /// Assists in readability.
/// `chain`. type CommitteesPerSlot = u64;
pub fn obtain_indexed_attestation<T: BeaconChainTypes>(
/// Returns the `indexed_attestation` and committee count per slot for the `attestation` using the
/// public keys cached in the `chain`.
pub fn obtain_indexed_attestation_and_committees_per_slot<T: BeaconChainTypes>(
chain: &BeaconChain<T>, chain: &BeaconChain<T>,
attestation: &Attestation<T::EthSpec>, attestation: &Attestation<T::EthSpec>,
) -> Result<IndexedAttestation<T::EthSpec>, Error> { ) -> Result<(IndexedAttestation<T::EthSpec>, CommitteesPerSlot), Error> {
map_attestation_committee(chain, attestation, |committee| { map_attestation_committee(chain, attestation, |(committee, committees_per_slot)| {
get_indexed_attestation(committee.committee, &attestation) get_indexed_attestation(committee.committee, &attestation)
.map(|attestation| (attestation, committees_per_slot))
.map_err(|e| BeaconChainError::from(e).into()) .map_err(|e| BeaconChainError::from(e).into())
}) })
} }
/// Runs the `map_fn` with the committee for the given `attestation`. /// Runs the `map_fn` with the committee and committee count per slot for the given `attestation`.
/// ///
/// This function exists in this odd "map" pattern because efficiently obtaining the committee for /// This function exists in this odd "map" pattern because efficiently obtaining the committee for
/// an attestation can be complex. It might involve reading straight from the /// an attestation can be complex. It might involve reading straight from the
@ -594,7 +624,7 @@ pub fn map_attestation_committee<'a, T, F, R>(
) -> Result<R, Error> ) -> Result<R, Error>
where where
T: BeaconChainTypes, T: BeaconChainTypes,
F: Fn(BeaconCommittee) -> Result<R, Error>, F: Fn((BeaconCommittee, CommitteesPerSlot)) -> Result<R, Error>,
{ {
let attestation_epoch = attestation.data.slot.epoch(T::EthSpec::slots_per_epoch()); let attestation_epoch = attestation.data.slot.epoch(T::EthSpec::slots_per_epoch());
let target = &attestation.data.target; let target = &attestation.data.target;
@ -624,9 +654,10 @@ where
metrics::stop_timer(cache_wait_timer); metrics::stop_timer(cache_wait_timer);
if let Some(committee_cache) = shuffling_cache.get(attestation_epoch, target.root) { if let Some(committee_cache) = shuffling_cache.get(attestation_epoch, target.root) {
let committees_per_slot = committee_cache.committees_per_slot();
committee_cache committee_cache
.get_beacon_committee(attestation.data.slot, attestation.data.index) .get_beacon_committee(attestation.data.slot, attestation.data.index)
.map(map_fn) .map(|committee| map_fn((committee, committees_per_slot)))
.unwrap_or_else(|| { .unwrap_or_else(|| {
Err(Error::NoCommitteeForSlotAndIndex { Err(Error::NoCommitteeForSlotAndIndex {
slot: attestation.data.slot, slot: attestation.data.slot,
@ -689,9 +720,10 @@ where
metrics::stop_timer(committee_building_timer); metrics::stop_timer(committee_building_timer);
let committees_per_slot = committee_cache.committees_per_slot();
committee_cache committee_cache
.get_beacon_committee(attestation.data.slot, attestation.data.index) .get_beacon_committee(attestation.data.slot, attestation.data.index)
.map(map_fn) .map(|committee| map_fn((committee, committees_per_slot)))
.unwrap_or_else(|| { .unwrap_or_else(|| {
Err(Error::NoCommitteeForSlotAndIndex { Err(Error::NoCommitteeForSlotAndIndex {
slot: attestation.data.slot, slot: attestation.data.slot,

View File

@ -872,12 +872,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
pub fn verify_unaggregated_attestation_for_gossip( pub fn verify_unaggregated_attestation_for_gossip(
&self, &self,
attestation: Attestation<T::EthSpec>, attestation: Attestation<T::EthSpec>,
subnet_id: SubnetId,
) -> Result<VerifiedUnaggregatedAttestation<T>, AttestationError> { ) -> Result<VerifiedUnaggregatedAttestation<T>, AttestationError> {
metrics::inc_counter(&metrics::UNAGGREGATED_ATTESTATION_PROCESSING_REQUESTS); metrics::inc_counter(&metrics::UNAGGREGATED_ATTESTATION_PROCESSING_REQUESTS);
let _timer = let _timer =
metrics::start_timer(&metrics::UNAGGREGATED_ATTESTATION_GOSSIP_VERIFICATION_TIMES); metrics::start_timer(&metrics::UNAGGREGATED_ATTESTATION_GOSSIP_VERIFICATION_TIMES);
VerifiedUnaggregatedAttestation::verify(attestation, self).map(|v| { VerifiedUnaggregatedAttestation::verify(attestation, subnet_id, self).map(|v| {
metrics::inc_counter(&metrics::UNAGGREGATED_ATTESTATION_PROCESSING_SUCCESSES); metrics::inc_counter(&metrics::UNAGGREGATED_ATTESTATION_PROCESSING_SUCCESSES);
v v
}) })

View File

@ -24,7 +24,7 @@ use tree_hash::TreeHash;
use types::{ use types::{
AggregateSignature, Attestation, BeaconState, BeaconStateHash, ChainSpec, Domain, EthSpec, AggregateSignature, Attestation, BeaconState, BeaconStateHash, ChainSpec, Domain, EthSpec,
Hash256, Keypair, SecretKey, SelectionProof, Signature, SignedAggregateAndProof, Hash256, Keypair, SecretKey, SelectionProof, Signature, SignedAggregateAndProof,
SignedBeaconBlock, SignedBeaconBlockHash, SignedRoot, Slot, SignedBeaconBlock, SignedBeaconBlockHash, SignedRoot, Slot, SubnetId,
}; };
pub use types::test_utils::generate_deterministic_keypairs; pub use types::test_utils::generate_deterministic_keypairs;
@ -536,12 +536,16 @@ where
state: &BeaconState<E>, state: &BeaconState<E>,
head_block_root: Hash256, head_block_root: Hash256,
attestation_slot: Slot, attestation_slot: Slot,
) -> Vec<Vec<Attestation<E>>> { ) -> Vec<Vec<(Attestation<E>, SubnetId)>> {
let spec = &self.spec; let spec = &self.spec;
let fork = &state.fork; let fork = &state.fork;
let attesting_validators = self.get_attesting_validators(attestation_strategy); let attesting_validators = self.get_attesting_validators(attestation_strategy);
let committee_count = state
.get_committee_count_at_slot(state.slot)
.expect("should get committee count");
state state
.get_beacon_committees_at_slot(state.slot) .get_beacon_committees_at_slot(state.slot)
.expect("should get committees") .expect("should get committees")
@ -589,7 +593,14 @@ where
agg_sig agg_sig
}; };
Some(attestation) let subnet_id = SubnetId::compute_subnet_for_attestation_data::<E>(
&attestation.data,
committee_count,
&self.chain.spec,
)
.expect("should get subnet_id");
Some((attestation, subnet_id))
}) })
.collect() .collect()
}) })
@ -634,16 +645,16 @@ where
.into_iter() .into_iter()
.for_each(|committee_attestations| { .for_each(|committee_attestations| {
// Submit each unaggregated attestation to the chain. // Submit each unaggregated attestation to the chain.
for attestation in &committee_attestations { for (attestation, subnet_id) in &committee_attestations {
self.chain self.chain
.verify_unaggregated_attestation_for_gossip(attestation.clone()) .verify_unaggregated_attestation_for_gossip(attestation.clone(), *subnet_id)
.expect("should not error during attestation processing") .expect("should not error during attestation processing")
.add_to_pool(&self.chain) .add_to_pool(&self.chain)
.expect("should add attestation to naive pool"); .expect("should add attestation to naive pool");
} }
// If there are any attestations in this committee, create an aggregate. // If there are any attestations in this committee, create an aggregate.
if let Some(attestation) = committee_attestations.first() { if let Some((attestation, _)) = committee_attestations.first() {
let bc = state.get_beacon_committee(attestation.data.slot, attestation.data.index) let bc = state.get_beacon_committee(attestation.data.slot, attestation.data.index)
.expect("should get committee"); .expect("should get committee");
@ -677,7 +688,7 @@ where
.get_aggregated_attestation(&attestation.data) .get_aggregated_attestation(&attestation.data)
.expect("should not error whilst finding aggregate") .expect("should not error whilst finding aggregate")
.unwrap_or_else(|| { .unwrap_or_else(|| {
committee_attestations.iter().skip(1).fold(attestation.clone(), |mut agg, att| { committee_attestations.iter().skip(1).fold(attestation.clone(), |mut agg, (att, _)| {
agg.aggregate(att); agg.aggregate(att);
agg agg
}) })

View File

@ -14,7 +14,7 @@ use tree_hash::TreeHash;
use types::{ use types::{
test_utils::generate_deterministic_keypair, AggregateSignature, Attestation, EthSpec, Hash256, test_utils::generate_deterministic_keypair, AggregateSignature, Attestation, EthSpec, Hash256,
Keypair, MainnetEthSpec, SecretKey, SelectionProof, Signature, SignedAggregateAndProof, Keypair, MainnetEthSpec, SecretKey, SelectionProof, Signature, SignedAggregateAndProof,
SignedBeaconBlock, Unsigned, SignedBeaconBlock, SubnetId, Unsigned,
}; };
pub type E = MainnetEthSpec; pub type E = MainnetEthSpec;
@ -49,7 +49,7 @@ fn get_harness(validator_count: usize) -> BeaconChainHarness<HarnessType<E>> {
/// Also returns some info about who created it. /// Also returns some info about who created it.
fn get_valid_unaggregated_attestation<T: BeaconChainTypes>( fn get_valid_unaggregated_attestation<T: BeaconChainTypes>(
chain: &BeaconChain<T>, chain: &BeaconChain<T>,
) -> (Attestation<T::EthSpec>, usize, usize, SecretKey) { ) -> (Attestation<T::EthSpec>, usize, usize, SecretKey, SubnetId) {
let head = chain.head().expect("should get head"); let head = chain.head().expect("should get head");
let current_slot = chain.slot().expect("should get slot"); let current_slot = chain.slot().expect("should get slot");
@ -78,11 +78,21 @@ fn get_valid_unaggregated_attestation<T: BeaconChainTypes>(
) )
.expect("should sign attestation"); .expect("should sign attestation");
let subnet_id = SubnetId::compute_subnet_for_attestation_data::<E>(
&valid_attestation.data,
head.beacon_state
.get_committee_count_at_slot(current_slot)
.expect("should get committee count"),
&chain.spec,
)
.expect("should get subnet_id");
( (
valid_attestation, valid_attestation,
validator_index, validator_index,
validator_committee_index, validator_committee_index,
validator_sk, validator_sk,
subnet_id,
) )
} }
@ -194,7 +204,7 @@ fn aggregated_gossip_verification() {
"the test requires a new epoch to avoid already-seen errors" "the test requires a new epoch to avoid already-seen errors"
); );
let (valid_attestation, _attester_index, _attester_committee_index, validator_sk) = let (valid_attestation, _attester_index, _attester_committee_index, validator_sk, _subnet_id) =
get_valid_unaggregated_attestation(&harness.chain); get_valid_unaggregated_attestation(&harness.chain);
let (valid_aggregate, aggregator_index, aggregator_sk) = let (valid_aggregate, aggregator_index, aggregator_sk) =
get_valid_aggregated_attestation(&harness.chain, valid_attestation); get_valid_aggregated_attestation(&harness.chain, valid_attestation);
@ -541,16 +551,21 @@ fn unaggregated_gossip_verification() {
"the test requires a new epoch to avoid already-seen errors" "the test requires a new epoch to avoid already-seen errors"
); );
let (valid_attestation, expected_validator_index, validator_committee_index, validator_sk) = let (
get_valid_unaggregated_attestation(&harness.chain); valid_attestation,
expected_validator_index,
validator_committee_index,
validator_sk,
subnet_id,
) = get_valid_unaggregated_attestation(&harness.chain);
macro_rules! assert_invalid { macro_rules! assert_invalid {
($desc: tt, $attn_getter: expr, $($error: pat) |+ $( if $guard: expr )?) => { ($desc: tt, $attn_getter: expr, $subnet_getter: expr, $($error: pat) |+ $( if $guard: expr )?) => {
assert!( assert!(
matches!( matches!(
harness harness
.chain .chain
.verify_unaggregated_attestation_for_gossip($attn_getter) .verify_unaggregated_attestation_for_gossip($attn_getter, $subnet_getter)
.err() .err()
.expect(&format!( .expect(&format!(
"{} should error during verify_unaggregated_attestation_for_gossip", "{} should error during verify_unaggregated_attestation_for_gossip",
@ -564,6 +579,29 @@ fn unaggregated_gossip_verification() {
}; };
} }
/*
* The following test ensures:
*
* Spec v0.12.1
*
* The attestation is for the correct subnet (i.e. compute_subnet_for_attestation(state,
* attestation.data.slot, attestation.data.index) == subnet_id).
*/
let id: u64 = subnet_id.into();
let invalid_subnet_id = SubnetId::new(id + 1);
assert_invalid!(
"attestation from future slot",
{
valid_attestation.clone()
},
invalid_subnet_id,
AttnError::InvalidSubnetId {
received,
expected,
}
if received == invalid_subnet_id && expected == subnet_id
);
/* /*
* The following two tests ensure: * The following two tests ensure:
* *
@ -583,6 +621,7 @@ fn unaggregated_gossip_verification() {
a.data.slot = future_slot; a.data.slot = future_slot;
a a
}, },
subnet_id,
AttnError::FutureSlot { AttnError::FutureSlot {
attestation_slot, attestation_slot,
latest_permissible_slot, latest_permissible_slot,
@ -602,6 +641,7 @@ fn unaggregated_gossip_verification() {
a.data.slot = early_slot; a.data.slot = early_slot;
a a
}, },
subnet_id,
AttnError::PastSlot { AttnError::PastSlot {
attestation_slot, attestation_slot,
// Subtract an additional slot since the harness will be exactly on the start of the // Subtract an additional slot since the harness will be exactly on the start of the
@ -634,6 +674,7 @@ fn unaggregated_gossip_verification() {
); );
a a
}, },
subnet_id,
AttnError::NotExactlyOneAggregationBitSet(0) AttnError::NotExactlyOneAggregationBitSet(0)
); );
@ -646,6 +687,7 @@ fn unaggregated_gossip_verification() {
.expect("should set second aggregation bit"); .expect("should set second aggregation bit");
a a
}, },
subnet_id,
AttnError::NotExactlyOneAggregationBitSet(2) AttnError::NotExactlyOneAggregationBitSet(2)
); );
@ -665,6 +707,7 @@ fn unaggregated_gossip_verification() {
a.data.beacon_block_root = unknown_root; a.data.beacon_block_root = unknown_root;
a a
}, },
subnet_id,
AttnError::UnknownHeadBlock { AttnError::UnknownHeadBlock {
beacon_block_root, beacon_block_root,
} }
@ -690,13 +733,14 @@ fn unaggregated_gossip_verification() {
a a
}, },
subnet_id,
AttnError::InvalidSignature AttnError::InvalidSignature
); );
assert!( assert!(
harness harness
.chain .chain
.verify_unaggregated_attestation_for_gossip(valid_attestation.clone()) .verify_unaggregated_attestation_for_gossip(valid_attestation.clone(), subnet_id)
.is_ok(), .is_ok(),
"valid attestation should be verified" "valid attestation should be verified"
); );
@ -714,6 +758,7 @@ fn unaggregated_gossip_verification() {
assert_invalid!( assert_invalid!(
"attestation that has already been seen", "attestation that has already been seen",
valid_attestation.clone(), valid_attestation.clone(),
subnet_id,
AttnError::PriorAttestationKnown { AttnError::PriorAttestationKnown {
validator_index, validator_index,
epoch, epoch,
@ -755,7 +800,7 @@ fn attestation_that_skips_epochs() {
per_slot_processing(&mut state, None, &harness.spec).expect("should process slot"); per_slot_processing(&mut state, None, &harness.spec).expect("should process slot");
} }
let attestation = harness let (attestation, subnet_id) = harness
.get_unaggregated_attestations( .get_unaggregated_attestations(
&AttestationStrategy::AllValidators, &AttestationStrategy::AllValidators,
&state, &state,
@ -785,6 +830,6 @@ fn attestation_that_skips_epochs() {
harness harness
.chain .chain
.verify_unaggregated_attestation_for_gossip(attestation) .verify_unaggregated_attestation_for_gossip(attestation, subnet_id)
.expect("should gossip verify attestation that skips slots"); .expect("should gossip verify attestation that skips slots");
} }

View File

@ -293,7 +293,7 @@ fn epoch_boundary_state_attestation_processing() {
let mut checked_pre_fin = false; let mut checked_pre_fin = false;
for attestation in late_attestations.into_iter().flatten() { for (attestation, subnet_id) in late_attestations.into_iter().flatten() {
// load_epoch_boundary_state is idempotent! // load_epoch_boundary_state is idempotent!
let block_root = attestation.data.beacon_block_root; let block_root = attestation.data.beacon_block_root;
let block = store.get_block(&block_root).unwrap().expect("block exists"); let block = store.get_block(&block_root).unwrap().expect("block exists");
@ -317,7 +317,7 @@ fn epoch_boundary_state_attestation_processing() {
let res = harness let res = harness
.chain .chain
.verify_unaggregated_attestation_for_gossip(attestation.clone()); .verify_unaggregated_attestation_for_gossip(attestation.clone(), subnet_id);
let current_slot = harness.chain.slot().expect("should get slot"); let current_slot = harness.chain.slot().expect("should get slot");
let expected_attestation_slot = attestation.data.slot; let expected_attestation_slot = attestation.data.slot;

View File

@ -459,10 +459,10 @@ fn attestations_with_increasing_slots() {
harness.advance_slot(); harness.advance_slot();
} }
for attestation in attestations.into_iter().flatten() { for (attestation, subnet_id) in attestations.into_iter().flatten() {
let res = harness let res = harness
.chain .chain
.verify_unaggregated_attestation_for_gossip(attestation.clone()); .verify_unaggregated_attestation_for_gossip(attestation.clone(), subnet_id);
let current_slot = harness.chain.slot().expect("should get slot"); let current_slot = harness.chain.slot().expect("should get slot");
let expected_attestation_slot = attestation.data.slot; let expected_attestation_slot = attestation.data.slot;

View File

@ -41,7 +41,7 @@ impl<T: EthSpec> PubsubMessage<T> {
PubsubMessage::BeaconBlock(_) => GossipKind::BeaconBlock, PubsubMessage::BeaconBlock(_) => GossipKind::BeaconBlock,
PubsubMessage::AggregateAndProofAttestation(_) => GossipKind::BeaconAggregateAndProof, PubsubMessage::AggregateAndProofAttestation(_) => GossipKind::BeaconAggregateAndProof,
PubsubMessage::Attestation(attestation_data) => { PubsubMessage::Attestation(attestation_data) => {
GossipKind::CommitteeIndex(attestation_data.0) GossipKind::Attestation(attestation_data.0)
} }
PubsubMessage::VoluntaryExit(_) => GossipKind::VoluntaryExit, PubsubMessage::VoluntaryExit(_) => GossipKind::VoluntaryExit,
PubsubMessage::ProposerSlashing(_) => GossipKind::ProposerSlashing, PubsubMessage::ProposerSlashing(_) => GossipKind::ProposerSlashing,
@ -97,7 +97,7 @@ impl<T: EthSpec> PubsubMessage<T> {
agg_and_proof, agg_and_proof,
))); )));
} }
GossipKind::CommitteeIndex(subnet_id) => { GossipKind::Attestation(subnet_id) => {
let attestation = Attestation::from_ssz_bytes(decompressed_data) let attestation = Attestation::from_ssz_bytes(decompressed_data)
.map_err(|e| format!("{:?}", e))?; .map_err(|e| format!("{:?}", e))?;
return Ok(PubsubMessage::Attestation(Box::new(( return Ok(PubsubMessage::Attestation(Box::new((

View File

@ -9,10 +9,7 @@ pub const TOPIC_PREFIX: &str = "eth2";
pub const SSZ_SNAPPY_ENCODING_POSTFIX: &str = "ssz_snappy"; pub const SSZ_SNAPPY_ENCODING_POSTFIX: &str = "ssz_snappy";
pub const BEACON_BLOCK_TOPIC: &str = "beacon_block"; pub const BEACON_BLOCK_TOPIC: &str = "beacon_block";
pub const BEACON_AGGREGATE_AND_PROOF_TOPIC: &str = "beacon_aggregate_and_proof"; pub const BEACON_AGGREGATE_AND_PROOF_TOPIC: &str = "beacon_aggregate_and_proof";
// for speed and easier string manipulation, committee topic index is split into a prefix and a pub const BEACON_ATTESTATION_PREFIX: &str = "beacon_attestation_";
// postfix. The topic is committee_index{}_beacon_attestation where {} is an integer.
pub const COMMITEE_INDEX_TOPIC_PREFIX: &str = "committee_index";
pub const COMMITEE_INDEX_TOPIC_POSTFIX: &str = "_beacon_attestation";
pub const VOLUNTARY_EXIT_TOPIC: &str = "voluntary_exit"; pub const VOLUNTARY_EXIT_TOPIC: &str = "voluntary_exit";
pub const PROPOSER_SLASHING_TOPIC: &str = "proposer_slashing"; pub const PROPOSER_SLASHING_TOPIC: &str = "proposer_slashing";
pub const ATTESTER_SLASHING_TOPIC: &str = "attester_slashing"; pub const ATTESTER_SLASHING_TOPIC: &str = "attester_slashing";
@ -38,7 +35,7 @@ pub enum GossipKind {
/// Topic for publishing aggregate attestations and proofs. /// Topic for publishing aggregate attestations and proofs.
BeaconAggregateAndProof, BeaconAggregateAndProof,
/// Topic for publishing raw attestations on a particular subnet. /// Topic for publishing raw attestations on a particular subnet.
CommitteeIndex(SubnetId), Attestation(SubnetId),
/// Topic for publishing voluntary exits. /// Topic for publishing voluntary exits.
VoluntaryExit, VoluntaryExit,
/// Topic for publishing block proposer slashings. /// Topic for publishing block proposer slashings.
@ -52,7 +49,7 @@ impl std::fmt::Display for GossipKind {
match self { match self {
GossipKind::BeaconBlock => write!(f, "beacon_block"), GossipKind::BeaconBlock => write!(f, "beacon_block"),
GossipKind::BeaconAggregateAndProof => write!(f, "beacon_aggregate_and_proof"), GossipKind::BeaconAggregateAndProof => write!(f, "beacon_aggregate_and_proof"),
GossipKind::CommitteeIndex(subnet_id) => write!(f, "committee_index_{}", **subnet_id), GossipKind::Attestation(subnet_id) => write!(f, "beacon_attestation_{}", **subnet_id),
GossipKind::VoluntaryExit => write!(f, "voluntary_exit"), GossipKind::VoluntaryExit => write!(f, "voluntary_exit"),
GossipKind::ProposerSlashing => write!(f, "proposer_slashing"), GossipKind::ProposerSlashing => write!(f, "proposer_slashing"),
GossipKind::AttesterSlashing => write!(f, "attester_slashing"), GossipKind::AttesterSlashing => write!(f, "attester_slashing"),
@ -124,7 +121,7 @@ impl GossipTopic {
PROPOSER_SLASHING_TOPIC => GossipKind::ProposerSlashing, PROPOSER_SLASHING_TOPIC => GossipKind::ProposerSlashing,
ATTESTER_SLASHING_TOPIC => GossipKind::AttesterSlashing, ATTESTER_SLASHING_TOPIC => GossipKind::AttesterSlashing,
topic => match committee_topic_index(topic) { topic => match committee_topic_index(topic) {
Some(subnet_id) => GossipKind::CommitteeIndex(subnet_id), Some(subnet_id) => GossipKind::Attestation(subnet_id),
None => return Err(format!("Unknown topic: {}", topic)), None => return Err(format!("Unknown topic: {}", topic)),
}, },
}; };
@ -158,10 +155,7 @@ impl Into<String> for GossipTopic {
GossipKind::VoluntaryExit => VOLUNTARY_EXIT_TOPIC.into(), GossipKind::VoluntaryExit => VOLUNTARY_EXIT_TOPIC.into(),
GossipKind::ProposerSlashing => PROPOSER_SLASHING_TOPIC.into(), GossipKind::ProposerSlashing => PROPOSER_SLASHING_TOPIC.into(),
GossipKind::AttesterSlashing => ATTESTER_SLASHING_TOPIC.into(), GossipKind::AttesterSlashing => ATTESTER_SLASHING_TOPIC.into(),
GossipKind::CommitteeIndex(index) => format!( GossipKind::Attestation(index) => format!("{}{}", BEACON_ATTESTATION_PREFIX, *index,),
"{}{}{}",
COMMITEE_INDEX_TOPIC_PREFIX, *index, COMMITEE_INDEX_TOPIC_POSTFIX
),
}; };
format!( format!(
"/{}/{}/{}/{}", "/{}/{}/{}/{}",
@ -175,7 +169,7 @@ impl Into<String> for GossipTopic {
impl From<SubnetId> for GossipKind { impl From<SubnetId> for GossipKind {
fn from(subnet_id: SubnetId) -> Self { fn from(subnet_id: SubnetId) -> Self {
GossipKind::CommitteeIndex(subnet_id) GossipKind::Attestation(subnet_id)
} }
} }
@ -183,17 +177,9 @@ impl From<SubnetId> for GossipKind {
// Determines if a string is a committee topic. // Determines if a string is a committee topic.
fn committee_topic_index(topic: &str) -> Option<SubnetId> { fn committee_topic_index(topic: &str) -> Option<SubnetId> {
if topic.starts_with(COMMITEE_INDEX_TOPIC_PREFIX) if topic.starts_with(BEACON_ATTESTATION_PREFIX) {
&& topic.ends_with(COMMITEE_INDEX_TOPIC_POSTFIX)
{
return Some(SubnetId::new( return Some(SubnetId::new(
u64::from_str_radix( u64::from_str_radix(topic.trim_start_matches(BEACON_ATTESTATION_PREFIX), 10).ok()?,
topic
.trim_start_matches(COMMITEE_INDEX_TOPIC_PREFIX)
.trim_end_matches(COMMITEE_INDEX_TOPIC_POSTFIX),
10,
)
.ok()?,
)); ));
} }
None None

View File

@ -3,12 +3,12 @@
//! determines whether attestations should be aggregated and/or passed to the beacon node. //! determines whether attestations should be aggregated and/or passed to the beacon node.
use beacon_chain::{BeaconChain, BeaconChainTypes}; use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2_libp2p::{types::GossipKind, MessageId, NetworkGlobals, PeerId}; use eth2_libp2p::{types::GossipKind, NetworkGlobals};
use futures::prelude::*; use futures::prelude::*;
use hashset_delay::HashSetDelay; use hashset_delay::HashSetDelay;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rest_types::ValidatorSubscription; use rest_types::ValidatorSubscription;
use slog::{crit, debug, error, o, warn}; use slog::{crit, debug, error, o, trace, warn};
use slot_clock::SlotClock; use slot_clock::SlotClock;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::pin::Pin; use std::pin::Pin;
@ -186,18 +186,34 @@ impl<T: BeaconChainTypes> AttestationService<T> {
pub fn validator_subscriptions( pub fn validator_subscriptions(
&mut self, &mut self,
subscriptions: Vec<ValidatorSubscription>, subscriptions: Vec<ValidatorSubscription>,
) -> Result<(), ()> { ) -> Result<(), String> {
for subscription in subscriptions { for subscription in subscriptions {
//NOTE: We assume all subscriptions have been verified before reaching this service //NOTE: We assume all subscriptions have been verified before reaching this service
// Registers the validator with the attestation service. // Registers the validator with the attestation service.
// This will subscribe to long-lived random subnets if required. // This will subscribe to long-lived random subnets if required.
trace!(self.log,
"Validator subscription";
"subscription" => format!("{:?}", subscription),
);
self.add_known_validator(subscription.validator_index); self.add_known_validator(subscription.validator_index);
let subnet_id = SubnetId::new( let subnet_id = match SubnetId::compute_subnet::<T::EthSpec>(
subscription.attestation_committee_index subscription.slot,
% self.beacon_chain.spec.attestation_subnet_count, subscription.attestation_committee_index,
); subscription.committee_count_at_slot,
&self.beacon_chain.spec,
) {
Ok(subnet_id) => subnet_id,
Err(e) => {
warn!(self.log,
"Failed to compute subnet id for validator subscription";
"error" => format!("{:?}", e),
"validator_index" => subscription.validator_index
);
continue;
}
};
let exact_subnet = ExactSubnet { let exact_subnet = ExactSubnet {
subnet_id, subnet_id,
@ -219,9 +235,18 @@ impl<T: BeaconChainTypes> AttestationService<T> {
if subscription.is_aggregator { if subscription.is_aggregator {
// set the subscription timer to subscribe to the next subnet if required // set the subscription timer to subscribe to the next subnet if required
if let Err(e) = self.subscribe_to_subnet(exact_subnet) { if let Err(e) = self.subscribe_to_subnet(exact_subnet.clone()) {
warn!(self.log, "Subscription to subnet error"; "error" => e); warn!(self.log,
return Err(()); "Subscription to subnet error";
"error" => e,
"validator_index" => subscription.validator_index,
);
} else {
trace!(self.log,
"Subscribed to subnet for aggregator duties";
"exact_subnet" => format!("{:?}", exact_subnet),
"validator_index" => subscription.validator_index
);
} }
} }
} }
@ -232,25 +257,9 @@ impl<T: BeaconChainTypes> AttestationService<T> {
/// verification, re-propagates and returns false. /// verification, re-propagates and returns false.
pub fn should_process_attestation( pub fn should_process_attestation(
&mut self, &mut self,
_message_id: &MessageId,
peer_id: &PeerId,
subnet: &SubnetId, subnet: &SubnetId,
attestation: &Attestation<T::EthSpec>, attestation: &Attestation<T::EthSpec>,
) -> bool { ) -> bool {
// verify the attestation is on the correct subnet
let expected_subnet = match attestation.subnet_id(&self.beacon_chain.spec) {
Ok(v) => v,
Err(e) => {
warn!(self.log, "Could not obtain attestation subnet_id"; "error" => format!("{:?}", e));
return false;
}
};
if expected_subnet != *subnet {
warn!(self.log, "Received an attestation on the wrong subnet"; "subnet_received" => format!("{:?}", subnet), "subnet_expected" => format!("{:?}",expected_subnet), "peer_id" => format!("{}", peer_id));
return false;
}
let exact_subnet = ExactSubnet { let exact_subnet = ExactSubnet {
subnet_id: subnet.clone(), subnet_id: subnet.clone(),
slot: attestation.data.slot, slot: attestation.data.slot,
@ -511,7 +520,7 @@ impl<T: BeaconChainTypes> AttestationService<T> {
self.random_subnets.insert(subnet_id); self.random_subnets.insert(subnet_id);
// if we are not already subscribed, then subscribe // if we are not already subscribed, then subscribe
let topic_kind = &GossipKind::CommitteeIndex(subnet_id); let topic_kind = &GossipKind::Attestation(subnet_id);
let already_subscribed = self let already_subscribed = self
.network_globals .network_globals
@ -574,7 +583,7 @@ impl<T: BeaconChainTypes> AttestationService<T> {
// we are also not un-subscribing from a subnet if the next slot requires us to be // we are also not un-subscribing from a subnet if the next slot requires us to be
// subscribed. Therefore there could be the case that we are already still subscribed // subscribed. Therefore there could be the case that we are already still subscribed
// to the required subnet. In which case we do not issue another subscription request. // to the required subnet. In which case we do not issue another subscription request.
let topic_kind = &GossipKind::CommitteeIndex(exact_subnet.subnet_id); let topic_kind = &GossipKind::Attestation(exact_subnet.subnet_id);
if self if self
.network_globals .network_globals
.gossipsub_subscriptions .gossipsub_subscriptions

View File

@ -108,17 +108,23 @@ mod tests {
validator_index: u64, validator_index: u64,
attestation_committee_index: CommitteeIndex, attestation_committee_index: CommitteeIndex,
slot: Slot, slot: Slot,
committee_count_at_slot: u64,
) -> ValidatorSubscription { ) -> ValidatorSubscription {
let is_aggregator = true; let is_aggregator = true;
ValidatorSubscription { ValidatorSubscription {
validator_index, validator_index,
attestation_committee_index, attestation_committee_index,
slot, slot,
committee_count_at_slot,
is_aggregator, is_aggregator,
} }
} }
fn _get_subscriptions(validator_count: u64, slot: Slot) -> Vec<ValidatorSubscription> { fn _get_subscriptions(
validator_count: u64,
slot: Slot,
committee_count_at_slot: u64,
) -> Vec<ValidatorSubscription> {
let mut subscriptions: Vec<ValidatorSubscription> = Vec::new(); let mut subscriptions: Vec<ValidatorSubscription> = Vec::new();
for validator_index in 0..validator_count { for validator_index in 0..validator_count {
let is_aggregator = true; let is_aggregator = true;
@ -126,6 +132,7 @@ mod tests {
validator_index, validator_index,
attestation_committee_index: validator_index, attestation_committee_index: validator_index,
slot, slot,
committee_count_at_slot,
is_aggregator, is_aggregator,
}); });
} }
@ -167,6 +174,7 @@ mod tests {
let committee_index = 1; let committee_index = 1;
let subscription_slot = 0; let subscription_slot = 0;
let no_events_expected = 4; let no_events_expected = 4;
let committee_count = 1;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(); let mut attestation_service = get_attestation_service();
@ -180,6 +188,7 @@ mod tests {
validator_index, validator_index,
committee_index, committee_index,
current_slot + Slot::new(subscription_slot), current_slot + Slot::new(subscription_slot),
committee_count,
)]; )];
// submit the subscriptions // submit the subscriptions
@ -188,7 +197,15 @@ mod tests {
.unwrap(); .unwrap();
// not enough time for peer discovery, just subscribe // not enough time for peer discovery, just subscribe
let expected = vec![AttServiceMessage::Subscribe(SubnetId::new(validator_index))]; let expected = vec![AttServiceMessage::Subscribe(
SubnetId::compute_subnet::<MinimalEthSpec>(
current_slot + Slot::new(subscription_slot),
committee_index,
committee_count,
&attestation_service.beacon_chain.spec,
)
.unwrap(),
)];
let events = get_events(attestation_service, no_events_expected, 1).await; let events = get_events(attestation_service, no_events_expected, 1).await;
assert_matches!( assert_matches!(
@ -215,6 +232,7 @@ mod tests {
let committee_index = 1; let committee_index = 1;
let subscription_slot = 0; let subscription_slot = 0;
let no_events_expected = 5; let no_events_expected = 5;
let committee_count = 1;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(); let mut attestation_service = get_attestation_service();
@ -228,6 +246,7 @@ mod tests {
validator_index, validator_index,
committee_index, committee_index,
current_slot + Slot::new(subscription_slot), current_slot + Slot::new(subscription_slot),
committee_count,
)]; )];
// submit the subscriptions // submit the subscriptions
@ -236,9 +255,16 @@ mod tests {
.unwrap(); .unwrap();
// not enough time for peer discovery, just subscribe, unsubscribe // not enough time for peer discovery, just subscribe, unsubscribe
let subnet_id = SubnetId::compute_subnet::<MinimalEthSpec>(
current_slot + Slot::new(subscription_slot),
committee_index,
committee_count,
&attestation_service.beacon_chain.spec,
)
.unwrap();
let expected = vec![ let expected = vec![
AttServiceMessage::Subscribe(SubnetId::new(validator_index)), AttServiceMessage::Subscribe(subnet_id),
AttServiceMessage::Unsubscribe(SubnetId::new(validator_index)), AttServiceMessage::Unsubscribe(subnet_id),
]; ];
let events = get_events(attestation_service, no_events_expected, 2).await; let events = get_events(attestation_service, no_events_expected, 2).await;
@ -266,6 +292,7 @@ mod tests {
let committee_index = 1; let committee_index = 1;
let subscription_slot = 5; let subscription_slot = 5;
let no_events_expected = 4; let no_events_expected = 4;
let committee_count = 1;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(); let mut attestation_service = get_attestation_service();
@ -279,6 +306,7 @@ mod tests {
validator_index, validator_index,
committee_index, committee_index,
current_slot + Slot::new(subscription_slot), current_slot + Slot::new(subscription_slot),
committee_count,
)]; )];
// submit the subscriptions // submit the subscriptions
@ -295,10 +323,14 @@ mod tests {
); );
// just discover peers, don't subscribe yet // just discover peers, don't subscribe yet
let expected = vec![AttServiceMessage::DiscoverPeers { let subnet_id = SubnetId::compute_subnet::<MinimalEthSpec>(
subnet_id: SubnetId::new(validator_index), current_slot + Slot::new(subscription_slot),
min_ttl, committee_index,
}]; committee_count,
&attestation_service.beacon_chain.spec,
)
.unwrap();
let expected = vec![AttServiceMessage::DiscoverPeers { subnet_id, min_ttl }];
let events = get_events(attestation_service, no_events_expected, 1).await; let events = get_events(attestation_service, no_events_expected, 1).await;
assert_matches!( assert_matches!(
@ -325,6 +357,7 @@ mod tests {
let committee_index = 1; let committee_index = 1;
let subscription_slot = 5; let subscription_slot = 5;
let no_events_expected = 5; let no_events_expected = 5;
let committee_count = 1;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(); let mut attestation_service = get_attestation_service();
@ -338,6 +371,7 @@ mod tests {
validator_index, validator_index,
committee_index, committee_index,
current_slot + Slot::new(subscription_slot), current_slot + Slot::new(subscription_slot),
committee_count,
)]; )];
// submit the subscriptions // submit the subscriptions
@ -354,12 +388,16 @@ mod tests {
); );
// we should discover peers, wait, then subscribe // we should discover peers, wait, then subscribe
let subnet_id = SubnetId::compute_subnet::<MinimalEthSpec>(
current_slot + Slot::new(subscription_slot),
committee_index,
committee_count,
&attestation_service.beacon_chain.spec,
)
.unwrap();
let expected = vec![ let expected = vec![
AttServiceMessage::DiscoverPeers { AttServiceMessage::DiscoverPeers { subnet_id, min_ttl },
subnet_id: SubnetId::new(validator_index), AttServiceMessage::Subscribe(subnet_id),
min_ttl,
},
AttServiceMessage::Subscribe(SubnetId::new(validator_index)),
]; ];
let events = get_events(attestation_service, no_events_expected, 5).await; let events = get_events(attestation_service, no_events_expected, 5).await;
@ -387,6 +425,7 @@ mod tests {
let committee_index = 1; let committee_index = 1;
let subscription_slot = 7; let subscription_slot = 7;
let no_events_expected = 3; let no_events_expected = 3;
let committee_count = 1;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(); let mut attestation_service = get_attestation_service();
@ -400,6 +439,7 @@ mod tests {
validator_index, validator_index,
committee_index, committee_index,
current_slot + Slot::new(subscription_slot), current_slot + Slot::new(subscription_slot),
committee_count,
)]; )];
// submit the subscriptions // submit the subscriptions
@ -436,9 +476,11 @@ mod tests {
let committee_index = 1; let committee_index = 1;
let subscription_slot = 10; let subscription_slot = 10;
let no_events_expected = 4; let no_events_expected = 4;
let committee_count = 1;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(); let mut attestation_service = get_attestation_service();
let current_slot = attestation_service let current_slot = attestation_service
.beacon_chain .beacon_chain
.slot_clock .slot_clock
@ -449,6 +491,7 @@ mod tests {
validator_index, validator_index,
committee_index, committee_index,
current_slot + Slot::new(subscription_slot), current_slot + Slot::new(subscription_slot),
committee_count,
)]; )];
// submit the subscriptions // submit the subscriptions
@ -464,11 +507,17 @@ mod tests {
.unwrap(), .unwrap(),
); );
let subnet_id = SubnetId::compute_subnet::<MinimalEthSpec>(
current_slot + Slot::new(subscription_slot),
committee_index,
committee_count,
&attestation_service.beacon_chain.spec,
)
.unwrap();
// expect discover peers because we will enter TARGET_PEER_DISCOVERY_SLOT_LOOK_AHEAD range // expect discover peers because we will enter TARGET_PEER_DISCOVERY_SLOT_LOOK_AHEAD range
let expected: Vec<AttServiceMessage> = vec![AttServiceMessage::DiscoverPeers { let expected: Vec<AttServiceMessage> =
subnet_id: SubnetId::new(validator_index), vec![AttServiceMessage::DiscoverPeers { subnet_id, min_ttl }];
min_ttl,
}];
let events = get_events(attestation_service, no_events_expected, 5).await; let events = get_events(attestation_service, no_events_expected, 5).await;
@ -494,6 +543,7 @@ mod tests {
// subscribe 10 slots ahead so we do not produce any exact subnet messages // subscribe 10 slots ahead so we do not produce any exact subnet messages
let subscription_slot = 10; let subscription_slot = 10;
let subscription_count = 64; let subscription_count = 64;
let committee_count = 1;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(); let mut attestation_service = get_attestation_service();
@ -503,8 +553,11 @@ mod tests {
.now() .now()
.expect("Could not get current slot"); .expect("Could not get current slot");
let subscriptions = let subscriptions = _get_subscriptions(
_get_subscriptions(subscription_count, current_slot + subscription_slot); subscription_count,
current_slot + subscription_slot,
committee_count,
);
// submit the subscriptions // submit the subscriptions
attestation_service attestation_service
@ -542,6 +595,7 @@ mod tests {
let subscription_slot = 10; let subscription_slot = 10;
// the 65th subscription should result in no more messages than the previous scenario // the 65th subscription should result in no more messages than the previous scenario
let subscription_count = 65; let subscription_count = 65;
let committee_count = 1;
// create the attestation service and subscriptions // create the attestation service and subscriptions
let mut attestation_service = get_attestation_service(); let mut attestation_service = get_attestation_service();
@ -551,8 +605,11 @@ mod tests {
.now() .now()
.expect("Could not get current slot"); .expect("Could not get current slot");
let subscriptions = let subscriptions = _get_subscriptions(
_get_subscriptions(subscription_count, current_slot + subscription_slot); subscription_count,
current_slot + subscription_slot,
committee_count,
);
// submit the subscriptions // submit the subscriptions
attestation_service attestation_service

View File

@ -234,6 +234,7 @@ impl<T: BeaconChainTypes> Router<T> {
self.processor.verify_unaggregated_attestation_for_gossip( self.processor.verify_unaggregated_attestation_for_gossip(
peer_id.clone(), peer_id.clone(),
subnet_attestation.1.clone(), subnet_attestation.1.clone(),
subnet_attestation.0,
) )
{ {
self.propagate_message(id, peer_id.clone()); self.propagate_message(id, peer_id.clone());

View File

@ -17,7 +17,7 @@ use std::sync::Arc;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use types::{ use types::{
Attestation, ChainSpec, Epoch, EthSpec, Hash256, SignedAggregateAndProof, SignedBeaconBlock, Attestation, ChainSpec, Epoch, EthSpec, Hash256, SignedAggregateAndProof, SignedBeaconBlock,
Slot, Slot, SubnetId,
}; };
//TODO: Rate limit requests //TODO: Rate limit requests
@ -758,6 +758,18 @@ impl<T: BeaconChainTypes> Processor<T> {
* The peer has published an invalid consensus message. * The peer has published an invalid consensus message.
*/ */
} }
AttnError::InvalidSubnetId { received, expected } => {
/*
* The attestation was received on an incorrect subnet id.
*/
debug!(
self.log,
"Received attestation on incorrect subnet";
"expected" => format!("{:?}", expected),
"received" => format!("{:?}", received),
)
}
AttnError::Invalid(_) => { AttnError::Invalid(_) => {
/* /*
* The attestation failed the state_processing verification. * The attestation failed the state_processing verification.
@ -833,12 +845,13 @@ impl<T: BeaconChainTypes> Processor<T> {
&mut self, &mut self,
peer_id: PeerId, peer_id: PeerId,
unaggregated_attestation: Attestation<T::EthSpec>, unaggregated_attestation: Attestation<T::EthSpec>,
subnet_id: SubnetId,
) -> Option<VerifiedUnaggregatedAttestation<T>> { ) -> Option<VerifiedUnaggregatedAttestation<T>> {
// This is provided to the error handling function to assist with debugging. // This is provided to the error handling function to assist with debugging.
let beacon_block_root = unaggregated_attestation.data.beacon_block_root; let beacon_block_root = unaggregated_attestation.data.beacon_block_root;
self.chain self.chain
.verify_unaggregated_attestation_for_gossip(unaggregated_attestation) .verify_unaggregated_attestation_for_gossip(unaggregated_attestation, subnet_id)
.map_err(|e| { .map_err(|e| {
self.handle_attestation_verification_failure( self.handle_attestation_verification_failure(
peer_id, peer_id,

View File

@ -14,7 +14,7 @@ use eth2_libp2p::{
use eth2_libp2p::{BehaviourEvent, Enr, MessageId, NetworkGlobals, PeerId}; use eth2_libp2p::{BehaviourEvent, Enr, MessageId, NetworkGlobals, PeerId};
use futures::prelude::*; use futures::prelude::*;
use rest_types::ValidatorSubscription; use rest_types::ValidatorSubscription;
use slog::{debug, error, info, o, trace}; use slog::{debug, error, info, o, trace, warn};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use store::HotColdDB; use store::HotColdDB;
@ -236,10 +236,11 @@ fn spawn_service<T: BeaconChainTypes>(
); );
} }
NetworkMessage::Subscribe { subscriptions } => { NetworkMessage::Subscribe { subscriptions } => {
// the result is dropped as it used solely for ergonomics if let Err(e) = service
let _ = service
.attestation_service .attestation_service
.validator_subscriptions(subscriptions); .validator_subscriptions(subscriptions) {
warn!(service.log, "Validator subscription failed"; "error" => e);
}
} }
} }
} }
@ -327,8 +328,6 @@ fn spawn_service<T: BeaconChainTypes>(
// checks if we have an aggregator for the slot. If so, we process // checks if we have an aggregator for the slot. If so, we process
// the attestation // the attestation
if service.attestation_service.should_process_attestation( if service.attestation_service.should_process_attestation(
&id,
&source,
subnet, subnet,
attestation, attestation,
) { ) {

View File

@ -16,7 +16,7 @@ use std::sync::Arc;
use types::beacon_state::EthSpec; use types::beacon_state::EthSpec;
use types::{ use types::{
Attestation, AttestationData, BeaconState, Epoch, RelativeEpoch, SelectionProof, Attestation, AttestationData, BeaconState, Epoch, RelativeEpoch, SelectionProof,
SignedAggregateAndProof, SignedBeaconBlock, Slot, SignedAggregateAndProof, SignedBeaconBlock, Slot, SubnetId,
}; };
/// HTTP Handler to retrieve the duties for a set of validators during a particular epoch. This /// HTTP Handler to retrieve the duties for a set of validators during a particular epoch. This
@ -220,6 +220,16 @@ fn return_validator_duties<T: BeaconChainTypes>(
)) ))
})?; })?;
let committee_count_at_slot = duties
.map(|d| state.get_committee_count_at_slot(d.slot))
.transpose()
.map_err(|e| {
ApiError::ServerError(format!(
"Unable to find committee count at slot: {:?}",
e
))
})?;
let aggregator_modulo = duties let aggregator_modulo = duties
.map(|duties| SelectionProof::modulo(duties.committee_len, &beacon_chain.spec)) .map(|duties| SelectionProof::modulo(duties.committee_len, &beacon_chain.spec))
.transpose() .transpose()
@ -238,6 +248,7 @@ fn return_validator_duties<T: BeaconChainTypes>(
validator_index: Some(validator_index as u64), validator_index: Some(validator_index as u64),
attestation_slot: duties.map(|d| d.slot), attestation_slot: duties.map(|d| d.slot),
attestation_committee_index: duties.map(|d| d.index), attestation_committee_index: duties.map(|d| d.index),
committee_count_at_slot,
attestation_committee_position: duties.map(|d| d.committee_position), attestation_committee_position: duties.map(|d| d.committee_position),
block_proposal_slots, block_proposal_slots,
aggregator_modulo, aggregator_modulo,
@ -249,6 +260,7 @@ fn return_validator_duties<T: BeaconChainTypes>(
attestation_slot: None, attestation_slot: None,
attestation_committee_index: None, attestation_committee_index: None,
attestation_committee_position: None, attestation_committee_position: None,
committee_count_at_slot: None,
block_proposal_slots: vec![], block_proposal_slots: vec![],
aggregator_modulo: None, aggregator_modulo: None,
}) })
@ -443,21 +455,24 @@ pub async fn publish_attestations<T: BeaconChainTypes>(
)) ))
}) })
// Process all of the aggregates _without_ exiting early if one fails. // Process all of the aggregates _without_ exiting early if one fails.
.map(move |attestations: Vec<Attestation<T::EthSpec>>| { .map(
attestations move |attestations: Vec<(Attestation<T::EthSpec>, SubnetId)>| {
.into_par_iter() attestations
.enumerate() .into_par_iter()
.map(|(i, attestation)| { .enumerate()
process_unaggregated_attestation( .map(|(i, (attestation, subnet_id))| {
&beacon_chain, process_unaggregated_attestation(
network_chan.clone(), &beacon_chain,
attestation, network_chan.clone(),
i, attestation,
&log, subnet_id,
) i,
}) &log,
.collect::<Vec<Result<_, _>>>() )
}) })
.collect::<Vec<Result<_, _>>>()
},
)
// Iterate through all the results and return on the first `Err`. // Iterate through all the results and return on the first `Err`.
// //
// Note: this will only provide info about the _first_ failure, not all failures. // Note: this will only provide info about the _first_ failure, not all failures.
@ -471,6 +486,7 @@ fn process_unaggregated_attestation<T: BeaconChainTypes>(
beacon_chain: &BeaconChain<T>, beacon_chain: &BeaconChain<T>,
network_chan: NetworkChannel<T::EthSpec>, network_chan: NetworkChannel<T::EthSpec>,
attestation: Attestation<T::EthSpec>, attestation: Attestation<T::EthSpec>,
subnet_id: SubnetId,
i: usize, i: usize,
log: &Logger, log: &Logger,
) -> Result<(), ApiError> { ) -> Result<(), ApiError> {
@ -478,7 +494,7 @@ fn process_unaggregated_attestation<T: BeaconChainTypes>(
// Verify that the attestation is valid to included on the gossip network. // Verify that the attestation is valid to included on the gossip network.
let verified_attestation = beacon_chain let verified_attestation = beacon_chain
.verify_unaggregated_attestation_for_gossip(attestation.clone()) .verify_unaggregated_attestation_for_gossip(attestation.clone(), subnet_id)
.map_err(|e| { .map_err(|e| {
handle_attestation_error( handle_attestation_error(
e, e,
@ -491,9 +507,7 @@ fn process_unaggregated_attestation<T: BeaconChainTypes>(
// Publish the attestation to the network // Publish the attestation to the network
if let Err(e) = network_chan.send(NetworkMessage::Publish { if let Err(e) = network_chan.send(NetworkMessage::Publish {
messages: vec![PubsubMessage::Attestation(Box::new(( messages: vec![PubsubMessage::Attestation(Box::new((
attestation subnet_id,
.subnet_id(&beacon_chain.spec)
.map_err(|e| ApiError::ServerError(format!("Unable to get subnet id: {:?}", e)))?,
attestation, attestation,
)))], )))],
}) { }) {

View File

@ -22,7 +22,7 @@ use types::{
}, },
BeaconBlock, BeaconState, ChainSpec, Domain, Epoch, EthSpec, MinimalEthSpec, PublicKey, BeaconBlock, BeaconState, ChainSpec, Domain, Epoch, EthSpec, MinimalEthSpec, PublicKey,
RelativeEpoch, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedRoot, Slot, RelativeEpoch, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedRoot, Slot,
Validator, SubnetId, Validator,
}; };
use version; use version;
@ -144,7 +144,16 @@ fn validator_produce_attestation() {
)) ))
.expect("should fetch duties from http api"); .expect("should fetch duties from http api");
let duties = &duties[0]; let duties = &duties[0];
let committee_count = duties
.committee_count_at_slot
.expect("should have committee count");
let subnet_id = SubnetId::compute_subnet::<E>(
attestation.data.slot,
attestation.data.index,
committee_count,
spec,
)
.unwrap();
// Try publishing the attestation without a signature or a committee bit set, ensure it is // Try publishing the attestation without a signature or a committee bit set, ensure it is
// raises an error. // raises an error.
let publish_status = env let publish_status = env
@ -153,7 +162,7 @@ fn validator_produce_attestation() {
remote_node remote_node
.http .http
.validator() .validator()
.publish_attestations(vec![attestation.clone()]), .publish_attestations(vec![(attestation.clone(), subnet_id)]),
) )
.expect("should publish unsigned attestation"); .expect("should publish unsigned attestation");
assert!( assert!(
@ -179,7 +188,7 @@ fn validator_produce_attestation() {
remote_node remote_node
.http .http
.validator() .validator()
.publish_attestations(vec![attestation.clone()]), .publish_attestations(vec![(attestation.clone(), subnet_id)]),
) )
.expect("should publish attestation with invalid signature"); .expect("should publish attestation with invalid signature");
assert!( assert!(
@ -217,7 +226,7 @@ fn validator_produce_attestation() {
remote_node remote_node
.http .http
.validator() .validator()
.publish_attestations(vec![attestation.clone()]), .publish_attestations(vec![(attestation.clone(), subnet_id)]),
) )
.expect("should publish attestation"); .expect("should publish attestation");
assert!( assert!(

View File

@ -12,7 +12,7 @@ use std::time::Duration;
use types::{ use types::{
Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, CommitteeIndex, Attestation, AttestationData, AttesterSlashing, BeaconBlock, BeaconState, CommitteeIndex,
Epoch, EthSpec, Fork, Hash256, ProposerSlashing, PublicKey, PublicKeyBytes, Signature, Epoch, EthSpec, Fork, Hash256, ProposerSlashing, PublicKey, PublicKeyBytes, Signature,
SignedAggregateAndProof, SignedBeaconBlock, Slot, SignedAggregateAndProof, SignedBeaconBlock, Slot, SubnetId,
}; };
use url::Url; use url::Url;
@ -227,7 +227,7 @@ impl<E: EthSpec> Validator<E> {
/// Posts a list of attestations to the beacon node, expecting it to verify it and publish it to the network. /// Posts a list of attestations to the beacon node, expecting it to verify it and publish it to the network.
pub async fn publish_attestations( pub async fn publish_attestations(
&self, &self,
attestation: Vec<Attestation<E>>, attestation: Vec<(Attestation<E>, SubnetId)>,
) -> Result<PublishStatus, Error> { ) -> Result<PublishStatus, Error> {
let client = self.0.clone(); let client = self.0.clone();
let url = self.url("attestations")?; let url = self.url("attestations")?;

View File

@ -22,6 +22,8 @@ pub struct ValidatorDutyBase<T> {
pub attestation_committee_index: Option<CommitteeIndex>, pub attestation_committee_index: Option<CommitteeIndex>,
/// The position of the validator in the committee. /// The position of the validator in the committee.
pub attestation_committee_position: Option<usize>, pub attestation_committee_position: Option<usize>,
/// The committee count at `attestation_slot`.
pub committee_count_at_slot: Option<u64>,
/// The slots in which a validator must propose a block (can be empty). /// The slots in which a validator must propose a block (can be empty).
pub block_proposal_slots: Vec<Slot>, pub block_proposal_slots: Vec<Slot>,
/// This provides the modulo: `max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)` /// This provides the modulo: `max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)`
@ -66,6 +68,8 @@ pub struct ValidatorSubscription {
pub attestation_committee_index: CommitteeIndex, pub attestation_committee_index: CommitteeIndex,
/// The slot in which to subscribe. /// The slot in which to subscribe.
pub slot: Slot, pub slot: Slot,
/// Committee count at slot to subscribe.
pub committee_count_at_slot: u64,
/// If true, the validator is an aggregator and the beacon node should aggregate attestations /// If true, the validator is an aggregator and the beacon node should aggregate attestations
/// for this slot. /// for this slot.
pub is_aggregator: bool, pub is_aggregator: bool,

View File

@ -12,7 +12,7 @@ use std::sync::Mutex;
use store::{MemoryStore, StoreConfig}; use store::{MemoryStore, StoreConfig};
use types::{ use types::{
test_utils::{generate_deterministic_keypair, generate_deterministic_keypairs}, test_utils::{generate_deterministic_keypair, generate_deterministic_keypairs},
Epoch, EthSpec, IndexedAttestation, MainnetEthSpec, Slot, Epoch, EthSpec, IndexedAttestation, MainnetEthSpec, Slot, SubnetId,
}; };
use types::{BeaconBlock, BeaconState, Hash256, SignedBeaconBlock}; use types::{BeaconBlock, BeaconState, Hash256, SignedBeaconBlock};
@ -285,6 +285,15 @@ impl ForkChoiceTest {
.get(validator_committee_index) .get(validator_committee_index)
.expect("there should be an attesting validator"); .expect("there should be an attesting validator");
let committee_count = head
.beacon_state
.get_committee_count_at_slot(current_slot)
.expect("should not error while getting committee count");
let subnet_id =
SubnetId::compute_subnet::<E>(current_slot, 0, committee_count, &chain.spec)
.expect("should compute subnet id");
let validator_sk = generate_deterministic_keypair(validator_index).sk; let validator_sk = generate_deterministic_keypair(validator_index).sk;
attestation attestation
@ -298,7 +307,7 @@ impl ForkChoiceTest {
.expect("should sign attestation"); .expect("should sign attestation");
let mut verified_attestation = chain let mut verified_attestation = chain
.verify_unaggregated_attestation_for_gossip(attestation) .verify_unaggregated_attestation_for_gossip(attestation, subnet_id)
.expect("precondition: should gossip verify attestation"); .expect("precondition: should gossip verify attestation");
if let MutationDelay::Blocks(slots) = delay { if let MutationDelay::Blocks(slots) = delay {

View File

@ -1,10 +1,9 @@
use super::{ use super::{
AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey, AggregateSignature, AttestationData, BitList, ChainSpec, Domain, EthSpec, Fork, SecretKey,
Signature, SignedRoot, SubnetId, Signature, SignedRoot,
}; };
use crate::{test_utils::TestRandom, Hash256}; use crate::{test_utils::TestRandom, Hash256};
use safe_arith::{ArithError, SafeArith}; use safe_arith::ArithError;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode}; use ssz_derive::{Decode, Encode};
use test_random_derive::TestRandom; use test_random_derive::TestRandom;
@ -84,18 +83,6 @@ impl<T: EthSpec> Attestation<T> {
Ok(()) Ok(())
} }
} }
/// Returns the subnet id associated with the attestation.
///
/// Note, this will return the subnet id for an aggregated attestation. This is done
/// to avoid checking aggregate bits every time we wish to get an id.
pub fn subnet_id(&self, spec: &ChainSpec) -> Result<SubnetId, Error> {
self.data
.index
.safe_rem(spec.attestation_subnet_count)
.map(SubnetId::new)
.map_err(Error::SubnetCountIsZero)
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -83,12 +83,12 @@ impl<T: EthSpec> BeaconBlock<T> {
}; };
let proposer_slashing = ProposerSlashing { let proposer_slashing = ProposerSlashing {
signed_header_1: signed_header.clone(), signed_header_1: signed_header.clone(),
signed_header_2: signed_header.clone(), signed_header_2: signed_header,
}; };
let attester_slashing = AttesterSlashing { let attester_slashing = AttesterSlashing {
attestation_1: indexed_attestation.clone(), attestation_1: indexed_attestation.clone(),
attestation_2: indexed_attestation.clone(), attestation_2: indexed_attestation,
}; };
let attestation: Attestation<T> = Attestation { let attestation: Attestation<T> = Attestation {

View File

@ -1,4 +1,6 @@
//! Identifies each shard by an integer identifier. //! Identifies each shard by an integer identifier.
use crate::{AttestationData, ChainSpec, CommitteeIndex, EthSpec, Slot};
use safe_arith::{ArithError, SafeArith};
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
@ -8,7 +10,42 @@ pub struct SubnetId(u64);
impl SubnetId { impl SubnetId {
pub fn new(id: u64) -> Self { pub fn new(id: u64) -> Self {
SubnetId(id) id.into()
}
/// Compute the subnet for an attestation with `attestation_data` where each slot in the
/// attestation epoch contains `committee_count_per_slot` committees.
pub fn compute_subnet_for_attestation_data<T: EthSpec>(
attestation_data: &AttestationData,
committee_count_per_slot: u64,
spec: &ChainSpec,
) -> Result<SubnetId, ArithError> {
Self::compute_subnet::<T>(
attestation_data.slot,
attestation_data.index,
committee_count_per_slot,
spec,
)
}
/// Compute the subnet for an attestation with `attestation.data.slot == slot` and
/// `attestation.data.index == committee_index` where each slot in the attestation epoch
/// contains `committee_count_at_slot` committees.
pub fn compute_subnet<T: EthSpec>(
slot: Slot,
committee_index: CommitteeIndex,
committee_count_at_slot: u64,
spec: &ChainSpec,
) -> Result<SubnetId, ArithError> {
let slots_since_epoch_start: u64 = slot.as_u64().safe_rem(T::slots_per_epoch())?;
let committees_since_epoch_start =
committee_count_at_slot.safe_mul(slots_since_epoch_start)?;
Ok(committees_since_epoch_start
.safe_add(committee_index)?
.safe_rem(spec.attestation_subnet_count)?
.into())
} }
} }
@ -25,3 +62,15 @@ impl DerefMut for SubnetId {
&mut self.0 &mut self.0
} }
} }
impl From<u64> for SubnetId {
fn from(x: u64) -> Self {
Self(x)
}
}
impl Into<u64> for SubnetId {
fn into(self) -> u64 {
self.0
}
}

View File

@ -5,13 +5,13 @@ use crate::{
use environment::RuntimeContext; use environment::RuntimeContext;
use futures::StreamExt; use futures::StreamExt;
use remote_beacon_node::{PublishStatus, RemoteBeaconNode}; use remote_beacon_node::{PublishStatus, RemoteBeaconNode};
use slog::{crit, debug, info, trace}; use slog::{crit, debug, error, info, trace};
use slot_clock::SlotClock; use slot_clock::SlotClock;
use std::collections::HashMap; use std::collections::HashMap;
use std::ops::Deref; use std::ops::Deref;
use std::sync::Arc; use std::sync::Arc;
use tokio::time::{delay_until, interval_at, Duration, Instant}; use tokio::time::{delay_until, interval_at, Duration, Instant};
use types::{Attestation, ChainSpec, CommitteeIndex, EthSpec, Slot}; use types::{Attestation, ChainSpec, CommitteeIndex, EthSpec, Slot, SubnetId};
/// Builds an `AttestationService`. /// Builds an `AttestationService`.
pub struct AttestationServiceBuilder<T, E: EthSpec> { pub struct AttestationServiceBuilder<T, E: EthSpec> {
@ -334,17 +334,22 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
.iter() .iter()
.filter_map(|duty| { .filter_map(|duty| {
// Ensure that all required fields are present in the validator duty. // Ensure that all required fields are present in the validator duty.
let (duty_slot, duty_committee_index, validator_committee_position, _) = let (
if let Some(tuple) = duty.attestation_duties() { duty_slot,
tuple duty_committee_index,
} else { validator_committee_position,
crit!( _,
log, committee_count_at_slot,
"Missing validator duties when signing"; ) = if let Some(tuple) = duty.attestation_duties() {
"duties" => format!("{:?}", duty) tuple
); } else {
return None; crit!(
}; log,
"Missing validator duties when signing";
"duties" => format!("{:?}", duty)
);
return None;
};
// Ensure that the attestation matches the duties. // Ensure that the attestation matches the duties.
if duty_slot != attestation.data.slot if duty_slot != attestation.data.slot
@ -363,7 +368,18 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
} }
let mut attestation = attestation.clone(); let mut attestation = attestation.clone();
let subnet_id = SubnetId::compute_subnet_for_attestation_data::<E>(
&attestation.data,
committee_count_at_slot,
&self.context.eth2_config().spec,
)
.map_err(|e| {
error!(
log,
"Failed to compute subnet id to publish attestation: {:?}", e
)
})
.ok()?;
self.validator_store self.validator_store
.sign_attestation( .sign_attestation(
duty.validator_pubkey(), duty.validator_pubkey(),
@ -371,7 +387,7 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
&mut attestation, &mut attestation,
current_epoch, current_epoch,
) )
.map(|_| attestation) .map(|_| (attestation, subnet_id))
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -379,7 +395,7 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
// just return early. // just return early.
if let Some(attestation) = signed_attestations.first().cloned() { if let Some(attestation) = signed_attestations.first().cloned() {
let num_attestations = signed_attestations.len(); let num_attestations = signed_attestations.len();
let beacon_block_root = attestation.data.beacon_block_root; let beacon_block_root = attestation.0.data.beacon_block_root;
self.beacon_node self.beacon_node
.http .http
@ -409,7 +425,7 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
crit!(log, "Unknown condition when publishing unagg. attestation") crit!(log, "Unknown condition when publishing unagg. attestation")
} }
}) })
.map(|()| Some(attestation)) .map(|()| Some(attestation.0))
} else { } else {
debug!( debug!(
log, log,
@ -459,7 +475,7 @@ impl<T: SlotClock + 'static, E: EthSpec> AttestationService<T, E> {
// subscribed aggregators. // subscribed aggregators.
let selection_proof = duty_and_proof.selection_proof.as_ref()?.clone(); let selection_proof = duty_and_proof.selection_proof.as_ref()?.clone();
let (duty_slot, duty_committee_index, _, validator_index) = let (duty_slot, duty_committee_index, _, validator_index, _) =
duty_and_proof.attestation_duties().or_else(|| { duty_and_proof.attestation_duties().or_else(|| {
crit!(log, "Missing duties when signing aggregate"); crit!(log, "Missing duties when signing aggregate");
None None

View File

@ -90,12 +90,13 @@ impl DutyAndProof {
/// Returns the information required for an attesting validator, if they are scheduled to /// Returns the information required for an attesting validator, if they are scheduled to
/// attest. /// attest.
pub fn attestation_duties(&self) -> Option<(Slot, CommitteeIndex, usize, u64)> { pub fn attestation_duties(&self) -> Option<(Slot, CommitteeIndex, usize, u64, u64)> {
Some(( Some((
self.duty.attestation_slot?, self.duty.attestation_slot?,
self.duty.attestation_committee_index?, self.duty.attestation_committee_index?,
self.duty.attestation_committee_position?, self.duty.attestation_committee_position?,
self.duty.validator_index?, self.duty.validator_index?,
self.duty.committee_count_at_slot?,
)) ))
} }
@ -116,6 +117,7 @@ impl TryInto<DutyAndProof> for ValidatorDutyBytes {
attestation_slot: self.attestation_slot, attestation_slot: self.attestation_slot,
attestation_committee_index: self.attestation_committee_index, attestation_committee_index: self.attestation_committee_index,
attestation_committee_position: self.attestation_committee_position, attestation_committee_position: self.attestation_committee_position,
committee_count_at_slot: self.committee_count_at_slot,
block_proposal_slots: self.block_proposal_slots, block_proposal_slots: self.block_proposal_slots,
aggregator_modulo: self.aggregator_modulo, aggregator_modulo: self.aggregator_modulo,
}; };
@ -609,6 +611,7 @@ impl<T: SlotClock + 'static, E: EthSpec> DutiesService<T, E> {
validator_index: remote_duties.validator_index?, validator_index: remote_duties.validator_index?,
attestation_committee_index: remote_duties.attestation_committee_index?, attestation_committee_index: remote_duties.attestation_committee_index?,
slot: remote_duties.attestation_slot?, slot: remote_duties.attestation_slot?,
committee_count_at_slot: remote_duties.committee_count_at_slot?,
is_aggregator, is_aggregator,
}) })
} else { } else {