mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-22 22:34:45 +00:00
Initial work towards v0.2.0 (#924)
* Remove ping protocol
* Initial renaming of network services
* Correct rebasing relative to latest master
* Start updating types
* Adds HashMapDelay struct to utils
* Initial network restructure
* Network restructure. Adds new types for v0.2.0
* Removes build artefacts
* Shift validation to beacon chain
* Temporarily remove gossip validation
This is to be updated to match current optimisation efforts.
* Adds AggregateAndProof
* Begin rebuilding pubsub encoding/decoding
* Signature hacking
* Shift gossipsup decoding into eth2_libp2p
* Existing EF tests passing with fake_crypto
* Shifts block encoding/decoding into RPC
* Delete outdated API spec
* 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
* Fast aggregate verify test
* Update REST API docs
* 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
* Progress on attestation service
* Address review comments; remove unused dependency
* Initial work on removing libp2p lock
* Add LRU caches to store (rollup)
* Update attestation validation for DB changes (WIP)
* Initial version of should_forward_block
* Scaffold
* Progress on attestation validation
Also, consolidate prod+testing slot clocks so that they share much
of the same implementation and can both handle sub-slot time changes.
* Removes lock from libp2p service
* Completed network lock removal
* Finish(?) attestation processing
* Correct network termination future
* Add slot check to block check
* Correct fmt issues
* Remove Drop implementation for network service
* 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
* Address reviewers suggestions
* Disable/delete two outdated tests
* Modification of validator for subscriptions
* Add slot signing to validator client
* Further progress on validation subscription
* Adds necessary validator subscription functionality
* Add new Pubkeys struct to signature_sets
* Refactor with functional approach
* Update beacon chain
* Clean up validator <-> beacon node http types
* Add aggregator status to ValidatorDuty
* Impl Clone for manual slot clock
* Fix minor errors
* Further progress validator client subscription
* Initial subscription and aggregation handling
* Remove decompressed member from pubkey bytes
* Progress to modifying val client for attestation aggregation
* First draft of validator client upgrade for aggregate attestations
* Add hashmap for indices lookup
* Add state cache, remove store cache
* Only build the head committee cache
* Removes lock on a network channel
* Partially implement beacon node subscription http api
* Correct compilation issues
* Change `get_attesting_indices` to use Vec
* Fix failing test
* Partial implementation of timer
* Adds timer, removes exit_future, http api to op pool
* Partial multiple aggregate attestation handling
* Permits bulk messages accross gossipsub network channel
* Correct compile issues
* Improve gosispsub messaging and correct rest api helpers
* Added global gossipsub subscriptions
* Update validator subscriptions data structs
* Tidy
* Re-structure validator subscriptions
* Initial handling of subscriptions
* Re-structure network service
* Add pubkey cache persistence file
* Add more comments
* Integrate persistence file into builder
* Add pubkey cache tests
* Add HashSetDelay and introduce into attestation service
* Handles validator subscriptions
* Add data_dir to beacon chain builder
* Remove Option in pubkey cache persistence file
* Ensure consistency between datadir/data_dir
* Fix failing network test
* Peer subnet discovery gets queued for future subscriptions
* Reorganise attestation service functions
* Initial wiring of attestation service
* First draft of attestation service timing logic
* Correct minor typos
* Tidy
* Fix todos
* Improve tests
* Add PeerInfo to connected peers mapping
* Fix compile error
* Fix compile error from merge
* Split up block processing metrics
* Tidy
* Refactor get_pubkey_from_state
* Remove commented-out code
* Rename state_cache -> checkpoint_cache
* Rename Checkpoint -> Snapshot
* Tidy, add comments
* Tidy up find_head function
* Change some checkpoint -> snapshot
* Add tests
* Expose max_len
* Remove dead code
* Tidy
* Fix bug
* Add sync-speed metric
* Add first attempt at VerifiableBlock
* Start integrating into beacon chain
* Integrate VerifiableBlock
* Rename VerifableBlock -> PartialBlockVerification
* Add start of typed methods
* Add progress
* Add further progress
* Rename structs
* Add full block verification to block_processing.rs
* Further beacon chain integration
* Update checks for gossip
* Add todo
* Start adding segement verification
* Add passing chain segement test
* Initial integration with batch sync
* Minor changes
* Tidy, add more error checking
* Start adding chain_segment tests
* Finish invalid signature tests
* Include single and gossip verified blocks in tests
* Add gossip verification tests
* Start adding docs
* Finish adding comments to block_processing.rs
* Rename block_processing.rs -> block_verification
* Start removing old block processing code
* Fixes beacon_chain compilation
* Fix project-wide compile errors
* Remove old code
* Correct code to pass all tests
* Fix bug with beacon proposer index
* Fix shim for BlockProcessingError
* Only process one epoch at a time
* Fix loop in chain segment processing
* Correct tests from master merge
* Add caching for state.eth1_data_votes
* Add BeaconChain::validator_pubkey
* Revert "Add caching for state.eth1_data_votes"
This reverts commit cd73dcd643.
Co-authored-by: Grant Wuerker <gwuerker@gmail.com>
Co-authored-by: Michael Sproul <michael@sigmaprime.io>
Co-authored-by: Michael Sproul <micsproul@gmail.com>
Co-authored-by: pawan <pawandhananjay@gmail.com>
Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "operation_pool"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
authors = ["Michael Sproul <michael@sigmaprime.io>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ use attestation_id::AttestationId;
|
||||
use max_cover::maximum_cover;
|
||||
use parking_lot::RwLock;
|
||||
use state_processing::per_block_processing::errors::{
|
||||
AttestationValidationError, AttesterSlashingValidationError, ExitValidationError,
|
||||
ProposerSlashingValidationError,
|
||||
AttestationInvalid, AttestationValidationError, AttesterSlashingValidationError,
|
||||
ExitValidationError, ProposerSlashingValidationError,
|
||||
};
|
||||
use state_processing::per_block_processing::{
|
||||
get_slashable_indices_modular, verify_attestation_for_block_inclusion,
|
||||
@@ -22,25 +22,43 @@ use std::collections::{hash_map, HashMap, HashSet};
|
||||
use std::marker::PhantomData;
|
||||
use types::{
|
||||
typenum::Unsigned, Attestation, AttesterSlashing, BeaconState, BeaconStateError, ChainSpec,
|
||||
EthSpec, Fork, ProposerSlashing, RelativeEpoch, SignedVoluntaryExit, Validator,
|
||||
CommitteeIndex, Epoch, EthSpec, Fork, ProposerSlashing, RelativeEpoch, SignedVoluntaryExit,
|
||||
Slot, Validator,
|
||||
};
|
||||
|
||||
/// The number of slots we keep shard subnet attestations in the operation pool for. A value of 0
|
||||
/// means we remove the attestation pool as soon as the slot ends.
|
||||
const ATTESTATION_SUBNET_SLOT_DURATION: u64 = 1;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct OperationPool<T: EthSpec + Default> {
|
||||
/// Map from attestation ID (see below) to vectors of attestations.
|
||||
attestations: RwLock<HashMap<AttestationId, Vec<Attestation<T>>>>,
|
||||
/// Map from attestation ID (see `attestation_id`) to vectors of attestations.
|
||||
///
|
||||
/// These are collected from the aggregate channel. They should already be aggregated but we
|
||||
/// check for disjoint attestations in the unlikely event we receive disjoint attestations.
|
||||
aggregate_attestations: RwLock<HashMap<AttestationId, Vec<Attestation<T>>>>,
|
||||
/// A collection of aggregated attestations for a particular slot and committee index.
|
||||
///
|
||||
/// Un-aggregated attestations are collected on a shard subnet and if a connected validator is
|
||||
/// required to aggregate these attestations they are aggregated and stored here until the
|
||||
/// validator is required to publish the aggregate attestation.
|
||||
/// This segregates attestations into (slot,committee_index) then by `AttestationId`.
|
||||
committee_attestations:
|
||||
RwLock<HashMap<(Slot, CommitteeIndex), HashMap<AttestationId, Attestation<T>>>>,
|
||||
/// Map from two attestation IDs to a slashing for those IDs.
|
||||
attester_slashings: RwLock<HashMap<(AttestationId, AttestationId), AttesterSlashing<T>>>,
|
||||
/// Map from proposer index to slashing.
|
||||
proposer_slashings: RwLock<HashMap<u64, ProposerSlashing>>,
|
||||
/// Map from exiting validator to their exit data.
|
||||
voluntary_exits: RwLock<HashMap<u64, SignedVoluntaryExit>>,
|
||||
/// Marker to pin the generics.
|
||||
_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum OpPoolError {
|
||||
GetAttestationsTotalBalanceError(BeaconStateError),
|
||||
NoAttestationsForSlotCommittee,
|
||||
}
|
||||
|
||||
impl<T: EthSpec> OperationPool<T> {
|
||||
@@ -49,12 +67,13 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Insert an attestation into the pool, aggregating it with existing attestations if possible.
|
||||
/// Insert an attestation from the aggregate channel into the pool, checking if the
|
||||
/// aggregate can be further aggregated
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// This function assumes the given `attestation` is valid.
|
||||
pub fn insert_attestation(
|
||||
pub fn insert_aggregate_attestation(
|
||||
&self,
|
||||
attestation: Attestation<T>,
|
||||
fork: &Fork,
|
||||
@@ -63,7 +82,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
let id = AttestationId::from_data(&attestation.data, fork, spec);
|
||||
|
||||
// Take a write lock on the attestations map.
|
||||
let mut attestations = self.attestations.write();
|
||||
let mut attestations = self.aggregate_attestations.write();
|
||||
|
||||
let existing_attestations = match attestations.entry(id) {
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
@@ -90,9 +109,90 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Total number of attestations in the pool, including attestations for the same data.
|
||||
/// Insert a raw un-aggregated attestation into the pool, for a given (slot, committee_index).
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// It would be a fair assumption that all attestations here are unaggregated and we
|
||||
/// therefore do not need to check if `signers_disjoint_form`. However the cost of doing
|
||||
/// so is low, so we perform this check for added safety.
|
||||
pub fn insert_raw_attestation(
|
||||
&self,
|
||||
attestation: Attestation<T>,
|
||||
fork: &Fork,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), AttestationValidationError> {
|
||||
let id = AttestationId::from_data(&attestation.data, fork, spec);
|
||||
|
||||
let slot = attestation.data.slot.clone();
|
||||
let committee_index = attestation.data.index.clone();
|
||||
|
||||
// Take a write lock on the attestations map.
|
||||
let mut attestations = self.committee_attestations.write();
|
||||
|
||||
let slot_index_map = attestations
|
||||
.entry((slot, committee_index))
|
||||
.or_insert_with(|| HashMap::new());
|
||||
|
||||
let existing_attestation = match slot_index_map.entry(id) {
|
||||
hash_map::Entry::Vacant(entry) => {
|
||||
entry.insert(attestation);
|
||||
return Ok(());
|
||||
}
|
||||
hash_map::Entry::Occupied(entry) => entry.into_mut(),
|
||||
};
|
||||
|
||||
if existing_attestation.signers_disjoint_from(&attestation) {
|
||||
existing_attestation.aggregate(&attestation);
|
||||
} else if *existing_attestation != attestation {
|
||||
return Err(AttestationValidationError::Invalid(
|
||||
AttestationInvalid::NotDisjoint,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Total number of aggregate attestations in the pool from the aggregate channel, including attestations for the same data.
|
||||
pub fn num_attestations(&self) -> usize {
|
||||
self.attestations.read().values().map(Vec::len).sum()
|
||||
self.aggregate_attestations
|
||||
.read()
|
||||
.values()
|
||||
.map(Vec::len)
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Total number of attestations in the pool, including attestations for the same data.
|
||||
pub fn total_num_attestations(&self) -> usize {
|
||||
self.num_attestations().saturating_add(
|
||||
self.committee_attestations
|
||||
.read()
|
||||
.values()
|
||||
.map(|map| map.values().len())
|
||||
.sum(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the aggregated raw attestations for a (slot, committee)
|
||||
//TODO: Check this logic and optimize
|
||||
pub fn get_raw_aggregated_attestations(
|
||||
&self,
|
||||
slot: &Slot,
|
||||
index: &CommitteeIndex,
|
||||
state: &BeaconState<T>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Attestation<T>, OpPoolError> {
|
||||
let curr_domain_bytes =
|
||||
AttestationId::compute_domain_bytes(state.current_epoch(), &state.fork, spec);
|
||||
self.committee_attestations
|
||||
.read()
|
||||
.get(&(*slot, *index))
|
||||
.ok_or_else(|| OpPoolError::NoAttestationsForSlotCommittee)?
|
||||
.iter()
|
||||
.filter(|(key, _)| key.domain_bytes_match(&curr_domain_bytes))
|
||||
.next()
|
||||
.map(|(_key, attestation)| attestation.clone())
|
||||
.ok_or_else(|| OpPoolError::NoAttestationsForSlotCommittee)
|
||||
}
|
||||
|
||||
/// Get a list of attestations for inclusion in a block.
|
||||
@@ -109,7 +209,7 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
let prev_domain_bytes = AttestationId::compute_domain_bytes(prev_epoch, &state.fork, spec);
|
||||
let curr_domain_bytes =
|
||||
AttestationId::compute_domain_bytes(current_epoch, &state.fork, spec);
|
||||
let reader = self.attestations.read();
|
||||
let reader = self.aggregate_attestations.read();
|
||||
let active_indices = state
|
||||
.get_cached_active_validator_indices(RelativeEpoch::Current)
|
||||
.map_err(OpPoolError::GetAttestationsTotalBalanceError)?;
|
||||
@@ -141,19 +241,38 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
))
|
||||
}
|
||||
|
||||
/// Remove attestations which are too old to be included in a block.
|
||||
pub fn prune_attestations(&self, finalized_state: &BeaconState<T>) {
|
||||
/// Removes aggregate attestations which are too old to be included in a block.
|
||||
///
|
||||
/// This leaves the committee_attestations intact. The committee attestations have their own
|
||||
/// prune function as these are not for block inclusion and can be pruned more frequently.
|
||||
/// See `prune_committee_attestations`.
|
||||
//TODO: Michael to check this before merge
|
||||
pub fn prune_attestations(&self, current_epoch: &Epoch) {
|
||||
// We know we can include an attestation if:
|
||||
// state.slot <= attestation_slot + SLOTS_PER_EPOCH
|
||||
// We approximate this check using the attestation's epoch, to avoid computing
|
||||
// the slot or relying on the committee cache of the finalized state.
|
||||
self.attestations.write().retain(|_, attestations| {
|
||||
// All the attestations in this bucket have the same data, so we only need to
|
||||
// check the first one.
|
||||
attestations.first().map_or(false, |att| {
|
||||
finalized_state.current_epoch() <= att.data.target.epoch + 1
|
||||
})
|
||||
});
|
||||
self.aggregate_attestations
|
||||
.write()
|
||||
.retain(|_, attestations| {
|
||||
// All the attestations in this bucket have the same data, so we only need to
|
||||
// check the first one.
|
||||
attestations
|
||||
.first()
|
||||
.map_or(false, |att| *current_epoch <= att.data.target.epoch + 1)
|
||||
});
|
||||
}
|
||||
|
||||
/// Removes old committee attestations. These should be used in the slot that they are
|
||||
/// collected. We keep these around for one extra slot (i.e current_slot + 1) to account for
|
||||
/// potential delays.
|
||||
///
|
||||
/// The beacon chain should call this function every slot with the current slot as the
|
||||
/// parameter.
|
||||
pub fn prune_committee_attestations(&self, current_slot: &Slot) {
|
||||
self.committee_attestations
|
||||
.write()
|
||||
.retain(|(slot, _), _| *slot + ATTESTATION_SUBNET_SLOT_DURATION >= *current_slot)
|
||||
}
|
||||
|
||||
/// Insert a proposer slashing into the pool.
|
||||
@@ -332,8 +451,8 @@ impl<T: EthSpec> OperationPool<T> {
|
||||
}
|
||||
|
||||
/// Prune all types of transactions given the latest finalized state.
|
||||
// TODO: Michael - Can we shift these to per-epoch?
|
||||
pub fn prune_all(&self, finalized_state: &BeaconState<T>, spec: &ChainSpec) {
|
||||
self.prune_attestations(finalized_state);
|
||||
self.prune_proposer_slashings(finalized_state);
|
||||
self.prune_attester_slashings(finalized_state, spec);
|
||||
self.prune_voluntary_exits(finalized_state);
|
||||
@@ -383,7 +502,8 @@ fn prune_validator_hash_map<T, F, E: EthSpec>(
|
||||
/// Compare two operation pools.
|
||||
impl<T: EthSpec + Default> PartialEq for OperationPool<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
*self.attestations.read() == *other.attestations.read()
|
||||
*self.aggregate_attestations.read() == *other.aggregate_attestations.read()
|
||||
&& *self.committee_attestations.read() == *other.committee_attestations.read()
|
||||
&& *self.attester_slashings.read() == *other.attester_slashings.read()
|
||||
&& *self.proposer_slashings.read() == *other.proposer_slashings.read()
|
||||
&& *self.voluntary_exits.read() == *other.voluntary_exits.read()
|
||||
@@ -397,6 +517,7 @@ mod release_tests {
|
||||
use super::*;
|
||||
use state_processing::common::{get_attesting_indices, get_base_reward};
|
||||
use std::collections::BTreeSet;
|
||||
use std::iter::FromIterator;
|
||||
use types::test_utils::*;
|
||||
use types::*;
|
||||
|
||||
@@ -820,11 +941,15 @@ mod release_tests {
|
||||
let committee = state
|
||||
.get_beacon_committee(att.data.slot, att.data.index)
|
||||
.expect("should get beacon committee");
|
||||
let att_indices = get_attesting_indices::<MainnetEthSpec>(
|
||||
committee.committee,
|
||||
&fresh_validators_bitlist,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let att_indices = BTreeSet::from_iter(
|
||||
get_attesting_indices::<MainnetEthSpec>(
|
||||
committee.committee,
|
||||
&fresh_validators_bitlist,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let fresh_indices = &att_indices - &seen_indices;
|
||||
|
||||
let rewards = fresh_indices
|
||||
|
||||
@@ -17,7 +17,9 @@ pub struct PersistedOperationPool<T: EthSpec> {
|
||||
/// Mapping from attestation ID to attestation mappings.
|
||||
// We could save space by not storing the attestation ID, but it might
|
||||
// be difficult to make that roundtrip due to eager aggregation.
|
||||
attestations: Vec<(AttestationId, Vec<Attestation<T>>)>,
|
||||
// Note: That we don't store the committee attestations as these are short lived and not worth
|
||||
// persisting
|
||||
aggregate_attestations: Vec<(AttestationId, Vec<Attestation<T>>)>,
|
||||
/// Attester slashings.
|
||||
attester_slashings: Vec<AttesterSlashing<T>>,
|
||||
/// Proposer slashings.
|
||||
@@ -29,8 +31,8 @@ pub struct PersistedOperationPool<T: EthSpec> {
|
||||
impl<T: EthSpec> PersistedOperationPool<T> {
|
||||
/// Convert an `OperationPool` into serializable form.
|
||||
pub fn from_operation_pool(operation_pool: &OperationPool<T>) -> Self {
|
||||
let attestations = operation_pool
|
||||
.attestations
|
||||
let aggregate_attestations = operation_pool
|
||||
.aggregate_attestations
|
||||
.read()
|
||||
.iter()
|
||||
.map(|(att_id, att)| (att_id.clone(), att.clone()))
|
||||
@@ -58,7 +60,7 @@ impl<T: EthSpec> PersistedOperationPool<T> {
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
attestations,
|
||||
aggregate_attestations,
|
||||
attester_slashings,
|
||||
proposer_slashings,
|
||||
voluntary_exits,
|
||||
@@ -67,7 +69,7 @@ impl<T: EthSpec> PersistedOperationPool<T> {
|
||||
|
||||
/// Reconstruct an `OperationPool`.
|
||||
pub fn into_operation_pool(self, state: &BeaconState<T>, spec: &ChainSpec) -> OperationPool<T> {
|
||||
let attestations = RwLock::new(self.attestations.into_iter().collect());
|
||||
let aggregate_attestations = RwLock::new(self.aggregate_attestations.into_iter().collect());
|
||||
let attester_slashings = RwLock::new(
|
||||
self.attester_slashings
|
||||
.into_iter()
|
||||
@@ -93,7 +95,8 @@ impl<T: EthSpec> PersistedOperationPool<T> {
|
||||
);
|
||||
|
||||
OperationPool {
|
||||
attestations,
|
||||
aggregate_attestations,
|
||||
committee_attestations: Default::default(),
|
||||
attester_slashings,
|
||||
proposer_slashings,
|
||||
voluntary_exits,
|
||||
|
||||
Reference in New Issue
Block a user