mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-18 20:32:45 +00:00
Attestation processing (#497)
* Renamed fork_choice::process_attestation_from_block * Processing attestation in fork choice * Retrieving state from store and checking signature * Looser check on beacon state validity. * Cleaned up get_attestation_state * Expanded fork choice api to provide latest validator message. * Checking if the an attestation contains a latest message * Correct process_attestation error handling. * Copy paste error in comment fixed. * Tidy ancestor iterators * Getting attestation slot via helper method * Refactored attestation creation in test utils * Revert "Refactored attestation creation in test utils" This reverts commit 4d277fe4239a7194758b18fb5c00dfe0b8231306. * Integration tests for free attestation processing * Implicit conflicts resolved. * formatting * Do first pass on Grants code * Add another attestation processing test * Tidy attestation processing * Remove old code fragment * Add non-compiling half finished changes * Simplify, fix bugs, add tests for chain iters * Remove attestation processing from op pool * Fix bug with fork choice, tidy * Fix overly restrictive check in fork choice. * Ensure committee cache is build during attn proc * Ignore unknown blocks at fork choice * Various minor fixes * Make fork choice write lock in to read lock * Remove unused method * Tidy comments * Fix attestation prod. target roots change * Fix compile error in store iters * Reject any attestation prior to finalization * Fix minor PR comments * Remove duplicated attestation finalization check * Remove awkward `let` statement
This commit is contained in:
@@ -11,9 +11,12 @@ use operation_pool::{OperationPool, PersistedOperationPool};
|
||||
use parking_lot::{RwLock, RwLockReadGuard};
|
||||
use slog::{error, info, warn, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::per_block_processing::errors::{
|
||||
AttestationValidationError, AttesterSlashingValidationError, DepositValidationError,
|
||||
ExitValidationError, ProposerSlashingValidationError, TransferValidationError,
|
||||
use state_processing::per_block_processing::{
|
||||
errors::{
|
||||
AttestationValidationError, AttesterSlashingValidationError, DepositValidationError,
|
||||
ExitValidationError, ProposerSlashingValidationError, TransferValidationError,
|
||||
},
|
||||
verify_attestation_for_state, VerifySignatures,
|
||||
};
|
||||
use state_processing::{
|
||||
per_block_processing, per_block_processing_without_verifying_block_signature,
|
||||
@@ -54,6 +57,26 @@ pub enum BlockProcessingOutcome {
|
||||
PerBlockProcessingError(BlockProcessingError),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AttestationProcessingOutcome {
|
||||
Processed,
|
||||
UnknownHeadBlock {
|
||||
beacon_block_root: Hash256,
|
||||
},
|
||||
/// The attestation is attesting to a state that is later than itself. (Viz., attesting to the
|
||||
/// future).
|
||||
AttestsToFutureState {
|
||||
state: Slot,
|
||||
attestation: Slot,
|
||||
},
|
||||
/// The slot is finalized, no need to import.
|
||||
FinalizedSlot {
|
||||
attestation: Epoch,
|
||||
finalized: Epoch,
|
||||
},
|
||||
Invalid(AttestationValidationError),
|
||||
}
|
||||
|
||||
pub trait BeaconChainTypes {
|
||||
type Store: store::Store;
|
||||
type SlotClock: slot_clock::SlotClock;
|
||||
@@ -237,15 +260,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// - Iterator returns `(Hash256, Slot)`.
|
||||
/// - As this iterator starts at the `head` of the chain (viz., the best block), the first slot
|
||||
/// returned may be earlier than the wall-clock slot.
|
||||
pub fn rev_iter_block_roots(
|
||||
&self,
|
||||
slot: Slot,
|
||||
) -> ReverseBlockRootIterator<T::EthSpec, T::Store> {
|
||||
pub fn rev_iter_block_roots(&self) -> ReverseBlockRootIterator<T::EthSpec, T::Store> {
|
||||
let state = &self.head().beacon_state;
|
||||
let block_root = self.head().beacon_block_root;
|
||||
let block_slot = state.slot;
|
||||
|
||||
let iter = BlockRootsIterator::owned(self.store.clone(), state.clone(), slot);
|
||||
let iter = BlockRootsIterator::owned(self.store.clone(), state.clone());
|
||||
|
||||
ReverseBlockRootIterator::new((block_root, block_slot), iter)
|
||||
}
|
||||
@@ -259,15 +279,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// - Iterator returns `(Hash256, Slot)`.
|
||||
/// - As this iterator starts at the `head` of the chain (viz., the best block), the first slot
|
||||
/// returned may be earlier than the wall-clock slot.
|
||||
pub fn rev_iter_state_roots(
|
||||
&self,
|
||||
slot: Slot,
|
||||
) -> ReverseStateRootIterator<T::EthSpec, T::Store> {
|
||||
pub fn rev_iter_state_roots(&self) -> ReverseStateRootIterator<T::EthSpec, T::Store> {
|
||||
let state = &self.head().beacon_state;
|
||||
let state_root = self.head().beacon_state_root;
|
||||
let state_slot = state.slot;
|
||||
|
||||
let iter = StateRootsIterator::owned(self.store.clone(), state.clone(), slot);
|
||||
let iter = StateRootsIterator::owned(self.store.clone(), state.clone());
|
||||
|
||||
ReverseStateRootIterator::new((state_root, state_slot), iter)
|
||||
}
|
||||
@@ -484,6 +501,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
} else {
|
||||
*state.get_block_root(current_epoch_start_slot)?
|
||||
};
|
||||
|
||||
let target = Checkpoint {
|
||||
epoch: state.current_epoch(),
|
||||
root: target_root,
|
||||
@@ -513,38 +531,212 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Accept a new attestation from the network.
|
||||
/// Accept a new, potentially invalid attestation from the network.
|
||||
///
|
||||
/// If valid, the attestation is added to the `op_pool` and aggregated with another attestation
|
||||
/// if possible.
|
||||
/// If valid, the attestation is added to `self.op_pool` and `self.fork_choice`.
|
||||
///
|
||||
/// Returns an `Ok(AttestationProcessingOutcome)` if the chain was able to make a determination
|
||||
/// about the `attestation` (whether it was invalid or not). Returns an `Err` if there was an
|
||||
/// error during this process and no determination was able to be made.
|
||||
///
|
||||
/// ## Notes
|
||||
///
|
||||
/// - Whilst the `attestation` is added to fork choice, the head is not updated. That must be
|
||||
/// done separately.
|
||||
pub fn process_attestation(
|
||||
&self,
|
||||
attestation: Attestation<T::EthSpec>,
|
||||
) -> Result<(), AttestationValidationError> {
|
||||
) -> Result<AttestationProcessingOutcome, Error> {
|
||||
// From the store, load the attestation's "head block".
|
||||
//
|
||||
// An honest validator would have set this block to be the head of the chain (i.e., the
|
||||
// result of running fork choice).
|
||||
if let Some(attestation_head_block) = self
|
||||
.store
|
||||
.get::<BeaconBlock<T::EthSpec>>(&attestation.data.beacon_block_root)?
|
||||
{
|
||||
// Attempt to process the attestation using the `self.head()` state.
|
||||
//
|
||||
// This is purely an effort to avoid loading a `BeaconState` unnecessarily from the DB.
|
||||
// Take a read lock on the head beacon state.
|
||||
let state = &self.head().beacon_state;
|
||||
|
||||
// If it turns out that the attestation was made using the head state, then there
|
||||
// is no need to load a state from the database to process the attestation.
|
||||
//
|
||||
// Note: use the epoch of the target because it indicates which epoch the
|
||||
// attestation was created in. You cannot use the epoch of the head block, because
|
||||
// the block doesn't necessarily need to be in the same epoch as the attestation
|
||||
// (e.g., if there are skip slots between the epoch the block was created in and
|
||||
// the epoch for the attestation).
|
||||
//
|
||||
// This check also ensures that the slot for `data.beacon_block_root` is not higher
|
||||
// than `state.root` by ensuring that the block is in the history of `state`.
|
||||
if state.current_epoch() == attestation.data.target.epoch
|
||||
&& (attestation.data.beacon_block_root == self.head().beacon_block_root
|
||||
|| state
|
||||
.get_block_root(attestation_head_block.slot)
|
||||
.map(|root| *root == attestation.data.beacon_block_root)
|
||||
.unwrap_or_else(|_| false))
|
||||
{
|
||||
// The head state is able to be used to validate this attestation. No need to load
|
||||
// anything from the database.
|
||||
return self.process_attestation_for_state_and_block(
|
||||
attestation.clone(),
|
||||
state,
|
||||
&attestation_head_block,
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure the read-lock from `self.head()` is dropped.
|
||||
//
|
||||
// This is likely unnecessary, however it remains as a reminder to ensure this lock
|
||||
// isn't hogged.
|
||||
std::mem::drop(state);
|
||||
|
||||
// Use the `data.beacon_block_root` to load the state from the latest non-skipped
|
||||
// slot preceding the attestation's creation.
|
||||
//
|
||||
// This state is guaranteed to be in the same chain as the attestation, but it's
|
||||
// not guaranteed to be from the same slot or epoch as the attestation.
|
||||
let mut state: BeaconState<T::EthSpec> = self
|
||||
.store
|
||||
.get(&attestation_head_block.state_root)?
|
||||
.ok_or_else(|| Error::MissingBeaconState(attestation_head_block.state_root))?;
|
||||
|
||||
// Ensure the state loaded from the database matches the state of the attestation
|
||||
// head block.
|
||||
//
|
||||
// The state needs to be advanced from the current slot through to the epoch in
|
||||
// which the attestation was created in. It would be an error to try and use
|
||||
// `state.get_attestation_data_slot(..)` because the state matching the
|
||||
// `data.beacon_block_root` isn't necessarily in a nearby epoch to the attestation
|
||||
// (e.g., if there were lots of skip slots since the head of the chain and the
|
||||
// epoch creation epoch).
|
||||
for _ in state.slot.as_u64()
|
||||
..attestation
|
||||
.data
|
||||
.target
|
||||
.epoch
|
||||
.start_slot(T::EthSpec::slots_per_epoch())
|
||||
.as_u64()
|
||||
{
|
||||
per_slot_processing(&mut state, &self.spec)?;
|
||||
}
|
||||
|
||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||
|
||||
let attestation_slot = state.get_attestation_data_slot(&attestation.data)?;
|
||||
|
||||
// Reject any attestation where the `state` loaded from `data.beacon_block_root`
|
||||
// has a higher slot than the attestation.
|
||||
//
|
||||
// Permitting this would allow for attesters to vote on _future_ slots.
|
||||
if attestation_slot > state.slot {
|
||||
Ok(AttestationProcessingOutcome::AttestsToFutureState {
|
||||
state: state.slot,
|
||||
attestation: attestation_slot,
|
||||
})
|
||||
} else {
|
||||
self.process_attestation_for_state_and_block(
|
||||
attestation,
|
||||
&state,
|
||||
&attestation_head_block,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// Drop any attestation where we have not processed `attestation.data.beacon_block_root`.
|
||||
//
|
||||
// This is likely overly restrictive, we could store the attestation for later
|
||||
// processing.
|
||||
warn!(
|
||||
self.log,
|
||||
"Dropped attestation for unknown block";
|
||||
"block" => format!("{}", attestation.data.beacon_block_root)
|
||||
);
|
||||
Ok(AttestationProcessingOutcome::UnknownHeadBlock {
|
||||
beacon_block_root: attestation.data.beacon_block_root,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies the `attestation` against the `state` to which it is attesting.
|
||||
///
|
||||
/// Updates fork choice with any new latest messages, but _does not_ find or update the head.
|
||||
///
|
||||
/// ## Notes
|
||||
///
|
||||
/// The given `state` must fulfil one of the following conditions:
|
||||
///
|
||||
/// - `state` corresponds to the `block.state_root` identified by
|
||||
/// `attestation.data.beacon_block_root`. (Viz., `attestation` was created using `state`).
|
||||
/// - `state.slot` is in the same epoch as `data.target.epoch` and
|
||||
/// `attestation.data.beacon_block_root` is in the history of `state`.
|
||||
///
|
||||
/// Additionally, `attestation.data.beacon_block_root` **must** be available to read in
|
||||
/// `self.store` _and_ be the root of the given `block`.
|
||||
///
|
||||
/// If the given conditions are not fulfilled, the function may error or provide a false
|
||||
/// negative (indicating that a given `attestation` is invalid when it is was validly formed).
|
||||
fn process_attestation_for_state_and_block(
|
||||
&self,
|
||||
attestation: Attestation<T::EthSpec>,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
block: &BeaconBlock<T::EthSpec>,
|
||||
) -> Result<AttestationProcessingOutcome, Error> {
|
||||
self.metrics.attestation_processing_requests.inc();
|
||||
let timer = self.metrics.attestation_processing_times.start_timer();
|
||||
|
||||
let result = self
|
||||
.op_pool
|
||||
.insert_attestation(attestation, &*self.state.read(), &self.spec);
|
||||
// Find the highest between:
|
||||
//
|
||||
// - The highest valid finalized epoch we've ever seen (i.e., the head).
|
||||
// - The finalized epoch that this attestation was created against.
|
||||
let finalized_epoch = std::cmp::max(
|
||||
self.head().beacon_state.finalized_checkpoint.epoch,
|
||||
state.finalized_checkpoint.epoch,
|
||||
);
|
||||
|
||||
let result = if block.slot <= finalized_epoch.start_slot(T::EthSpec::slots_per_epoch()) {
|
||||
// Ignore any attestation where the slot of `data.beacon_block_root` is equal to or
|
||||
// prior to the finalized epoch.
|
||||
//
|
||||
// For any valid attestation if the `beacon_block_root` is prior to finalization, then
|
||||
// all other parameters (source, target, etc) must all be prior to finalization and
|
||||
// therefore no longer interesting.
|
||||
Ok(AttestationProcessingOutcome::FinalizedSlot {
|
||||
attestation: block.slot.epoch(T::EthSpec::slots_per_epoch()),
|
||||
finalized: finalized_epoch,
|
||||
})
|
||||
} else if let Err(e) =
|
||||
verify_attestation_for_state(state, &attestation, &self.spec, VerifySignatures::True)
|
||||
{
|
||||
warn!(
|
||||
self.log,
|
||||
"Invalid attestation";
|
||||
"state_epoch" => state.current_epoch(),
|
||||
"error" => format!("{:?}", e),
|
||||
);
|
||||
|
||||
Ok(AttestationProcessingOutcome::Invalid(e))
|
||||
} else {
|
||||
// Provide the attestation to fork choice, updating the validator latest messages but
|
||||
// _without_ finding and updating the head.
|
||||
self.fork_choice
|
||||
.process_attestation(&state, &attestation, block)?;
|
||||
|
||||
// Provide the valid attestation to op pool, which may choose to retain the
|
||||
// attestation for inclusion in a future block.
|
||||
self.op_pool
|
||||
.insert_attestation(attestation, state, &self.spec)?;
|
||||
|
||||
// Update the metrics.
|
||||
self.metrics.attestation_processing_successes.inc();
|
||||
|
||||
Ok(AttestationProcessingOutcome::Processed)
|
||||
};
|
||||
|
||||
timer.observe_duration();
|
||||
|
||||
if result.is_ok() {
|
||||
self.metrics.attestation_processing_successes.inc();
|
||||
}
|
||||
|
||||
// TODO: process attestation. Please consider:
|
||||
//
|
||||
// - Because a block was not added to the op pool does not mean it's invalid (it might
|
||||
// just be old).
|
||||
// - The attestation should be rejected if we don't know the block (ideally it should be
|
||||
// queued, but this may be overkill).
|
||||
// - The attestation _must_ be validated against it's state before being added to fork
|
||||
// choice.
|
||||
// - You can avoid verifying some attestations by first checking if they're a latest
|
||||
// message. This would involve expanding the `LmdGhost` API.
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
@@ -612,7 +804,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
return Ok(BlockProcessingOutcome::GenesisBlock);
|
||||
}
|
||||
|
||||
let block_root = block.block_header().canonical_root();
|
||||
let block_root = block.canonical_root();
|
||||
|
||||
if block_root == self.genesis_block_root {
|
||||
return Ok(BlockProcessingOutcome::GenesisBlock);
|
||||
@@ -658,6 +850,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
per_slot_processing(&mut state, &self.spec)?;
|
||||
}
|
||||
|
||||
state.build_committee_cache(RelativeEpoch::Previous, &self.spec)?;
|
||||
state.build_committee_cache(RelativeEpoch::Current, &self.spec)?;
|
||||
|
||||
// Apply the received block to its parent state (which has been transitioned into this
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
use crate::fork_choice::Error as ForkChoiceError;
|
||||
use crate::metrics::Error as MetricsError;
|
||||
use state_processing::per_block_processing::errors::{
|
||||
AttestationValidationError, IndexedAttestationValidationError,
|
||||
};
|
||||
use state_processing::BlockProcessingError;
|
||||
use state_processing::SlotProcessingError;
|
||||
use types::*;
|
||||
@@ -23,6 +26,7 @@ pub enum BeaconChainError {
|
||||
previous_epoch: Epoch,
|
||||
new_epoch: Epoch,
|
||||
},
|
||||
UnableToFindTargetRoot(Slot),
|
||||
BeaconStateError(BeaconStateError),
|
||||
DBInconsistent(String),
|
||||
DBError(store::Error),
|
||||
@@ -31,6 +35,11 @@ pub enum BeaconChainError {
|
||||
MissingBeaconState(Hash256),
|
||||
SlotProcessingError(SlotProcessingError),
|
||||
MetricsError(String),
|
||||
NoStateForAttestation {
|
||||
beacon_block_root: Hash256,
|
||||
},
|
||||
AttestationValidationError(AttestationValidationError),
|
||||
IndexedAttestationValidationError(IndexedAttestationValidationError),
|
||||
}
|
||||
|
||||
easy_from_to!(SlotProcessingError, BeaconChainError);
|
||||
@@ -53,3 +62,5 @@ pub enum BlockProductionError {
|
||||
easy_from_to!(BlockProcessingError, BlockProductionError);
|
||||
easy_from_to!(BeaconStateError, BlockProductionError);
|
||||
easy_from_to!(SlotProcessingError, BlockProductionError);
|
||||
easy_from_to!(AttestationValidationError, BeaconChainError);
|
||||
easy_from_to!(IndexedAttestationValidationError, BeaconChainError);
|
||||
|
||||
@@ -3,7 +3,9 @@ use lmd_ghost::LmdGhost;
|
||||
use state_processing::common::get_attesting_indices;
|
||||
use std::sync::Arc;
|
||||
use store::{Error as StoreError, Store};
|
||||
use types::{Attestation, BeaconBlock, BeaconState, BeaconStateError, Epoch, EthSpec, Hash256};
|
||||
use types::{
|
||||
Attestation, BeaconBlock, BeaconState, BeaconStateError, Epoch, EthSpec, Hash256, Slot,
|
||||
};
|
||||
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
@@ -17,8 +19,8 @@ pub enum Error {
|
||||
}
|
||||
|
||||
pub struct ForkChoice<T: BeaconChainTypes> {
|
||||
backend: T::LmdGhost,
|
||||
store: Arc<T::Store>,
|
||||
backend: T::LmdGhost,
|
||||
/// Used for resolving the `0x00..00` alias back to genesis.
|
||||
///
|
||||
/// Does not necessarily need to be the _actual_ genesis, it suffices to be the finalized root
|
||||
@@ -117,18 +119,34 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
//
|
||||
// https://github.com/ethereum/eth2.0-specs/blob/v0.7.0/specs/core/0_fork-choice.md
|
||||
for attestation in &block.body.attestations {
|
||||
self.process_attestation_from_block(state, attestation)?;
|
||||
// If the `data.beacon_block_root` block is not known to us, simply ignore the latest
|
||||
// vote.
|
||||
if let Some(block) = self
|
||||
.store
|
||||
.get::<BeaconBlock<T::EthSpec>>(&attestation.data.beacon_block_root)?
|
||||
{
|
||||
self.process_attestation(state, attestation, &block)?;
|
||||
}
|
||||
}
|
||||
|
||||
// This does not apply a vote to the block, it just makes fork choice aware of the block so
|
||||
// it can still be identified as the head even if it doesn't have any votes.
|
||||
//
|
||||
// A case where a block without any votes can be the head is where it is the only child of
|
||||
// a block that has the majority of votes applied to it.
|
||||
self.backend.process_block(block, block_root)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_attestation_from_block(
|
||||
/// Process an attestation which references `block` in `attestation.data.beacon_block_root`.
|
||||
///
|
||||
/// Assumes the attestation is valid.
|
||||
pub fn process_attestation(
|
||||
&self,
|
||||
state: &BeaconState<T::EthSpec>,
|
||||
attestation: &Attestation<T::EthSpec>,
|
||||
block: &BeaconBlock<T::EthSpec>,
|
||||
) -> Result<()> {
|
||||
let block_hash = attestation.data.beacon_block_root;
|
||||
|
||||
@@ -147,26 +165,26 @@ impl<T: BeaconChainTypes> ForkChoice<T> {
|
||||
// to genesis just by being present in the chain.
|
||||
//
|
||||
// Additionally, don't add any block hash to fork choice unless we have imported the block.
|
||||
if block_hash != Hash256::zero()
|
||||
&& self
|
||||
.store
|
||||
.exists::<BeaconBlock<T::EthSpec>>(&block_hash)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
if block_hash != Hash256::zero() {
|
||||
let validator_indices =
|
||||
get_attesting_indices(state, &attestation.data, &attestation.aggregation_bits)?;
|
||||
|
||||
let block_slot = state.get_attestation_data_slot(&attestation.data)?;
|
||||
|
||||
for validator_index in validator_indices {
|
||||
self.backend
|
||||
.process_attestation(validator_index, block_hash, block_slot)?;
|
||||
.process_attestation(validator_index, block_hash, block.slot)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the latest message for a given validator, if any.
|
||||
///
|
||||
/// Returns `(block_root, block_slot)`.
|
||||
pub fn latest_message(&self, validator_index: usize) -> Option<(Hash256, Slot)> {
|
||||
self.backend.latest_message(validator_index)
|
||||
}
|
||||
|
||||
/// Inform the fork choice that the given block (and corresponding root) have been finalized so
|
||||
/// it may prune it's storage.
|
||||
///
|
||||
|
||||
@@ -7,7 +7,9 @@ mod metrics;
|
||||
mod persisted_beacon_chain;
|
||||
pub mod test_utils;
|
||||
|
||||
pub use self::beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
|
||||
pub use self::beacon_chain::{
|
||||
AttestationProcessingOutcome, BeaconChain, BeaconChainTypes, BlockProcessingOutcome,
|
||||
};
|
||||
pub use self::checkpoint::CheckPoint;
|
||||
pub use self::errors::{BeaconChainError, BlockProductionError};
|
||||
pub use lmd_ghost;
|
||||
|
||||
@@ -84,14 +84,30 @@ where
|
||||
{
|
||||
/// Instantiate a new harness with `validator_count` initial validators.
|
||||
pub fn new(validator_count: usize) -> Self {
|
||||
let state_builder = TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(
|
||||
validator_count,
|
||||
&E::default_spec(),
|
||||
);
|
||||
let (genesis_state, keypairs) = state_builder.build();
|
||||
|
||||
Self::from_state_and_keypairs(genesis_state, keypairs)
|
||||
}
|
||||
|
||||
/// Instantiate a new harness with an initial validator for each key supplied.
|
||||
pub fn from_keypairs(keypairs: Vec<Keypair>) -> Self {
|
||||
let state_builder = TestingBeaconStateBuilder::from_keypairs(keypairs, &E::default_spec());
|
||||
let (genesis_state, keypairs) = state_builder.build();
|
||||
|
||||
Self::from_state_and_keypairs(genesis_state, keypairs)
|
||||
}
|
||||
|
||||
/// Instantiate a new harness with the given genesis state and a keypair for each of the
|
||||
/// initial validators in the given state.
|
||||
pub fn from_state_and_keypairs(genesis_state: BeaconState<E>, keypairs: Vec<Keypair>) -> Self {
|
||||
let spec = E::default_spec();
|
||||
|
||||
let store = Arc::new(MemoryStore::open());
|
||||
|
||||
let state_builder =
|
||||
TestingBeaconStateBuilder::from_default_keypairs_file_if_exists(validator_count, &spec);
|
||||
let (genesis_state, keypairs) = state_builder.build();
|
||||
|
||||
let mut genesis_block = BeaconBlock::empty(&spec);
|
||||
genesis_block.state_root = Hash256::from_slice(&genesis_state.tree_hash_root());
|
||||
|
||||
@@ -178,12 +194,7 @@ where
|
||||
if let BlockProcessingOutcome::Processed { block_root } = outcome {
|
||||
head_block_root = Some(block_root);
|
||||
|
||||
self.add_attestations_to_op_pool(
|
||||
&attestation_strategy,
|
||||
&new_state,
|
||||
block_root,
|
||||
slot,
|
||||
);
|
||||
self.add_free_attestations(&attestation_strategy, &new_state, block_root, slot);
|
||||
} else {
|
||||
panic!("block should be successfully processed: {:?}", outcome);
|
||||
}
|
||||
@@ -198,7 +209,7 @@ where
|
||||
fn get_state_at_slot(&self, state_slot: Slot) -> BeaconState<E> {
|
||||
let state_root = self
|
||||
.chain
|
||||
.rev_iter_state_roots(self.chain.head().beacon_state.slot - 1)
|
||||
.rev_iter_state_roots()
|
||||
.find(|(_hash, slot)| *slot == state_slot)
|
||||
.map(|(hash, _slot)| hash)
|
||||
.expect("could not find state root");
|
||||
@@ -263,16 +274,38 @@ where
|
||||
(block, state)
|
||||
}
|
||||
|
||||
/// Adds attestations to the `BeaconChain` operations pool to be included in future blocks.
|
||||
/// Adds attestations to the `BeaconChain` operations pool and fork choice.
|
||||
///
|
||||
/// The `attestation_strategy` dictates which validators should attest.
|
||||
fn add_attestations_to_op_pool(
|
||||
fn add_free_attestations(
|
||||
&self,
|
||||
attestation_strategy: &AttestationStrategy,
|
||||
state: &BeaconState<E>,
|
||||
head_block_root: Hash256,
|
||||
head_block_slot: Slot,
|
||||
) {
|
||||
self.get_free_attestations(
|
||||
attestation_strategy,
|
||||
state,
|
||||
head_block_root,
|
||||
head_block_slot,
|
||||
)
|
||||
.into_iter()
|
||||
.for_each(|attestation| {
|
||||
self.chain
|
||||
.process_attestation(attestation)
|
||||
.expect("should process attestation");
|
||||
});
|
||||
}
|
||||
|
||||
/// Generates a `Vec<Attestation>` for some attestation strategy and head_block.
|
||||
pub fn get_free_attestations(
|
||||
&self,
|
||||
attestation_strategy: &AttestationStrategy,
|
||||
state: &BeaconState<E>,
|
||||
head_block_root: Hash256,
|
||||
head_block_slot: Slot,
|
||||
) -> Vec<Attestation<E>> {
|
||||
let spec = &self.spec;
|
||||
let fork = &state.fork;
|
||||
|
||||
@@ -281,6 +314,8 @@ where
|
||||
AttestationStrategy::SomeValidators(vec) => vec.clone(),
|
||||
};
|
||||
|
||||
let mut vec = vec![];
|
||||
|
||||
state
|
||||
.get_crosslink_committees_at_slot(state.slot)
|
||||
.expect("should get committees")
|
||||
@@ -326,19 +361,17 @@ where
|
||||
agg_sig
|
||||
};
|
||||
|
||||
let attestation = Attestation {
|
||||
vec.push(Attestation {
|
||||
aggregation_bits,
|
||||
data,
|
||||
custody_bits,
|
||||
signature,
|
||||
};
|
||||
|
||||
self.chain
|
||||
.process_attestation(attestation)
|
||||
.expect("should process attestation");
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
vec
|
||||
}
|
||||
|
||||
/// Creates two forks:
|
||||
|
||||
Reference in New Issue
Block a user