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:
parent
81c9fe3817
commit
3199b1a6f2
@ -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,
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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
|
||||||
})
|
})
|
||||||
|
@ -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");
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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((
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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());
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
) {
|
) {
|
||||||
|
@ -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,
|
||||||
)))],
|
)))],
|
||||||
}) {
|
}) {
|
||||||
|
@ -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!(
|
||||||
|
@ -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")?;
|
||||||
|
@ -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,
|
||||||
|
@ -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 {
|
||||||
|
@ -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)]
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user