lighthouse/beacon_node/beacon_chain/tests/attestation_tests.rs
Paul Hauner 6656cb00e4
Optimize attestation processing (#841)
* Start updating types

* WIP

* Signature hacking

* Existing EF tests passing with fake_crypto

* Updates

* Delete outdated API spec

* The refactor continues

* It compiles

* WIP test fixes

* All release tests passing bar genesis state parsing

* Update and test YamlConfig

* Update to spec v0.10 compatible BLS

* Updates to BLS EF tests

* Add EF test for AggregateVerify

And delete unused hash2curve tests for uncompressed points

* Update EF tests to v0.10.1

* Use optional block root correctly in block proc

* Use genesis fork in deposit domain. All tests pass

* Cargo fmt

* Fast aggregate verify test

* Update REST API docs

* Cargo fmt

* Fix unused import

* Bump spec tags to v0.10.1

* Add `seconds_per_eth1_block` to chainspec

* Update to timestamp based eth1 voting scheme

* Return None from `get_votes_to_consider` if block cache is empty

* Handle overflows in `is_candidate_block`

* Revert to failing tests

* Fix eth1 data sets test

* Choose default vote according to spec

* Fix collect_valid_votes tests

* Fix `get_votes_to_consider` to choose all eligible blocks

* Uncomment winning_vote tests

* Add comments; remove unused code

* Reduce seconds_per_eth1_block for simulation

* Addressed review comments

* Add test for default vote case

* Fix logs

* Remove unused functions

* Meter default eth1 votes

* Fix comments

* Address review comments; remove unused dependency

* Add first attempt at attestation proc. re-write

* Add version 2 of attestation processing

* Minor fixes

* Add validator pubkey cache

* Make get_indexed_attestation take a committee

* Link signature processing into new attn verification

* First working version

* Ensure pubkey cache is updated

* Add more metrics, slight optimizations

* Clone committee cache during attestation processing

* Update shuffling cache during block processing

* Remove old commented-out code

* Fix shuffling cache insert bug

* Used indexed attestation in fork choice

* Restructure attn processing, add metrics

* Add more detailed metrics

* Tidy, fix failing tests

* Fix failing tests, tidy

* Disable/delete two outdated tests

* Tidy

* Add pubkey cache persistence file

* Add more comments

* Integrate persistence file into builder

* Add pubkey cache tests

* Add data_dir to beacon chain builder

* Remove Option in pubkey cache persistence file

* Ensure consistency between datadir/data_dir

* Fix failing network test

* Tidy

* Fix todos

* Add attestation processing tests

* Add another test

* Only run attestation tests in release

* Make attestation tests MainnetEthSpec

* Address Michael's comments

* Remove redundant check

* Fix warning

* Fix failing test

Co-authored-by: Michael Sproul <micsproul@gmail.com>
Co-authored-by: Pawan Dhananjay <pawandhananjay@gmail.com>
2020-03-05 17:19:35 +11:00

255 lines
8.0 KiB
Rust

#![cfg(not(debug_assertions))]
#[macro_use]
extern crate lazy_static;
use beacon_chain::test_utils::{
AttestationStrategy, BeaconChainHarness, BlockStrategy, HarnessType,
};
use beacon_chain::AttestationProcessingOutcome;
use state_processing::per_slot_processing;
use types::{
test_utils::generate_deterministic_keypair, AggregateSignature, BitList, EthSpec, Hash256,
Keypair, MainnetEthSpec, Signature,
};
pub const VALIDATOR_COUNT: usize = 128;
lazy_static! {
/// A cached set of keys.
static ref KEYPAIRS: Vec<Keypair> = types::test_utils::generate_deterministic_keypairs(VALIDATOR_COUNT);
}
fn get_harness(validator_count: usize) -> BeaconChainHarness<HarnessType<MainnetEthSpec>> {
let harness = BeaconChainHarness::new(MainnetEthSpec, KEYPAIRS[0..validator_count].to_vec());
harness.advance_slot();
harness
}
#[test]
fn attestation_validity() {
let harness = get_harness(VALIDATOR_COUNT);
let chain = &harness.chain;
// Extend the chain out a few epochs so we have some chain depth to play with.
harness.extend_chain(
MainnetEthSpec::slots_per_epoch() as usize * 3 + 1,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
let head = chain.head().expect("should get head");
let current_slot = chain.slot().expect("should get slot");
let current_epoch = chain.epoch().expect("should get epoch");
let valid_attestation = harness
.get_free_attestations(
&AttestationStrategy::AllValidators,
&head.beacon_state,
head.beacon_block_root,
head.beacon_block.slot(),
)
.first()
.cloned()
.expect("should get at least one attestation");
assert_eq!(
chain.process_attestation(valid_attestation.clone()),
Ok(AttestationProcessingOutcome::Processed),
"should accept valid attestation"
);
/*
* Should reject attestations if the slot does not match the target epoch.
*/
let mut epoch_mismatch_attestation = valid_attestation.clone();
epoch_mismatch_attestation.data.target.epoch = current_epoch + 1;
assert_eq!(
harness
.chain
.process_attestation(epoch_mismatch_attestation),
Ok(AttestationProcessingOutcome::BadTargetEpoch),
"should not accept attestation where the slot is not in the same epoch as the target"
);
/*
* Should reject attestations from future epochs.
*/
let mut early_attestation = valid_attestation.clone();
early_attestation.data.target.epoch = current_epoch + 1;
early_attestation.data.slot = (current_epoch + 1).start_slot(MainnetEthSpec::slots_per_epoch());
assert_eq!(
harness.chain.process_attestation(early_attestation),
Ok(AttestationProcessingOutcome::FutureEpoch {
attestation_epoch: current_epoch + 1,
current_epoch
}),
"should not accept early attestation"
);
/*
* Should reject attestations from epochs prior to the previous epoch.
*/
let late_slot = (current_epoch - 2).start_slot(MainnetEthSpec::slots_per_epoch());
let late_block = chain
.block_at_slot(late_slot)
.expect("should not error getting block at slot")
.expect("should find block at slot");
let late_state = chain
.get_state(&late_block.state_root(), Some(late_slot))
.expect("should not error getting state")
.expect("should find state");
let late_attestation = harness
.get_free_attestations(
&AttestationStrategy::AllValidators,
&late_state,
late_block.canonical_root(),
late_slot,
)
.first()
.cloned()
.expect("should get at least one late attestation");
assert_eq!(
harness.chain.process_attestation(late_attestation),
Ok(AttestationProcessingOutcome::PastEpoch {
attestation_epoch: current_epoch - 2,
current_epoch
}),
"should not accept late attestation"
);
/*
* Should reject attestations if the target is unknown.
*/
let mut bad_target_attestation = valid_attestation.clone();
bad_target_attestation.data.target.root = Hash256::from_low_u64_be(42);
assert_eq!(
harness.chain.process_attestation(bad_target_attestation),
Ok(AttestationProcessingOutcome::UnknownTargetRoot(
Hash256::from_low_u64_be(42)
)),
"should not accept bad_target attestation"
);
/*
* Should reject attestations if the target is unknown.
*/
let mut future_block_attestation = valid_attestation.clone();
future_block_attestation.data.slot -= 1;
assert_eq!(
harness.chain.process_attestation(future_block_attestation),
Ok(AttestationProcessingOutcome::AttestsToFutureBlock {
block: current_slot,
attestation: current_slot - 1
}),
"should not accept future_block attestation"
);
/*
* Should reject attestations if the target is unknown.
*/
let mut bad_head_attestation = valid_attestation.clone();
bad_head_attestation.data.beacon_block_root = Hash256::from_low_u64_be(42);
assert_eq!(
harness.chain.process_attestation(bad_head_attestation),
Ok(AttestationProcessingOutcome::UnknownHeadBlock {
beacon_block_root: Hash256::from_low_u64_be(42)
}),
"should not accept bad_head attestation"
);
/*
* Should reject attestations with a bad signature.
*/
let mut bad_signature_attestation = valid_attestation.clone();
let kp = generate_deterministic_keypair(0);
let mut agg_sig = AggregateSignature::new();
agg_sig.add(&Signature::new(&[42, 42], &kp.sk));
bad_signature_attestation.signature = agg_sig;
assert_eq!(
harness.chain.process_attestation(bad_signature_attestation),
Ok(AttestationProcessingOutcome::InvalidSignature),
"should not accept bad_signature attestation"
);
/*
* Should reject attestations with an empty bitfield.
*/
let mut empty_bitfield_attestation = valid_attestation.clone();
empty_bitfield_attestation.aggregation_bits =
BitList::with_capacity(1).expect("should build bitfield");
assert_eq!(
harness
.chain
.process_attestation(empty_bitfield_attestation),
Ok(AttestationProcessingOutcome::EmptyAggregationBitfield),
"should not accept empty_bitfield attestation"
);
}
#[test]
fn attestation_that_skips_epochs() {
let harness = get_harness(VALIDATOR_COUNT);
let chain = &harness.chain;
// Extend the chain out a few epochs so we have some chain depth to play with.
harness.extend_chain(
MainnetEthSpec::slots_per_epoch() as usize * 3 + 1,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
let current_slot = chain.slot().expect("should get slot");
let current_epoch = chain.epoch().expect("should get epoch");
let earlier_slot = (current_epoch - 2).start_slot(MainnetEthSpec::slots_per_epoch());
let earlier_block = chain
.block_at_slot(earlier_slot)
.expect("should not error getting block at slot")
.expect("should find block at slot");
let mut state = chain
.get_state(&earlier_block.state_root(), Some(earlier_slot))
.expect("should not error getting state")
.expect("should find state");
while state.slot < current_slot {
per_slot_processing(&mut state, None, &harness.spec).expect("should process slot");
}
let attestation = harness
.get_free_attestations(
&AttestationStrategy::AllValidators,
&state,
earlier_block.canonical_root(),
current_slot,
)
.first()
.cloned()
.expect("should get at least one attestation");
assert_eq!(
harness.chain.process_attestation(attestation),
Ok(AttestationProcessingOutcome::Processed),
"should process attestation that skips slots"
);
}