mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-20 22:38:34 +00:00
Merge branch 'unstable' into dvt
This commit is contained in:
@@ -8,10 +8,11 @@ edition = { workspace = true }
|
||||
[dependencies]
|
||||
ethereum_ssz = { workspace = true }
|
||||
ethereum_ssz_derive = { workspace = true }
|
||||
logging = { workspace = true }
|
||||
metrics = { workspace = true }
|
||||
proto_array = { workspace = true }
|
||||
slog = { workspace = true }
|
||||
state_processing = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
types = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::metrics::{self, scrape_for_metrics};
|
||||
use crate::{ForkChoiceStore, InvalidationOperation};
|
||||
use logging::crit;
|
||||
use proto_array::{
|
||||
Block as ProtoBlock, DisallowedReOrgOffsets, ExecutionStatus, ProposerHeadError,
|
||||
ProposerHeadInfo, ProtoArrayForkChoice, ReOrgThreshold,
|
||||
};
|
||||
use slog::{crit, debug, warn, Logger};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use state_processing::{
|
||||
per_block_processing::errors::AttesterSlashingValidationError, per_epoch_processing,
|
||||
@@ -13,6 +13,7 @@ use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
use std::marker::PhantomData;
|
||||
use std::time::Duration;
|
||||
use tracing::{debug, warn};
|
||||
use types::{
|
||||
consts::bellatrix::INTERVALS_PER_SLOT, AbstractExecPayload, AttestationShufflingId,
|
||||
AttesterSlashingRef, BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, Checkpoint,
|
||||
@@ -1255,6 +1256,11 @@ where
|
||||
.is_finalized_checkpoint_or_descendant::<E>(block_root)
|
||||
}
|
||||
|
||||
pub fn is_descendant(&self, ancestor_root: Hash256, descendant_root: Hash256) -> bool {
|
||||
self.proto_array
|
||||
.is_descendant(ancestor_root, descendant_root)
|
||||
}
|
||||
|
||||
/// Returns `Ok(true)` if `block_root` has been imported optimistically or deemed invalid.
|
||||
///
|
||||
/// Returns `Ok(false)` if `block_root`'s execution payload has been elected as fully VALID, if
|
||||
@@ -1365,17 +1371,14 @@ where
|
||||
persisted: &PersistedForkChoice,
|
||||
reset_payload_statuses: ResetPayloadStatuses,
|
||||
spec: &ChainSpec,
|
||||
log: &Logger,
|
||||
) -> Result<ProtoArrayForkChoice, Error<T::Error>> {
|
||||
let mut proto_array = ProtoArrayForkChoice::from_bytes(&persisted.proto_array_bytes)
|
||||
.map_err(Error::InvalidProtoArrayBytes)?;
|
||||
let contains_invalid_payloads = proto_array.contains_invalid_payloads();
|
||||
|
||||
debug!(
|
||||
log,
|
||||
"Restoring fork choice from persisted";
|
||||
"reset_payload_statuses" => ?reset_payload_statuses,
|
||||
"contains_invalid_payloads" => contains_invalid_payloads,
|
||||
?reset_payload_statuses,
|
||||
contains_invalid_payloads, "Restoring fork choice from persisted"
|
||||
);
|
||||
|
||||
// Exit early if there are no "invalid" payloads, if requested.
|
||||
@@ -1394,18 +1397,14 @@ where
|
||||
// back to a proto-array which does not have the reset applied. This indicates a
|
||||
// significant error in Lighthouse and warrants detailed investigation.
|
||||
crit!(
|
||||
log,
|
||||
"Failed to reset payload statuses";
|
||||
"error" => e,
|
||||
"info" => "please report this error",
|
||||
error = ?e,
|
||||
info = "please report this error",
|
||||
"Failed to reset payload statuses"
|
||||
);
|
||||
ProtoArrayForkChoice::from_bytes(&persisted.proto_array_bytes)
|
||||
.map_err(Error::InvalidProtoArrayBytes)
|
||||
} else {
|
||||
debug!(
|
||||
log,
|
||||
"Successfully reset all payload statuses";
|
||||
);
|
||||
debug!("Successfully reset all payload statuses");
|
||||
Ok(proto_array)
|
||||
}
|
||||
}
|
||||
@@ -1417,10 +1416,9 @@ where
|
||||
reset_payload_statuses: ResetPayloadStatuses,
|
||||
fc_store: T,
|
||||
spec: &ChainSpec,
|
||||
log: &Logger,
|
||||
) -> Result<Self, Error<T::Error>> {
|
||||
let proto_array =
|
||||
Self::proto_array_from_persisted(&persisted, reset_payload_statuses, spec, log)?;
|
||||
Self::proto_array_from_persisted(&persisted, reset_payload_statuses, spec)?;
|
||||
|
||||
let current_slot = fc_store.get_current_slot();
|
||||
|
||||
@@ -1444,10 +1442,9 @@ where
|
||||
// an optimistic status so that we can have a head to start from.
|
||||
if let Err(e) = fork_choice.get_head(current_slot, spec) {
|
||||
warn!(
|
||||
log,
|
||||
"Could not find head on persisted FC";
|
||||
"info" => "resetting all payload statuses and retrying",
|
||||
"error" => ?e
|
||||
info = "resetting all payload statuses and retrying",
|
||||
error = ?e,
|
||||
"Could not find head on persisted FC"
|
||||
);
|
||||
// Although we may have already made this call whilst loading `proto_array`, try it
|
||||
// again since we may have mutated the `proto_array` during `get_head` and therefore may
|
||||
|
||||
@@ -25,6 +25,9 @@ pub type E = MainnetEthSpec;
|
||||
|
||||
pub const VALIDATOR_COUNT: usize = 64;
|
||||
|
||||
// When set to true, cache any states fetched from the db.
|
||||
pub const CACHE_STATE_IN_TESTS: bool = true;
|
||||
|
||||
/// Defines some delay between when an attestation is created and when it is mutated.
|
||||
pub enum MutationDelay {
|
||||
/// No delay between creation and mutation.
|
||||
@@ -373,7 +376,7 @@ impl ForkChoiceTest {
|
||||
let state = harness
|
||||
.chain
|
||||
.store
|
||||
.get_state(&state_root, None)
|
||||
.get_state(&state_root, None, CACHE_STATE_IN_TESTS)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let balances = state
|
||||
|
||||
@@ -34,6 +34,8 @@ pub enum MerkleTree {
|
||||
pub enum MerkleTreeError {
|
||||
// Trying to push in a leaf
|
||||
LeafReached,
|
||||
// Trying to generate a proof for a non-leaf node
|
||||
NonLeafProof,
|
||||
// No more space in the MerkleTree
|
||||
MerkleTreeFull,
|
||||
// MerkleTree is invalid
|
||||
@@ -313,8 +315,17 @@ impl MerkleTree {
|
||||
current_depth -= 1;
|
||||
}
|
||||
|
||||
debug_assert_eq!(proof.len(), depth);
|
||||
debug_assert!(current_node.is_leaf());
|
||||
if proof.len() != depth {
|
||||
// This should be unreachable regardless of how the method is called, because we push
|
||||
// one proof element for each layer of `depth`.
|
||||
return Err(MerkleTreeError::PleaseNotifyTheDevs);
|
||||
}
|
||||
|
||||
// Generating a proof for a non-leaf node is invalid and indicates an error on the part of
|
||||
// the caller.
|
||||
if !current_node.is_leaf() {
|
||||
return Err(MerkleTreeError::NonLeafProof);
|
||||
}
|
||||
|
||||
// Put proof in bottom-up order.
|
||||
proof.reverse();
|
||||
|
||||
@@ -293,7 +293,6 @@ where
|
||||
)?);
|
||||
Ok(())
|
||||
})
|
||||
.map_err(Error::into)
|
||||
}
|
||||
|
||||
/// Includes all signatures in `self.block.body.voluntary_exits` for verification.
|
||||
|
||||
@@ -60,6 +60,7 @@ pub enum BlockProcessingError {
|
||||
SignatureSetError(SignatureSetError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
SszDecodeError(DecodeError),
|
||||
BitfieldError(ssz::BitfieldError),
|
||||
MerkleTreeError(MerkleTreeError),
|
||||
ArithError(ArithError),
|
||||
InconsistentBlockFork(InconsistentFork),
|
||||
@@ -153,6 +154,7 @@ impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
|
||||
BlockOperationError::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockProcessingError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockProcessingError::SszTypesError(e),
|
||||
BlockOperationError::BitfieldError(e) => BlockProcessingError::BitfieldError(e),
|
||||
BlockOperationError::ConsensusContext(e) => BlockProcessingError::ConsensusContext(e),
|
||||
BlockOperationError::ArithError(e) => BlockProcessingError::ArithError(e),
|
||||
}
|
||||
@@ -181,6 +183,7 @@ macro_rules! impl_into_block_processing_error_with_index {
|
||||
BlockOperationError::BeaconStateError(e) => BlockProcessingError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockProcessingError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockProcessingError::SszTypesError(e),
|
||||
BlockOperationError::BitfieldError(e) => BlockProcessingError::BitfieldError(e),
|
||||
BlockOperationError::ConsensusContext(e) => BlockProcessingError::ConsensusContext(e),
|
||||
BlockOperationError::ArithError(e) => BlockProcessingError::ArithError(e),
|
||||
}
|
||||
@@ -215,6 +218,7 @@ pub enum BlockOperationError<T> {
|
||||
BeaconStateError(BeaconStateError),
|
||||
SignatureSetError(SignatureSetError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
BitfieldError(ssz::BitfieldError),
|
||||
ConsensusContext(ContextError),
|
||||
ArithError(ArithError),
|
||||
}
|
||||
@@ -242,6 +246,12 @@ impl<T> From<ssz_types::Error> for BlockOperationError<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<ssz::BitfieldError> for BlockOperationError<T> {
|
||||
fn from(error: ssz::BitfieldError) -> Self {
|
||||
BlockOperationError::BitfieldError(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<ArithError> for BlockOperationError<T> {
|
||||
fn from(e: ArithError) -> Self {
|
||||
BlockOperationError::ArithError(e)
|
||||
@@ -367,6 +377,7 @@ impl From<BlockOperationError<IndexedAttestationInvalid>>
|
||||
BlockOperationError::BeaconStateError(e) => BlockOperationError::BeaconStateError(e),
|
||||
BlockOperationError::SignatureSetError(e) => BlockOperationError::SignatureSetError(e),
|
||||
BlockOperationError::SszTypesError(e) => BlockOperationError::SszTypesError(e),
|
||||
BlockOperationError::BitfieldError(e) => BlockOperationError::BitfieldError(e),
|
||||
BlockOperationError::ConsensusContext(e) => BlockOperationError::ConsensusContext(e),
|
||||
BlockOperationError::ArithError(e) => BlockOperationError::ArithError(e),
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ pub const VALIDATOR_COUNT: usize = 64;
|
||||
pub const EPOCH_OFFSET: u64 = 4;
|
||||
pub const NUM_ATTESTATIONS: u64 = 1;
|
||||
|
||||
// When set to true, cache any states fetched from the db.
|
||||
pub const CACHE_STATE_IN_TESTS: bool = true;
|
||||
|
||||
/// A cached set of keys.
|
||||
static KEYPAIRS: LazyLock<Vec<Keypair>> =
|
||||
LazyLock::new(|| generate_deterministic_keypairs(MAX_VALIDATOR_COUNT));
|
||||
@@ -1114,9 +1117,10 @@ async fn block_replayer_peeking_state_roots() {
|
||||
.get_blinded_block(&parent_block_root)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
// Cache the state to make CI go brr.
|
||||
let parent_state = harness
|
||||
.chain
|
||||
.get_state(&parent_block.state_root(), Some(parent_block.slot()))
|
||||
.get_state(&parent_block.state_root(), Some(parent_block.slot()), true)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
|
||||
@@ -19,9 +19,10 @@ pub enum EpochProcessingError {
|
||||
BeaconStateError(BeaconStateError),
|
||||
InclusionError(InclusionError),
|
||||
SszTypesError(ssz_types::Error),
|
||||
BitfieldError(ssz::BitfieldError),
|
||||
ArithError(safe_arith::ArithError),
|
||||
InconsistentStateFork(InconsistentFork),
|
||||
InvalidJustificationBit(ssz_types::Error),
|
||||
InvalidJustificationBit(ssz::BitfieldError),
|
||||
InvalidFlagIndex(usize),
|
||||
MilhouseError(milhouse::Error),
|
||||
EpochCache(EpochCacheError),
|
||||
@@ -49,6 +50,12 @@ impl From<ssz_types::Error> for EpochProcessingError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ssz::BitfieldError> for EpochProcessingError {
|
||||
fn from(e: ssz::BitfieldError) -> EpochProcessingError {
|
||||
EpochProcessingError::BitfieldError(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<safe_arith::ArithError> for EpochProcessingError {
|
||||
fn from(e: safe_arith::ArithError) -> EpochProcessingError {
|
||||
EpochProcessingError::ArithError(e)
|
||||
|
||||
@@ -175,6 +175,7 @@ pub fn process_epoch_single_pass<E: EthSpec>(
|
||||
|
||||
let mut earliest_exit_epoch = state.earliest_exit_epoch().ok();
|
||||
let mut exit_balance_to_consume = state.exit_balance_to_consume().ok();
|
||||
let validators_in_consolidations = get_validators_in_consolidations(state);
|
||||
|
||||
// Split the state into several disjoint mutable borrows.
|
||||
let (
|
||||
@@ -317,17 +318,26 @@ pub fn process_epoch_single_pass<E: EthSpec>(
|
||||
|
||||
// `process_effective_balance_updates`
|
||||
if conf.effective_balance_updates {
|
||||
process_single_effective_balance_update(
|
||||
validator_info.index,
|
||||
*balance,
|
||||
&mut validator,
|
||||
validator_info.current_epoch_participation,
|
||||
&mut next_epoch_cache,
|
||||
progressive_balances,
|
||||
effective_balances_ctxt,
|
||||
state_ctxt,
|
||||
spec,
|
||||
)?;
|
||||
if validators_in_consolidations.contains(&validator_info.index) {
|
||||
process_single_dummy_effective_balance_update(
|
||||
validator_info.index,
|
||||
&validator,
|
||||
&mut next_epoch_cache,
|
||||
state_ctxt,
|
||||
)?;
|
||||
} else {
|
||||
process_single_effective_balance_update(
|
||||
validator_info.index,
|
||||
*balance,
|
||||
&mut validator,
|
||||
validator_info.current_epoch_participation,
|
||||
&mut next_epoch_cache,
|
||||
progressive_balances,
|
||||
effective_balances_ctxt,
|
||||
state_ctxt,
|
||||
spec,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,6 +440,7 @@ pub fn process_epoch_single_pass<E: EthSpec>(
|
||||
if fork_name.electra_enabled() && conf.pending_consolidations {
|
||||
process_pending_consolidations(
|
||||
state,
|
||||
&validators_in_consolidations,
|
||||
&mut next_epoch_cache,
|
||||
effective_balances_ctxt,
|
||||
conf.effective_balance_updates,
|
||||
@@ -1026,12 +1037,38 @@ fn process_pending_deposits_for_validator(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Return the set of validators referenced by consolidations, either as source or target.
|
||||
///
|
||||
/// This function is blind to whether the consolidations are valid and capable of being processed,
|
||||
/// it just returns the set of all indices present in consolidations. This is *sufficient* to
|
||||
/// make consolidations play nicely with effective balance updates. The algorithm used is:
|
||||
///
|
||||
/// - In the single pass: apply effective balance updates for all validators *not* referenced by
|
||||
/// consolidations.
|
||||
/// - Apply consolidations.
|
||||
/// - Apply effective balance updates for all validators previously skipped.
|
||||
///
|
||||
/// Prior to Electra, the empty set is returned.
|
||||
fn get_validators_in_consolidations<E: EthSpec>(state: &BeaconState<E>) -> BTreeSet<usize> {
|
||||
let mut referenced_validators = BTreeSet::new();
|
||||
|
||||
if let Ok(pending_consolidations) = state.pending_consolidations() {
|
||||
for pending_consolidation in pending_consolidations {
|
||||
referenced_validators.insert(pending_consolidation.source_index as usize);
|
||||
referenced_validators.insert(pending_consolidation.target_index as usize);
|
||||
}
|
||||
}
|
||||
|
||||
referenced_validators
|
||||
}
|
||||
|
||||
/// We process pending consolidations after all of single-pass epoch processing, and then patch up
|
||||
/// the effective balances for affected validators.
|
||||
///
|
||||
/// This is safe because processing consolidations does not depend on the `effective_balance`.
|
||||
fn process_pending_consolidations<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
validators_in_consolidations: &BTreeSet<usize>,
|
||||
next_epoch_cache: &mut PreEpochCache,
|
||||
effective_balances_ctxt: &EffectiveBalancesContext,
|
||||
perform_effective_balance_updates: bool,
|
||||
@@ -1042,8 +1079,6 @@ fn process_pending_consolidations<E: EthSpec>(
|
||||
let next_epoch = state.next_epoch()?;
|
||||
let pending_consolidations = state.pending_consolidations()?.clone();
|
||||
|
||||
let mut affected_validators = BTreeSet::new();
|
||||
|
||||
for pending_consolidation in &pending_consolidations {
|
||||
let source_index = pending_consolidation.source_index as usize;
|
||||
let target_index = pending_consolidation.target_index as usize;
|
||||
@@ -1069,9 +1104,6 @@ fn process_pending_consolidations<E: EthSpec>(
|
||||
decrease_balance(state, source_index, source_effective_balance)?;
|
||||
increase_balance(state, target_index, source_effective_balance)?;
|
||||
|
||||
affected_validators.insert(source_index);
|
||||
affected_validators.insert(target_index);
|
||||
|
||||
next_pending_consolidation.safe_add_assign(1)?;
|
||||
}
|
||||
|
||||
@@ -1087,7 +1119,7 @@ fn process_pending_consolidations<E: EthSpec>(
|
||||
// Re-process effective balance updates for validators affected by consolidations.
|
||||
let (validators, balances, _, current_epoch_participation, _, progressive_balances, _, _) =
|
||||
state.mutable_validator_fields()?;
|
||||
for validator_index in affected_validators {
|
||||
for &validator_index in validators_in_consolidations {
|
||||
let balance = *balances
|
||||
.get(validator_index)
|
||||
.ok_or(BeaconStateError::UnknownValidator(validator_index))?;
|
||||
@@ -1129,6 +1161,28 @@ impl EffectiveBalancesContext {
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is called for validators that do not have their effective balance updated as
|
||||
/// part of the single-pass loop. For these validators we compute their true effective balance
|
||||
/// update after processing consolidations. However, to maintain the invariants of the
|
||||
/// `PreEpochCache` we must register _some_ effective balance for them immediately.
|
||||
fn process_single_dummy_effective_balance_update(
|
||||
validator_index: usize,
|
||||
validator: &Cow<Validator>,
|
||||
next_epoch_cache: &mut PreEpochCache,
|
||||
state_ctxt: &StateContext,
|
||||
) -> Result<(), Error> {
|
||||
// Populate the effective balance cache with the current effective balance. This will be
|
||||
// overriden when `process_single_effective_balance_update` is called.
|
||||
let is_active_next_epoch = validator.is_active_at(state_ctxt.next_epoch);
|
||||
let temporary_effective_balance = validator.effective_balance;
|
||||
next_epoch_cache.update_effective_balance(
|
||||
validator_index,
|
||||
temporary_effective_balance,
|
||||
is_active_next_epoch,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This function abstracts over phase0 and Electra effective balance processing.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn process_single_effective_balance_update(
|
||||
|
||||
@@ -28,7 +28,6 @@ hex = { workspace = true }
|
||||
int_to_bytes = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
kzg = { workspace = true }
|
||||
log = { workspace = true }
|
||||
maplit = { workspace = true }
|
||||
merkle_proof = { workspace = true }
|
||||
metastruct = "0.1.0"
|
||||
@@ -39,18 +38,18 @@ rand_xorshift = "0.3.0"
|
||||
rayon = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
rpds = { workspace = true }
|
||||
rusqlite = { workspace = true }
|
||||
rusqlite = { workspace = true, optional = true }
|
||||
safe_arith = { workspace = true }
|
||||
serde = { workspace = true, features = ["rc"] }
|
||||
serde_json = { workspace = true }
|
||||
serde_yaml = { workspace = true }
|
||||
slog = { workspace = true }
|
||||
smallvec = { workspace = true }
|
||||
ssz_types = { workspace = true, features = ["arbitrary"] }
|
||||
superstruct = { workspace = true }
|
||||
swap_or_not_shuffle = { workspace = true, features = ["arbitrary"] }
|
||||
tempfile = { workspace = true }
|
||||
test_random_derive = { path = "../../common/test_random_derive" }
|
||||
tracing = { workspace = true }
|
||||
tree_hash = { workspace = true }
|
||||
tree_hash_derive = { workspace = true }
|
||||
|
||||
@@ -65,7 +64,7 @@ tokio = { workspace = true }
|
||||
default = ["sqlite", "legacy-arith"]
|
||||
# Allow saturating arithmetic on slots and epochs. Enabled by default, but deprecated.
|
||||
legacy-arith = []
|
||||
sqlite = []
|
||||
sqlite = ["dep:rusqlite"]
|
||||
# The `arbitrary-fuzz` feature is a no-op provided for backwards compatibility.
|
||||
# For simplicity `Arbitrary` is now derived regardless of the feature's presence.
|
||||
arbitrary-fuzz = []
|
||||
|
||||
@@ -18,6 +18,7 @@ use super::{
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
SszTypesError(ssz_types::Error),
|
||||
BitfieldError(ssz::BitfieldError),
|
||||
AlreadySigned(usize),
|
||||
IncorrectStateVariant,
|
||||
InvalidCommitteeLength,
|
||||
@@ -223,7 +224,7 @@ impl<E: EthSpec> Attestation<E> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_aggregation_bit(&self, index: usize) -> Result<bool, ssz_types::Error> {
|
||||
pub fn get_aggregation_bit(&self, index: usize) -> Result<bool, ssz::BitfieldError> {
|
||||
match self {
|
||||
Attestation::Base(att) => att.aggregation_bits.get(index),
|
||||
Attestation::Electra(att) => att.aggregation_bits.get(index),
|
||||
@@ -353,13 +354,13 @@ impl<E: EthSpec> AttestationElectra<E> {
|
||||
if self
|
||||
.aggregation_bits
|
||||
.get(committee_position)
|
||||
.map_err(Error::SszTypesError)?
|
||||
.map_err(Error::BitfieldError)?
|
||||
{
|
||||
Err(Error::AlreadySigned(committee_position))
|
||||
} else {
|
||||
self.aggregation_bits
|
||||
.set(committee_position, true)
|
||||
.map_err(Error::SszTypesError)?;
|
||||
.map_err(Error::BitfieldError)?;
|
||||
|
||||
self.signature.add_assign(signature);
|
||||
|
||||
@@ -427,13 +428,13 @@ impl<E: EthSpec> AttestationBase<E> {
|
||||
if self
|
||||
.aggregation_bits
|
||||
.get(committee_position)
|
||||
.map_err(Error::SszTypesError)?
|
||||
.map_err(Error::BitfieldError)?
|
||||
{
|
||||
Err(Error::AlreadySigned(committee_position))
|
||||
} else {
|
||||
self.aggregation_bits
|
||||
.set(committee_position, true)
|
||||
.map_err(Error::SszTypesError)?;
|
||||
.map_err(Error::BitfieldError)?;
|
||||
|
||||
self.signature.add_assign(signature);
|
||||
|
||||
@@ -443,7 +444,7 @@ impl<E: EthSpec> AttestationBase<E> {
|
||||
|
||||
pub fn extend_aggregation_bits(
|
||||
&self,
|
||||
) -> Result<BitList<E::MaxValidatorsPerSlot>, ssz_types::Error> {
|
||||
) -> Result<BitList<E::MaxValidatorsPerSlot>, ssz::BitfieldError> {
|
||||
self.aggregation_bits.resize::<E::MaxValidatorsPerSlot>()
|
||||
}
|
||||
}
|
||||
@@ -600,12 +601,12 @@ mod tests {
|
||||
let attestation_data = size_of::<AttestationData>();
|
||||
let signature = size_of::<AggregateSignature>();
|
||||
|
||||
assert_eq!(aggregation_bits, 56);
|
||||
assert_eq!(aggregation_bits, 152);
|
||||
assert_eq!(attestation_data, 128);
|
||||
assert_eq!(signature, 288 + 16);
|
||||
|
||||
let attestation_expected = aggregation_bits + attestation_data + signature;
|
||||
assert_eq!(attestation_expected, 488);
|
||||
assert_eq!(attestation_expected, 584);
|
||||
assert_eq!(
|
||||
size_of::<AttestationBase<MainnetEthSpec>>(),
|
||||
attestation_expected
|
||||
@@ -623,13 +624,13 @@ mod tests {
|
||||
size_of::<BitList<<MainnetEthSpec as EthSpec>::MaxCommitteesPerSlot>>();
|
||||
let signature = size_of::<AggregateSignature>();
|
||||
|
||||
assert_eq!(aggregation_bits, 56);
|
||||
assert_eq!(committee_bits, 56);
|
||||
assert_eq!(aggregation_bits, 152);
|
||||
assert_eq!(committee_bits, 152);
|
||||
assert_eq!(attestation_data, 128);
|
||||
assert_eq!(signature, 288 + 16);
|
||||
|
||||
let attestation_expected = aggregation_bits + committee_bits + attestation_data + signature;
|
||||
assert_eq!(attestation_expected, 544);
|
||||
assert_eq!(attestation_expected, 736);
|
||||
assert_eq!(
|
||||
size_of::<AttestationElectra<MainnetEthSpec>>(),
|
||||
attestation_expected
|
||||
|
||||
@@ -277,9 +277,9 @@ impl<'a, E: EthSpec, Payload: AbstractExecPayload<E>> BeaconBlockBodyRef<'a, E,
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/deneb/beacon-chain.md#beaconblockbody
|
||||
generalized_index
|
||||
.checked_sub(NUM_BEACON_BLOCK_BODY_HASH_TREE_ROOT_LEAVES)
|
||||
.ok_or(Error::IndexNotSupported(generalized_index))?
|
||||
.ok_or(Error::GeneralizedIndexNotSupported(generalized_index))?
|
||||
}
|
||||
_ => return Err(Error::IndexNotSupported(generalized_index)),
|
||||
_ => return Err(Error::GeneralizedIndexNotSupported(generalized_index)),
|
||||
};
|
||||
|
||||
let leaves = self.body_merkle_leaves();
|
||||
@@ -971,6 +971,7 @@ impl<E: EthSpec> From<BeaconBlockBody<E, FullPayload<E>>>
|
||||
Option<ExecutionPayload<E>>,
|
||||
)
|
||||
{
|
||||
#[allow(clippy::useless_conversion)] // Not a useless conversion
|
||||
fn from(body: BeaconBlockBody<E, FullPayload<E>>) -> Self {
|
||||
map_beacon_block_body!(body, |inner, cons| {
|
||||
let (block, payload) = inner.into();
|
||||
|
||||
@@ -157,6 +157,7 @@ pub enum Error {
|
||||
current_fork: ForkName,
|
||||
},
|
||||
TotalActiveBalanceDiffUninitialized,
|
||||
GeneralizedIndexNotSupported(usize),
|
||||
IndexNotSupported(usize),
|
||||
InvalidFlagIndex(usize),
|
||||
MerkleTreeError(merkle_proof::MerkleTreeError),
|
||||
@@ -2580,11 +2581,12 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
// for the internal nodes. Result should be 22 or 23, the field offset of the committee
|
||||
// in the `BeaconState`:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate
|
||||
let field_index = if self.fork_name_unchecked().electra_enabled() {
|
||||
let field_gindex = if self.fork_name_unchecked().electra_enabled() {
|
||||
light_client_update::CURRENT_SYNC_COMMITTEE_INDEX_ELECTRA
|
||||
} else {
|
||||
light_client_update::CURRENT_SYNC_COMMITTEE_INDEX
|
||||
};
|
||||
let field_index = field_gindex.safe_sub(self.num_fields_pow2())?;
|
||||
let leaves = self.get_beacon_state_leaves();
|
||||
self.generate_proof(field_index, &leaves)
|
||||
}
|
||||
@@ -2594,11 +2596,12 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
// for the internal nodes. Result should be 22 or 23, the field offset of the committee
|
||||
// in the `BeaconState`:
|
||||
// https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#beaconstate
|
||||
let field_index = if self.fork_name_unchecked().electra_enabled() {
|
||||
let field_gindex = if self.fork_name_unchecked().electra_enabled() {
|
||||
light_client_update::NEXT_SYNC_COMMITTEE_INDEX_ELECTRA
|
||||
} else {
|
||||
light_client_update::NEXT_SYNC_COMMITTEE_INDEX
|
||||
};
|
||||
let field_index = field_gindex.safe_sub(self.num_fields_pow2())?;
|
||||
let leaves = self.get_beacon_state_leaves();
|
||||
self.generate_proof(field_index, &leaves)
|
||||
}
|
||||
@@ -2606,17 +2609,24 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
pub fn compute_finalized_root_proof(&self) -> Result<Vec<Hash256>, Error> {
|
||||
// Finalized root is the right child of `finalized_checkpoint`, divide by two to get
|
||||
// the generalized index of `state.finalized_checkpoint`.
|
||||
let field_index = if self.fork_name_unchecked().electra_enabled() {
|
||||
// Index should be 169/2 - 64 = 20 which matches the position
|
||||
// of `finalized_checkpoint` in `BeaconState`
|
||||
let checkpoint_root_gindex = if self.fork_name_unchecked().electra_enabled() {
|
||||
light_client_update::FINALIZED_ROOT_INDEX_ELECTRA
|
||||
} else {
|
||||
// Index should be 105/2 - 32 = 20 which matches the position
|
||||
// of `finalized_checkpoint` in `BeaconState`
|
||||
light_client_update::FINALIZED_ROOT_INDEX
|
||||
};
|
||||
let checkpoint_gindex = checkpoint_root_gindex / 2;
|
||||
|
||||
// Convert gindex to index by subtracting 2**depth (gindex = 2**depth + index).
|
||||
//
|
||||
// After Electra, the index should be 169/2 - 64 = 20 which matches the position
|
||||
// of `finalized_checkpoint` in `BeaconState`.
|
||||
//
|
||||
// Prior to Electra, the index should be 105/2 - 32 = 20 which matches the position
|
||||
// of `finalized_checkpoint` in `BeaconState`.
|
||||
let checkpoint_index = checkpoint_gindex.safe_sub(self.num_fields_pow2())?;
|
||||
|
||||
let leaves = self.get_beacon_state_leaves();
|
||||
let mut proof = self.generate_proof(field_index, &leaves)?;
|
||||
let mut proof = self.generate_proof(checkpoint_index, &leaves)?;
|
||||
proof.insert(0, self.finalized_checkpoint().epoch.tree_hash_root());
|
||||
Ok(proof)
|
||||
}
|
||||
@@ -2626,6 +2636,10 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
field_index: usize,
|
||||
leaves: &[Hash256],
|
||||
) -> Result<Vec<Hash256>, Error> {
|
||||
if field_index >= leaves.len() {
|
||||
return Err(Error::IndexNotSupported(field_index));
|
||||
}
|
||||
|
||||
let depth = self.num_fields_pow2().ilog2() as usize;
|
||||
let tree = merkle_proof::MerkleTree::create(leaves, depth);
|
||||
let (_, proof) = tree.generate_proof(field_index, depth)?;
|
||||
|
||||
@@ -213,12 +213,16 @@ impl<E: EthSpec> LightClientUpdate<E> {
|
||||
.map_err(|_| Error::InconsistentFork)?
|
||||
{
|
||||
ForkName::Base => return Err(Error::AltairForkNotActive),
|
||||
ForkName::Altair | ForkName::Bellatrix => {
|
||||
fork_name @ ForkName::Altair | fork_name @ ForkName::Bellatrix => {
|
||||
let attested_header =
|
||||
LightClientHeaderAltair::block_to_light_client_header(attested_block)?;
|
||||
|
||||
let finalized_header = if let Some(finalized_block) = finalized_block {
|
||||
LightClientHeaderAltair::block_to_light_client_header(finalized_block)?
|
||||
if finalized_block.fork_name_unchecked() == fork_name {
|
||||
LightClientHeaderAltair::block_to_light_client_header(finalized_block)?
|
||||
} else {
|
||||
LightClientHeaderAltair::default()
|
||||
}
|
||||
} else {
|
||||
LightClientHeaderAltair::default()
|
||||
};
|
||||
@@ -233,12 +237,16 @@ impl<E: EthSpec> LightClientUpdate<E> {
|
||||
signature_slot: block_slot,
|
||||
})
|
||||
}
|
||||
ForkName::Capella => {
|
||||
fork_name @ ForkName::Capella => {
|
||||
let attested_header =
|
||||
LightClientHeaderCapella::block_to_light_client_header(attested_block)?;
|
||||
|
||||
let finalized_header = if let Some(finalized_block) = finalized_block {
|
||||
LightClientHeaderCapella::block_to_light_client_header(finalized_block)?
|
||||
if finalized_block.fork_name_unchecked() == fork_name {
|
||||
LightClientHeaderCapella::block_to_light_client_header(finalized_block)?
|
||||
} else {
|
||||
LightClientHeaderCapella::default()
|
||||
}
|
||||
} else {
|
||||
LightClientHeaderCapella::default()
|
||||
};
|
||||
@@ -253,12 +261,16 @@ impl<E: EthSpec> LightClientUpdate<E> {
|
||||
signature_slot: block_slot,
|
||||
})
|
||||
}
|
||||
ForkName::Deneb => {
|
||||
fork_name @ ForkName::Deneb => {
|
||||
let attested_header =
|
||||
LightClientHeaderDeneb::block_to_light_client_header(attested_block)?;
|
||||
|
||||
let finalized_header = if let Some(finalized_block) = finalized_block {
|
||||
LightClientHeaderDeneb::block_to_light_client_header(finalized_block)?
|
||||
if finalized_block.fork_name_unchecked() == fork_name {
|
||||
LightClientHeaderDeneb::block_to_light_client_header(finalized_block)?
|
||||
} else {
|
||||
LightClientHeaderDeneb::default()
|
||||
}
|
||||
} else {
|
||||
LightClientHeaderDeneb::default()
|
||||
};
|
||||
@@ -273,12 +285,16 @@ impl<E: EthSpec> LightClientUpdate<E> {
|
||||
signature_slot: block_slot,
|
||||
})
|
||||
}
|
||||
ForkName::Electra => {
|
||||
fork_name @ ForkName::Electra => {
|
||||
let attested_header =
|
||||
LightClientHeaderElectra::block_to_light_client_header(attested_block)?;
|
||||
|
||||
let finalized_header = if let Some(finalized_block) = finalized_block {
|
||||
LightClientHeaderElectra::block_to_light_client_header(finalized_block)?
|
||||
if finalized_block.fork_name_unchecked() == fork_name {
|
||||
LightClientHeaderElectra::block_to_light_client_header(finalized_block)?
|
||||
} else {
|
||||
LightClientHeaderElectra::default()
|
||||
}
|
||||
} else {
|
||||
LightClientHeaderElectra::default()
|
||||
};
|
||||
@@ -293,12 +309,16 @@ impl<E: EthSpec> LightClientUpdate<E> {
|
||||
signature_slot: block_slot,
|
||||
})
|
||||
}
|
||||
ForkName::Fulu => {
|
||||
fork_name @ ForkName::Fulu => {
|
||||
let attested_header =
|
||||
LightClientHeaderFulu::block_to_light_client_header(attested_block)?;
|
||||
|
||||
let finalized_header = if let Some(finalized_block) = finalized_block {
|
||||
LightClientHeaderFulu::block_to_light_client_header(finalized_block)?
|
||||
if finalized_block.fork_name_unchecked() == fork_name {
|
||||
LightClientHeaderFulu::block_to_light_client_header(finalized_block)?
|
||||
} else {
|
||||
LightClientHeaderFulu::default()
|
||||
}
|
||||
} else {
|
||||
LightClientHeaderFulu::default()
|
||||
};
|
||||
|
||||
@@ -134,13 +134,13 @@ impl<T: Decode> RuntimeVariableList<T> {
|
||||
)));
|
||||
}
|
||||
|
||||
bytes
|
||||
.chunks(<T as Decode>::ssz_fixed_len())
|
||||
.try_fold(Vec::with_capacity(num_items), |mut vec, chunk| {
|
||||
bytes.chunks(<T as Decode>::ssz_fixed_len()).try_fold(
|
||||
Vec::with_capacity(num_items),
|
||||
|mut vec, chunk| {
|
||||
vec.push(<T as Decode>::from_ssz_bytes(chunk)?);
|
||||
Ok(vec)
|
||||
})
|
||||
.map(Into::into)?
|
||||
},
|
||||
)?
|
||||
} else {
|
||||
ssz::decode_list_of_variable_length_items(bytes, Some(max_len))?
|
||||
};
|
||||
|
||||
@@ -227,17 +227,6 @@ macro_rules! impl_display {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl slog::Value for $type {
|
||||
fn serialize(
|
||||
&self,
|
||||
record: &slog::Record,
|
||||
key: slog::Key,
|
||||
serializer: &mut dyn slog::Serializer,
|
||||
) -> slog::Result {
|
||||
slog::Value::serialize(&self.0, record, key, serializer)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use tree_hash_derive::TreeHash;
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
SszTypesError(ssz_types::Error),
|
||||
BitfieldError(ssz::BitfieldError),
|
||||
ArithError(ArithError),
|
||||
}
|
||||
|
||||
@@ -68,7 +69,7 @@ impl<E: EthSpec> SyncAggregate<E> {
|
||||
sync_aggregate
|
||||
.sync_committee_bits
|
||||
.set(participant_index, true)
|
||||
.map_err(Error::SszTypesError)?;
|
||||
.map_err(Error::BitfieldError)?;
|
||||
}
|
||||
}
|
||||
sync_aggregate
|
||||
|
||||
@@ -9,6 +9,7 @@ use tree_hash_derive::TreeHash;
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Error {
|
||||
SszTypesError(ssz_types::Error),
|
||||
BitfieldError(ssz::BitfieldError),
|
||||
AlreadySigned(usize),
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ impl<E: EthSpec> SyncCommitteeContribution<E> {
|
||||
) -> Result<Self, Error> {
|
||||
let mut bits = BitVector::new();
|
||||
bits.set(validator_sync_committee_index, true)
|
||||
.map_err(Error::SszTypesError)?;
|
||||
.map_err(Error::BitfieldError)?;
|
||||
Ok(Self {
|
||||
slot: message.slot,
|
||||
beacon_block_root: message.beacon_block_root,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::*;
|
||||
use eth2_interop_keypairs::{keypair, keypairs_from_yaml_file};
|
||||
use log::debug;
|
||||
use rayon::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
use tracing::debug;
|
||||
|
||||
/// Generates `validator_count` keypairs where the secret key is derived solely from the index of
|
||||
/// the validator.
|
||||
|
||||
@@ -4,7 +4,7 @@ use ssz_derive::{Decode, Encode};
|
||||
use tree_hash_derive::TreeHash;
|
||||
|
||||
/// Validator registration, for use in interacting with servers implementing the builder API.
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone, Encode, Decode)]
|
||||
pub struct SignedValidatorRegistrationData {
|
||||
pub message: ValidatorRegistrationData,
|
||||
pub signature: Signature,
|
||||
|
||||
Reference in New Issue
Block a user