mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-18 05:18:30 +00:00
Add attestation gossip pre-verification (#983)
* Add PH & MS slot clock changes * Account for genesis time * Add progress on duties refactor * Add simple is_aggregator bool to val subscription * Start work on attestation_verification.rs * Add progress on ObservedAttestations * Progress with ObservedAttestations * Fix tests * Add observed attestations to the beacon chain * Add attestation observation to processing code * Add progress on attestation verification * Add first draft of ObservedAttesters * Add more tests * Add observed attesters to beacon chain * Add observers to attestation processing * Add more attestation verification * Create ObservedAggregators map * Remove commented-out code * Add observed aggregators into chain * Add progress * Finish adding features to attestation verification * Ensure beacon chain compiles * Link attn verification into chain * Integrate new attn verification in chain * Remove old attestation processing code * Start trying to fix beacon_chain tests * Split adding into pools into two functions * Add aggregation to harness * Get test harness working again * Adjust the number of aggregators for test harness * Fix edge-case in harness * Integrate new attn processing in network * Fix compile bug in validator_client * Update validator API endpoints * Fix aggreagation in test harness * Fix enum thing * Fix attestation observation bug: * Patch failing API tests * Start adding comments to attestation verification * Remove unused attestation field * Unify "is block known" logic * Update comments * Supress fork choice errors for network processing * Add todos * Tidy * Add gossip attn tests * Disallow test harness to produce old attns * Comment out in-progress tests * Partially address pruning tests * Fix failing store test * Add aggregate tests * Add comments about which spec conditions we check * Dont re-aggregate * Split apart test harness attn production * Fix compile error in network * Make progress on commented-out test * Fix skipping attestation test * Add fork choice verification tests * Tidy attn tests, remove dead code * Remove some accidentally added code * Fix clippy lint * Rename test file * Add block tests, add cheap block proposer check * Rename block testing file * Add observed_block_producers * Tidy * Switch around block signature verification * Finish block testing * Remove gossip from signature tests * First pass of self review * Fix deviation in spec * Update test spec tags * Start moving over to hashset * Finish moving observed attesters to hashmap * Move aggregation pool over to hashmap * Make fc attn borrow again * Fix rest_api compile error * Fix missing comments * Fix monster test * Uncomment increasing slots test * Address remaining comments * Remove unsafe, use cfg test * Remove cfg test flag * Fix dodgy comment * Ignore aggregates that are already known. * Unify aggregator modulo logic * Fix typo in logs * Refactor validator subscription logic * Avoid reproducing selection proof * Skip HTTP call if no subscriptions * Rename DutyAndState -> DutyAndProof * Tidy logs * Print root as dbg * Fix compile errors in tests * Fix compile error in test
This commit is contained in:
@@ -27,22 +27,28 @@ pub struct AggregateAndProof<T: EthSpec> {
|
||||
impl<T: EthSpec> AggregateAndProof<T> {
|
||||
/// Produces a new `AggregateAndProof` with a `selection_proof` generated by signing
|
||||
/// `aggregate.data.slot` with `secret_key`.
|
||||
///
|
||||
/// If `selection_proof.is_none()` it will be computed locally.
|
||||
pub fn from_aggregate(
|
||||
aggregator_index: u64,
|
||||
aggregate: Attestation<T>,
|
||||
selection_proof: Option<SelectionProof>,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> Self {
|
||||
let selection_proof = SelectionProof::new::<T>(
|
||||
aggregate.data.slot,
|
||||
secret_key,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
.into();
|
||||
let selection_proof = selection_proof
|
||||
.unwrap_or_else(|| {
|
||||
SelectionProof::new::<T>(
|
||||
aggregate.data.slot,
|
||||
secret_key,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
spec,
|
||||
)
|
||||
})
|
||||
.into();
|
||||
|
||||
Self {
|
||||
aggregator_index,
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use crate::{ChainSpec, Domain, EthSpec, Fork, Hash256, SecretKey, Signature, SignedRoot, Slot};
|
||||
use crate::{
|
||||
ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey, SecretKey, Signature, SignedRoot, Slot,
|
||||
};
|
||||
use safe_arith::{ArithError, SafeArith};
|
||||
use std::cmp;
|
||||
use std::convert::TryInto;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
@@ -26,7 +29,23 @@ impl SelectionProof {
|
||||
Self(Signature::new(message.as_bytes(), secret_key))
|
||||
}
|
||||
|
||||
pub fn is_aggregator(&self, modulo: u64) -> Result<bool, ArithError> {
|
||||
/// Returns the "modulo" used for determining if a `SelectionProof` elects an aggregator.
|
||||
pub fn modulo(committee_len: usize, spec: &ChainSpec) -> Result<u64, ArithError> {
|
||||
Ok(cmp::max(
|
||||
1,
|
||||
(committee_len as u64).safe_div(spec.target_aggregators_per_committee)?,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn is_aggregator(
|
||||
&self,
|
||||
committee_len: usize,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<bool, ArithError> {
|
||||
self.is_aggregator_from_modulo(Self::modulo(committee_len, spec)?)
|
||||
}
|
||||
|
||||
pub fn is_aggregator_from_modulo(&self, modulo: u64) -> Result<bool, ArithError> {
|
||||
let signature_hash = self.0.tree_hash_root();
|
||||
let signature_hash_int = u64::from_le_bytes(
|
||||
signature_hash[0..8]
|
||||
@@ -37,6 +56,25 @@ impl SelectionProof {
|
||||
|
||||
signature_hash_int.safe_rem(modulo).map(|rem| rem == 0)
|
||||
}
|
||||
|
||||
pub fn verify<T: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
pubkey: &PublicKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> bool {
|
||||
let domain = spec.get_domain(
|
||||
slot.epoch(T::slots_per_epoch()),
|
||||
Domain::SelectionProof,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = slot.signing_root(domain);
|
||||
|
||||
self.0.verify(message.as_bytes(), pubkey)
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Signature> for SelectionProof {
|
||||
@@ -44,3 +82,9 @@ impl Into<Signature> for SelectionProof {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signature> for SelectionProof {
|
||||
fn from(sig: Signature) -> Self {
|
||||
Self(sig)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
AggregateAndProof, Attestation, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey,
|
||||
SecretKey, Signature, SignedRoot,
|
||||
SecretKey, SelectionProof, Signature, SignedRoot,
|
||||
};
|
||||
use crate::test_utils::TestRandom;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@@ -25,9 +25,12 @@ pub struct SignedAggregateAndProof<T: EthSpec> {
|
||||
impl<T: EthSpec> SignedAggregateAndProof<T> {
|
||||
/// Produces a new `SignedAggregateAndProof` with a `selection_proof` generated by signing
|
||||
/// `aggregate.data.slot` with `secret_key`.
|
||||
///
|
||||
/// If `selection_proof.is_none()` it will be computed locally.
|
||||
pub fn from_aggregate(
|
||||
aggregator_index: u64,
|
||||
aggregate: Attestation<T>,
|
||||
selection_proof: Option<SelectionProof>,
|
||||
secret_key: &SecretKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
@@ -36,6 +39,7 @@ impl<T: EthSpec> SignedAggregateAndProof<T> {
|
||||
let message = AggregateAndProof::from_aggregate(
|
||||
aggregator_index,
|
||||
aggregate,
|
||||
selection_proof,
|
||||
secret_key,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use crate::{test_utils::TestRandom, BeaconBlock, EthSpec, Hash256, Slot};
|
||||
use std::fmt;
|
||||
|
||||
use crate::{
|
||||
test_utils::TestRandom, BeaconBlock, ChainSpec, Domain, EthSpec, Fork, Hash256, PublicKey,
|
||||
SignedRoot, SigningRoot, Slot,
|
||||
};
|
||||
use bls::Signature;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use std::fmt;
|
||||
use test_random_derive::TestRandom;
|
||||
use tree_hash::TreeHash;
|
||||
|
||||
@@ -47,6 +49,38 @@ pub struct SignedBeaconBlock<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> SignedBeaconBlock<E> {
|
||||
/// Verify `self.signature`.
|
||||
///
|
||||
/// If the root of `block.message` is already known it can be passed in via `object_root_opt`.
|
||||
/// Otherwise, it will be computed locally.
|
||||
pub fn verify_signature(
|
||||
&self,
|
||||
object_root_opt: Option<Hash256>,
|
||||
pubkey: &PublicKey,
|
||||
fork: &Fork,
|
||||
genesis_validators_root: Hash256,
|
||||
spec: &ChainSpec,
|
||||
) -> bool {
|
||||
let domain = spec.get_domain(
|
||||
self.message.slot.epoch(E::slots_per_epoch()),
|
||||
Domain::BeaconProposer,
|
||||
fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
|
||||
let message = if let Some(object_root) = object_root_opt {
|
||||
SigningRoot {
|
||||
object_root,
|
||||
domain,
|
||||
}
|
||||
.tree_hash_root()
|
||||
} else {
|
||||
self.message.signing_root(domain)
|
||||
};
|
||||
|
||||
self.signature.verify(message.as_bytes(), pubkey)
|
||||
}
|
||||
|
||||
/// Convenience accessor for the block's slot.
|
||||
pub fn slot(&self) -> Slot {
|
||||
self.message.slot
|
||||
|
||||
Reference in New Issue
Block a user