mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-30 12:47:05 +00:00
Gloas alpha spec 8 (#9315)
https://github.com/ethereum/consensus-specs/releases/tag/v1.7.0-alpha.8 Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu> Co-Authored-By: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
@@ -96,8 +96,8 @@ use eth2::types::{
|
||||
SseExtendedPayloadAttributes, SseHead,
|
||||
};
|
||||
use execution_layer::{
|
||||
BlockProposalContents, BlockProposalContentsType, BuilderParams, ChainHealth, ExecutionLayer,
|
||||
FailedCondition, PayloadAttributes, PayloadStatus,
|
||||
BlockProposalContents, BlockProposalContentsType, BuilderParams, ChainHealth,
|
||||
DEFAULT_GAS_LIMIT, ExecutionLayer, FailedCondition, PayloadAttributes, PayloadStatus,
|
||||
};
|
||||
use fixed_bytes::FixedBytesExtended;
|
||||
use fork_choice::{
|
||||
@@ -2185,12 +2185,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
// TODO(gloas) do we want to use a dedicated envelope cache instead?
|
||||
// Maybe the new gloas DA cache? (Or should the gloas DA cache use
|
||||
// the envelopes_times_cache internally?)
|
||||
// the envelopes_times_cache internally?
|
||||
// The payload is considered present only if it was observed before
|
||||
// the payload due deadline (PAYLOAD_DUE_BPS into the slot).
|
||||
let payload_due = self.spec.get_payload_due();
|
||||
let payload_present = self
|
||||
.envelope_times_cache
|
||||
.read()
|
||||
.cache
|
||||
.contains_key(&beacon_block_root);
|
||||
.get(&beacon_block_root)
|
||||
.and_then(|entry| entry.timestamps.observed)
|
||||
.is_some_and(|observed| {
|
||||
let slot_start = self.slot_clock.start_of(request_slot);
|
||||
slot_start.is_some_and(|start| observed.saturating_sub(start) < payload_due)
|
||||
});
|
||||
|
||||
// TODO(EIP-7732): Check blob data availability. For now, default to true.
|
||||
let blob_data_available = true;
|
||||
@@ -6476,6 +6484,19 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
None
|
||||
};
|
||||
|
||||
let target_gas_limit = if prepare_slot_fork.gloas_enabled() {
|
||||
let proposer_gas_limit = execution_layer.get_proposer_gas_limit(proposer).await;
|
||||
if proposer_gas_limit.is_none() {
|
||||
warn!(
|
||||
%proposer,
|
||||
"No proposer gas limit configured, falling back to parent gas limit"
|
||||
);
|
||||
}
|
||||
proposer_gas_limit.or(Some(DEFAULT_GAS_LIMIT))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload_attributes = PayloadAttributes::new(
|
||||
self.slot_clock
|
||||
.start_of(prepare_slot)
|
||||
@@ -6486,6 +6507,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
withdrawals.map(Into::into),
|
||||
parent_beacon_block_root,
|
||||
slot_number,
|
||||
target_gas_limit,
|
||||
);
|
||||
|
||||
execution_layer
|
||||
|
||||
@@ -2,11 +2,13 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::Arc;
|
||||
|
||||
use proto_array::PayloadStatus;
|
||||
|
||||
use bls::{PublicKeyBytes, Signature};
|
||||
use execution_layer::{
|
||||
BlockProposalContentsGloas, BuilderParams, PayloadAttributes, PayloadParameters,
|
||||
BlockProposalContentsGloas, BuilderParams, DEFAULT_GAS_LIMIT, PayloadAttributes,
|
||||
PayloadParameters,
|
||||
};
|
||||
use fork_choice::PayloadStatus;
|
||||
use operation_pool::CompactAttestationRef;
|
||||
use ssz::Encode;
|
||||
use state_processing::common::{get_attesting_indices_from_state, get_indexed_payload_attestation};
|
||||
@@ -150,8 +152,24 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
verification: ProduceBlockVerification,
|
||||
builder_boost_factor: Option<u64>,
|
||||
) -> Result<BlockProductionResult<T::EthSpec>, BlockProductionError> {
|
||||
// Extract the parent's execution requests from the envelope (if parent was full).
|
||||
let parent_execution_requests = if parent_payload_status == PayloadStatus::Full {
|
||||
let parent_root = if state.slot() > 0 {
|
||||
*state
|
||||
.get_block_root(state.slot() - 1)
|
||||
.map_err(|_| BlockProductionError::UnableToGetBlockRootFromState)?
|
||||
} else {
|
||||
state.latest_block_header().canonical_root()
|
||||
};
|
||||
|
||||
let should_build_on_full = self
|
||||
.canonical_head
|
||||
.fork_choice_read_lock()
|
||||
.should_build_on_full(&parent_root, parent_payload_status)
|
||||
.map_err(|e| {
|
||||
BlockProductionError::BeaconChain(Box::new(BeaconChainError::ForkChoiceError(e)))
|
||||
})?;
|
||||
|
||||
// Extract the parent's execution requests from the envelope (if building on full).
|
||||
let parent_execution_requests = if should_build_on_full {
|
||||
parent_envelope
|
||||
.as_ref()
|
||||
.map(|env| env.message.execution_requests.clone())
|
||||
@@ -197,7 +215,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.clone()
|
||||
.produce_execution_payload_bid(
|
||||
state,
|
||||
parent_payload_status,
|
||||
should_build_on_full,
|
||||
parent_envelope,
|
||||
produce_at_slot,
|
||||
BID_VALUE_SELF_BUILD,
|
||||
@@ -700,12 +718,12 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
/// data needed to construct the `ExecutionPayloadEnvelope` after the beacon block is
|
||||
/// created, plus the EL block value and `should_override_builder` flag used by the
|
||||
/// caller to compare against any cached p2p builder bid.
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[allow(clippy::type_complexity, clippy::too_many_arguments)]
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
pub async fn produce_execution_payload_bid(
|
||||
self: Arc<Self>,
|
||||
state: BeaconState<T::EthSpec>,
|
||||
parent_payload_status: PayloadStatus,
|
||||
should_build_on_full: bool,
|
||||
parent_envelope: Option<Arc<SignedExecutionPayloadEnvelope<T::EthSpec>>>,
|
||||
produce_at_slot: Slot,
|
||||
bid_value: u64,
|
||||
@@ -751,20 +769,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
let parent_bid = state.latest_execution_payload_bid()?;
|
||||
|
||||
// TODO(gloas): need should_extend_payload check here as well
|
||||
let parent_block_slot = state.latest_block_header().slot;
|
||||
let parent_is_pre_gloas = !self
|
||||
.spec
|
||||
.fork_name_at_slot::<T::EthSpec>(parent_block_slot)
|
||||
.gloas_enabled();
|
||||
let parent_block_hash =
|
||||
if parent_payload_status == PayloadStatus::Full || parent_is_pre_gloas {
|
||||
// Build on parent bid's payload.
|
||||
parent_bid.block_hash
|
||||
} else {
|
||||
// Skip parent bid's payload. For genesis this is the EL genesis hash.
|
||||
parent_bid.parent_block_hash
|
||||
};
|
||||
let parent_block_hash = if should_build_on_full || parent_is_pre_gloas {
|
||||
// Build on parent bid's payload.
|
||||
parent_bid.block_hash
|
||||
} else {
|
||||
// Skip parent bid's payload. For genesis this is the EL genesis hash.
|
||||
parent_bid.parent_block_hash
|
||||
};
|
||||
|
||||
// TODO(gloas) this should be BlockProductionVersion::V4
|
||||
// V3 is okay for now as long as we're not connected to a builder
|
||||
@@ -953,10 +969,7 @@ fn get_execution_payload_gloas<T: BeaconChainTypes>(
|
||||
compute_timestamp_at_slot(state, state.slot(), spec).map_err(BeaconStateError::from)?;
|
||||
let random = *state.get_randao_mix(current_epoch)?;
|
||||
|
||||
// TODO(gloas): this gas limit calc is not necessarily right
|
||||
let parent_bid = state.latest_execution_payload_bid()?;
|
||||
let latest_gas_limit = parent_bid.gas_limit;
|
||||
|
||||
let is_parent_block_full = parent_block_hash == parent_bid.block_hash;
|
||||
|
||||
let withdrawals = if is_parent_block_full {
|
||||
@@ -992,7 +1005,6 @@ fn get_execution_payload_gloas<T: BeaconChainTypes>(
|
||||
random,
|
||||
proposer_index,
|
||||
parent_block_hash,
|
||||
latest_gas_limit,
|
||||
builder_params,
|
||||
withdrawals,
|
||||
parent_beacon_block_root,
|
||||
@@ -1020,7 +1032,6 @@ async fn prepare_execution_payload<T>(
|
||||
random: Hash256,
|
||||
proposer_index: u64,
|
||||
parent_block_hash: ExecutionBlockHash,
|
||||
parent_gas_limit: u64,
|
||||
builder_params: BuilderParams,
|
||||
withdrawals: Vec<Withdrawal>,
|
||||
parent_beacon_block_root: Hash256,
|
||||
@@ -1058,6 +1069,10 @@ where
|
||||
.get_suggested_fee_recipient(proposer_index)
|
||||
.await;
|
||||
let slot_number = Some(builder_params.slot.as_u64());
|
||||
let target_gas_limit = execution_layer
|
||||
.get_proposer_gas_limit(proposer_index)
|
||||
.await
|
||||
.unwrap_or(DEFAULT_GAS_LIMIT);
|
||||
|
||||
let payload_attributes = PayloadAttributes::new(
|
||||
timestamp,
|
||||
@@ -1066,13 +1081,12 @@ where
|
||||
Some(withdrawals),
|
||||
Some(parent_beacon_block_root),
|
||||
slot_number,
|
||||
Some(target_gas_limit),
|
||||
);
|
||||
|
||||
let target_gas_limit = execution_layer.get_proposer_gas_limit(proposer_index).await;
|
||||
let payload_parameters = PayloadParameters {
|
||||
parent_hash: parent_block_hash,
|
||||
parent_gas_limit,
|
||||
proposer_gas_limit: target_gas_limit,
|
||||
parent_gas_limit: None,
|
||||
proposer_gas_limit: Some(target_gas_limit),
|
||||
payload_attributes: &payload_attributes,
|
||||
forkchoice_update_params: &forkchoice_update_params,
|
||||
current_fork: fork,
|
||||
|
||||
@@ -342,7 +342,7 @@ pub fn get_execution_payload<T: BeaconChainTypes>(
|
||||
Ok(join_handle)
|
||||
}
|
||||
|
||||
/// Prepares an execution payload for inclusion in a block.
|
||||
/// Prepares an execution payload (pre-gloas) for inclusion in a block.
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
@@ -373,6 +373,13 @@ where
|
||||
{
|
||||
let spec = &chain.spec;
|
||||
let fork = spec.fork_name_at_slot::<T::EthSpec>(builder_params.slot);
|
||||
|
||||
if fork.gloas_enabled() {
|
||||
return Err(BlockProductionError::InvalidBlockVariant(
|
||||
"Called pre-gloas prepare_execution_payload on a gloas block".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let execution_layer = chain
|
||||
.execution_layer
|
||||
.as_ref()
|
||||
@@ -403,25 +410,20 @@ where
|
||||
.get_suggested_fee_recipient(proposer_index)
|
||||
.await;
|
||||
|
||||
let slot_number = if fork.gloas_enabled() {
|
||||
Some(builder_params.slot.as_u64())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let payload_attributes = PayloadAttributes::new(
|
||||
timestamp,
|
||||
random,
|
||||
suggested_fee_recipient,
|
||||
withdrawals,
|
||||
parent_beacon_block_root,
|
||||
slot_number,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
let target_gas_limit = execution_layer.get_proposer_gas_limit(proposer_index).await;
|
||||
let payload_parameters = PayloadParameters {
|
||||
parent_hash,
|
||||
parent_gas_limit: latest_execution_payload_header_gas_limit,
|
||||
parent_gas_limit: Some(latest_execution_payload_header_gas_limit),
|
||||
proposer_gas_limit: target_gas_limit,
|
||||
payload_attributes: &payload_attributes,
|
||||
forkchoice_update_params: &forkchoice_update_params,
|
||||
|
||||
@@ -43,9 +43,6 @@ pub(crate) fn verify_bid_consistency<E: EthSpec>(
|
||||
if bid.fee_recipient != proposer_preferences.message.fee_recipient {
|
||||
return Err(PayloadBidError::InvalidFeeRecipient);
|
||||
}
|
||||
if bid.gas_limit != proposer_preferences.message.gas_limit {
|
||||
return Err(PayloadBidError::InvalidGasLimit);
|
||||
}
|
||||
|
||||
let max_blobs_per_block =
|
||||
spec.max_blobs_per_block(bid_slot.epoch(E::slots_per_epoch())) as usize;
|
||||
@@ -161,7 +158,23 @@ impl<T: BeaconChainTypes> GossipVerifiedPayloadBid<T> {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(gloas) [IGNORE] bid.parent_block_hash is the block hash of a known execution payload in fork choice.
|
||||
// TODO(gloas): [IGNORE] bid.parent_block_hash is the block hash of a known execution
|
||||
// payload in fork choice.
|
||||
|
||||
// TODO(gloas): This uses head state's bid gas_limit as parent_gas_limit, which is only
|
||||
// correct when the bid's parent is the head. If the parent is an ancestor further back
|
||||
// this check may be inaccurate. Fixing this requires storing
|
||||
// gas_limit in fork choice or looking it up from the store by parent_block_hash. Taking the above
|
||||
// TODO into consideration maybe should persist parent block hash and gas limit in fork choice?
|
||||
if let Ok(parent_bid) = head_state.latest_execution_payload_bid()
|
||||
&& !is_gas_limit_target_compatible(
|
||||
parent_bid.gas_limit,
|
||||
signed_bid.message.gas_limit,
|
||||
proposer_preferences.message.target_gas_limit,
|
||||
)?
|
||||
{
|
||||
return Err(PayloadBidError::InvalidGasLimit);
|
||||
}
|
||||
|
||||
drop(fork_choice);
|
||||
|
||||
@@ -263,8 +276,36 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if `gas_limit` is compatible with `target_gas_limit` under the
|
||||
/// EIP-1559 transition rule from `parent_gas_limit`.
|
||||
pub fn is_gas_limit_target_compatible(
|
||||
parent_gas_limit: u64,
|
||||
gas_limit: u64,
|
||||
target_gas_limit: u64,
|
||||
) -> Result<bool, PayloadBidError> {
|
||||
let max_gas_limit_difference = (parent_gas_limit / 1024)
|
||||
.max(1)
|
||||
.checked_sub(1)
|
||||
.ok_or(PayloadBidError::InvalidGasLimit)?;
|
||||
let min_gas_limit = parent_gas_limit
|
||||
.checked_sub(max_gas_limit_difference)
|
||||
.ok_or(PayloadBidError::InvalidGasLimit)?;
|
||||
let max_gas_limit = parent_gas_limit
|
||||
.checked_add(max_gas_limit_difference)
|
||||
.ok_or(PayloadBidError::InvalidGasLimit)?;
|
||||
|
||||
if target_gas_limit >= min_gas_limit && target_gas_limit <= max_gas_limit {
|
||||
Ok(gas_limit == target_gas_limit)
|
||||
} else if target_gas_limit > max_gas_limit {
|
||||
Ok(gas_limit == max_gas_limit)
|
||||
} else {
|
||||
Ok(gas_limit == min_gas_limit)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::is_gas_limit_target_compatible;
|
||||
use bls::Signature;
|
||||
use kzg::KzgCommitment;
|
||||
use ssz_types::VariableList;
|
||||
@@ -288,11 +329,14 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn make_preferences(fee_recipient: Address, gas_limit: u64) -> SignedProposerPreferences {
|
||||
fn make_preferences(
|
||||
fee_recipient: Address,
|
||||
target_gas_limit: u64,
|
||||
) -> SignedProposerPreferences {
|
||||
SignedProposerPreferences {
|
||||
message: ProposerPreferences {
|
||||
fee_recipient,
|
||||
gas_limit,
|
||||
target_gas_limit,
|
||||
..ProposerPreferences::default()
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
@@ -382,13 +426,41 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gas_limit_mismatch() {
|
||||
let (state, spec) = state_and_spec();
|
||||
let current_slot = Slot::new(10);
|
||||
let bid = make_bid(current_slot, Address::ZERO, 30_000_000);
|
||||
let prefs = make_preferences(Address::ZERO, 50_000_000);
|
||||
fn test_is_gas_limit_target_compatible_increase_within_limit() {
|
||||
assert!(is_gas_limit_target_compatible(60_000_000, 60_000_100, 60_000_100).unwrap());
|
||||
}
|
||||
|
||||
let result = verify_bid_consistency::<E>(&bid, current_slot, &prefs, &state, &spec);
|
||||
assert!(matches!(result, Err(PayloadBidError::InvalidGasLimit)));
|
||||
#[test]
|
||||
fn test_is_gas_limit_target_compatible_increase_exceeding_limit() {
|
||||
// max_diff = 60_000_000 / 1024 - 1 = 58_592
|
||||
// max_gas_limit = 60_000_000 + 58_592 = 60_058_592
|
||||
assert!(is_gas_limit_target_compatible(60_000_000, 60_058_592, 100_000_000).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_gas_limit_target_compatible_increase_exceeding_off_by_one() {
|
||||
assert!(!is_gas_limit_target_compatible(60_000_000, 60_058_593, 100_000_000).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_gas_limit_target_compatible_decrease_within_limit() {
|
||||
assert!(is_gas_limit_target_compatible(60_000_000, 59_999_990, 59_999_990).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_gas_limit_target_compatible_decrease_exceeding_limit() {
|
||||
// min_gas_limit = 60_000_000 - 58_592 = 59_941_408
|
||||
assert!(is_gas_limit_target_compatible(60_000_000, 59_941_408, 30_000_000).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_gas_limit_target_compatible_target_equals_parent() {
|
||||
assert!(is_gas_limit_target_compatible(60_000_000, 60_000_000, 60_000_000).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_gas_limit_target_compatible_parent_underflows() {
|
||||
// parent=1023: max(1023/1024, 1) - 1 = max(0, 1) - 1 = 0, no change allowed
|
||||
assert!(is_gas_limit_target_compatible(1023, 1023, 60_000_000).unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ pub enum PayloadBidError {
|
||||
},
|
||||
/// The bids fee recipient doesn't match the proposer preferences fee recipient.
|
||||
InvalidFeeRecipient,
|
||||
/// The bids gas limit doesn't match the proposer preferences gas limit.
|
||||
/// The bid's gas limit is not compatible with the proposer's target gas limit.
|
||||
InvalidGasLimit,
|
||||
/// The bids execution payment is non-zero
|
||||
ExecutionPaymentNonZero { execution_payment: u64 },
|
||||
|
||||
@@ -101,6 +101,17 @@ impl TestContext {
|
||||
root: Hash256::ZERO,
|
||||
};
|
||||
|
||||
// Set a non-zero gas_limit on latest_execution_payload_bid so the gas limit
|
||||
// compatibility check doesn't reject all bids at genesis.
|
||||
if let Ok(bid) = state.latest_execution_payload_bid_mut() {
|
||||
bid.gas_limit = 30_000_000;
|
||||
}
|
||||
// Update body_root to reflect the modified bid (genesis block embeds it).
|
||||
let genesis_body_root = genesis_block(&state, &spec)
|
||||
.expect("should build genesis block")
|
||||
.body_root();
|
||||
state.latest_block_header_mut().body_root = genesis_body_root;
|
||||
|
||||
let inactive_keypair = &keypairs[NUM_BUILDERS];
|
||||
let inactive_creds = builder_withdrawal_credentials(&inactive_keypair.pk, &spec);
|
||||
let inactive_builder_index = state
|
||||
@@ -248,7 +259,7 @@ fn make_signed_preferences(
|
||||
proposal_slot: Slot,
|
||||
validator_index: u64,
|
||||
fee_recipient: Address,
|
||||
gas_limit: u64,
|
||||
target_gas_limit: u64,
|
||||
) -> Arc<SignedProposerPreferences> {
|
||||
Arc::new(SignedProposerPreferences {
|
||||
message: ProposerPreferences {
|
||||
@@ -256,7 +267,7 @@ fn make_signed_preferences(
|
||||
proposal_slot,
|
||||
validator_index,
|
||||
fee_recipient,
|
||||
gas_limit,
|
||||
target_gas_limit,
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
})
|
||||
|
||||
@@ -18,13 +18,16 @@ pub(crate) fn verify_preferences_consistency<E: EthSpec>(
|
||||
preferences: &ProposerPreferences,
|
||||
current_slot: Slot,
|
||||
head_state: &BeaconState<E>,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), ProposerPreferencesError> {
|
||||
let proposal_slot = preferences.proposal_slot;
|
||||
let validator_index = preferences.validator_index;
|
||||
let current_epoch = current_slot.epoch(E::slots_per_epoch());
|
||||
let proposal_epoch = proposal_slot.epoch(E::slots_per_epoch());
|
||||
|
||||
if proposal_epoch < current_epoch || proposal_epoch > current_epoch.saturating_add(1u64) {
|
||||
if proposal_epoch < current_epoch
|
||||
|| proposal_epoch > current_epoch.saturating_add(spec.min_seed_lookahead)
|
||||
{
|
||||
return Err(ProposerPreferencesError::InvalidProposalEpoch { proposal_epoch });
|
||||
}
|
||||
|
||||
@@ -35,7 +38,7 @@ pub(crate) fn verify_preferences_consistency<E: EthSpec>(
|
||||
});
|
||||
}
|
||||
|
||||
if !head_state.is_valid_proposal_slot(preferences)? {
|
||||
if !head_state.is_valid_proposal_slot(preferences, spec)? {
|
||||
return Err(ProposerPreferencesError::InvalidProposalSlot {
|
||||
validator_index,
|
||||
proposal_slot,
|
||||
@@ -83,7 +86,12 @@ impl GossipVerifiedProposerPreferences {
|
||||
});
|
||||
}
|
||||
|
||||
verify_preferences_consistency(&signed_preferences.message, current_slot, head_state)?;
|
||||
verify_preferences_consistency(
|
||||
&signed_preferences.message,
|
||||
current_slot,
|
||||
head_state,
|
||||
ctx.spec,
|
||||
)?;
|
||||
|
||||
// Verify signature
|
||||
proposer_preferences_signature_set(
|
||||
@@ -155,11 +163,13 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use types::{
|
||||
Address, BeaconState, EthSpec, Hash256, MinimalEthSpec, ProposerPreferences, Slot,
|
||||
Address, BeaconState, ChainSpec, EthSpec, Hash256, MinimalEthSpec, ProposerPreferences,
|
||||
Slot,
|
||||
};
|
||||
|
||||
use super::verify_preferences_consistency;
|
||||
use crate::proposer_preferences_verification::ProposerPreferencesError;
|
||||
use crate::test_utils::{fork_name_from_env, test_spec};
|
||||
|
||||
type E = MinimalEthSpec;
|
||||
|
||||
@@ -169,20 +179,28 @@ mod tests {
|
||||
proposal_slot,
|
||||
validator_index,
|
||||
fee_recipient: Address::ZERO,
|
||||
gas_limit: 30_000_000,
|
||||
target_gas_limit: 30_000_000,
|
||||
}
|
||||
}
|
||||
|
||||
fn state() -> BeaconState<E> {
|
||||
BeaconState::new(0, <_>::default(), &E::default_spec())
|
||||
let spec = spec();
|
||||
BeaconState::new(0, <_>::default(), &spec)
|
||||
}
|
||||
|
||||
fn spec() -> ChainSpec {
|
||||
test_spec::<E>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_epoch_too_old() {
|
||||
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
|
||||
return;
|
||||
}
|
||||
let current_slot = Slot::new(2 * E::slots_per_epoch());
|
||||
let prefs = make_preferences(Slot::new(3), 0);
|
||||
|
||||
let result = verify_preferences_consistency::<E>(&prefs, current_slot, &state());
|
||||
let result = verify_preferences_consistency::<E>(&prefs, current_slot, &state(), &spec());
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ProposerPreferencesError::InvalidProposalEpoch { .. })
|
||||
@@ -191,10 +209,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_invalid_epoch_too_far_ahead() {
|
||||
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
|
||||
return;
|
||||
}
|
||||
let current_slot = Slot::new(E::slots_per_epoch());
|
||||
let prefs = make_preferences(Slot::new(3 * E::slots_per_epoch() + 1), 0);
|
||||
|
||||
let result = verify_preferences_consistency::<E>(&prefs, current_slot, &state());
|
||||
let result = verify_preferences_consistency::<E>(&prefs, current_slot, &state(), &spec());
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ProposerPreferencesError::InvalidProposalEpoch { .. })
|
||||
@@ -203,10 +224,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_proposal_slot_already_passed() {
|
||||
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
|
||||
return;
|
||||
}
|
||||
let current_slot = Slot::new(10);
|
||||
let prefs = make_preferences(Slot::new(9), 0);
|
||||
|
||||
let result = verify_preferences_consistency::<E>(&prefs, current_slot, &state());
|
||||
let result = verify_preferences_consistency::<E>(&prefs, current_slot, &state(), &spec());
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ProposerPreferencesError::ProposalSlotAlreadyPassed { .. })
|
||||
@@ -215,10 +239,13 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_proposal_slot_equal_to_current() {
|
||||
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
|
||||
return;
|
||||
}
|
||||
let current_slot = Slot::new(10);
|
||||
let prefs = make_preferences(Slot::new(10), 0);
|
||||
|
||||
let result = verify_preferences_consistency::<E>(&prefs, current_slot, &state());
|
||||
let result = verify_preferences_consistency::<E>(&prefs, current_slot, &state(), &spec());
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(ProposerPreferencesError::ProposalSlotAlreadyPassed { .. })
|
||||
|
||||
@@ -24,7 +24,7 @@ mod tests;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ProposerPreferencesError {
|
||||
/// The proposal slot is not in the current or next epoch.
|
||||
/// The proposal slot is not within the proposer lookahead.
|
||||
InvalidProposalEpoch { proposal_epoch: Epoch },
|
||||
/// The proposal slot has already passed.
|
||||
ProposalSlotAlreadyPassed {
|
||||
|
||||
@@ -87,7 +87,7 @@ mod tests {
|
||||
proposal_slot: slot,
|
||||
validator_index,
|
||||
fee_recipient: Address::ZERO,
|
||||
gas_limit: 30_000_000,
|
||||
target_gas_limit: 30_000_000,
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
}),
|
||||
|
||||
@@ -112,7 +112,7 @@ impl TestContext {
|
||||
let slot_in_epoch = slot.as_usize() % E::slots_per_epoch() as usize;
|
||||
let epoch = slot.epoch(E::slots_per_epoch());
|
||||
let current_epoch = state.slot().epoch(E::slots_per_epoch());
|
||||
let index = if epoch == current_epoch.saturating_add(1u64) {
|
||||
let index = if epoch == current_epoch.saturating_add(self.spec.min_seed_lookahead) {
|
||||
E::slots_per_epoch() as usize + slot_in_epoch
|
||||
} else {
|
||||
slot_in_epoch
|
||||
@@ -131,7 +131,7 @@ fn make_signed_preferences(
|
||||
proposal_slot,
|
||||
validator_index,
|
||||
fee_recipient: Address::ZERO,
|
||||
gas_limit: 30_000_000,
|
||||
target_gas_limit: 30_000_000,
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
})
|
||||
@@ -271,7 +271,7 @@ fn same_validator_different_dependent_root_not_deduplicated() {
|
||||
validator_index: 42,
|
||||
dependent_root: Hash256::repeat_byte(0xaa),
|
||||
fee_recipient: Address::ZERO,
|
||||
gas_limit: 30_000_000,
|
||||
target_gas_limit: 30_000_000,
|
||||
},
|
||||
signature: Signature::empty(),
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user