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:
Paul Hauner
2020-05-06 21:42:56 +10:00
committed by GitHub
parent 1552f9997e
commit ad5bd6412a
38 changed files with 4952 additions and 1479 deletions

View File

@@ -22,6 +22,10 @@ pub enum Error {
/// There was an error attempting to read from a `BeaconState`. Block
/// validity was not determined.
BeaconStateError(BeaconStateError),
/// The `BeaconBlock` has a `proposer_index` that does not match the index we computed locally.
///
/// The block is invalid.
IncorrectBlockProposer { block: u64, local_shuffling: u64 },
/// Failed to load a signature set. The block may be invalid or we failed to process it.
SignatureSetError(SignatureSetError),
}
@@ -34,7 +38,18 @@ impl From<BeaconStateError> for Error {
impl From<SignatureSetError> for Error {
fn from(e: SignatureSetError) -> Error {
Error::SignatureSetError(e)
match e {
// Make a special distinction for `IncorrectBlockProposer` since it indicates an
// invalid block, not an internal error.
SignatureSetError::IncorrectBlockProposer {
block,
local_shuffling,
} => Error::IncorrectBlockProposer {
block,
local_shuffling,
},
e => Error::SignatureSetError(e),
}
}
}

View File

@@ -10,8 +10,8 @@ use tree_hash::TreeHash;
use types::{
AggregateSignature, AttesterSlashing, BeaconBlock, BeaconState, BeaconStateError, ChainSpec,
DepositData, Domain, EthSpec, Fork, Hash256, IndexedAttestation, ProposerSlashing, PublicKey,
Signature, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot, SignedVoluntaryExit,
SigningRoot,
Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockHeader, SignedRoot,
SignedVoluntaryExit, SigningRoot,
};
pub type Result<T> = std::result::Result<T, Error>;
@@ -26,6 +26,10 @@ pub enum Error {
/// Attempted to find the public key of a validator that does not exist. You cannot distinguish
/// between an error and an invalid block in this case.
ValidatorUnknown(u64),
/// The `BeaconBlock` has a `proposer_index` that does not match the index we computed locally.
///
/// The block is invalid.
IncorrectBlockProposer { block: u64, local_shuffling: u64 },
/// The public keys supplied do not match the number of objects requiring keys. Block validity
/// was not determined.
MismatchedPublicKeyLen { pubkey_len: usize, other_len: usize },
@@ -73,6 +77,13 @@ where
let block = &signed_block.message;
let proposer_index = state.get_beacon_proposer_index(block.slot, spec)?;
if proposer_index as u64 != block.proposer_index {
return Err(Error::IncorrectBlockProposer {
block: block.proposer_index,
local_shuffling: proposer_index as u64,
});
}
let domain = spec.get_domain(
block.slot.epoch(T::slots_per_epoch()),
Domain::BeaconProposer,
@@ -343,3 +354,74 @@ where
message,
))
}
pub fn signed_aggregate_selection_proof_signature_set<'a, T, F>(
get_pubkey: F,
signed_aggregate_and_proof: &'a SignedAggregateAndProof<T>,
fork: &Fork,
genesis_validators_root: Hash256,
spec: &'a ChainSpec,
) -> Result<SignatureSet>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let slot = signed_aggregate_and_proof.message.aggregate.data.slot;
let domain = spec.get_domain(
slot.epoch(T::slots_per_epoch()),
Domain::SelectionProof,
fork,
genesis_validators_root,
);
let message = slot.signing_root(domain).as_bytes().to_vec();
let signature = &signed_aggregate_and_proof.message.selection_proof;
let validator_index = signed_aggregate_and_proof.message.aggregator_index;
Ok(SignatureSet::single(
signature,
get_pubkey(validator_index as usize)
.ok_or_else(|| Error::ValidatorUnknown(validator_index))?,
message,
))
}
pub fn signed_aggregate_signature_set<'a, T, F>(
get_pubkey: F,
signed_aggregate_and_proof: &'a SignedAggregateAndProof<T>,
fork: &Fork,
genesis_validators_root: Hash256,
spec: &'a ChainSpec,
) -> Result<SignatureSet>
where
T: EthSpec,
F: Fn(usize) -> Option<Cow<'a, PublicKey>>,
{
let target_epoch = signed_aggregate_and_proof
.message
.aggregate
.data
.target
.epoch;
let domain = spec.get_domain(
target_epoch,
Domain::AggregateAndProof,
fork,
genesis_validators_root,
);
let message = signed_aggregate_and_proof
.message
.signing_root(domain)
.as_bytes()
.to_vec();
let signature = &signed_aggregate_and_proof.signature;
let validator_index = signed_aggregate_and_proof.message.aggregator_index;
Ok(SignatureSet::single(
signature,
get_pubkey(validator_index as usize)
.ok_or_else(|| Error::ValidatorUnknown(validator_index))?,
message,
))
}

View File

@@ -1039,7 +1039,12 @@ fn invalid_proposer_slashing_duplicate_slashing() {
let slashing = block.message.body.proposer_slashings[0].clone();
let slashed_proposer = slashing.signed_header_1.message.proposer_index;
block.message.body.proposer_slashings.push(slashing);
block
.message
.body
.proposer_slashings
.push(slashing)
.expect("should push slashing");
let result = per_block_processing(
&mut state,