mirror of
https://github.com/sigp/lighthouse.git
synced 2026-05-01 11:43:44 +00:00
Re-issue ForkchoiceUpdate based on updated PayloadStatus (#9102)
Co-Authored-By: hopinheimer <knmanas6@gmail.com> Co-Authored-By: Michael Sproul <michael@sigmaprime.io> Co-Authored-By: Michael Sproul <michaelsproul@users.noreply.github.com>
This commit is contained in:
@@ -117,8 +117,8 @@ use state_processing::{
|
||||
epoch_cache::initialize_epoch_cache,
|
||||
per_block_processing,
|
||||
per_block_processing::{
|
||||
VerifySignatures, errors::AttestationValidationError, get_expected_withdrawals,
|
||||
verify_attestation_for_block_inclusion,
|
||||
VerifySignatures, apply_parent_execution_payload, errors::AttestationValidationError,
|
||||
get_expected_withdrawals, verify_attestation_for_block_inclusion,
|
||||
},
|
||||
per_slot_processing,
|
||||
state_advance::{complete_state_advance, partial_state_advance},
|
||||
@@ -4858,16 +4858,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
proposal_slot: Slot,
|
||||
) -> Result<Withdrawals<T::EthSpec>, Error> {
|
||||
let cached_head = self.canonical_head.cached_head();
|
||||
let head_block = &cached_head.snapshot.beacon_block;
|
||||
let head_block_root = cached_head.head_block_root();
|
||||
let head_state = &cached_head.snapshot.beacon_state;
|
||||
|
||||
let parent_block_root = forkchoice_update_params.head_root;
|
||||
|
||||
let (unadvanced_state, unadvanced_state_root) =
|
||||
if cached_head.head_block_root() == parent_block_root {
|
||||
(Cow::Borrowed(head_state), cached_head.head_state_root())
|
||||
let (unadvanced_state, unadvanced_state_root, parent_bid_block_hash) =
|
||||
if parent_block_root == head_block_root {
|
||||
(
|
||||
Cow::Borrowed(head_state),
|
||||
cached_head.head_state_root(),
|
||||
head_block.payload_bid_block_hash().ok(),
|
||||
)
|
||||
} else {
|
||||
// TODO(gloas): this function needs updating to be envelope-aware
|
||||
// See: https://github.com/sigp/lighthouse/issues/8957
|
||||
let block = self
|
||||
.get_blinded_block(&parent_block_root)?
|
||||
.ok_or(Error::MissingBeaconBlock(parent_block_root))?;
|
||||
@@ -4875,20 +4879,27 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.store
|
||||
.get_advanced_hot_state(parent_block_root, proposal_slot, block.state_root())?
|
||||
.ok_or(Error::MissingBeaconState(block.state_root()))?;
|
||||
(Cow::Owned(state), state_root)
|
||||
(
|
||||
Cow::Owned(state),
|
||||
state_root,
|
||||
block.payload_bid_block_hash().ok(),
|
||||
)
|
||||
};
|
||||
|
||||
// Parent state epoch is the same as the proposal, we don't need to advance because the
|
||||
// list of expected withdrawals can only change after an epoch advance or a
|
||||
// block application.
|
||||
let proposal_epoch = proposal_slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
if head_state.current_epoch() == proposal_epoch {
|
||||
return get_expected_withdrawals(&unadvanced_state, &self.spec)
|
||||
.map(Into::into)
|
||||
.map_err(Error::PrepareProposerFailed);
|
||||
}
|
||||
let parent_payload_status = if let Some(block_hash) = parent_bid_block_hash
|
||||
&& block_hash != ExecutionBlockHash::default()
|
||||
&& forkchoice_update_params.head_hash == Some(block_hash)
|
||||
{
|
||||
fork_choice::PayloadStatus::Full
|
||||
} else {
|
||||
fork_choice::PayloadStatus::Empty
|
||||
};
|
||||
|
||||
// Advance the state using the partial method.
|
||||
// TODO(gloas): we might want to optimise this further by using:
|
||||
// - `get_advanced_hot_state` instead of the cached head
|
||||
// - restoring the pre-Gloas optimisation to avoid advancing further than the epoch
|
||||
// boundary
|
||||
debug!(
|
||||
%proposal_slot,
|
||||
?parent_block_root,
|
||||
@@ -4898,9 +4909,33 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
partial_state_advance(
|
||||
&mut advanced_state,
|
||||
Some(unadvanced_state_root),
|
||||
proposal_epoch.start_slot(T::EthSpec::slots_per_epoch()),
|
||||
proposal_slot,
|
||||
&self.spec,
|
||||
)?;
|
||||
|
||||
// For Gloas, when the head payload is Full, we need to apply the parent's
|
||||
// execution requests to the state to get the correct withdrawals.
|
||||
if parent_payload_status == fork_choice::PayloadStatus::Full {
|
||||
let envelope = if parent_block_root == head_block_root {
|
||||
cached_head.snapshot.execution_envelope.clone()
|
||||
} else {
|
||||
self.store
|
||||
.get_payload_envelope(&parent_block_root)?
|
||||
.map(Arc::new)
|
||||
}
|
||||
.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,
|
||||
)
|
||||
.map_err(Error::PrepareProposerFailed)?;
|
||||
}
|
||||
|
||||
get_expected_withdrawals(&advanced_state, &self.spec)
|
||||
.map(Into::into)
|
||||
.map_err(Error::PrepareProposerFailed)
|
||||
@@ -6112,13 +6147,20 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
fcu_params.head_root,
|
||||
&cached_head,
|
||||
)?;
|
||||
Ok::<_, Error>(Some((fcu_params, pre_payload_attributes)))
|
||||
let head_payload_status = cached_head.head_payload_status();
|
||||
Ok::<_, Error>(Some((
|
||||
fcu_params,
|
||||
pre_payload_attributes,
|
||||
head_payload_status,
|
||||
)))
|
||||
},
|
||||
"prepare_beacon_proposer_head_read",
|
||||
)
|
||||
.await??;
|
||||
|
||||
let Some((forkchoice_update_params, Some(pre_payload_attributes))) = maybe_prep_data else {
|
||||
let Some((forkchoice_update_params, Some(pre_payload_attributes), head_payload_status)) =
|
||||
maybe_prep_data
|
||||
else {
|
||||
// Appropriate log messages have already been logged above and in
|
||||
// `get_pre_payload_attributes`.
|
||||
return Ok(None);
|
||||
@@ -6140,7 +6182,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
// considerable time to compute if a state load is required.
|
||||
let head_root = forkchoice_update_params.head_root;
|
||||
let payload_attributes = if let Some(payload_attributes) = execution_layer
|
||||
.payload_attributes(prepare_slot, head_root)
|
||||
.payload_attributes(prepare_slot, head_root, head_payload_status)
|
||||
.await
|
||||
{
|
||||
payload_attributes
|
||||
@@ -6187,6 +6229,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
.insert_proposer(
|
||||
prepare_slot,
|
||||
head_root,
|
||||
head_payload_status,
|
||||
proposer,
|
||||
payload_attributes.clone(),
|
||||
)
|
||||
@@ -6198,6 +6241,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
%prepare_slot,
|
||||
validator = proposer,
|
||||
parent_root = ?head_root,
|
||||
payload_status = ?head_payload_status,
|
||||
"Prepared beacon proposer"
|
||||
);
|
||||
payload_attributes
|
||||
@@ -6250,6 +6294,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self.update_execution_engine_forkchoice(
|
||||
current_slot,
|
||||
forkchoice_update_params,
|
||||
head_payload_status,
|
||||
OverrideForkchoiceUpdate::AlreadyApplied,
|
||||
)
|
||||
.await?;
|
||||
@@ -6262,6 +6307,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
self: &Arc<Self>,
|
||||
current_slot: Slot,
|
||||
input_params: ForkchoiceUpdateParameters,
|
||||
head_payload_status: fork_choice::PayloadStatus,
|
||||
override_forkchoice_update: OverrideForkchoiceUpdate,
|
||||
) -> Result<(), Error> {
|
||||
let execution_layer = self
|
||||
@@ -6322,6 +6368,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
finalized_hash,
|
||||
current_slot,
|
||||
head_block_root,
|
||||
head_payload_status,
|
||||
)
|
||||
.await
|
||||
.map_err(Error::ExecutionForkChoiceUpdateFailed);
|
||||
|
||||
@@ -827,8 +827,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
||||
|
||||
// The execution layer updates might attempt to take a write-lock on fork choice, so it's
|
||||
// important to ensure the fork-choice lock isn't being held.
|
||||
let el_update_handle =
|
||||
spawn_execution_layer_updates(self.clone(), new_forkchoice_update_parameters)?;
|
||||
let el_update_handle = spawn_execution_layer_updates(
|
||||
self.clone(),
|
||||
new_forkchoice_update_parameters,
|
||||
new_payload_status,
|
||||
)?;
|
||||
|
||||
// We have completed recomputing the head and it's now valid for another process to do the
|
||||
// same.
|
||||
@@ -1186,6 +1189,7 @@ fn perform_debug_logging<T: BeaconChainTypes>(
|
||||
fn spawn_execution_layer_updates<T: BeaconChainTypes>(
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||
head_payload_status: PayloadStatus,
|
||||
) -> Result<JoinHandle<Option<()>>, Error> {
|
||||
let current_slot = chain
|
||||
.slot_clock
|
||||
@@ -1208,6 +1212,7 @@ fn spawn_execution_layer_updates<T: BeaconChainTypes>(
|
||||
.update_execution_engine_forkchoice(
|
||||
current_slot,
|
||||
forkchoice_update_params,
|
||||
head_payload_status,
|
||||
OverrideForkchoiceUpdate::Yes,
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -771,6 +771,36 @@ where
|
||||
.execution_block_generator()
|
||||
}
|
||||
|
||||
/// Create a switch-to-compounding `ConsolidationRequest` for the given validator.
|
||||
///
|
||||
/// Panics if the validator doesn't exist, doesn't have eth1 withdrawal credentials,
|
||||
/// or doesn't have an execution withdrawal address.
|
||||
pub fn make_switch_to_compounding_request(
|
||||
&self,
|
||||
validator_index: usize,
|
||||
) -> ConsolidationRequest {
|
||||
let head = self.chain.canonical_head.cached_head();
|
||||
let head_state = &head.snapshot.beacon_state;
|
||||
let validator = head_state
|
||||
.get_validator(validator_index)
|
||||
.expect("validator should exist");
|
||||
|
||||
assert!(
|
||||
validator.has_eth1_withdrawal_credential(&self.spec),
|
||||
"validator {validator_index} should have eth1 withdrawal credentials"
|
||||
);
|
||||
|
||||
let source_address = validator
|
||||
.get_execution_withdrawal_address(&self.spec)
|
||||
.expect("validator should have execution withdrawal address");
|
||||
|
||||
ConsolidationRequest {
|
||||
source_address,
|
||||
source_pubkey: validator.pubkey,
|
||||
target_pubkey: validator.pubkey,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_mock_builder(
|
||||
&mut self,
|
||||
beacon_url: SensitiveUrl,
|
||||
|
||||
@@ -6,6 +6,7 @@ mod column_verification;
|
||||
mod events;
|
||||
mod op_verification;
|
||||
mod payload_invalidation;
|
||||
mod prepare_payload;
|
||||
mod rewards;
|
||||
mod schema_stability;
|
||||
mod store_tests;
|
||||
|
||||
575
beacon_node/beacon_chain/tests/prepare_payload.rs
Normal file
575
beacon_node/beacon_chain/tests/prepare_payload.rs
Normal file
@@ -0,0 +1,575 @@
|
||||
#![cfg(not(debug_assertions))]
|
||||
#![allow(clippy::result_large_err)]
|
||||
|
||||
use beacon_chain::test_utils::{
|
||||
AttestationStrategy, BeaconChainHarness, BlockStrategy, DiskHarnessType, test_spec,
|
||||
};
|
||||
use beacon_chain::{ChainConfig, custody_context::NodeCustodyType};
|
||||
use bls::Keypair;
|
||||
use eth2::types::ProposerPreparationData;
|
||||
use fork_choice::PayloadStatus;
|
||||
use logging::create_test_tracing_subscriber;
|
||||
use ssz_types::VariableList;
|
||||
use state_processing::{
|
||||
per_block_processing::{apply_parent_execution_payload, withdrawals::get_expected_withdrawals},
|
||||
state_advance::complete_state_advance,
|
||||
};
|
||||
use std::sync::{Arc, LazyLock};
|
||||
use store::database::interface::BeaconNodeBackend;
|
||||
use store::{HotColdDB, StoreConfig};
|
||||
use tempfile::{TempDir, tempdir};
|
||||
use types::*;
|
||||
|
||||
// Should ideally be divisible by 3.
|
||||
pub const LOW_VALIDATOR_COUNT: usize = 32;
|
||||
pub const HIGH_VALIDATOR_COUNT: usize = 64;
|
||||
|
||||
/// A cached set of keys.
|
||||
static KEYPAIRS: LazyLock<Vec<Keypair>> =
|
||||
LazyLock::new(|| types::test_utils::generate_deterministic_keypairs(HIGH_VALIDATOR_COUNT));
|
||||
|
||||
type E = MinimalEthSpec;
|
||||
type TestHarness = BeaconChainHarness<DiskHarnessType<E>>;
|
||||
|
||||
fn get_store(
|
||||
db_path: &TempDir,
|
||||
spec: Arc<ChainSpec>,
|
||||
) -> Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>> {
|
||||
let store_config = StoreConfig {
|
||||
prune_payloads: false,
|
||||
..StoreConfig::default()
|
||||
};
|
||||
get_store_generic(db_path, store_config, spec)
|
||||
}
|
||||
|
||||
fn get_store_generic(
|
||||
db_path: &TempDir,
|
||||
config: StoreConfig,
|
||||
spec: Arc<ChainSpec>,
|
||||
) -> Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>> {
|
||||
create_test_tracing_subscriber();
|
||||
let hot_path = db_path.path().join("chain_db");
|
||||
let cold_path = db_path.path().join("freezer_db");
|
||||
let blobs_path = db_path.path().join("blobs_db");
|
||||
|
||||
HotColdDB::open(
|
||||
&hot_path,
|
||||
&cold_path,
|
||||
&blobs_path,
|
||||
|_, _, _| Ok(()),
|
||||
config,
|
||||
spec,
|
||||
)
|
||||
.expect("disk store should initialize")
|
||||
}
|
||||
|
||||
fn get_harness(
|
||||
store: Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>,
|
||||
validator_count: usize,
|
||||
) -> TestHarness {
|
||||
// Most tests expect to retain historic states, so we use this as the default.
|
||||
let chain_config = ChainConfig {
|
||||
archive: true,
|
||||
..ChainConfig::default()
|
||||
};
|
||||
get_harness_generic(
|
||||
store,
|
||||
validator_count,
|
||||
chain_config,
|
||||
NodeCustodyType::Fullnode,
|
||||
)
|
||||
}
|
||||
|
||||
fn get_harness_generic(
|
||||
store: Arc<HotColdDB<E, BeaconNodeBackend<E>, BeaconNodeBackend<E>>>,
|
||||
validator_count: usize,
|
||||
chain_config: ChainConfig,
|
||||
node_custody_type: NodeCustodyType,
|
||||
) -> TestHarness {
|
||||
let harness = TestHarness::builder(MinimalEthSpec)
|
||||
.spec(store.get_chain_spec().clone())
|
||||
.keypairs(KEYPAIRS[0..validator_count].to_vec())
|
||||
.fresh_disk_store(store)
|
||||
.mock_execution_layer()
|
||||
.chain_config(chain_config)
|
||||
.node_custody_type(node_custody_type)
|
||||
.build();
|
||||
harness.advance_slot();
|
||||
harness
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_full_parent_next_slot() {
|
||||
prepare_payload_generic(
|
||||
PayloadStatus::Full,
|
||||
Slot::new(3 * E::slots_per_epoch() + 1),
|
||||
Slot::new(3 * E::slots_per_epoch() + 2),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_full_parent_one_epoch_skip() {
|
||||
prepare_payload_generic(
|
||||
PayloadStatus::Full,
|
||||
Slot::new(3 * E::slots_per_epoch() + 1),
|
||||
Slot::new(4 * E::slots_per_epoch()),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_full_parent_uneven_one_epoch_skip() {
|
||||
prepare_payload_generic(
|
||||
PayloadStatus::Full,
|
||||
Slot::new(3 * E::slots_per_epoch() + 1),
|
||||
Slot::new(5 * E::slots_per_epoch() - 1),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_empty_parent_next_slot() {
|
||||
prepare_payload_generic(
|
||||
PayloadStatus::Empty,
|
||||
Slot::new(3 * E::slots_per_epoch() + 1),
|
||||
Slot::new(3 * E::slots_per_epoch() + 2),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_empty_parent_one_epoch_skip() {
|
||||
prepare_payload_generic(
|
||||
PayloadStatus::Empty,
|
||||
Slot::new(3 * E::slots_per_epoch() + 1),
|
||||
Slot::new(4 * E::slots_per_epoch()),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn prepare_payload_generic(
|
||||
parent_payload_status: PayloadStatus,
|
||||
parent_block_slot: Slot,
|
||||
prepare_slot: Slot,
|
||||
) {
|
||||
assert!(parent_block_slot > 0);
|
||||
|
||||
// Post-Gloas test.
|
||||
let spec = Arc::new(test_spec::<E>());
|
||||
if !spec.fork_name_at_slot::<E>(Slot::new(0)).gloas_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
let num_blocks_produced = parent_block_slot.as_u64() - 1;
|
||||
let db_path = tempdir().unwrap();
|
||||
let store = get_store(&db_path, spec.clone());
|
||||
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
|
||||
|
||||
harness
|
||||
.extend_chain(
|
||||
num_blocks_produced as usize,
|
||||
BlockStrategy::OnCanonicalHead,
|
||||
AttestationStrategy::AllValidators,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Advance the slot so the next extend_chain produces at a fresh slot.
|
||||
harness.advance_slot();
|
||||
|
||||
// Produce a block with a payload that affects withdrawals for the next slot.
|
||||
// A switch-to-compounding consolidation changes withdrawal credentials from 0x01 to 0x02,
|
||||
// which queues the validator's excess balance as a pending deposit and removes it from the
|
||||
// partial withdrawal sweep. We target an odd-indexed validator since odd validators are
|
||||
// created with eth1 withdrawal credentials in the interop genesis builder.
|
||||
let consolidation_request = harness.make_switch_to_compounding_request(1);
|
||||
|
||||
let execution_requests = ExecutionRequests::<E> {
|
||||
deposits: VariableList::empty(),
|
||||
withdrawals: VariableList::empty(),
|
||||
consolidations: VariableList::new(vec![consolidation_request]).unwrap(),
|
||||
};
|
||||
|
||||
// Inject the execution requests into the mock EL so the next payload includes them.
|
||||
harness
|
||||
.execution_block_generator()
|
||||
.set_next_execution_requests(execution_requests);
|
||||
|
||||
// Produce and import one more block. Its envelope will contain the consolidation request.
|
||||
// TODO(gloas): all this ugly plumbing could be avoided with some more "implicit" context
|
||||
// methods
|
||||
let state = harness.get_current_state();
|
||||
let (block_contents, opt_envelope, parent_block_state) = harness
|
||||
.make_block_with_envelope(state, parent_block_slot)
|
||||
.await;
|
||||
let envelope = opt_envelope.unwrap();
|
||||
let block_root = harness
|
||||
.process_block(
|
||||
parent_block_slot,
|
||||
block_contents.0.canonical_root(),
|
||||
block_contents.clone(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// TODO(gloas): try a case where head is empty even though envelope is processed
|
||||
if parent_payload_status == PayloadStatus::Full {
|
||||
harness
|
||||
.process_envelope(
|
||||
block_root.into(),
|
||||
envelope.clone(),
|
||||
&parent_block_state,
|
||||
block_contents.0.state_root(),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// Verify that the withdrawals computed from the block's state differ from the withdrawals
|
||||
// computed from the block's state with its payload applied by
|
||||
// `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();
|
||||
|
||||
let mut unadvanced_full_state = unadvanced_empty_state.clone();
|
||||
apply_parent_execution_payload(
|
||||
&mut unadvanced_full_state,
|
||||
parent_bid,
|
||||
&envelope.message.execution_requests,
|
||||
&spec,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut advanced_full_state = advanced_empty_state.clone();
|
||||
apply_parent_execution_payload(
|
||||
&mut advanced_full_state,
|
||||
parent_bid,
|
||||
&envelope.message.execution_requests,
|
||||
&spec,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let withdrawals_unadvanced_empty: Withdrawals<E> =
|
||||
get_expected_withdrawals(unadvanced_empty_state, &spec)
|
||||
.unwrap()
|
||||
.into();
|
||||
let withdrawals_advanced_empty: Withdrawals<E> =
|
||||
get_expected_withdrawals(&advanced_empty_state, &spec)
|
||||
.unwrap()
|
||||
.into();
|
||||
let withdrawals_unadvanced_full: Withdrawals<E> =
|
||||
get_expected_withdrawals(&unadvanced_full_state, &spec)
|
||||
.unwrap()
|
||||
.into();
|
||||
let withdrawals_advanced_full: Withdrawals<E> =
|
||||
get_expected_withdrawals(&advanced_full_state, &spec)
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
assert_ne!(
|
||||
withdrawals_advanced_empty, withdrawals_advanced_full,
|
||||
"Applying execution requests should change the expected withdrawals"
|
||||
);
|
||||
|
||||
let expect_state_advance_to_change_withdrawals =
|
||||
prepare_slot.epoch(E::slots_per_epoch()) > parent_block_slot.epoch(E::slots_per_epoch());
|
||||
if expect_state_advance_to_change_withdrawals {
|
||||
if parent_payload_status == fork_choice::PayloadStatus::Full {
|
||||
assert_ne!(
|
||||
withdrawals_unadvanced_full, withdrawals_advanced_full,
|
||||
"Advancing the state should change the withdrawals"
|
||||
);
|
||||
} else {
|
||||
assert_ne!(
|
||||
withdrawals_unadvanced_empty, withdrawals_advanced_empty,
|
||||
"Advancing the state should change the withdrawals"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Call `prepare_beacon_proposer` for the next slot and ensure that it primes the execution
|
||||
// layer payload attributes cache with the correct withdrawals (the ones taking into account
|
||||
// the applied execution_requests).
|
||||
let current_slot = prepare_slot - 1;
|
||||
let proposer_index = advanced_empty_state
|
||||
.get_beacon_proposer_index(prepare_slot, &spec)
|
||||
.expect("should get proposer index");
|
||||
|
||||
// Register the proposer so prepare_beacon_proposer doesn't skip it.
|
||||
let el = harness.chain.execution_layer.as_ref().unwrap();
|
||||
el.update_proposer_preparation(
|
||||
prepare_slot.epoch(E::slots_per_epoch()),
|
||||
[(
|
||||
&ProposerPreparationData {
|
||||
validator_index: proposer_index as u64,
|
||||
fee_recipient: Address::repeat_byte(42),
|
||||
},
|
||||
&None,
|
||||
)],
|
||||
)
|
||||
.await;
|
||||
|
||||
// Advance the slot clock to just before the prepare slot so the lookahead check passes.
|
||||
harness.advance_to_slot_lookahead(prepare_slot, harness.chain.config.prepare_payload_lookahead);
|
||||
|
||||
harness
|
||||
.chain
|
||||
.prepare_beacon_proposer(current_slot)
|
||||
.await
|
||||
.expect("prepare_beacon_proposer should succeed");
|
||||
|
||||
// Read the payload attributes from the EL cache and verify the withdrawals.
|
||||
let el = harness.chain.execution_layer.as_ref().unwrap();
|
||||
let head_root = harness.head_block_root();
|
||||
let attributes = el
|
||||
.payload_attributes(prepare_slot, head_root, parent_payload_status)
|
||||
.await
|
||||
.expect("should have cached payload attributes for prepare_slot");
|
||||
|
||||
let actual_withdrawals = attributes.withdrawals().unwrap();
|
||||
let expected_withdrawals: Vec<Withdrawal> = if parent_payload_status == PayloadStatus::Full {
|
||||
withdrawals_advanced_full.to_vec()
|
||||
} else {
|
||||
withdrawals_advanced_empty.to_vec()
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
actual_withdrawals, &expected_withdrawals,
|
||||
"prepare_beacon_proposer should use withdrawals computed from the \
|
||||
{parent_payload_status:?} state"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_genesis_next_slot() {
|
||||
prepare_payload_on_genesis_generic(Slot::new(1)).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_genesis_skip_two_epochs() {
|
||||
prepare_payload_on_genesis_generic(Slot::new(2 * E::slots_per_epoch())).await;
|
||||
}
|
||||
|
||||
async fn prepare_payload_on_genesis_generic(prepare_slot: Slot) {
|
||||
// Post-Gloas test.
|
||||
let spec = Arc::new(test_spec::<E>());
|
||||
if !spec.fork_name_at_slot::<E>(Slot::new(0)).gloas_enabled() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Genesis is always considered Empty.
|
||||
let parent_payload_status = PayloadStatus::Empty;
|
||||
|
||||
let db_path = tempdir().unwrap();
|
||||
let store = get_store(&db_path, spec.clone());
|
||||
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
|
||||
|
||||
// At genesis withdrawals are empty (because nothing has happened yet), so we don't assert
|
||||
// anything about the advanced vs unadvanced state. This test just exists to test that
|
||||
// calculating payload attributes at genesis works and doesn't error.
|
||||
let cached_head = harness.chain.canonical_head.cached_head();
|
||||
let unadvanced_state = &cached_head.snapshot.beacon_state;
|
||||
|
||||
let mut advanced_state = unadvanced_state.clone();
|
||||
complete_state_advance(&mut advanced_state, None, prepare_slot, &spec).unwrap();
|
||||
|
||||
let withdrawals_advanced: Withdrawals<E> = get_expected_withdrawals(&advanced_state, &spec)
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
// Call `prepare_beacon_proposer` for the next slot and ensure that it primes the execution
|
||||
// layer payload attributes cache with the correct withdrawals (the ones taking into account
|
||||
// the state advance).
|
||||
let current_slot = prepare_slot - 1;
|
||||
let proposer_index = advanced_state
|
||||
.get_beacon_proposer_index(prepare_slot, &spec)
|
||||
.unwrap();
|
||||
|
||||
// Register the proposer so prepare_beacon_proposer doesn't skip it.
|
||||
let el = harness.chain.execution_layer.as_ref().unwrap();
|
||||
el.update_proposer_preparation(
|
||||
prepare_slot.epoch(E::slots_per_epoch()),
|
||||
[(
|
||||
&ProposerPreparationData {
|
||||
validator_index: proposer_index as u64,
|
||||
fee_recipient: Address::repeat_byte(42),
|
||||
},
|
||||
&None,
|
||||
)],
|
||||
)
|
||||
.await;
|
||||
|
||||
// Advance the slot clock to just before the prepare slot so the lookahead check passes.
|
||||
harness.advance_to_slot_lookahead(prepare_slot, harness.chain.config.prepare_payload_lookahead);
|
||||
|
||||
harness
|
||||
.chain
|
||||
.prepare_beacon_proposer(current_slot)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Read the payload attributes from the EL cache and verify the withdrawals.
|
||||
let el = harness.chain.execution_layer.as_ref().unwrap();
|
||||
let head_root = harness.head_block_root();
|
||||
let attributes = el
|
||||
.payload_attributes(prepare_slot, head_root, parent_payload_status)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let actual_withdrawals = attributes.withdrawals().unwrap();
|
||||
let expected_withdrawals: Vec<Withdrawal> = withdrawals_advanced.to_vec();
|
||||
|
||||
assert_eq!(
|
||||
actual_withdrawals, &expected_withdrawals,
|
||||
"prepare_beacon_proposer should use withdrawals computed from the \
|
||||
{parent_payload_status:?} advanced genesis state"
|
||||
);
|
||||
assert!(actual_withdrawals.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_fork_boundary_no_skip() {
|
||||
prepare_payload_on_fork_boundary(
|
||||
Slot::new(2 * E::slots_per_epoch()) - 1,
|
||||
Slot::new(2 * E::slots_per_epoch()),
|
||||
Epoch::new(2),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_fork_boundary_skip_one_prior() {
|
||||
prepare_payload_on_fork_boundary(
|
||||
Slot::new(2 * E::slots_per_epoch()) - 2,
|
||||
Slot::new(2 * E::slots_per_epoch()),
|
||||
Epoch::new(2),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_fork_boundary_skip_one_after() {
|
||||
prepare_payload_on_fork_boundary(
|
||||
Slot::new(2 * E::slots_per_epoch()) - 1,
|
||||
Slot::new(2 * E::slots_per_epoch()) + 1,
|
||||
Epoch::new(2),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn prepare_payload_on_fork_boundary_skip_whole_epoch() {
|
||||
prepare_payload_on_fork_boundary(
|
||||
Slot::new(E::slots_per_epoch()),
|
||||
Slot::new(2 * E::slots_per_epoch()),
|
||||
Epoch::new(2),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn prepare_payload_on_fork_boundary(
|
||||
parent_block_slot: Slot,
|
||||
prepare_slot: Slot,
|
||||
gloas_fork_epoch: Epoch,
|
||||
) {
|
||||
// Post-Gloas test.
|
||||
let mut spec = test_spec::<E>();
|
||||
if !spec.fork_name_at_slot::<E>(Slot::new(0)).gloas_enabled() {
|
||||
return;
|
||||
}
|
||||
spec.gloas_fork_epoch = Some(gloas_fork_epoch);
|
||||
let spec = Arc::new(spec);
|
||||
|
||||
// Pre-Gloas blocks are always considered Empty.
|
||||
let parent_payload_status = PayloadStatus::Empty;
|
||||
|
||||
let num_blocks_produced = parent_block_slot.as_u64();
|
||||
let db_path = tempdir().unwrap();
|
||||
let store = get_store(&db_path, spec.clone());
|
||||
let harness = get_harness(store.clone(), LOW_VALIDATOR_COUNT);
|
||||
|
||||
harness
|
||||
.extend_chain(
|
||||
num_blocks_produced as usize,
|
||||
BlockStrategy::OnCanonicalHead,
|
||||
AttestationStrategy::AllValidators,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Verify that the withdrawals computed from the block's state differ from the withdrawals
|
||||
// computed from the block's state with its payload applied by
|
||||
// `apply_parent_execution_payload`.
|
||||
let cached_head = harness.chain.canonical_head.cached_head();
|
||||
let unadvanced_state = &cached_head.snapshot.beacon_state;
|
||||
|
||||
let mut advanced_state = unadvanced_state.clone();
|
||||
complete_state_advance(&mut advanced_state, None, prepare_slot, &spec).unwrap();
|
||||
|
||||
let withdrawals_unadvanced: Withdrawals<E> = get_expected_withdrawals(unadvanced_state, &spec)
|
||||
.unwrap()
|
||||
.into();
|
||||
let withdrawals_advanced: Withdrawals<E> = get_expected_withdrawals(&advanced_state, &spec)
|
||||
.unwrap()
|
||||
.into();
|
||||
|
||||
let expect_state_advance_to_change_withdrawals = prepare_slot.epoch(E::slots_per_epoch()) > 0;
|
||||
if expect_state_advance_to_change_withdrawals {
|
||||
assert_ne!(
|
||||
withdrawals_unadvanced, withdrawals_advanced,
|
||||
"Advancing the state should change the withdrawals"
|
||||
);
|
||||
}
|
||||
|
||||
// Call `prepare_beacon_proposer` for the next slot and ensure that it primes the execution
|
||||
// layer payload attributes cache with the correct withdrawals (the ones taking into account
|
||||
// the applied execution_requests).
|
||||
let current_slot = prepare_slot - 1;
|
||||
let proposer_index = advanced_state
|
||||
.get_beacon_proposer_index(prepare_slot, &spec)
|
||||
.unwrap();
|
||||
|
||||
// Register the proposer so prepare_beacon_proposer doesn't skip it.
|
||||
let el = harness.chain.execution_layer.as_ref().unwrap();
|
||||
el.update_proposer_preparation(
|
||||
prepare_slot.epoch(E::slots_per_epoch()),
|
||||
[(
|
||||
&ProposerPreparationData {
|
||||
validator_index: proposer_index as u64,
|
||||
fee_recipient: Address::repeat_byte(42),
|
||||
},
|
||||
&None,
|
||||
)],
|
||||
)
|
||||
.await;
|
||||
|
||||
// Advance the slot clock to just before the prepare slot so the lookahead check passes.
|
||||
harness.advance_to_slot_lookahead(prepare_slot, harness.chain.config.prepare_payload_lookahead);
|
||||
|
||||
harness
|
||||
.chain
|
||||
.prepare_beacon_proposer(current_slot)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Read the payload attributes from the EL cache and verify the withdrawals.
|
||||
let el = harness.chain.execution_layer.as_ref().unwrap();
|
||||
let head_root = harness.head_block_root();
|
||||
let attributes = el
|
||||
.payload_attributes(prepare_slot, head_root, parent_payload_status)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let actual_withdrawals = attributes.withdrawals().unwrap();
|
||||
let expected_withdrawals: Vec<Withdrawal> = withdrawals_advanced.to_vec();
|
||||
|
||||
assert_eq!(
|
||||
actual_withdrawals, &expected_withdrawals,
|
||||
"prepare_beacon_proposer should use withdrawals computed from the \
|
||||
advanced state"
|
||||
);
|
||||
}
|
||||
@@ -721,10 +721,9 @@ where
|
||||
if let Some(execution_layer) = beacon_chain.execution_layer.as_ref() {
|
||||
// Only send a head update *after* genesis.
|
||||
if let Ok(current_slot) = beacon_chain.slot() {
|
||||
let params = beacon_chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.forkchoice_update_parameters();
|
||||
let cached_head = beacon_chain.canonical_head.cached_head();
|
||||
let head_payload_status = cached_head.head_payload_status();
|
||||
let params = cached_head.forkchoice_update_parameters();
|
||||
if params
|
||||
.head_hash
|
||||
.is_some_and(|hash| hash != ExecutionBlockHash::zero())
|
||||
@@ -737,6 +736,7 @@ where
|
||||
.update_execution_engine_forkchoice(
|
||||
current_slot,
|
||||
params,
|
||||
head_payload_status,
|
||||
Default::default(),
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use super::*;
|
||||
use alloy_rlp::RlpEncodable;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz::{Decode, TryFromIter};
|
||||
use ssz::{Decode, Encode, TryFromIter};
|
||||
use ssz_types::{FixedVector, VariableList, typenum::Unsigned};
|
||||
use strum::EnumString;
|
||||
use superstruct::superstruct;
|
||||
@@ -481,6 +481,34 @@ pub enum RequestsError {
|
||||
#[serde(transparent)]
|
||||
pub struct JsonExecutionRequests(pub Vec<String>);
|
||||
|
||||
impl<E: EthSpec> From<ExecutionRequests<E>> for JsonExecutionRequests {
|
||||
fn from(requests: ExecutionRequests<E>) -> Self {
|
||||
let mut result = Vec::new();
|
||||
if !requests.deposits.is_empty() {
|
||||
result.push(format!(
|
||||
"0x{:02x}{}",
|
||||
RequestType::Deposit.to_u8(),
|
||||
hex::encode(requests.deposits.as_ssz_bytes())
|
||||
));
|
||||
}
|
||||
if !requests.withdrawals.is_empty() {
|
||||
result.push(format!(
|
||||
"0x{:02x}{}",
|
||||
RequestType::Withdrawal.to_u8(),
|
||||
hex::encode(requests.withdrawals.as_ssz_bytes())
|
||||
));
|
||||
}
|
||||
if !requests.consolidations.is_empty() {
|
||||
result.push(format!(
|
||||
"0x{:02x}{}",
|
||||
RequestType::Consolidation.to_u8(),
|
||||
hex::encode(requests.consolidations.as_ssz_bytes())
|
||||
));
|
||||
}
|
||||
JsonExecutionRequests(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: EthSpec> TryFrom<JsonExecutionRequests> for ExecutionRequests<E> {
|
||||
type Error = RequestsError;
|
||||
|
||||
|
||||
@@ -403,6 +403,7 @@ impl ProposerPreparationDataEntry {
|
||||
pub struct ProposerKey {
|
||||
slot: Slot,
|
||||
head_block_root: Hash256,
|
||||
head_payload_status: fork_choice::PayloadStatus,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
@@ -1461,12 +1462,14 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
&self,
|
||||
slot: Slot,
|
||||
head_block_root: Hash256,
|
||||
head_payload_status: fork_choice::PayloadStatus,
|
||||
validator_index: u64,
|
||||
payload_attributes: PayloadAttributes,
|
||||
) -> bool {
|
||||
let proposers_key = ProposerKey {
|
||||
slot,
|
||||
head_block_root,
|
||||
head_payload_status,
|
||||
};
|
||||
|
||||
let existing = self.proposers().write().await.insert(
|
||||
@@ -1485,16 +1488,18 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
}
|
||||
|
||||
/// If there has been a proposer registered via `Self::insert_proposer` with a matching `slot`
|
||||
/// `head_block_root`, then return the appropriate `PayloadAttributes` for inclusion in
|
||||
/// `forkchoiceUpdated` calls.
|
||||
/// `head_block_root`, and `head_payload_status` then return the appropriate `PayloadAttributes`
|
||||
/// for inclusion in `forkchoiceUpdated` calls.
|
||||
pub async fn payload_attributes(
|
||||
&self,
|
||||
current_slot: Slot,
|
||||
head_block_root: Hash256,
|
||||
head_payload_status: fork_choice::PayloadStatus,
|
||||
) -> Option<PayloadAttributes> {
|
||||
let proposers_key = ProposerKey {
|
||||
slot: current_slot,
|
||||
head_block_root,
|
||||
head_payload_status,
|
||||
};
|
||||
|
||||
let proposer = self.proposers().read().await.get(&proposers_key).cloned()?;
|
||||
@@ -1518,6 +1523,7 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
finalized_block_hash: ExecutionBlockHash,
|
||||
current_slot: Slot,
|
||||
head_block_root: Hash256,
|
||||
head_payload_status: fork_choice::PayloadStatus,
|
||||
) -> Result<PayloadStatus, Error> {
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
@@ -1534,7 +1540,9 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
);
|
||||
|
||||
let next_slot = current_slot + 1;
|
||||
let payload_attributes = self.payload_attributes(next_slot, head_block_root).await;
|
||||
let payload_attributes = self
|
||||
.payload_attributes(next_slot, head_block_root, head_payload_status)
|
||||
.await;
|
||||
|
||||
// Compute the "lookahead", the time between when the payload will be produced and now.
|
||||
if let Some(ref payload_attributes) = payload_attributes
|
||||
|
||||
@@ -26,8 +26,8 @@ use tree_hash_derive::TreeHash;
|
||||
use types::{
|
||||
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix,
|
||||
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
|
||||
ExecutionPayloadGloas, ExecutionPayloadHeader, ForkName, Hash256, KzgProofs, Transaction,
|
||||
Transactions, Uint256,
|
||||
ExecutionPayloadGloas, ExecutionPayloadHeader, ExecutionRequests, ForkName, Hash256, KzgProofs,
|
||||
Transaction, Transactions, Uint256,
|
||||
};
|
||||
|
||||
const TEST_BLOB_BUNDLE: &[u8] = include_bytes!("fixtures/mainnet/test_blobs_bundle.ssz");
|
||||
@@ -161,6 +161,14 @@ pub struct ExecutionBlockGenerator<E: EthSpec> {
|
||||
pub blobs_bundles: HashMap<PayloadId, BlobsBundle<E>>,
|
||||
pub kzg: Option<Arc<Kzg>>,
|
||||
rng: Arc<Mutex<StdRng>>,
|
||||
/*
|
||||
* Execution requests (electra+)
|
||||
*/
|
||||
/// Per-payload execution requests returned by `getPayload`.
|
||||
execution_requests: HashMap<PayloadId, ExecutionRequests<E>>,
|
||||
/// If set, the next call to `build_new_execution_payload` will associate these
|
||||
/// execution requests with the generated payload ID.
|
||||
next_execution_requests: Option<ExecutionRequests<E>>,
|
||||
}
|
||||
|
||||
fn make_rng() -> Arc<Mutex<StdRng>> {
|
||||
@@ -199,6 +207,8 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
blobs_bundles: <_>::default(),
|
||||
kzg,
|
||||
rng: make_rng(),
|
||||
execution_requests: <_>::default(),
|
||||
next_execution_requests: None,
|
||||
};
|
||||
|
||||
generator.insert_pow_block(0).unwrap();
|
||||
@@ -458,6 +468,15 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
self.blobs_bundles.get(id).cloned()
|
||||
}
|
||||
|
||||
pub fn get_execution_requests(&self, id: &PayloadId) -> Option<ExecutionRequests<E>> {
|
||||
self.execution_requests.get(id).cloned()
|
||||
}
|
||||
|
||||
/// Set execution requests to be returned alongside the next generated payload.
|
||||
pub fn set_next_execution_requests(&mut self, requests: ExecutionRequests<E>) {
|
||||
self.next_execution_requests = Some(requests);
|
||||
}
|
||||
|
||||
/// Look up a blob and proof by versioned hash across all stored bundles.
|
||||
pub fn get_blob_and_proof(&self, versioned_hash: &Hash256) -> Option<BlobAndProof<E>> {
|
||||
self.blobs_bundles
|
||||
@@ -763,6 +782,11 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
},
|
||||
};
|
||||
|
||||
// Store execution requests for this payload if configured.
|
||||
if let Some(requests) = self.next_execution_requests.take() {
|
||||
self.execution_requests.insert(id, requests);
|
||||
}
|
||||
|
||||
let fork_name = execution_payload.fork_name();
|
||||
if fork_name.deneb_enabled() {
|
||||
// get random number between 0 and 1 blobs by default
|
||||
|
||||
@@ -295,6 +295,10 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
})?;
|
||||
|
||||
let maybe_blobs = ctx.execution_block_generator.write().get_blobs_bundle(&id);
|
||||
let maybe_execution_requests = ctx
|
||||
.execution_block_generator
|
||||
.read()
|
||||
.get_execution_requests(&id);
|
||||
|
||||
// validate method called correctly according to shanghai fork time
|
||||
if ctx
|
||||
@@ -432,8 +436,10 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
))?
|
||||
.into(),
|
||||
should_override_builder: false,
|
||||
// TODO(electra): add EL requests in mock el
|
||||
execution_requests: Default::default(),
|
||||
execution_requests: maybe_execution_requests
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
@@ -453,7 +459,10 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
))?
|
||||
.into(),
|
||||
should_override_builder: false,
|
||||
execution_requests: Default::default(),
|
||||
execution_requests: maybe_execution_requests
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
@@ -473,7 +482,9 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
))?
|
||||
.into(),
|
||||
should_override_builder: false,
|
||||
execution_requests: Default::default(),
|
||||
execution_requests: maybe_execution_requests
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
@@ -800,6 +800,10 @@ impl<E: EthSpec> MockBuilder<E> {
|
||||
|
||||
let head_block_root = head_block_root.unwrap_or(head.canonical_root());
|
||||
|
||||
// TODO(gloas): Currently the tests are pre-Gloas and we are not considering
|
||||
// other payload statuses. This codepath may not be relevant for Gloas.
|
||||
let head_payload_status = fork_choice::PayloadStatus::Pending;
|
||||
|
||||
let head_execution_payload = head
|
||||
.message()
|
||||
.body()
|
||||
@@ -934,7 +938,13 @@ impl<E: EthSpec> MockBuilder<E> {
|
||||
);
|
||||
|
||||
self.el
|
||||
.insert_proposer(slot, head_block_root, val_index, payload_attributes.clone())
|
||||
.insert_proposer(
|
||||
slot,
|
||||
head_block_root,
|
||||
head_payload_status,
|
||||
val_index,
|
||||
payload_attributes.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let forkchoice_update_params = ForkchoiceUpdateParameters {
|
||||
@@ -952,6 +962,7 @@ impl<E: EthSpec> MockBuilder<E> {
|
||||
finalized_execution_hash,
|
||||
slot - 1,
|
||||
head_block_root,
|
||||
head_payload_status,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| format!("fcu call failed : {:?}", e))?;
|
||||
|
||||
@@ -90,6 +90,8 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
let timestamp = block_number;
|
||||
let prev_randao = Hash256::from_low_u64_be(block_number);
|
||||
let head_block_root = Hash256::repeat_byte(42);
|
||||
// TODO(gloas): allow statuses other than Pending?
|
||||
let head_payload_status = fork_choice::PayloadStatus::Pending;
|
||||
let forkchoice_update_params = ForkchoiceUpdateParameters {
|
||||
head_root: head_block_root,
|
||||
head_hash: Some(parent_hash),
|
||||
@@ -109,7 +111,13 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
let slot = Slot::new(0);
|
||||
let validator_index = 0;
|
||||
self.el
|
||||
.insert_proposer(slot, head_block_root, validator_index, payload_attributes)
|
||||
.insert_proposer(
|
||||
slot,
|
||||
head_block_root,
|
||||
head_payload_status,
|
||||
validator_index,
|
||||
payload_attributes,
|
||||
)
|
||||
.await;
|
||||
|
||||
self.el
|
||||
@@ -119,6 +127,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
ExecutionBlockHash::zero(),
|
||||
slot,
|
||||
head_block_root,
|
||||
head_payload_status,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
@@ -280,6 +289,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
// Use junk values for slot/head-root to ensure there is no payload supplied.
|
||||
let slot = Slot::new(0);
|
||||
let head_block_root = Hash256::repeat_byte(13);
|
||||
// TODO(gloas): reconsider the state_payload_status
|
||||
self.el
|
||||
.notify_forkchoice_updated(
|
||||
block_hash,
|
||||
@@ -287,6 +297,7 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
ExecutionBlockHash::zero(),
|
||||
slot,
|
||||
head_block_root,
|
||||
fork_choice::PayloadStatus::Pending,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user