mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-05 05:44:30 +00:00
Spec v1.7.0-alpha.6 and Gloas genesis (#9190)
Co-Authored-By: Josh King <josh@sigmaprime.io> Co-Authored-By: Jimmy Chen <jchen.tc@gmail.com> Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
@@ -5023,11 +5023,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
.ok_or(Error::MissingExecutionPayloadEnvelope(parent_block_root))?;
|
||||
|
||||
let parent_bid = advanced_state.latest_execution_payload_bid()?.clone();
|
||||
|
||||
apply_parent_execution_payload(
|
||||
&mut advanced_state,
|
||||
&parent_bid,
|
||||
&envelope.message.execution_requests,
|
||||
&self.spec,
|
||||
)
|
||||
|
||||
@@ -623,11 +623,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// For trustless building, the builder will provide the envelope separately.
|
||||
if let Some(payload_data) = payload_data {
|
||||
let beacon_block_root = block.tree_hash_root();
|
||||
let parent_beacon_block_root = block.parent_root();
|
||||
let execution_payload_envelope = ExecutionPayloadEnvelope {
|
||||
payload: payload_data.payload,
|
||||
execution_requests: payload_data.execution_requests,
|
||||
builder_index: payload_data.builder_index,
|
||||
beacon_block_root,
|
||||
parent_beacon_block_root,
|
||||
};
|
||||
|
||||
let signed_envelope = SignedExecutionPayloadEnvelope {
|
||||
@@ -854,7 +856,6 @@ fn get_execution_payload_gloas<T: BeaconChainTypes>(
|
||||
let mut withdrawals_state = state.clone();
|
||||
apply_parent_execution_payload(
|
||||
&mut withdrawals_state,
|
||||
parent_bid,
|
||||
&envelope.message.execution_requests,
|
||||
spec,
|
||||
)?;
|
||||
|
||||
@@ -256,6 +256,7 @@ fn make_signed_preferences(
|
||||
validator_index,
|
||||
fee_recipient,
|
||||
gas_limit,
|
||||
..ProposerPreferences::default()
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
})
|
||||
|
||||
@@ -72,6 +72,7 @@ fn build_chain(
|
||||
execution_requests: Default::default(),
|
||||
builder_index: 0,
|
||||
beacon_block_root: block_root,
|
||||
parent_beacon_block_root: Hash256::ZERO,
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
})
|
||||
|
||||
@@ -339,6 +339,7 @@ mod tests {
|
||||
execution_requests: ExecutionRequests::default(),
|
||||
builder_index,
|
||||
beacon_block_root: Hash256::ZERO,
|
||||
parent_beacon_block_root: Hash256::ZERO,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
||||
Ok(NewPayloadRequest::Gloas(NewPayloadRequestGloas {
|
||||
execution_payload: &envelope.message.payload,
|
||||
versioned_hashes,
|
||||
parent_beacon_block_root: block.message().parent_root(),
|
||||
parent_beacon_block_root: envelope.message.parent_beacon_block_root,
|
||||
execution_requests: &envelope.message.execution_requests,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -105,6 +105,7 @@ mod tests {
|
||||
execution_requests: ExecutionRequests::default(),
|
||||
builder_index: 0,
|
||||
beacon_block_root: Hash256::ZERO,
|
||||
parent_beacon_block_root: Hash256::ZERO,
|
||||
},
|
||||
blobs: None,
|
||||
}
|
||||
|
||||
@@ -64,6 +64,7 @@ impl GossipVerifiedProposerPreferences {
|
||||
ctx: &GossipVerificationContext<'_, T>,
|
||||
) -> Result<Self, ProposerPreferencesError> {
|
||||
let proposal_slot = signed_preferences.message.proposal_slot;
|
||||
let checkpoint_root = signed_preferences.message.checkpoint_root;
|
||||
let validator_index = signed_preferences.message.validator_index;
|
||||
let cached_head = ctx.canonical_head.cached_head();
|
||||
let current_slot = ctx
|
||||
@@ -74,7 +75,7 @@ impl GossipVerifiedProposerPreferences {
|
||||
|
||||
if ctx
|
||||
.gossip_verified_proposer_preferences_cache
|
||||
.get_seen_validator(&proposal_slot, validator_index)
|
||||
.get_seen_validator(&proposal_slot, checkpoint_root, validator_index)
|
||||
{
|
||||
return Err(ProposerPreferencesError::AlreadySeen {
|
||||
validator_index,
|
||||
@@ -162,6 +163,7 @@ mod tests {
|
||||
|
||||
fn make_preferences(proposal_slot: Slot, validator_index: u64) -> ProposerPreferences {
|
||||
ProposerPreferences {
|
||||
checkpoint_root: types::Hash256::ZERO,
|
||||
proposal_slot,
|
||||
validator_index,
|
||||
fee_recipient: Address::ZERO,
|
||||
|
||||
@@ -5,11 +5,11 @@ use std::{
|
||||
|
||||
use crate::proposer_preferences_verification::gossip_verified_proposer_preferences::GossipVerifiedProposerPreferences;
|
||||
use parking_lot::RwLock;
|
||||
use types::{SignedProposerPreferences, Slot};
|
||||
use types::{Hash256, SignedProposerPreferences, Slot};
|
||||
|
||||
pub struct GossipVerifiedProposerPreferenceCache {
|
||||
preferences: RwLock<BTreeMap<Slot, GossipVerifiedProposerPreferences>>,
|
||||
seen: RwLock<BTreeMap<Slot, HashSet<u64>>>,
|
||||
seen: RwLock<BTreeMap<Slot, HashSet<(Hash256, u64)>>>,
|
||||
}
|
||||
|
||||
impl Default for GossipVerifiedProposerPreferenceCache {
|
||||
@@ -34,21 +34,27 @@ impl GossipVerifiedProposerPreferenceCache {
|
||||
self.preferences.write().insert(slot, preferences);
|
||||
}
|
||||
|
||||
pub fn get_seen_validator(&self, slot: &Slot, validator_index: u64) -> bool {
|
||||
pub fn get_seen_validator(
|
||||
&self,
|
||||
slot: &Slot,
|
||||
checkpoint_root: Hash256,
|
||||
validator_index: u64,
|
||||
) -> bool {
|
||||
self.seen
|
||||
.read()
|
||||
.get(slot)
|
||||
.is_some_and(|seen| seen.contains(&validator_index))
|
||||
.is_some_and(|seen| seen.contains(&(checkpoint_root, validator_index)))
|
||||
}
|
||||
|
||||
pub fn insert_seen_validator(&self, preferences: &GossipVerifiedProposerPreferences) {
|
||||
let slot = preferences.signed_preferences.message.proposal_slot;
|
||||
let checkpoint_root = preferences.signed_preferences.message.checkpoint_root;
|
||||
let validator_index = preferences.signed_preferences.message.validator_index;
|
||||
self.seen
|
||||
.write()
|
||||
.entry(slot)
|
||||
.or_default()
|
||||
.insert(validator_index);
|
||||
.insert((checkpoint_root, validator_index));
|
||||
}
|
||||
|
||||
pub fn prune(&self, current_slot: Slot) {
|
||||
@@ -77,6 +83,7 @@ mod tests {
|
||||
validator_index,
|
||||
fee_recipient: Address::ZERO,
|
||||
gas_limit: 30_000_000,
|
||||
..ProposerPreferences::default()
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
}),
|
||||
@@ -97,11 +104,11 @@ mod tests {
|
||||
|
||||
for slot in [1, 2, 3, 7] {
|
||||
assert!(cache.get_preferences(&Slot::new(slot)).is_none());
|
||||
assert!(!cache.get_seen_validator(&Slot::new(slot), slot));
|
||||
assert!(!cache.get_seen_validator(&Slot::new(slot), types::Hash256::ZERO, slot));
|
||||
}
|
||||
for slot in [8, 9, 10] {
|
||||
assert!(cache.get_preferences(&Slot::new(slot)).is_some());
|
||||
assert!(cache.get_seen_validator(&Slot::new(slot), slot));
|
||||
assert!(cache.get_seen_validator(&Slot::new(slot), types::Hash256::ZERO, slot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ fn make_signed_preferences(
|
||||
validator_index,
|
||||
fee_recipient: Address::ZERO,
|
||||
gas_limit: 30_000_000,
|
||||
..ProposerPreferences::default()
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
})
|
||||
@@ -230,10 +231,11 @@ fn correct_proposer_bad_signature() {
|
||||
result,
|
||||
Err(ProposerPreferencesError::BadSignature)
|
||||
));
|
||||
assert!(
|
||||
!ctx.preferences_cache
|
||||
.get_seen_validator(&slot, actual_proposer)
|
||||
);
|
||||
assert!(!ctx.preferences_cache.get_seen_validator(
|
||||
&slot,
|
||||
types::Hash256::ZERO,
|
||||
actual_proposer
|
||||
));
|
||||
assert!(ctx.preferences_cache.get_preferences(&slot).is_none());
|
||||
}
|
||||
|
||||
|
||||
@@ -229,9 +229,6 @@ async fn prepare_payload_generic(
|
||||
// `apply_parent_execution_payload`.
|
||||
let cached_head = harness.chain.canonical_head.cached_head();
|
||||
let unadvanced_empty_state = &cached_head.snapshot.beacon_state;
|
||||
let parent_bid = unadvanced_empty_state
|
||||
.latest_execution_payload_bid()
|
||||
.unwrap();
|
||||
|
||||
let mut advanced_empty_state = unadvanced_empty_state.clone();
|
||||
complete_state_advance(&mut advanced_empty_state, None, prepare_slot, &spec).unwrap();
|
||||
@@ -239,7 +236,6 @@ async fn prepare_payload_generic(
|
||||
let mut unadvanced_full_state = unadvanced_empty_state.clone();
|
||||
apply_parent_execution_payload(
|
||||
&mut unadvanced_full_state,
|
||||
parent_bid,
|
||||
&envelope.message.execution_requests,
|
||||
&spec,
|
||||
)
|
||||
@@ -248,7 +244,6 @@ async fn prepare_payload_generic(
|
||||
let mut advanced_full_state = advanced_empty_state.clone();
|
||||
apply_parent_execution_payload(
|
||||
&mut advanced_full_state,
|
||||
parent_bid,
|
||||
&envelope.message.execution_requests,
|
||||
&spec,
|
||||
)
|
||||
|
||||
@@ -2131,6 +2131,7 @@ fn make_test_payload_envelope(
|
||||
execution_requests: ExecutionRequests::default(),
|
||||
builder_index: 0,
|
||||
beacon_block_root,
|
||||
parent_beacon_block_root: Hash256::ZERO,
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
}
|
||||
|
||||
@@ -23,14 +23,6 @@ use types::{
|
||||
four_byte_option_impl!(four_byte_option_usize, usize);
|
||||
four_byte_option_impl!(four_byte_option_checkpoint, Checkpoint);
|
||||
|
||||
fn all_true_bitvector<N: typenum::Unsigned + Clone>() -> BitVector<N> {
|
||||
let mut bv = BitVector::new();
|
||||
for i in 0..bv.len() {
|
||||
let _ = bv.set(i, true);
|
||||
}
|
||||
bv
|
||||
}
|
||||
|
||||
/// Defines an operation which may invalidate the `execution_status` of some nodes.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum InvalidationOperation {
|
||||
@@ -568,10 +560,8 @@ impl ProtoArray {
|
||||
ProtoNode::V29(v29) => {
|
||||
// Both parent and child are Gloas blocks. The parent is full if the
|
||||
// block hash in the parent node matches the parent block hash in the
|
||||
// child bid and the parent block isn't the genesis block.
|
||||
if v29.execution_payload_block_hash != ExecutionBlockHash::zero()
|
||||
&& execution_payload_parent_hash == v29.execution_payload_block_hash
|
||||
{
|
||||
// child bid.
|
||||
if execution_payload_parent_hash == v29.execution_payload_block_hash {
|
||||
PayloadStatus::Full
|
||||
} else {
|
||||
PayloadStatus::Empty
|
||||
@@ -613,18 +603,8 @@ impl ProtoArray {
|
||||
full_payload_weight: 0,
|
||||
execution_payload_block_hash,
|
||||
execution_payload_parent_hash,
|
||||
// Per spec `get_forkchoice_store`: the anchor block's PTC votes are
|
||||
// initialized to all-True.
|
||||
payload_timeliness_votes: if is_anchor {
|
||||
all_true_bitvector()
|
||||
} else {
|
||||
BitVector::default()
|
||||
},
|
||||
payload_data_availability_votes: if is_anchor {
|
||||
all_true_bitvector()
|
||||
} else {
|
||||
BitVector::default()
|
||||
},
|
||||
payload_timeliness_votes: BitVector::default(),
|
||||
payload_data_availability_votes: BitVector::default(),
|
||||
payload_received: false,
|
||||
proposer_index,
|
||||
// Spec: `record_block_timeliness` + `get_forkchoice_store`.
|
||||
|
||||
@@ -26,6 +26,12 @@ pub enum EnvelopeProcessingError {
|
||||
envelope_root: Hash256,
|
||||
block_header_root: Hash256,
|
||||
},
|
||||
/// Envelope's `parent_beacon_block_root` doesn't match the parent root of the latest
|
||||
/// block header.
|
||||
ParentBeaconBlockRootMismatch {
|
||||
envelope: Hash256,
|
||||
state: Hash256,
|
||||
},
|
||||
/// Envelope doesn't match latest beacon block slot
|
||||
SlotMismatch {
|
||||
envelope_slot: Slot,
|
||||
@@ -126,6 +132,13 @@ pub fn verify_execution_payload_envelope<E: EthSpec>(
|
||||
block_header_root: latest_block_header_root,
|
||||
}
|
||||
);
|
||||
envelope_verify!(
|
||||
envelope.parent_beacon_block_root == state.latest_block_header().parent_root,
|
||||
EnvelopeProcessingError::ParentBeaconBlockRootMismatch {
|
||||
envelope: envelope.parent_beacon_block_root,
|
||||
state: state.latest_block_header().parent_root,
|
||||
}
|
||||
);
|
||||
envelope_verify!(
|
||||
envelope.slot() == state.slot(),
|
||||
EnvelopeProcessingError::SlotMismatch {
|
||||
|
||||
@@ -175,13 +175,11 @@ pub fn initialize_beacon_state_from_eth1<E: EthSpec>(
|
||||
bid.parent_block_hash = el_genesis_hash;
|
||||
bid.block_hash = ExecutionBlockHash::default();
|
||||
|
||||
// Update latest_block_header to reflect the Gloas genesis block body which contains
|
||||
// the EL genesis hash in the signed_execution_payload_bid. This is needed because
|
||||
// BeaconState::new() created the header from BeaconBlock::empty() which has zero bid
|
||||
// fields, but the spec requires the genesis block's bid to contain the EL block hash
|
||||
// and the tree hash root of empty ExecutionRequests.
|
||||
let block = genesis_block(&state, spec)?;
|
||||
state.latest_block_header_mut().body_root = block.body_root();
|
||||
// Update the `latest_block_header.body_root` so that it matches the body of the
|
||||
// Gloas genesis block, which embeds `state.latest_execution_payload_bid` in its
|
||||
// `signed_execution_payload_bid` field (see `genesis_block`).
|
||||
let genesis_body_root = genesis_block(&state, spec)?.body_root();
|
||||
state.latest_block_header_mut().body_root = genesis_body_root;
|
||||
}
|
||||
|
||||
// Now that we have our validators, initialize the caches (including the committees)
|
||||
@@ -193,24 +191,23 @@ pub fn initialize_beacon_state_from_eth1<E: EthSpec>(
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
/// Create an unsigned genesis `BeaconBlock` whose body matches the genesis state.
|
||||
/// Create an unsigned genesis `BeaconBlock`.
|
||||
///
|
||||
/// For Gloas, the block's `signed_execution_payload_bid` is populated from the state's
|
||||
/// `latest_execution_payload_bid` so that the body root is consistent with
|
||||
/// `state.latest_block_header.body_root`.
|
||||
/// Per spec, the genesis block body is empty (all default fields) except for Gloas,
|
||||
/// where `body.signed_execution_payload_bid.message` is initialised from
|
||||
/// `state.latest_execution_payload_bid` so that the first post-genesis proposer can
|
||||
/// build on the correct execution layer head.
|
||||
///
|
||||
/// The returned block has `state_root == Hash256::ZERO`; callers that need the real
|
||||
/// state root should set it themselves.
|
||||
/// `state.latest_block_header.body_root` is set from this same block's body, so the
|
||||
/// two must stay in sync.
|
||||
pub fn genesis_block<E: EthSpec>(
|
||||
genesis_state: &BeaconState<E>,
|
||||
state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<BeaconBlock<E>, BeaconStateError> {
|
||||
let mut block = BeaconBlock::empty(spec);
|
||||
if let Ok(block) = block.as_gloas_mut() {
|
||||
let state_bid = genesis_state.latest_execution_payload_bid()?;
|
||||
let bid = &mut block.body.signed_execution_payload_bid.message;
|
||||
bid.block_hash = state_bid.block_hash;
|
||||
bid.execution_requests_root = state_bid.execution_requests_root;
|
||||
if let BeaconBlock::Gloas(ref mut gloas_block) = block {
|
||||
let bid = state.latest_execution_payload_bid()?.clone();
|
||||
gloas_block.body.signed_execution_payload_bid.message = bid;
|
||||
}
|
||||
Ok(block)
|
||||
}
|
||||
|
||||
@@ -555,13 +555,10 @@ pub fn process_parent_execution_payload<E: EthSpec, Payload: AbstractExecPayload
|
||||
.signed_execution_payload_bid()?
|
||||
.message
|
||||
.parent_block_hash;
|
||||
let parent_bid = state.latest_execution_payload_bid()?.clone();
|
||||
let parent_bid = state.latest_execution_payload_bid()?;
|
||||
let requests = block.body().parent_execution_requests()?;
|
||||
|
||||
let is_genesis_block = parent_bid.block_hash == ExecutionBlockHash::zero();
|
||||
let is_parent_block_empty = bid_parent_block_hash != parent_bid.block_hash;
|
||||
|
||||
if is_genesis_block || is_parent_block_empty {
|
||||
if bid_parent_block_hash != parent_bid.block_hash {
|
||||
// Parent was EMPTY -- no execution requests expected
|
||||
block_verify!(
|
||||
*requests == ExecutionRequests::default(),
|
||||
@@ -580,7 +577,7 @@ pub fn process_parent_execution_payload<E: EthSpec, Payload: AbstractExecPayload
|
||||
}
|
||||
);
|
||||
|
||||
apply_parent_execution_payload(state, &parent_bid, requests, spec)
|
||||
apply_parent_execution_payload(state, requests, spec)
|
||||
}
|
||||
|
||||
/// Apply the parent execution payload's deferred effects to the state.
|
||||
@@ -591,10 +588,10 @@ pub fn process_parent_execution_payload<E: EthSpec, Payload: AbstractExecPayload
|
||||
/// 3. Updates `execution_payload_availability` and `latest_block_hash`
|
||||
pub fn apply_parent_execution_payload<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
parent_bid: &ExecutionPayloadBid<E>,
|
||||
requests: &ExecutionRequests<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
let parent_bid = state.latest_execution_payload_bid()?.clone();
|
||||
let parent_slot = parent_bid.slot;
|
||||
let parent_epoch = parent_slot.epoch(E::slots_per_epoch());
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ use safe_arith::{SafeArith, SafeArithIter};
|
||||
use tree_hash::TreeHash;
|
||||
use types::{
|
||||
AbstractExecPayload, BeaconState, BeaconStateError, ChainSpec, EthSpec, ExecPayload,
|
||||
ExecutionBlockHash, ExpectedWithdrawals, ExpectedWithdrawalsCapella,
|
||||
ExpectedWithdrawalsElectra, ExpectedWithdrawalsGloas, Validator, Withdrawal, Withdrawals,
|
||||
ExpectedWithdrawals, ExpectedWithdrawalsCapella, ExpectedWithdrawalsElectra,
|
||||
ExpectedWithdrawalsGloas, Validator, Withdrawal, Withdrawals,
|
||||
};
|
||||
|
||||
/// Compute the next batch of withdrawals which should be included in a block.
|
||||
@@ -495,10 +495,7 @@ pub mod gloas {
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
// Return early if the parent block is empty.
|
||||
let is_genesis_block = *state.latest_block_hash()? == ExecutionBlockHash::default();
|
||||
let is_parent_block_empty =
|
||||
*state.latest_block_hash()? != state.latest_execution_payload_bid()?.block_hash;
|
||||
if is_genesis_block || is_parent_block_empty {
|
||||
if *state.latest_block_hash()? != state.latest_execution_payload_bid()?.block_hash {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
||||
@@ -962,7 +962,11 @@ fn compute_exit_epoch_and_update_churn(
|
||||
spec.compute_activation_exit_epoch(state_ctxt.current_epoch)?,
|
||||
);
|
||||
|
||||
let per_epoch_churn = get_activation_exit_churn_limit(state_ctxt, spec)?;
|
||||
let per_epoch_churn = if state_ctxt.fork_name.gloas_enabled() {
|
||||
get_balance_churn_limit(state_ctxt, spec)?
|
||||
} else {
|
||||
get_activation_exit_churn_limit(state_ctxt, spec)?
|
||||
};
|
||||
// New epoch for exits
|
||||
let mut exit_balance_to_consume = if *earliest_exit_epoch_state < earliest_exit_epoch {
|
||||
per_epoch_churn
|
||||
@@ -991,17 +995,27 @@ fn get_activation_exit_churn_limit(
|
||||
state_ctxt: &StateContext,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, Error> {
|
||||
let max_limit = if state_ctxt.fork_name.gloas_enabled() {
|
||||
spec.max_per_epoch_activation_churn_limit_gloas
|
||||
} else {
|
||||
spec.max_per_epoch_activation_exit_churn_limit
|
||||
};
|
||||
Ok(std::cmp::min(
|
||||
spec.max_per_epoch_activation_exit_churn_limit,
|
||||
max_limit,
|
||||
get_balance_churn_limit(state_ctxt, spec)?,
|
||||
))
|
||||
}
|
||||
|
||||
fn get_balance_churn_limit(state_ctxt: &StateContext, spec: &ChainSpec) -> Result<u64, Error> {
|
||||
let total_active_balance = state_ctxt.total_active_balance;
|
||||
let quotient = if state_ctxt.fork_name.gloas_enabled() {
|
||||
spec.churn_limit_quotient_gloas
|
||||
} else {
|
||||
spec.churn_limit_quotient
|
||||
};
|
||||
let churn = std::cmp::max(
|
||||
spec.min_per_epoch_churn_limit_electra,
|
||||
total_active_balance.safe_div(spec.churn_limit_quotient)?,
|
||||
total_active_balance.safe_div(quotient)?,
|
||||
);
|
||||
|
||||
Ok(churn.safe_sub(churn.safe_rem(spec.effective_balance_increment)?)?)
|
||||
|
||||
@@ -105,12 +105,8 @@ CONTRIBUTION_DUE_BPS_GLOAS: 5000
|
||||
PAYLOAD_ATTESTATION_DUE_BPS: 7500
|
||||
|
||||
# Heze
|
||||
# 7500 basis points, 75% of SLOT_DURATION_MS
|
||||
VIEW_FREEZE_CUTOFF_BPS: 7500
|
||||
# 6667 basis points, ~67% of SLOT_DURATION_MS
|
||||
INCLUSION_LIST_SUBMISSION_DUE_BPS: 6667
|
||||
# 9167 basis points, ~92% of SLOT_DURATION_MS
|
||||
PROPOSER_INCLUSION_LIST_CUTOFF_BPS: 9167
|
||||
INCLUSION_LIST_DUE_BPS: 6667
|
||||
|
||||
# Validator cycle
|
||||
# ---------------------------------------------------------------
|
||||
@@ -135,6 +131,14 @@ MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 128000000000
|
||||
# 2**8 * 10**9 (= 256,000,000,000) Gwei
|
||||
MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 256000000000
|
||||
|
||||
# Gloas
|
||||
# 2**15 (= 32,768)
|
||||
CHURN_LIMIT_QUOTIENT_GLOAS: 32768
|
||||
# 2**16 (= 65,536)
|
||||
CONSOLIDATION_CHURN_LIMIT_QUOTIENT: 65536
|
||||
# 2**8 * 10**9 (= 256,000,000,000) Gwei
|
||||
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT_GLOAS: 256000000000
|
||||
|
||||
# Fork choice
|
||||
# ---------------------------------------------------------------
|
||||
# 40%
|
||||
|
||||
@@ -101,12 +101,8 @@ CONTRIBUTION_DUE_BPS_GLOAS: 5000
|
||||
PAYLOAD_ATTESTATION_DUE_BPS: 7500
|
||||
|
||||
# Heze
|
||||
# 7500 basis points, 75% of SLOT_DURATION_MS
|
||||
VIEW_FREEZE_CUTOFF_BPS: 7500
|
||||
# 6667 basis points, ~67% of SLOT_DURATION_MS
|
||||
INCLUSION_LIST_SUBMISSION_DUE_BPS: 6667
|
||||
# 9167 basis points, ~92% of SLOT_DURATION_MS
|
||||
PROPOSER_INCLUSION_LIST_CUTOFF_BPS: 9167
|
||||
INCLUSION_LIST_DUE_BPS: 6667
|
||||
|
||||
# Validator cycle
|
||||
# ---------------------------------------------------------------
|
||||
@@ -131,6 +127,14 @@ MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: 64000000000
|
||||
# [customized] 2**7 * 10**9 (= 128,000,000,000) Gwei
|
||||
MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: 128000000000
|
||||
|
||||
# Gloas
|
||||
# [customized] 2**4 (= 16)
|
||||
CHURN_LIMIT_QUOTIENT_GLOAS: 16
|
||||
# [customized] 2**5 (= 32)
|
||||
CONSOLIDATION_CHURN_LIMIT_QUOTIENT: 32
|
||||
# [customized] 2**7 * 10**9 (= 128,000,000,000) Gwei
|
||||
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT_GLOAS: 128000000000
|
||||
|
||||
# Fork choice
|
||||
# ---------------------------------------------------------------
|
||||
# 40%
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
# Misc
|
||||
# ---------------------------------------------------------------
|
||||
# [customized] 2**1 (= 2) validators
|
||||
PTC_SIZE: 2
|
||||
# [customized] 2**4 (= 16) validators
|
||||
PTC_SIZE: 16
|
||||
|
||||
# Max operations per block
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
@@ -394,15 +394,13 @@ impl<E: EthSpec, Payload: AbstractExecPayload<E>> SignedBeaconBlock<E, Payload>
|
||||
/// `block_hash` from the parent beacon block's bid. If the parent beacon state is available
|
||||
/// this can alternatively be fetched from `state.latest_payload_bid`.
|
||||
///
|
||||
/// This function returns `false` for all blocks prior to Gloas and for the zero
|
||||
/// `parent_block_hash`.
|
||||
/// This function returns `false` for all blocks prior to Gloas.
|
||||
pub fn is_parent_block_full(&self, parent_block_hash: ExecutionBlockHash) -> bool {
|
||||
let Ok(signed_payload_bid) = self.message().body().signed_execution_payload_bid() else {
|
||||
// Prior to Gloas.
|
||||
return false;
|
||||
};
|
||||
parent_block_hash != ExecutionBlockHash::zero()
|
||||
&& signed_payload_bid.message.parent_block_hash == parent_block_hash
|
||||
signed_payload_bid.message.parent_block_hash == parent_block_hash
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::test_utils::TestRandom;
|
||||
use crate::{Address, ForkName, SignedRoot, Slot};
|
||||
use crate::{Address, ForkName, Hash256, SignedRoot, Slot};
|
||||
use bls::Signature;
|
||||
use context_deserialize::context_deserialize;
|
||||
use educe::Educe;
|
||||
@@ -16,6 +16,7 @@ use tree_hash_derive::TreeHash;
|
||||
#[context_deserialize(ForkName)]
|
||||
// https://github.com/ethereum/consensus-specs/blob/master/specs/gloas/p2p-interface.md#new-proposerpreferences
|
||||
pub struct ProposerPreferences {
|
||||
pub checkpoint_root: Hash256,
|
||||
pub proposal_slot: Slot,
|
||||
pub validator_index: u64,
|
||||
pub fee_recipient: Address,
|
||||
|
||||
@@ -251,6 +251,9 @@ pub struct ChainSpec {
|
||||
pub builder_payment_threshold_numerator: u64,
|
||||
pub builder_payment_threshold_denominator: u64,
|
||||
pub min_builder_withdrawability_delay: Epoch,
|
||||
pub churn_limit_quotient_gloas: u64,
|
||||
pub consolidation_churn_limit_quotient: u64,
|
||||
pub max_per_epoch_activation_churn_limit_gloas: u64,
|
||||
|
||||
/*
|
||||
* Networking
|
||||
@@ -1268,6 +1271,14 @@ impl ChainSpec {
|
||||
builder_payment_threshold_numerator: 6,
|
||||
builder_payment_threshold_denominator: 10,
|
||||
min_builder_withdrawability_delay: Epoch::new(64),
|
||||
churn_limit_quotient_gloas: option_wrapper(|| u64::checked_pow(2, 15))
|
||||
.expect("calculation does not overflow"),
|
||||
consolidation_churn_limit_quotient: option_wrapper(|| u64::checked_pow(2, 16))
|
||||
.expect("calculation does not overflow"),
|
||||
max_per_epoch_activation_churn_limit_gloas: option_wrapper(|| {
|
||||
u64::checked_pow(2, 8)?.checked_mul(u64::checked_pow(10, 9)?)
|
||||
})
|
||||
.expect("calculation does not overflow"),
|
||||
max_request_payloads: 128,
|
||||
|
||||
/*
|
||||
@@ -1414,6 +1425,14 @@ impl ChainSpec {
|
||||
gloas_fork_version: [0x07, 0x00, 0x00, 0x01],
|
||||
gloas_fork_epoch: None,
|
||||
min_builder_withdrawability_delay: Epoch::new(2),
|
||||
churn_limit_quotient_gloas: option_wrapper(|| u64::checked_pow(2, 4))
|
||||
.expect("calculation does not overflow"),
|
||||
consolidation_churn_limit_quotient: option_wrapper(|| u64::checked_pow(2, 5))
|
||||
.expect("calculation does not overflow"),
|
||||
max_per_epoch_activation_churn_limit_gloas: option_wrapper(|| {
|
||||
u64::checked_pow(2, 7)?.checked_mul(u64::checked_pow(10, 9)?)
|
||||
})
|
||||
.expect("calculation does not overflow"),
|
||||
|
||||
/*
|
||||
* Derived time values (set by `compute_derived_values()`)
|
||||
@@ -1675,6 +1694,14 @@ impl ChainSpec {
|
||||
builder_payment_threshold_numerator: 6,
|
||||
builder_payment_threshold_denominator: 10,
|
||||
min_builder_withdrawability_delay: Epoch::new(64),
|
||||
churn_limit_quotient_gloas: option_wrapper(|| u64::checked_pow(2, 15))
|
||||
.expect("calculation does not overflow"),
|
||||
consolidation_churn_limit_quotient: option_wrapper(|| u64::checked_pow(2, 16))
|
||||
.expect("calculation does not overflow"),
|
||||
max_per_epoch_activation_churn_limit_gloas: option_wrapper(|| {
|
||||
u64::checked_pow(2, 8)?.checked_mul(u64::checked_pow(10, 9)?)
|
||||
})
|
||||
.expect("calculation does not overflow"),
|
||||
max_request_payloads: 128,
|
||||
|
||||
/*
|
||||
@@ -2125,6 +2152,16 @@ pub struct Config {
|
||||
#[serde(default = "default_min_builder_withdrawability_delay")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
min_builder_withdrawability_delay: u64,
|
||||
|
||||
#[serde(default = "default_churn_limit_quotient_gloas")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
churn_limit_quotient_gloas: u64,
|
||||
#[serde(default = "default_consolidation_churn_limit_quotient")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
consolidation_churn_limit_quotient: u64,
|
||||
#[serde(default = "default_max_per_epoch_activation_churn_limit_gloas")]
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
max_per_epoch_activation_churn_limit_gloas: u64,
|
||||
}
|
||||
|
||||
fn default_bellatrix_fork_version() -> [u8; 4] {
|
||||
@@ -2362,6 +2399,18 @@ const fn default_min_builder_withdrawability_delay() -> u64 {
|
||||
64
|
||||
}
|
||||
|
||||
const fn default_churn_limit_quotient_gloas() -> u64 {
|
||||
32_768
|
||||
}
|
||||
|
||||
const fn default_consolidation_churn_limit_quotient() -> u64 {
|
||||
65_536
|
||||
}
|
||||
|
||||
const fn default_max_per_epoch_activation_churn_limit_gloas() -> u64 {
|
||||
256_000_000_000
|
||||
}
|
||||
|
||||
fn max_blocks_by_root_request_common(max_request_blocks: u64) -> usize {
|
||||
let max_request_blocks = max_request_blocks as usize;
|
||||
RuntimeVariableList::<Hash256>::new(
|
||||
@@ -2613,6 +2662,11 @@ impl Config {
|
||||
contribution_due_bps: spec.contribution_due_bps,
|
||||
|
||||
min_builder_withdrawability_delay: spec.min_builder_withdrawability_delay.as_u64(),
|
||||
|
||||
churn_limit_quotient_gloas: spec.churn_limit_quotient_gloas,
|
||||
consolidation_churn_limit_quotient: spec.consolidation_churn_limit_quotient,
|
||||
max_per_epoch_activation_churn_limit_gloas: spec
|
||||
.max_per_epoch_activation_churn_limit_gloas,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2710,6 +2764,9 @@ impl Config {
|
||||
sync_message_due_bps,
|
||||
contribution_due_bps,
|
||||
min_builder_withdrawability_delay,
|
||||
churn_limit_quotient_gloas,
|
||||
consolidation_churn_limit_quotient,
|
||||
max_per_epoch_activation_churn_limit_gloas,
|
||||
} = self;
|
||||
|
||||
if preset_base != E::spec_name().to_string().as_str() {
|
||||
@@ -2817,6 +2874,10 @@ impl Config {
|
||||
|
||||
min_builder_withdrawability_delay: Epoch::new(min_builder_withdrawability_delay),
|
||||
|
||||
churn_limit_quotient_gloas,
|
||||
consolidation_churn_limit_quotient,
|
||||
max_per_epoch_activation_churn_limit_gloas,
|
||||
|
||||
..chain_spec.clone()
|
||||
};
|
||||
Some(spec.compute_derived_values::<E>())
|
||||
@@ -3719,9 +3780,7 @@ mod yaml_tests {
|
||||
"CONTRIBUTION_DUE_BPS_GLOAS",
|
||||
"MAX_REQUEST_PAYLOADS",
|
||||
// Heze networking
|
||||
"VIEW_FREEZE_CUTOFF_BPS",
|
||||
"INCLUSION_LIST_SUBMISSION_DUE_BPS",
|
||||
"PROPOSER_INCLUSION_LIST_CUTOFF_BPS",
|
||||
"INCLUSION_LIST_DUE_BPS",
|
||||
"MAX_REQUEST_INCLUSION_LIST",
|
||||
"MAX_BYTES_PER_INCLUSION_LIST",
|
||||
];
|
||||
|
||||
@@ -572,7 +572,7 @@ impl EthSpec for MinimalEthSpec {
|
||||
type NumberOfColumns = U128;
|
||||
type ProposerLookaheadSlots = U16; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH
|
||||
type BuilderPendingPaymentsLimit = U16; // 2 * SLOTS_PER_EPOCH = 2 * 8 = 16
|
||||
type PTCSize = U2;
|
||||
type PTCSize = U16;
|
||||
type PtcWindowLength = U24; // (2 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH
|
||||
type MaxBuildersPerWithdrawalsSweep = U16;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ pub struct ExecutionPayloadEnvelope<E: EthSpec> {
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
pub builder_index: u64,
|
||||
pub beacon_block_root: Hash256,
|
||||
pub parent_beacon_block_root: Hash256,
|
||||
}
|
||||
|
||||
impl<E: EthSpec> ExecutionPayloadEnvelope<E> {
|
||||
@@ -30,6 +31,7 @@ impl<E: EthSpec> ExecutionPayloadEnvelope<E> {
|
||||
execution_requests: ExecutionRequests::default(),
|
||||
builder_index: 0,
|
||||
beacon_block_root: Hash256::zero(),
|
||||
parent_beacon_block_root: Hash256::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2762,29 +2762,55 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
/// Return the churn limit for the current epoch.
|
||||
pub fn get_balance_churn_limit(&self, spec: &ChainSpec) -> Result<u64, BeaconStateError> {
|
||||
let total_active_balance = self.get_total_active_balance()?;
|
||||
let quotient = if self.fork_name_unchecked().gloas_enabled() {
|
||||
spec.churn_limit_quotient_gloas
|
||||
} else {
|
||||
spec.churn_limit_quotient
|
||||
};
|
||||
let churn = std::cmp::max(
|
||||
spec.min_per_epoch_churn_limit_electra,
|
||||
total_active_balance.safe_div(spec.churn_limit_quotient)?,
|
||||
total_active_balance.safe_div(quotient)?,
|
||||
);
|
||||
|
||||
Ok(churn.safe_sub(churn.safe_rem(spec.effective_balance_increment)?)?)
|
||||
}
|
||||
|
||||
/// Return the churn limit for the current epoch dedicated to activations and exits.
|
||||
///
|
||||
/// From Gloas onwards this is the activation-only churn limit (EIP-8061); exits use
|
||||
/// [`Self::get_exit_churn_limit`].
|
||||
pub fn get_activation_exit_churn_limit(
|
||||
&self,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<u64, BeaconStateError> {
|
||||
let max_limit = if self.fork_name_unchecked().gloas_enabled() {
|
||||
spec.max_per_epoch_activation_churn_limit_gloas
|
||||
} else {
|
||||
spec.max_per_epoch_activation_exit_churn_limit
|
||||
};
|
||||
Ok(std::cmp::min(
|
||||
spec.max_per_epoch_activation_exit_churn_limit,
|
||||
max_limit,
|
||||
self.get_balance_churn_limit(spec)?,
|
||||
))
|
||||
}
|
||||
|
||||
/// Return the Gloas (EIP-8061) exit churn limit for the current epoch.
|
||||
///
|
||||
/// Unlike [`Self::get_activation_exit_churn_limit`], this is uncapped.
|
||||
pub fn get_exit_churn_limit(&self, spec: &ChainSpec) -> Result<u64, BeaconStateError> {
|
||||
self.get_balance_churn_limit(spec)
|
||||
}
|
||||
|
||||
pub fn get_consolidation_churn_limit(&self, spec: &ChainSpec) -> Result<u64, BeaconStateError> {
|
||||
self.get_balance_churn_limit(spec)?
|
||||
.safe_sub(self.get_activation_exit_churn_limit(spec)?)
|
||||
.map_err(Into::into)
|
||||
if self.fork_name_unchecked().gloas_enabled() {
|
||||
let total_active_balance = self.get_total_active_balance()?;
|
||||
let churn = total_active_balance.safe_div(spec.consolidation_churn_limit_quotient)?;
|
||||
Ok(churn.safe_sub(churn.safe_rem(spec.effective_balance_increment)?)?)
|
||||
} else {
|
||||
self.get_balance_churn_limit(spec)?
|
||||
.safe_sub(self.get_activation_exit_churn_limit(spec)?)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pending_balance_to_withdraw(
|
||||
@@ -2879,7 +2905,11 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
self.compute_activation_exit_epoch(self.current_epoch(), spec)?,
|
||||
);
|
||||
|
||||
let per_epoch_churn = self.get_activation_exit_churn_limit(spec)?;
|
||||
let per_epoch_churn = if self.fork_name_unchecked().gloas_enabled() {
|
||||
self.get_exit_churn_limit(spec)?
|
||||
} else {
|
||||
self.get_activation_exit_churn_limit(spec)?
|
||||
};
|
||||
// New epoch for exits
|
||||
let mut exit_balance_to_consume = if self.earliest_exit_epoch()? < earliest_exit_epoch {
|
||||
per_epoch_churn
|
||||
@@ -3103,7 +3133,19 @@ impl<E: EthSpec> BeaconState<E> {
|
||||
let total_active_balance = self.get_total_active_balance()?;
|
||||
let fork_name = self.fork_name_unchecked();
|
||||
|
||||
if fork_name.electra_enabled() {
|
||||
if fork_name.gloas_enabled() {
|
||||
// [Modified in Gloas:EIP8061]
|
||||
let exit_churn = self.get_exit_churn_limit(spec)?;
|
||||
let activation_churn = self.get_activation_exit_churn_limit(spec)?;
|
||||
let consolidation_churn = self.get_consolidation_churn_limit(spec)?;
|
||||
compute_weak_subjectivity_period_gloas(
|
||||
total_active_balance,
|
||||
exit_churn,
|
||||
activation_churn,
|
||||
consolidation_churn,
|
||||
spec,
|
||||
)
|
||||
} else if fork_name.electra_enabled() {
|
||||
let balance_churn_limit = self.get_balance_churn_limit(spec)?;
|
||||
compute_weak_subjectivity_period_electra(
|
||||
total_active_balance,
|
||||
@@ -3601,6 +3643,30 @@ pub fn compute_weak_subjectivity_period_electra(
|
||||
Ok(ws_period)
|
||||
}
|
||||
|
||||
/// Spec: https://github.com/ethereum/consensus-specs/blob/v1.7.0-alpha.6/specs/gloas/weak-subjectivity.md
|
||||
pub fn compute_weak_subjectivity_period_gloas(
|
||||
total_active_balance: u64,
|
||||
exit_churn_limit: u64,
|
||||
activation_churn_limit: u64,
|
||||
consolidation_churn_limit: u64,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Epoch, BeaconStateError> {
|
||||
// delta = 2 * exit_churn // 3 + activation_churn // 3 + consolidation_churn
|
||||
let delta = exit_churn_limit
|
||||
.safe_mul(2)?
|
||||
.safe_div(3)?
|
||||
.safe_add(activation_churn_limit.safe_div(3)?)?
|
||||
.safe_add(consolidation_churn_limit)?;
|
||||
let epochs_for_validator_set_churn = SAFETY_DECAY
|
||||
.safe_mul(total_active_balance)?
|
||||
.safe_div(delta.safe_mul(200)?)?;
|
||||
let ws_period = spec
|
||||
.min_validator_withdrawability_delay
|
||||
.safe_add(epochs_for_validator_set_churn)?;
|
||||
|
||||
Ok(ws_period)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod weak_subjectivity_tests {
|
||||
use crate::state::beacon_state::compute_weak_subjectivity_period_electra;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# To download/extract nightly tests, run:
|
||||
# CONSENSUS_SPECS_TEST_VERSION=nightly make
|
||||
CONSENSUS_SPECS_TEST_VERSION ?= v1.7.0-alpha.5
|
||||
CONSENSUS_SPECS_TEST_VERSION ?= v1.7.0-alpha.6
|
||||
REPO_NAME := consensus-spec-tests
|
||||
OUTPUT_DIR := ./$(REPO_NAME)
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ excluded_paths = [
|
||||
"tests/.*/.*/ssz_static/PartialDataColumn.*/.*",
|
||||
# TODO(gloas): Ignore Gloas light client stuff for now
|
||||
"tests/.*/gloas/ssz_static/LightClient.*/.*",
|
||||
"tests/.*/gloas/light_client",
|
||||
# Execution payload header is irrelevant after Gloas, this type will probably be deleted.
|
||||
"tests/.*/gloas/ssz_static/ExecutionPayloadHeader/.*",
|
||||
# ForkChoiceNode is internal to fork choice and probably doesn't need SSZ tests.
|
||||
|
||||
@@ -23,7 +23,7 @@ if [[ "$version" == "nightly" || "$version" =~ ^nightly-[0-9]+$ ]]; then
|
||||
|
||||
if [[ "$version" == "nightly" ]]; then
|
||||
run_id=$(curl --fail -s -H "${auth_header}" \
|
||||
"${api}/repos/${repo}/actions/workflows/nightly-reftests.yml/runs?branch=master&status=success&per_page=1" |
|
||||
"${api}/repos/${repo}/actions/workflows/tests.yml/runs?branch=master&status=success&per_page=1" |
|
||||
jq -r '.workflow_runs[0].id')
|
||||
else
|
||||
run_id="${version#nightly-}"
|
||||
|
||||
@@ -58,6 +58,8 @@ pub struct Eth1DataReset;
|
||||
#[derive(Debug)]
|
||||
pub struct PendingBalanceDeposits;
|
||||
#[derive(Debug)]
|
||||
pub struct PendingDepositsChurn;
|
||||
#[derive(Debug)]
|
||||
pub struct PendingConsolidations;
|
||||
#[derive(Debug)]
|
||||
pub struct EffectiveBalanceUpdates;
|
||||
@@ -93,6 +95,7 @@ type_name!(RegistryUpdates, "registry_updates");
|
||||
type_name!(Slashings, "slashings");
|
||||
type_name!(Eth1DataReset, "eth1_data_reset");
|
||||
type_name!(PendingBalanceDeposits, "pending_deposits");
|
||||
type_name!(PendingDepositsChurn, "pending_deposits_churn");
|
||||
type_name!(PendingConsolidations, "pending_consolidations");
|
||||
type_name!(EffectiveBalanceUpdates, "effective_balance_updates");
|
||||
type_name!(SlashingsReset, "slashings_reset");
|
||||
@@ -191,6 +194,20 @@ impl<E: EthSpec> EpochTransition<E> for PendingBalanceDeposits {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for PendingDepositsChurn {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
process_epoch_single_pass(
|
||||
state,
|
||||
spec,
|
||||
SinglePassConfig {
|
||||
pending_deposits: true,
|
||||
..SinglePassConfig::disable_all()
|
||||
},
|
||||
)
|
||||
.map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EpochTransition<E> for PendingConsolidations {
|
||||
fn run(state: &mut BeaconState<E>, spec: &ChainSpec) -> Result<(), EpochProcessingError> {
|
||||
initialize_epoch_cache(state, spec)?;
|
||||
@@ -387,7 +404,9 @@ impl<E: EthSpec, T: EpochTransition<E>> Case for EpochProcessing<E, T> {
|
||||
}
|
||||
|
||||
if !fork_name.gloas_enabled()
|
||||
&& (T::name() == "builder_pending_payments" || T::name() == "ptc_window")
|
||||
&& (T::name() == "builder_pending_payments"
|
||||
|| T::name() == "ptc_window"
|
||||
|| T::name() == "pending_deposits_churn")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,15 @@ pub struct WithdrawalsPayload<E: EthSpec> {
|
||||
payload: Option<ExecutionPayload<E>>,
|
||||
}
|
||||
|
||||
/// Newtype for testing voluntary exit churn (Gloas+).
|
||||
///
|
||||
/// The test case applies the same `process_voluntary_exit` operation as the regular
|
||||
/// `voluntary_exit` test, but under the `voluntary_exit_churn` handler directory.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct VoluntaryExitChurn {
|
||||
exit: SignedVoluntaryExit,
|
||||
}
|
||||
|
||||
/// Newtype for testing execution payload bids.
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct ExecutionPayloadBidBlock<E: EthSpec> {
|
||||
@@ -265,6 +274,40 @@ impl<E: EthSpec> Operation<E> for SignedVoluntaryExit {
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Operation<E> for VoluntaryExitChurn {
|
||||
type Error = BlockProcessingError;
|
||||
|
||||
fn handler_name() -> String {
|
||||
"voluntary_exit_churn".into()
|
||||
}
|
||||
|
||||
fn filename() -> String {
|
||||
"voluntary_exit.ssz_snappy".into()
|
||||
}
|
||||
|
||||
fn is_enabled_for_fork(fork_name: ForkName) -> bool {
|
||||
fork_name.gloas_enabled()
|
||||
}
|
||||
|
||||
fn decode(path: &Path, _fork_name: ForkName, _spec: &ChainSpec) -> Result<Self, Error> {
|
||||
ssz_decode_file(path).map(|exit| VoluntaryExitChurn { exit })
|
||||
}
|
||||
|
||||
fn apply_to(
|
||||
&self,
|
||||
state: &mut BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
_: &Operations<E, Self>,
|
||||
) -> Result<(), BlockProcessingError> {
|
||||
process_exits(
|
||||
state,
|
||||
std::slice::from_ref(&self.exit),
|
||||
VerifySignatures::True,
|
||||
spec,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Operation<E> for BeaconBlock<E> {
|
||||
type Error = BlockProcessingError;
|
||||
|
||||
|
||||
@@ -340,6 +340,10 @@ impl<T, E> SszStaticHandler<T, E> {
|
||||
pub fn pre_electra() -> Self {
|
||||
Self::for_forks(ForkName::list_all()[0..5].to_vec())
|
||||
}
|
||||
|
||||
pub fn pre_capella() -> Self {
|
||||
Self::for_forks(ForkName::list_all()[0..3].to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
/// Handler for SSZ types that implement `CachedTreeHash`.
|
||||
|
||||
@@ -3,9 +3,10 @@ pub use cases::{
|
||||
BuilderPendingPayments, Case, EffectiveBalanceUpdates, Eth1DataReset, ExecutionPayloadBidBlock,
|
||||
FeatureName, HistoricalRootsUpdate, HistoricalSummariesUpdate, InactivityUpdates,
|
||||
JustificationAndFinalization, ParentExecutionPayloadBlock, ParticipationFlagUpdates,
|
||||
ParticipationRecordUpdates, PendingBalanceDeposits, PendingConsolidations, ProposerLookahead,
|
||||
PtcWindow, RandaoMixesReset, RegistryUpdates, RewardsAndPenalties, Slashings, SlashingsReset,
|
||||
SyncCommitteeUpdates, WithdrawalsPayload,
|
||||
ParticipationRecordUpdates, PendingBalanceDeposits, PendingConsolidations,
|
||||
PendingDepositsChurn, ProposerLookahead, PtcWindow, RandaoMixesReset, RegistryUpdates,
|
||||
RewardsAndPenalties, Slashings, SlashingsReset, SyncCommitteeUpdates, VoluntaryExitChurn,
|
||||
WithdrawalsPayload,
|
||||
};
|
||||
pub use decode::log_file_access;
|
||||
pub use error::Error;
|
||||
|
||||
@@ -142,6 +142,12 @@ fn operations_bls_to_execution_change() {
|
||||
OperationsHandler::<MainnetEthSpec, SignedBlsToExecutionChange>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn operations_voluntary_exit_churn() {
|
||||
OperationsHandler::<MinimalEthSpec, VoluntaryExitChurn>::default().run();
|
||||
OperationsHandler::<MainnetEthSpec, VoluntaryExitChurn>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sanity_blocks() {
|
||||
SanityBlocksHandler::<MinimalEthSpec>::default().run();
|
||||
@@ -285,8 +291,19 @@ mod ssz_static {
|
||||
ssz_static_test!(eth1_data, Eth1Data);
|
||||
ssz_static_test!(fork, Fork);
|
||||
ssz_static_test!(fork_data, ForkData);
|
||||
ssz_static_test!(historical_batch, HistoricalBatch<_>);
|
||||
ssz_static_test!(pending_attestation, PendingAttestation<_>);
|
||||
// `HistoricalBatch` was removed in Capella, so test vectors only exist for Base,
|
||||
// Altair and Bellatrix.
|
||||
#[test]
|
||||
fn historical_batch() {
|
||||
SszStaticHandler::<HistoricalBatch<MinimalEthSpec>, MinimalEthSpec>::pre_capella().run();
|
||||
SszStaticHandler::<HistoricalBatch<MainnetEthSpec>, MainnetEthSpec>::pre_capella().run();
|
||||
}
|
||||
// `PendingAttestation` was removed in Altair, so test vectors only exist for Base.
|
||||
#[test]
|
||||
fn pending_attestation() {
|
||||
SszStaticHandler::<PendingAttestation<MinimalEthSpec>, MinimalEthSpec>::base_only().run();
|
||||
SszStaticHandler::<PendingAttestation<MainnetEthSpec>, MainnetEthSpec>::base_only().run();
|
||||
}
|
||||
ssz_static_test!(proposer_slashing, ProposerSlashing);
|
||||
ssz_static_test!(
|
||||
signed_beacon_block,
|
||||
@@ -899,6 +916,12 @@ fn epoch_processing_pending_balance_deposits() {
|
||||
EpochProcessingHandler::<MainnetEthSpec, PendingBalanceDeposits>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_pending_deposits_churn() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, PendingDepositsChurn>::default().run();
|
||||
EpochProcessingHandler::<MainnetEthSpec, PendingDepositsChurn>::default().run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epoch_processing_pending_consolidations() {
|
||||
EpochProcessingHandler::<MinimalEthSpec, PendingConsolidations>::default().run();
|
||||
|
||||
Reference in New Issue
Block a user