Changes for devnet-8
(#4518)
* Addressed #4487 Add override threshold flag Added tests for Override Threshold Flag Override default shown in decimal * Addressed #4445 Addressed Jimmy's Comments No need for matches Fix Mock Execution Engine Tests Fix clippy fix fcuv3 bug * Fix Block Root Calculation post-Deneb * Addressed #4444 Attestation Verification Post-Deneb Fix Gossip Attestation Verification Test * Addressed #4443 Fix Exit Signing for EIP-7044 Fix cross exit test Move 7044 Logic to signing_context() * Update EF Tests * Addressed #4560 * Added Comments around EIP7045 * Combine Altair Deneb to Eliminate Duplicated Code
This commit is contained in:
parent
02c7a2eaf5
commit
2b5385fb46
@ -14,7 +14,7 @@ use slot_clock::{SlotClock, SystemTimeSlotClock};
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use types::{ChainSpec, Epoch, EthSpec, Fork, VoluntaryExit};
|
use types::{ChainSpec, Epoch, EthSpec, VoluntaryExit};
|
||||||
|
|
||||||
pub const CMD: &str = "exit";
|
pub const CMD: &str = "exit";
|
||||||
pub const KEYSTORE_FLAG: &str = "keystore";
|
pub const KEYSTORE_FLAG: &str = "keystore";
|
||||||
@ -148,7 +148,6 @@ async fn publish_voluntary_exit<E: EthSpec>(
|
|||||||
.ok_or("Failed to get current epoch. Please check your system time")?;
|
.ok_or("Failed to get current epoch. Please check your system time")?;
|
||||||
let validator_index = get_validator_index_for_exit(client, &keypair.pk, epoch, spec).await?;
|
let validator_index = get_validator_index_for_exit(client, &keypair.pk, epoch, spec).await?;
|
||||||
|
|
||||||
let fork = get_beacon_state_fork(client).await?;
|
|
||||||
let voluntary_exit = VoluntaryExit {
|
let voluntary_exit = VoluntaryExit {
|
||||||
epoch,
|
epoch,
|
||||||
validator_index,
|
validator_index,
|
||||||
@ -175,12 +174,8 @@ async fn publish_voluntary_exit<E: EthSpec>(
|
|||||||
|
|
||||||
if confirmation == CONFIRMATION_PHRASE {
|
if confirmation == CONFIRMATION_PHRASE {
|
||||||
// Sign and publish the voluntary exit to network
|
// Sign and publish the voluntary exit to network
|
||||||
let signed_voluntary_exit = voluntary_exit.sign(
|
let signed_voluntary_exit =
|
||||||
&keypair.sk,
|
voluntary_exit.sign(&keypair.sk, genesis_data.genesis_validators_root, spec);
|
||||||
&fork,
|
|
||||||
genesis_data.genesis_validators_root,
|
|
||||||
spec,
|
|
||||||
);
|
|
||||||
client
|
client
|
||||||
.post_beacon_pool_voluntary_exits(&signed_voluntary_exit)
|
.post_beacon_pool_voluntary_exits(&signed_voluntary_exit)
|
||||||
.await
|
.await
|
||||||
@ -318,16 +313,6 @@ async fn is_syncing(client: &BeaconNodeHttpClient) -> Result<bool, String> {
|
|||||||
.is_syncing)
|
.is_syncing)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get fork object for the current state by querying the beacon node client.
|
|
||||||
async fn get_beacon_state_fork(client: &BeaconNodeHttpClient) -> Result<Fork, String> {
|
|
||||||
Ok(client
|
|
||||||
.get_beacon_states_fork(StateId::Head)
|
|
||||||
.await
|
|
||||||
.map_err(|e| format!("Failed to get get fork: {:?}", e))?
|
|
||||||
.ok_or("Failed to get fork, state not found")?
|
|
||||||
.data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculates the current epoch from the genesis time and current time.
|
/// Calculates the current epoch from the genesis time and current time.
|
||||||
fn get_current_epoch<E: EthSpec>(genesis_time: u64, spec: &ChainSpec) -> Option<Epoch> {
|
fn get_current_epoch<E: EthSpec>(genesis_time: u64, spec: &ChainSpec) -> Option<Epoch> {
|
||||||
let slot_clock = SystemTimeSlotClock::new(
|
let slot_clock = SystemTimeSlotClock::new(
|
||||||
|
@ -55,7 +55,7 @@ use std::borrow::Cow;
|
|||||||
use strum::AsRefStr;
|
use strum::AsRefStr;
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
use types::{
|
use types::{
|
||||||
Attestation, BeaconCommittee, ChainSpec, CommitteeIndex, Epoch, EthSpec, Hash256,
|
Attestation, BeaconCommittee, ChainSpec, CommitteeIndex, Epoch, EthSpec, ForkName, Hash256,
|
||||||
IndexedAttestation, SelectionProof, SignedAggregateAndProof, Slot, SubnetId,
|
IndexedAttestation, SelectionProof, SignedAggregateAndProof, Slot, SubnetId,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1049,10 +1049,21 @@ pub fn verify_propagation_slot_range<S: SlotClock, E: EthSpec>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Taking advantage of saturating subtraction on `Slot`.
|
// Taking advantage of saturating subtraction on `Slot`.
|
||||||
let earliest_permissible_slot = slot_clock
|
let one_epoch_prior = slot_clock
|
||||||
.now_with_past_tolerance(spec.maximum_gossip_clock_disparity())
|
.now_with_past_tolerance(spec.maximum_gossip_clock_disparity())
|
||||||
.ok_or(BeaconChainError::UnableToReadSlot)?
|
.ok_or(BeaconChainError::UnableToReadSlot)?
|
||||||
- E::slots_per_epoch();
|
- E::slots_per_epoch();
|
||||||
|
|
||||||
|
let current_fork =
|
||||||
|
spec.fork_name_at_slot::<E>(slot_clock.now().ok_or(BeaconChainError::UnableToReadSlot)?);
|
||||||
|
let earliest_permissible_slot = match current_fork {
|
||||||
|
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => one_epoch_prior,
|
||||||
|
// EIP-7045
|
||||||
|
ForkName::Deneb => one_epoch_prior
|
||||||
|
.epoch(E::slots_per_epoch())
|
||||||
|
.start_slot(E::slots_per_epoch()),
|
||||||
|
};
|
||||||
|
|
||||||
if attestation_slot < earliest_permissible_slot {
|
if attestation_slot < earliest_permissible_slot {
|
||||||
return Err(Error::PastSlot {
|
return Err(Error::PastSlot {
|
||||||
attestation_slot,
|
attestation_slot,
|
||||||
|
@ -71,7 +71,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
BeaconChainError::BlockRewardAttestationError
|
BeaconChainError::BlockRewardAttestationError
|
||||||
})?
|
})?
|
||||||
} else {
|
} else {
|
||||||
self.compute_beacon_block_attestation_reward_altair(block, state)
|
self.compute_beacon_block_attestation_reward_altair_deneb(block, state)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
error!(
|
error!(
|
||||||
self.log,
|
self.log,
|
||||||
@ -173,7 +173,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
Ok(block_attestation_reward)
|
Ok(block_attestation_reward)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compute_beacon_block_attestation_reward_altair<Payload: AbstractExecPayload<T::EthSpec>>(
|
fn compute_beacon_block_attestation_reward_altair_deneb<
|
||||||
|
Payload: AbstractExecPayload<T::EthSpec>,
|
||||||
|
>(
|
||||||
&self,
|
&self,
|
||||||
block: BeaconBlockRef<'_, T::EthSpec, Payload>,
|
block: BeaconBlockRef<'_, T::EthSpec, Payload>,
|
||||||
state: &mut BeaconState<T::EthSpec>,
|
state: &mut BeaconState<T::EthSpec>,
|
||||||
@ -192,6 +194,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
for attestation in block.body().attestations() {
|
for attestation in block.body().attestations() {
|
||||||
let data = &attestation.data;
|
let data = &attestation.data;
|
||||||
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
|
let inclusion_delay = state.slot().safe_sub(data.slot)?.as_u64();
|
||||||
|
// [Modified in Deneb:EIP7045]
|
||||||
let participation_flag_indices = get_attestation_participation_flag_indices(
|
let participation_flag_indices = get_attestation_participation_flag_indices(
|
||||||
state,
|
state,
|
||||||
data,
|
data,
|
||||||
|
@ -237,6 +237,7 @@ pub struct PrePayloadAttributes {
|
|||||||
/// The parent block number is not part of the payload attributes sent to the EL, but *is*
|
/// The parent block number is not part of the payload attributes sent to the EL, but *is*
|
||||||
/// sent to builders via SSE.
|
/// sent to builders via SSE.
|
||||||
pub parent_block_number: u64,
|
pub parent_block_number: u64,
|
||||||
|
pub parent_beacon_block_root: Hash256,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about a state/block at a specific slot.
|
/// Information about a state/block at a specific slot.
|
||||||
@ -4200,6 +4201,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
proposer_index,
|
proposer_index,
|
||||||
prev_randao,
|
prev_randao,
|
||||||
parent_block_number,
|
parent_block_number,
|
||||||
|
parent_beacon_block_root: parent_block_root,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5260,7 +5262,8 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
{
|
{
|
||||||
payload_attributes
|
payload_attributes
|
||||||
} else {
|
} else {
|
||||||
let withdrawals = match self.spec.fork_name_at_slot::<T::EthSpec>(prepare_slot) {
|
let prepare_slot_fork = self.spec.fork_name_at_slot::<T::EthSpec>(prepare_slot);
|
||||||
|
let withdrawals = match prepare_slot_fork {
|
||||||
ForkName::Base | ForkName::Altair | ForkName::Merge => None,
|
ForkName::Base | ForkName::Altair | ForkName::Merge => None,
|
||||||
ForkName::Capella | ForkName::Deneb => {
|
ForkName::Capella | ForkName::Deneb => {
|
||||||
let chain = self.clone();
|
let chain = self.clone();
|
||||||
@ -5275,6 +5278,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let parent_beacon_block_root = match prepare_slot_fork {
|
||||||
|
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => None,
|
||||||
|
ForkName::Deneb => Some(pre_payload_attributes.parent_beacon_block_root),
|
||||||
|
};
|
||||||
|
|
||||||
let payload_attributes = PayloadAttributes::new(
|
let payload_attributes = PayloadAttributes::new(
|
||||||
self.slot_clock
|
self.slot_clock
|
||||||
.start_of(prepare_slot)
|
.start_of(prepare_slot)
|
||||||
@ -5283,6 +5291,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
|
|||||||
pre_payload_attributes.prev_randao,
|
pre_payload_attributes.prev_randao,
|
||||||
execution_layer.get_suggested_fee_recipient(proposer).await,
|
execution_layer.get_suggested_fee_recipient(proposer).await,
|
||||||
withdrawals.map(Into::into),
|
withdrawals.map(Into::into),
|
||||||
|
parent_beacon_block_root,
|
||||||
);
|
);
|
||||||
|
|
||||||
execution_layer
|
execution_layer
|
||||||
|
@ -12,13 +12,15 @@ use crate::{
|
|||||||
BeaconChain, BeaconChainError, BeaconChainTypes, BlockError, BlockProductionError,
|
BeaconChain, BeaconChainError, BeaconChainTypes, BlockError, BlockProductionError,
|
||||||
ExecutionPayloadError,
|
ExecutionPayloadError,
|
||||||
};
|
};
|
||||||
use execution_layer::{BlockProposalContents, BuilderParams, PayloadAttributes, PayloadStatus};
|
use execution_layer::{
|
||||||
|
BlockProposalContents, BuilderParams, NewPayloadRequest, PayloadAttributes, PayloadStatus,
|
||||||
|
};
|
||||||
use fork_choice::{InvalidationOperation, PayloadVerificationStatus};
|
use fork_choice::{InvalidationOperation, PayloadVerificationStatus};
|
||||||
use proto_array::{Block as ProtoBlock, ExecutionStatus};
|
use proto_array::{Block as ProtoBlock, ExecutionStatus};
|
||||||
use slog::{debug, warn};
|
use slog::{debug, warn};
|
||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
use state_processing::per_block_processing::{
|
use state_processing::per_block_processing::{
|
||||||
self, compute_timestamp_at_slot, get_expected_withdrawals, is_execution_enabled,
|
compute_timestamp_at_slot, get_expected_withdrawals, is_execution_enabled,
|
||||||
is_merge_transition_complete, partially_verify_execution_payload,
|
is_merge_transition_complete, partially_verify_execution_payload,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -76,8 +78,6 @@ impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
|||||||
)
|
)
|
||||||
.map_err(BlockError::PerBlockProcessingError)?;
|
.map_err(BlockError::PerBlockProcessingError)?;
|
||||||
|
|
||||||
let payload = block_message.execution_payload()?;
|
|
||||||
|
|
||||||
match notify_execution_layer {
|
match notify_execution_layer {
|
||||||
NotifyExecutionLayer::No if chain.config.optimistic_finalized_sync => {
|
NotifyExecutionLayer::No if chain.config.optimistic_finalized_sync => {
|
||||||
// Verify the block hash here in Lighthouse and immediately mark the block as
|
// Verify the block hash here in Lighthouse and immediately mark the block as
|
||||||
@ -87,13 +87,11 @@ impl<T: BeaconChainTypes> PayloadNotifier<T> {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(ExecutionPayloadError::NoExecutionConnection)?;
|
.ok_or(ExecutionPayloadError::NoExecutionConnection)?;
|
||||||
|
|
||||||
if let Err(e) =
|
if let Err(e) = execution_layer.verify_payload_block_hash(block_message) {
|
||||||
execution_layer.verify_payload_block_hash(payload.execution_payload_ref())
|
|
||||||
{
|
|
||||||
warn!(
|
warn!(
|
||||||
chain.log,
|
chain.log,
|
||||||
"Falling back to slow block hash verification";
|
"Falling back to slow block hash verification";
|
||||||
"block_number" => payload.block_number(),
|
"block_number" => ?block_message.execution_payload().map(|payload| payload.block_number()),
|
||||||
"info" => "you can silence this warning with --disable-optimistic-finalized-sync",
|
"info" => "you can silence this warning with --disable-optimistic-finalized-sync",
|
||||||
"error" => ?e,
|
"error" => ?e,
|
||||||
);
|
);
|
||||||
@ -139,23 +137,15 @@ async fn notify_new_payload<'a, T: BeaconChainTypes>(
|
|||||||
chain: &Arc<BeaconChain<T>>,
|
chain: &Arc<BeaconChain<T>>,
|
||||||
block: BeaconBlockRef<'a, T::EthSpec>,
|
block: BeaconBlockRef<'a, T::EthSpec>,
|
||||||
) -> Result<PayloadVerificationStatus, BlockError<T::EthSpec>> {
|
) -> Result<PayloadVerificationStatus, BlockError<T::EthSpec>> {
|
||||||
let execution_payload = block.execution_payload()?;
|
|
||||||
let versioned_hashes = block.body().blob_kzg_commitments().ok().map(|commitments| {
|
|
||||||
commitments
|
|
||||||
.into_iter()
|
|
||||||
.map(|commitment| {
|
|
||||||
per_block_processing::deneb::deneb::kzg_commitment_to_versioned_hash(commitment)
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
});
|
|
||||||
|
|
||||||
let execution_layer = chain
|
let execution_layer = chain
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(ExecutionPayloadError::NoExecutionConnection)?;
|
.ok_or(ExecutionPayloadError::NoExecutionConnection)?;
|
||||||
|
|
||||||
|
let new_payload_request: NewPayloadRequest<T::EthSpec> = block.try_into()?;
|
||||||
|
let execution_block_hash = new_payload_request.block_hash();
|
||||||
let new_payload_response = execution_layer
|
let new_payload_response = execution_layer
|
||||||
.notify_new_payload(&execution_payload.into(), versioned_hashes)
|
.notify_new_payload(new_payload_request)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
match new_payload_response {
|
match new_payload_response {
|
||||||
@ -173,7 +163,7 @@ async fn notify_new_payload<'a, T: BeaconChainTypes>(
|
|||||||
"Invalid execution payload";
|
"Invalid execution payload";
|
||||||
"validation_error" => ?validation_error,
|
"validation_error" => ?validation_error,
|
||||||
"latest_valid_hash" => ?latest_valid_hash,
|
"latest_valid_hash" => ?latest_valid_hash,
|
||||||
"execution_block_hash" => ?execution_payload.block_hash(),
|
"execution_block_hash" => ?execution_block_hash,
|
||||||
"root" => ?block.tree_hash_root(),
|
"root" => ?block.tree_hash_root(),
|
||||||
"graffiti" => block.body().graffiti().as_utf8_lossy(),
|
"graffiti" => block.body().graffiti().as_utf8_lossy(),
|
||||||
"proposer_index" => block.proposer_index(),
|
"proposer_index" => block.proposer_index(),
|
||||||
@ -219,7 +209,7 @@ async fn notify_new_payload<'a, T: BeaconChainTypes>(
|
|||||||
chain.log,
|
chain.log,
|
||||||
"Invalid execution payload block hash";
|
"Invalid execution payload block hash";
|
||||||
"validation_error" => ?validation_error,
|
"validation_error" => ?validation_error,
|
||||||
"execution_block_hash" => ?execution_payload.block_hash(),
|
"execution_block_hash" => ?execution_block_hash,
|
||||||
"root" => ?block.tree_hash_root(),
|
"root" => ?block.tree_hash_root(),
|
||||||
"graffiti" => block.body().graffiti().as_utf8_lossy(),
|
"graffiti" => block.body().graffiti().as_utf8_lossy(),
|
||||||
"proposer_index" => block.proposer_index(),
|
"proposer_index" => block.proposer_index(),
|
||||||
@ -435,6 +425,12 @@ pub fn get_execution_payload<
|
|||||||
// These shouldn't happen but they're here to make the pattern irrefutable
|
// These shouldn't happen but they're here to make the pattern irrefutable
|
||||||
&BeaconState::Base(_) | &BeaconState::Altair(_) => None,
|
&BeaconState::Base(_) | &BeaconState::Altair(_) => None,
|
||||||
};
|
};
|
||||||
|
let parent_beacon_block_root = match state {
|
||||||
|
&BeaconState::Deneb(_) => Some(state.latest_block_header().canonical_root()),
|
||||||
|
&BeaconState::Merge(_) | &BeaconState::Capella(_) => None,
|
||||||
|
// These shouldn't happen but they're here to make the pattern irrefutable
|
||||||
|
&BeaconState::Base(_) | &BeaconState::Altair(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
// Spawn a task to obtain the execution payload from the EL via a series of async calls. The
|
// Spawn a task to obtain the execution payload from the EL via a series of async calls. The
|
||||||
// `join_handle` can be used to await the result of the function.
|
// `join_handle` can be used to await the result of the function.
|
||||||
@ -452,6 +448,7 @@ pub fn get_execution_payload<
|
|||||||
latest_execution_payload_header_block_hash,
|
latest_execution_payload_header_block_hash,
|
||||||
builder_params,
|
builder_params,
|
||||||
withdrawals,
|
withdrawals,
|
||||||
|
parent_beacon_block_root,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
},
|
},
|
||||||
@ -486,6 +483,7 @@ pub async fn prepare_execution_payload<T, Payload>(
|
|||||||
latest_execution_payload_header_block_hash: ExecutionBlockHash,
|
latest_execution_payload_header_block_hash: ExecutionBlockHash,
|
||||||
builder_params: BuilderParams,
|
builder_params: BuilderParams,
|
||||||
withdrawals: Option<Vec<Withdrawal>>,
|
withdrawals: Option<Vec<Withdrawal>>,
|
||||||
|
parent_beacon_block_root: Option<Hash256>,
|
||||||
) -> Result<BlockProposalContents<T::EthSpec, Payload>, BlockProductionError>
|
) -> Result<BlockProposalContents<T::EthSpec, Payload>, BlockProductionError>
|
||||||
where
|
where
|
||||||
T: BeaconChainTypes,
|
T: BeaconChainTypes,
|
||||||
@ -547,8 +545,13 @@ where
|
|||||||
let suggested_fee_recipient = execution_layer
|
let suggested_fee_recipient = execution_layer
|
||||||
.get_suggested_fee_recipient(proposer_index)
|
.get_suggested_fee_recipient(proposer_index)
|
||||||
.await;
|
.await;
|
||||||
let payload_attributes =
|
let payload_attributes = PayloadAttributes::new(
|
||||||
PayloadAttributes::new(timestamp, random, suggested_fee_recipient, withdrawals);
|
timestamp,
|
||||||
|
random,
|
||||||
|
suggested_fee_recipient,
|
||||||
|
withdrawals,
|
||||||
|
parent_beacon_block_root,
|
||||||
|
);
|
||||||
|
|
||||||
// Note: the suggested_fee_recipient is stored in the `execution_layer`, it will add this parameter.
|
// Note: the suggested_fee_recipient is stored in the `execution_layer`, it will add this parameter.
|
||||||
//
|
//
|
||||||
|
@ -1663,14 +1663,13 @@ where
|
|||||||
|
|
||||||
pub fn make_voluntary_exit(&self, validator_index: u64, epoch: Epoch) -> SignedVoluntaryExit {
|
pub fn make_voluntary_exit(&self, validator_index: u64, epoch: Epoch) -> SignedVoluntaryExit {
|
||||||
let sk = &self.validator_keypairs[validator_index as usize].sk;
|
let sk = &self.validator_keypairs[validator_index as usize].sk;
|
||||||
let fork = self.chain.canonical_head.cached_head().head_fork();
|
|
||||||
let genesis_validators_root = self.chain.genesis_validators_root;
|
let genesis_validators_root = self.chain.genesis_validators_root;
|
||||||
|
|
||||||
VoluntaryExit {
|
VoluntaryExit {
|
||||||
epoch,
|
epoch,
|
||||||
validator_index,
|
validator_index,
|
||||||
}
|
}
|
||||||
.sign(sk, &fork, genesis_validators_root, &self.chain.spec)
|
.sign(sk, genesis_validators_root, &self.chain.spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_proposer_slashing(&self, validator_index: u64) -> Result<(), String> {
|
pub fn add_proposer_slashing(&self, validator_index: u64) -> Result<(), String> {
|
||||||
|
@ -326,10 +326,28 @@ impl GossipTester {
|
|||||||
self.harness.chain.epoch().unwrap()
|
self.harness.chain.epoch().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn two_epochs_ago(&self) -> Slot {
|
pub fn earliest_valid_attestation_slot(&self) -> Slot {
|
||||||
|
let offset = match self.harness.spec.fork_name_at_epoch(self.epoch()) {
|
||||||
|
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
||||||
|
// Subtract an additional slot since the harness will be exactly on the start of the
|
||||||
|
// slot and the propagation tolerance will allow an extra slot.
|
||||||
|
E::slots_per_epoch() + 1
|
||||||
|
}
|
||||||
|
// EIP-7045
|
||||||
|
ForkName::Deneb => {
|
||||||
|
let epoch_slot_offset = (self.slot() % E::slots_per_epoch()).as_u64();
|
||||||
|
if epoch_slot_offset != 0 {
|
||||||
|
E::slots_per_epoch() + epoch_slot_offset
|
||||||
|
} else {
|
||||||
|
// Here the propagation tolerance will cause the cutoff to be an entire epoch earlier
|
||||||
|
2 * E::slots_per_epoch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
self.slot()
|
self.slot()
|
||||||
.as_u64()
|
.as_u64()
|
||||||
.checked_sub(E::slots_per_epoch() + 2)
|
.checked_sub(offset)
|
||||||
.expect("chain is not sufficiently deep for test")
|
.expect("chain is not sufficiently deep for test")
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@ -476,18 +494,21 @@ async fn aggregated_gossip_verification() {
|
|||||||
)
|
)
|
||||||
.inspect_aggregate_err(
|
.inspect_aggregate_err(
|
||||||
"aggregate from past slot",
|
"aggregate from past slot",
|
||||||
|tester, a| a.message.aggregate.data.slot = tester.two_epochs_ago(),
|
|tester, a| {
|
||||||
|
let too_early_slot = tester.earliest_valid_attestation_slot() - 1;
|
||||||
|
a.message.aggregate.data.slot = too_early_slot;
|
||||||
|
a.message.aggregate.data.target.epoch = too_early_slot.epoch(E::slots_per_epoch());
|
||||||
|
},
|
||||||
|tester, err| {
|
|tester, err| {
|
||||||
|
let valid_early_slot = tester.earliest_valid_attestation_slot();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err,
|
err,
|
||||||
AttnError::PastSlot {
|
AttnError::PastSlot {
|
||||||
attestation_slot,
|
attestation_slot,
|
||||||
// Subtract an additional slot since the harness will be exactly on the start of the
|
|
||||||
// slot and the propagation tolerance will allow an extra slot.
|
|
||||||
earliest_permissible_slot
|
earliest_permissible_slot
|
||||||
}
|
}
|
||||||
if attestation_slot == tester.two_epochs_ago()
|
if attestation_slot == valid_early_slot - 1
|
||||||
&& earliest_permissible_slot == tester.slot() - E::slots_per_epoch() - 1
|
&& earliest_permissible_slot == valid_early_slot
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -792,21 +813,20 @@ async fn unaggregated_gossip_verification() {
|
|||||||
.inspect_unaggregate_err(
|
.inspect_unaggregate_err(
|
||||||
"attestation from past slot",
|
"attestation from past slot",
|
||||||
|tester, a, _| {
|
|tester, a, _| {
|
||||||
let early_slot = tester.two_epochs_ago();
|
let too_early_slot = tester.earliest_valid_attestation_slot() - 1;
|
||||||
a.data.slot = early_slot;
|
a.data.slot = too_early_slot;
|
||||||
a.data.target.epoch = early_slot.epoch(E::slots_per_epoch());
|
a.data.target.epoch = too_early_slot.epoch(E::slots_per_epoch());
|
||||||
},
|
},
|
||||||
|tester, err| {
|
|tester, err| {
|
||||||
|
let valid_early_slot = tester.earliest_valid_attestation_slot();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
err,
|
err,
|
||||||
AttnError::PastSlot {
|
AttnError::PastSlot {
|
||||||
attestation_slot,
|
attestation_slot,
|
||||||
// Subtract an additional slot since the harness will be exactly on the start of the
|
|
||||||
// slot and the propagation tolerance will allow an extra slot.
|
|
||||||
earliest_permissible_slot,
|
earliest_permissible_slot,
|
||||||
}
|
}
|
||||||
if attestation_slot == tester.two_epochs_ago()
|
if attestation_slot == valid_early_slot - 1
|
||||||
&& earliest_permissible_slot == tester.slot() - E::slots_per_epoch() - 1
|
&& earliest_permissible_slot == valid_early_slot
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1017,6 +1017,7 @@ async fn payload_preparation() {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
fee_recipient,
|
fee_recipient,
|
||||||
None,
|
None,
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
assert_eq!(rig.previous_payload_attributes(), payload_attributes);
|
assert_eq!(rig.previous_payload_attributes(), payload_attributes);
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@ use ethers_core::utils::rlp::RlpStream;
|
|||||||
use keccak_hash::KECCAK_EMPTY_LIST_RLP;
|
use keccak_hash::KECCAK_EMPTY_LIST_RLP;
|
||||||
use triehash::ordered_trie_root;
|
use triehash::ordered_trie_root;
|
||||||
use types::{
|
use types::{
|
||||||
map_execution_block_header_fields_except_withdrawals, Address, EthSpec, ExecutionBlockHash,
|
map_execution_block_header_fields_except_withdrawals, Address, BeaconBlockRef, EthSpec,
|
||||||
ExecutionBlockHeader, ExecutionPayloadRef, Hash256, Hash64, Uint256,
|
ExecutionBlockHash, ExecutionBlockHeader, ExecutionPayloadRef, Hash256, Hash64, Uint256,
|
||||||
};
|
};
|
||||||
|
|
||||||
impl<T: EthSpec> ExecutionLayer<T> {
|
impl<T: EthSpec> ExecutionLayer<T> {
|
||||||
@ -18,6 +18,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
/// transactions.
|
/// transactions.
|
||||||
pub fn calculate_execution_block_hash(
|
pub fn calculate_execution_block_hash(
|
||||||
payload: ExecutionPayloadRef<T>,
|
payload: ExecutionPayloadRef<T>,
|
||||||
|
parent_beacon_block_root: Hash256,
|
||||||
) -> (ExecutionBlockHash, Hash256) {
|
) -> (ExecutionBlockHash, Hash256) {
|
||||||
// Calculate the transactions root.
|
// Calculate the transactions root.
|
||||||
// We're currently using a deprecated Parity library for this. We should move to a
|
// We're currently using a deprecated Parity library for this. We should move to a
|
||||||
@ -37,8 +38,13 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let rlp_data_gas_used = payload.data_gas_used().ok();
|
let rlp_blob_gas_used = payload.blob_gas_used().ok();
|
||||||
let rlp_excess_data_gas = payload.excess_data_gas().ok();
|
let rlp_excess_blob_gas = payload.excess_blob_gas().ok();
|
||||||
|
|
||||||
|
// Calculate parent beacon block root (post-Deneb).
|
||||||
|
let rlp_parent_beacon_block_root = rlp_excess_blob_gas
|
||||||
|
.as_ref()
|
||||||
|
.map(|_| parent_beacon_block_root);
|
||||||
|
|
||||||
// Construct the block header.
|
// Construct the block header.
|
||||||
let exec_block_header = ExecutionBlockHeader::from_payload(
|
let exec_block_header = ExecutionBlockHeader::from_payload(
|
||||||
@ -46,8 +52,9 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
KECCAK_EMPTY_LIST_RLP.as_fixed_bytes().into(),
|
KECCAK_EMPTY_LIST_RLP.as_fixed_bytes().into(),
|
||||||
rlp_transactions_root,
|
rlp_transactions_root,
|
||||||
rlp_withdrawals_root,
|
rlp_withdrawals_root,
|
||||||
rlp_data_gas_used.copied(),
|
rlp_blob_gas_used,
|
||||||
rlp_excess_data_gas.copied(),
|
rlp_excess_blob_gas,
|
||||||
|
rlp_parent_beacon_block_root,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Hash the RLP encoding of the block header.
|
// Hash the RLP encoding of the block header.
|
||||||
@ -61,10 +68,14 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
/// Verify `payload.block_hash` locally within Lighthouse.
|
/// Verify `payload.block_hash` locally within Lighthouse.
|
||||||
///
|
///
|
||||||
/// No remote calls to the execution client will be made, so this is quite a cheap check.
|
/// No remote calls to the execution client will be made, so this is quite a cheap check.
|
||||||
pub fn verify_payload_block_hash(&self, payload: ExecutionPayloadRef<T>) -> Result<(), Error> {
|
pub fn verify_payload_block_hash(&self, block: BeaconBlockRef<T>) -> Result<(), Error> {
|
||||||
|
let payload = block.execution_payload()?.execution_payload_ref();
|
||||||
|
let parent_beacon_block_root = block.parent_root();
|
||||||
|
|
||||||
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_VERIFY_BLOCK_HASH);
|
let _timer = metrics::start_timer(&metrics::EXECUTION_LAYER_VERIFY_BLOCK_HASH);
|
||||||
|
|
||||||
let (header_hash, rlp_transactions_root) = Self::calculate_execution_block_hash(payload);
|
let (header_hash, rlp_transactions_root) =
|
||||||
|
Self::calculate_execution_block_hash(payload, parent_beacon_block_root);
|
||||||
|
|
||||||
if header_hash != payload.block_hash() {
|
if header_hash != payload.block_hash() {
|
||||||
return Err(Error::BlockHashMismatch {
|
return Err(Error::BlockHashMismatch {
|
||||||
@ -99,11 +110,14 @@ pub fn rlp_encode_block_header(header: &ExecutionBlockHeader) -> Vec<u8> {
|
|||||||
if let Some(withdrawals_root) = &header.withdrawals_root {
|
if let Some(withdrawals_root) = &header.withdrawals_root {
|
||||||
rlp_header_stream.append(withdrawals_root);
|
rlp_header_stream.append(withdrawals_root);
|
||||||
}
|
}
|
||||||
if let Some(data_gas_used) = &header.data_gas_used {
|
if let Some(blob_gas_used) = &header.blob_gas_used {
|
||||||
rlp_header_stream.append(data_gas_used);
|
rlp_header_stream.append(blob_gas_used);
|
||||||
}
|
}
|
||||||
if let Some(excess_data_gas) = &header.excess_data_gas {
|
if let Some(excess_blob_gas) = &header.excess_blob_gas {
|
||||||
rlp_header_stream.append(excess_data_gas);
|
rlp_header_stream.append(excess_blob_gas);
|
||||||
|
}
|
||||||
|
if let Some(parent_beacon_block_root) = &header.parent_beacon_block_root {
|
||||||
|
rlp_header_stream.append(parent_beacon_block_root);
|
||||||
}
|
}
|
||||||
rlp_header_stream.finalize_unbounded_list();
|
rlp_header_stream.finalize_unbounded_list();
|
||||||
rlp_header_stream.out().into()
|
rlp_header_stream.out().into()
|
||||||
@ -151,8 +165,9 @@ mod test {
|
|||||||
nonce: Hash64::zero(),
|
nonce: Hash64::zero(),
|
||||||
base_fee_per_gas: 0x036b_u64.into(),
|
base_fee_per_gas: 0x036b_u64.into(),
|
||||||
withdrawals_root: None,
|
withdrawals_root: None,
|
||||||
data_gas_used: None,
|
blob_gas_used: None,
|
||||||
excess_data_gas: None,
|
excess_blob_gas: None,
|
||||||
|
parent_beacon_block_root: None,
|
||||||
};
|
};
|
||||||
let expected_rlp = "f90200a0e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ba5e000000000000000000000000000000000000a0ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7a050f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accfa029b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000188016345785d8a00008301553482079e42a0000000000000000000000000000000000000000000000000000000000000000088000000000000000082036b";
|
let expected_rlp = "f90200a0e0a94a7a3c9617401586b1a27025d2d9671332d22d540e0af72b069170380f2aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ba5e000000000000000000000000000000000000a0ec3c94b18b8a1cff7d60f8d258ec723312932928626b4c9355eb4ab3568ec7f7a050f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accfa029b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000830200000188016345785d8a00008301553482079e42a0000000000000000000000000000000000000000000000000000000000000000088000000000000000082036b";
|
||||||
let expected_hash =
|
let expected_hash =
|
||||||
@ -181,8 +196,9 @@ mod test {
|
|||||||
nonce: Hash64::zero(),
|
nonce: Hash64::zero(),
|
||||||
base_fee_per_gas: 0x036b_u64.into(),
|
base_fee_per_gas: 0x036b_u64.into(),
|
||||||
withdrawals_root: None,
|
withdrawals_root: None,
|
||||||
data_gas_used: None,
|
blob_gas_used: None,
|
||||||
excess_data_gas: None,
|
excess_blob_gas: None,
|
||||||
|
parent_beacon_block_root: None,
|
||||||
};
|
};
|
||||||
let expected_rlp = "f901fda0927ca537f06c783a3a2635b8805eef1c8c2124f7444ad4a3389898dd832f2dbea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ba5e000000000000000000000000000000000000a0e97859b065bd8dbbb4519c7cb935024de2484c2b7f881181b4360492f0b06b82a050f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accfa029b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800188016345785d8a00008301553482079e42a0000000000000000000000000000000000000000000000000000000000002000088000000000000000082036b";
|
let expected_rlp = "f901fda0927ca537f06c783a3a2635b8805eef1c8c2124f7444ad4a3389898dd832f2dbea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794ba5e000000000000000000000000000000000000a0e97859b065bd8dbbb4519c7cb935024de2484c2b7f881181b4360492f0b06b82a050f738580ed699f0469702c7ccc63ed2e51bc034be9479b7bff4e68dee84accfa029b0562f7140574dd0d50dee8a271b22e1a0a7b78fca58f7c60370d8317ba2a9b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800188016345785d8a00008301553482079e42a0000000000000000000000000000000000000000000000000000000000002000088000000000000000082036b";
|
||||||
let expected_hash =
|
let expected_hash =
|
||||||
@ -212,8 +228,9 @@ mod test {
|
|||||||
nonce: Hash64::zero(),
|
nonce: Hash64::zero(),
|
||||||
base_fee_per_gas: 0x34187b238_u64.into(),
|
base_fee_per_gas: 0x34187b238_u64.into(),
|
||||||
withdrawals_root: None,
|
withdrawals_root: None,
|
||||||
data_gas_used: None,
|
blob_gas_used: None,
|
||||||
excess_data_gas: None,
|
excess_blob_gas: None,
|
||||||
|
parent_beacon_block_root: None,
|
||||||
};
|
};
|
||||||
let expected_hash =
|
let expected_hash =
|
||||||
Hash256::from_str("6da69709cd5a34079b6604d29cd78fc01dacd7c6268980057ad92a2bede87351")
|
Hash256::from_str("6da69709cd5a34079b6604d29cd78fc01dacd7c6268980057ad92a2bede87351")
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
use crate::engines::ForkchoiceState;
|
use crate::engines::ForkchoiceState;
|
||||||
use crate::http::{
|
use crate::http::{
|
||||||
ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2,
|
ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3,
|
||||||
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1, ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
|
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1, ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
|
||||||
ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2, ENGINE_GET_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V1,
|
ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2, ENGINE_GET_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V1,
|
||||||
ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3,
|
ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3,
|
||||||
};
|
};
|
||||||
use crate::BlobTxConversionError;
|
use crate::BlobTxConversionError;
|
||||||
use eth2::types::{SsePayloadAttributes, SsePayloadAttributesV1, SsePayloadAttributesV2};
|
use eth2::types::{
|
||||||
|
SsePayloadAttributes, SsePayloadAttributesV1, SsePayloadAttributesV2, SsePayloadAttributesV3,
|
||||||
|
};
|
||||||
use ethers_core::types::Transaction;
|
use ethers_core::types::Transaction;
|
||||||
use ethers_core::utils::rlp::{self, Decodable, Rlp};
|
use ethers_core::utils::rlp::{self, Decodable, Rlp};
|
||||||
use http::deposit_methods::RpcError;
|
use http::deposit_methods::RpcError;
|
||||||
@ -14,17 +16,21 @@ pub use json_structures::{JsonWithdrawal, TransitionConfigurationV1};
|
|||||||
use pretty_reqwest_error::PrettyReqwestError;
|
use pretty_reqwest_error::PrettyReqwestError;
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use state_processing::per_block_processing::deneb::deneb::kzg_commitment_to_versioned_hash;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use strum::IntoStaticStr;
|
use strum::IntoStaticStr;
|
||||||
use superstruct::superstruct;
|
use superstruct::superstruct;
|
||||||
use types::beacon_block_body::KzgCommitments;
|
use types::beacon_block_body::KzgCommitments;
|
||||||
use types::blob_sidecar::Blobs;
|
use types::blob_sidecar::Blobs;
|
||||||
pub use types::{
|
pub use types::{
|
||||||
Address, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader,
|
Address, BeaconBlockRef, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadHeader,
|
||||||
ExecutionPayloadRef, FixedVector, ForkName, Hash256, Transactions, Uint256, VariableList,
|
ExecutionPayloadRef, FixedVector, ForkName, Hash256, Transactions, Uint256, VariableList,
|
||||||
Withdrawal, Withdrawals,
|
Withdrawal, Withdrawals,
|
||||||
};
|
};
|
||||||
use types::{ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge, KzgProofs};
|
use types::{
|
||||||
|
BeaconStateError, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge,
|
||||||
|
KzgProofs, VersionedHash,
|
||||||
|
};
|
||||||
|
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
@ -191,10 +197,10 @@ pub struct ExecutionBlockWithTransactions<T: EthSpec> {
|
|||||||
pub withdrawals: Vec<JsonWithdrawal>,
|
pub withdrawals: Vec<JsonWithdrawal>,
|
||||||
#[superstruct(only(Deneb))]
|
#[superstruct(only(Deneb))]
|
||||||
#[serde(with = "serde_utils::u64_hex_be")]
|
#[serde(with = "serde_utils::u64_hex_be")]
|
||||||
pub data_gas_used: u64,
|
pub blob_gas_used: u64,
|
||||||
#[superstruct(only(Deneb))]
|
#[superstruct(only(Deneb))]
|
||||||
#[serde(with = "serde_utils::u64_hex_be")]
|
#[serde(with = "serde_utils::u64_hex_be")]
|
||||||
pub excess_data_gas: u64,
|
pub excess_blob_gas: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> TryFrom<ExecutionPayload<T>> for ExecutionBlockWithTransactions<T> {
|
impl<T: EthSpec> TryFrom<ExecutionPayload<T>> for ExecutionBlockWithTransactions<T> {
|
||||||
@ -271,8 +277,8 @@ impl<T: EthSpec> TryFrom<ExecutionPayload<T>> for ExecutionBlockWithTransactions
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|withdrawal| withdrawal.into())
|
.map(|withdrawal| withdrawal.into())
|
||||||
.collect(),
|
.collect(),
|
||||||
data_gas_used: block.data_gas_used,
|
blob_gas_used: block.blob_gas_used,
|
||||||
excess_data_gas: block.excess_data_gas,
|
excess_blob_gas: block.excess_blob_gas,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
Ok(json_payload)
|
Ok(json_payload)
|
||||||
@ -280,7 +286,7 @@ impl<T: EthSpec> TryFrom<ExecutionPayload<T>> for ExecutionBlockWithTransactions
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[superstruct(
|
#[superstruct(
|
||||||
variants(V1, V2),
|
variants(V1, V2, V3),
|
||||||
variant_attributes(derive(Clone, Debug, Eq, Hash, PartialEq),),
|
variant_attributes(derive(Clone, Debug, Eq, Hash, PartialEq),),
|
||||||
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
cast_error(ty = "Error", expr = "Error::IncorrectStateVariant"),
|
||||||
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
partial_getter_error(ty = "Error", expr = "Error::IncorrectStateVariant")
|
||||||
@ -293,8 +299,10 @@ pub struct PayloadAttributes {
|
|||||||
pub prev_randao: Hash256,
|
pub prev_randao: Hash256,
|
||||||
#[superstruct(getter(copy))]
|
#[superstruct(getter(copy))]
|
||||||
pub suggested_fee_recipient: Address,
|
pub suggested_fee_recipient: Address,
|
||||||
#[superstruct(only(V2))]
|
#[superstruct(only(V2, V3))]
|
||||||
pub withdrawals: Vec<Withdrawal>,
|
pub withdrawals: Vec<Withdrawal>,
|
||||||
|
#[superstruct(only(V3), partial_getter(copy))]
|
||||||
|
pub parent_beacon_block_root: Hash256,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PayloadAttributes {
|
impl PayloadAttributes {
|
||||||
@ -303,14 +311,24 @@ impl PayloadAttributes {
|
|||||||
prev_randao: Hash256,
|
prev_randao: Hash256,
|
||||||
suggested_fee_recipient: Address,
|
suggested_fee_recipient: Address,
|
||||||
withdrawals: Option<Vec<Withdrawal>>,
|
withdrawals: Option<Vec<Withdrawal>>,
|
||||||
|
parent_beacon_block_root: Option<Hash256>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
match withdrawals {
|
match withdrawals {
|
||||||
Some(withdrawals) => PayloadAttributes::V2(PayloadAttributesV2 {
|
Some(withdrawals) => match parent_beacon_block_root {
|
||||||
|
Some(parent_beacon_block_root) => PayloadAttributes::V3(PayloadAttributesV3 {
|
||||||
|
timestamp,
|
||||||
|
prev_randao,
|
||||||
|
suggested_fee_recipient,
|
||||||
|
withdrawals,
|
||||||
|
parent_beacon_block_root,
|
||||||
|
}),
|
||||||
|
None => PayloadAttributes::V2(PayloadAttributesV2 {
|
||||||
timestamp,
|
timestamp,
|
||||||
prev_randao,
|
prev_randao,
|
||||||
suggested_fee_recipient,
|
suggested_fee_recipient,
|
||||||
withdrawals,
|
withdrawals,
|
||||||
}),
|
}),
|
||||||
|
},
|
||||||
None => PayloadAttributes::V1(PayloadAttributesV1 {
|
None => PayloadAttributes::V1(PayloadAttributesV1 {
|
||||||
timestamp,
|
timestamp,
|
||||||
prev_randao,
|
prev_randao,
|
||||||
@ -343,6 +361,19 @@ impl From<PayloadAttributes> for SsePayloadAttributes {
|
|||||||
suggested_fee_recipient,
|
suggested_fee_recipient,
|
||||||
withdrawals,
|
withdrawals,
|
||||||
}),
|
}),
|
||||||
|
PayloadAttributes::V3(PayloadAttributesV3 {
|
||||||
|
timestamp,
|
||||||
|
prev_randao,
|
||||||
|
suggested_fee_recipient,
|
||||||
|
withdrawals,
|
||||||
|
parent_beacon_block_root,
|
||||||
|
}) => Self::V3(SsePayloadAttributesV3 {
|
||||||
|
timestamp,
|
||||||
|
prev_randao,
|
||||||
|
suggested_fee_recipient,
|
||||||
|
withdrawals,
|
||||||
|
parent_beacon_block_root,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,6 +417,22 @@ pub struct GetPayloadResponse<T: EthSpec> {
|
|||||||
pub block_value: Uint256,
|
pub block_value: Uint256,
|
||||||
#[superstruct(only(Deneb))]
|
#[superstruct(only(Deneb))]
|
||||||
pub blobs_bundle: BlobsBundleV1<T>,
|
pub blobs_bundle: BlobsBundleV1<T>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(copy))]
|
||||||
|
pub should_override_builder: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> GetPayloadResponse<E> {
|
||||||
|
pub fn fee_recipient(&self) -> Address {
|
||||||
|
ExecutionPayloadRef::from(self.to_ref()).fee_recipient()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_hash(&self) -> ExecutionBlockHash {
|
||||||
|
ExecutionPayloadRef::from(self.to_ref()).block_hash()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_number(&self) -> u64 {
|
||||||
|
ExecutionPayloadRef::from(self.to_ref()).block_number()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: EthSpec> From<GetPayloadResponseRef<'a, T>> for ExecutionPayloadRef<'a, T> {
|
impl<'a, T: EthSpec> From<GetPayloadResponseRef<'a, T>> for ExecutionPayloadRef<'a, T> {
|
||||||
@ -514,8 +561,8 @@ impl<E: EthSpec> ExecutionPayloadBodyV1<E> {
|
|||||||
block_hash: header.block_hash,
|
block_hash: header.block_hash,
|
||||||
transactions: self.transactions,
|
transactions: self.transactions,
|
||||||
withdrawals,
|
withdrawals,
|
||||||
data_gas_used: header.data_gas_used,
|
blob_gas_used: header.blob_gas_used,
|
||||||
excess_data_gas: header.excess_data_gas,
|
excess_blob_gas: header.excess_blob_gas,
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
Err(format!(
|
Err(format!(
|
||||||
@ -535,6 +582,110 @@ pub struct BlobsBundleV1<E: EthSpec> {
|
|||||||
pub blobs: Blobs<E>,
|
pub blobs: Blobs<E>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[superstruct(
|
||||||
|
variants(Merge, Capella, Deneb),
|
||||||
|
variant_attributes(derive(Clone, Debug, PartialEq),),
|
||||||
|
map_into(ExecutionPayload),
|
||||||
|
map_ref_into(ExecutionPayloadRef),
|
||||||
|
cast_error(
|
||||||
|
ty = "BeaconStateError",
|
||||||
|
expr = "BeaconStateError::IncorrectStateVariant"
|
||||||
|
),
|
||||||
|
partial_getter_error(
|
||||||
|
ty = "BeaconStateError",
|
||||||
|
expr = "BeaconStateError::IncorrectStateVariant"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct NewPayloadRequest<E: EthSpec> {
|
||||||
|
#[superstruct(only(Merge), partial_getter(rename = "execution_payload_merge"))]
|
||||||
|
pub execution_payload: ExecutionPayloadMerge<E>,
|
||||||
|
#[superstruct(only(Capella), partial_getter(rename = "execution_payload_capella"))]
|
||||||
|
pub execution_payload: ExecutionPayloadCapella<E>,
|
||||||
|
#[superstruct(only(Deneb), partial_getter(rename = "execution_payload_deneb"))]
|
||||||
|
pub execution_payload: ExecutionPayloadDeneb<E>,
|
||||||
|
#[superstruct(only(Deneb))]
|
||||||
|
pub versioned_hashes: Vec<VersionedHash>,
|
||||||
|
#[superstruct(only(Deneb))]
|
||||||
|
pub parent_beacon_block_root: Hash256,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> NewPayloadRequest<E> {
|
||||||
|
pub fn parent_hash(&self) -> ExecutionBlockHash {
|
||||||
|
match self {
|
||||||
|
Self::Merge(payload) => payload.execution_payload.parent_hash,
|
||||||
|
Self::Capella(payload) => payload.execution_payload.parent_hash,
|
||||||
|
Self::Deneb(payload) => payload.execution_payload.parent_hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_hash(&self) -> ExecutionBlockHash {
|
||||||
|
match self {
|
||||||
|
Self::Merge(payload) => payload.execution_payload.block_hash,
|
||||||
|
Self::Capella(payload) => payload.execution_payload.block_hash,
|
||||||
|
Self::Deneb(payload) => payload.execution_payload.block_hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_number(&self) -> u64 {
|
||||||
|
match self {
|
||||||
|
Self::Merge(payload) => payload.execution_payload.block_number,
|
||||||
|
Self::Capella(payload) => payload.execution_payload.block_number,
|
||||||
|
Self::Deneb(payload) => payload.execution_payload.block_number,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_execution_payload(self) -> ExecutionPayload<E> {
|
||||||
|
map_new_payload_request_into_execution_payload!(self, |request, cons| {
|
||||||
|
cons(request.execution_payload)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, E: EthSpec> TryFrom<BeaconBlockRef<'a, E>> for NewPayloadRequest<E> {
|
||||||
|
type Error = BeaconStateError;
|
||||||
|
|
||||||
|
fn try_from(block: BeaconBlockRef<'a, E>) -> Result<Self, Self::Error> {
|
||||||
|
match block {
|
||||||
|
BeaconBlockRef::Base(_) | BeaconBlockRef::Altair(_) => {
|
||||||
|
Err(Self::Error::IncorrectStateVariant)
|
||||||
|
}
|
||||||
|
BeaconBlockRef::Merge(block_ref) => Ok(Self::Merge(NewPayloadRequestMerge {
|
||||||
|
execution_payload: block_ref.body.execution_payload.execution_payload.clone(),
|
||||||
|
})),
|
||||||
|
BeaconBlockRef::Capella(block_ref) => Ok(Self::Capella(NewPayloadRequestCapella {
|
||||||
|
execution_payload: block_ref.body.execution_payload.execution_payload.clone(),
|
||||||
|
})),
|
||||||
|
BeaconBlockRef::Deneb(block_ref) => Ok(Self::Deneb(NewPayloadRequestDeneb {
|
||||||
|
execution_payload: block_ref.body.execution_payload.execution_payload.clone(),
|
||||||
|
versioned_hashes: block_ref
|
||||||
|
.body
|
||||||
|
.blob_kzg_commitments
|
||||||
|
.iter()
|
||||||
|
.map(kzg_commitment_to_versioned_hash)
|
||||||
|
.collect(),
|
||||||
|
parent_beacon_block_root: block_ref.parent_root,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: EthSpec> TryFrom<ExecutionPayload<E>> for NewPayloadRequest<E> {
|
||||||
|
type Error = BeaconStateError;
|
||||||
|
|
||||||
|
fn try_from(payload: ExecutionPayload<E>) -> Result<Self, Self::Error> {
|
||||||
|
match payload {
|
||||||
|
ExecutionPayload::Merge(payload) => Ok(Self::Merge(NewPayloadRequestMerge {
|
||||||
|
execution_payload: payload,
|
||||||
|
})),
|
||||||
|
ExecutionPayload::Capella(payload) => Ok(Self::Capella(NewPayloadRequestCapella {
|
||||||
|
execution_payload: payload,
|
||||||
|
})),
|
||||||
|
ExecutionPayload::Deneb(_) => Err(Self::Error::IncorrectStateVariant),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct EngineCapabilities {
|
pub struct EngineCapabilities {
|
||||||
pub new_payload_v1: bool,
|
pub new_payload_v1: bool,
|
||||||
@ -542,6 +693,7 @@ pub struct EngineCapabilities {
|
|||||||
pub new_payload_v3: bool,
|
pub new_payload_v3: bool,
|
||||||
pub forkchoice_updated_v1: bool,
|
pub forkchoice_updated_v1: bool,
|
||||||
pub forkchoice_updated_v2: bool,
|
pub forkchoice_updated_v2: bool,
|
||||||
|
pub forkchoice_updated_v3: bool,
|
||||||
pub get_payload_bodies_by_hash_v1: bool,
|
pub get_payload_bodies_by_hash_v1: bool,
|
||||||
pub get_payload_bodies_by_range_v1: bool,
|
pub get_payload_bodies_by_range_v1: bool,
|
||||||
pub get_payload_v1: bool,
|
pub get_payload_v1: bool,
|
||||||
@ -567,6 +719,9 @@ impl EngineCapabilities {
|
|||||||
if self.forkchoice_updated_v2 {
|
if self.forkchoice_updated_v2 {
|
||||||
response.push(ENGINE_FORKCHOICE_UPDATED_V2);
|
response.push(ENGINE_FORKCHOICE_UPDATED_V2);
|
||||||
}
|
}
|
||||||
|
if self.forkchoice_updated_v3 {
|
||||||
|
response.push(ENGINE_FORKCHOICE_UPDATED_V3);
|
||||||
|
}
|
||||||
if self.get_payload_bodies_by_hash_v1 {
|
if self.get_payload_bodies_by_hash_v1 {
|
||||||
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1);
|
response.push(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1);
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use std::collections::HashSet;
|
|||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
|
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use types::{EthSpec, VersionedHash};
|
use types::EthSpec;
|
||||||
|
|
||||||
pub use deposit_log::{DepositLog, Log};
|
pub use deposit_log::{DepositLog, Log};
|
||||||
pub use reqwest::Client;
|
pub use reqwest::Client;
|
||||||
@ -42,6 +42,7 @@ pub const ENGINE_GET_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(2);
|
|||||||
|
|
||||||
pub const ENGINE_FORKCHOICE_UPDATED_V1: &str = "engine_forkchoiceUpdatedV1";
|
pub const ENGINE_FORKCHOICE_UPDATED_V1: &str = "engine_forkchoiceUpdatedV1";
|
||||||
pub const ENGINE_FORKCHOICE_UPDATED_V2: &str = "engine_forkchoiceUpdatedV2";
|
pub const ENGINE_FORKCHOICE_UPDATED_V2: &str = "engine_forkchoiceUpdatedV2";
|
||||||
|
pub const ENGINE_FORKCHOICE_UPDATED_V3: &str = "engine_forkchoiceUpdatedV3";
|
||||||
pub const ENGINE_FORKCHOICE_UPDATED_TIMEOUT: Duration = Duration::from_secs(8);
|
pub const ENGINE_FORKCHOICE_UPDATED_TIMEOUT: Duration = Duration::from_secs(8);
|
||||||
|
|
||||||
pub const ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1: &str = "engine_getPayloadBodiesByHashV1";
|
pub const ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1: &str = "engine_getPayloadBodiesByHashV1";
|
||||||
@ -80,6 +81,7 @@ pub static PRE_CAPELLA_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilit
|
|||||||
new_payload_v3: false,
|
new_payload_v3: false,
|
||||||
forkchoice_updated_v1: true,
|
forkchoice_updated_v1: true,
|
||||||
forkchoice_updated_v2: false,
|
forkchoice_updated_v2: false,
|
||||||
|
forkchoice_updated_v3: false,
|
||||||
get_payload_bodies_by_hash_v1: false,
|
get_payload_bodies_by_hash_v1: false,
|
||||||
get_payload_bodies_by_range_v1: false,
|
get_payload_bodies_by_range_v1: false,
|
||||||
get_payload_v1: true,
|
get_payload_v1: true,
|
||||||
@ -801,12 +803,12 @@ impl HttpJsonRpc {
|
|||||||
|
|
||||||
pub async fn new_payload_v3<T: EthSpec>(
|
pub async fn new_payload_v3<T: EthSpec>(
|
||||||
&self,
|
&self,
|
||||||
execution_payload: ExecutionPayloadDeneb<T>,
|
new_payload_request_deneb: NewPayloadRequestDeneb<T>,
|
||||||
versioned_hashes: Vec<VersionedHash>,
|
|
||||||
) -> Result<PayloadStatusV1, Error> {
|
) -> Result<PayloadStatusV1, Error> {
|
||||||
let params = json!([
|
let params = json!([
|
||||||
JsonExecutionPayload::V3(execution_payload.into()),
|
JsonExecutionPayload::V3(new_payload_request_deneb.execution_payload.into()),
|
||||||
versioned_hashes
|
new_payload_request_deneb.versioned_hashes,
|
||||||
|
new_payload_request_deneb.parent_beacon_block_root,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let response: JsonPayloadStatusV1 = self
|
let response: JsonPayloadStatusV1 = self
|
||||||
@ -944,6 +946,27 @@ impl HttpJsonRpc {
|
|||||||
Ok(response.into())
|
Ok(response.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn forkchoice_updated_v3(
|
||||||
|
&self,
|
||||||
|
forkchoice_state: ForkchoiceState,
|
||||||
|
payload_attributes: Option<PayloadAttributes>,
|
||||||
|
) -> Result<ForkchoiceUpdatedResponse, Error> {
|
||||||
|
let params = json!([
|
||||||
|
JsonForkchoiceStateV1::from(forkchoice_state),
|
||||||
|
payload_attributes.map(JsonPayloadAttributes::from)
|
||||||
|
]);
|
||||||
|
|
||||||
|
let response: JsonForkchoiceUpdatedV1Response = self
|
||||||
|
.rpc_request(
|
||||||
|
ENGINE_FORKCHOICE_UPDATED_V3,
|
||||||
|
params,
|
||||||
|
ENGINE_FORKCHOICE_UPDATED_TIMEOUT * self.execution_timeout_multiplier,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(response.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn get_payload_bodies_by_hash_v1<E: EthSpec>(
|
pub async fn get_payload_bodies_by_hash_v1<E: EthSpec>(
|
||||||
&self,
|
&self,
|
||||||
block_hashes: Vec<ExecutionBlockHash>,
|
block_hashes: Vec<ExecutionBlockHash>,
|
||||||
@ -1013,6 +1036,7 @@ impl HttpJsonRpc {
|
|||||||
new_payload_v3: capabilities.contains(ENGINE_NEW_PAYLOAD_V3),
|
new_payload_v3: capabilities.contains(ENGINE_NEW_PAYLOAD_V3),
|
||||||
forkchoice_updated_v1: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V1),
|
forkchoice_updated_v1: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V1),
|
||||||
forkchoice_updated_v2: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V2),
|
forkchoice_updated_v2: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V2),
|
||||||
|
forkchoice_updated_v3: capabilities.contains(ENGINE_FORKCHOICE_UPDATED_V3),
|
||||||
get_payload_bodies_by_hash_v1: capabilities
|
get_payload_bodies_by_hash_v1: capabilities
|
||||||
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1),
|
.contains(ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1),
|
||||||
get_payload_bodies_by_range_v1: capabilities
|
get_payload_bodies_by_range_v1: capabilities
|
||||||
@ -1056,27 +1080,24 @@ impl HttpJsonRpc {
|
|||||||
// new_payload that the execution engine supports
|
// new_payload that the execution engine supports
|
||||||
pub async fn new_payload<T: EthSpec>(
|
pub async fn new_payload<T: EthSpec>(
|
||||||
&self,
|
&self,
|
||||||
execution_payload: ExecutionPayload<T>,
|
new_payload_request: NewPayloadRequest<T>,
|
||||||
versioned_hashes_opt: Option<Vec<VersionedHash>>,
|
|
||||||
) -> Result<PayloadStatusV1, Error> {
|
) -> Result<PayloadStatusV1, Error> {
|
||||||
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
||||||
match execution_payload {
|
match new_payload_request {
|
||||||
ExecutionPayload::Merge(_) | ExecutionPayload::Capella(_) => {
|
NewPayloadRequest::Merge(_) | NewPayloadRequest::Capella(_) => {
|
||||||
if engine_capabilities.new_payload_v2 {
|
if engine_capabilities.new_payload_v2 {
|
||||||
self.new_payload_v2(execution_payload).await
|
self.new_payload_v2(new_payload_request.into_execution_payload())
|
||||||
|
.await
|
||||||
} else if engine_capabilities.new_payload_v1 {
|
} else if engine_capabilities.new_payload_v1 {
|
||||||
self.new_payload_v1(execution_payload).await
|
self.new_payload_v1(new_payload_request.into_execution_payload())
|
||||||
|
.await
|
||||||
} else {
|
} else {
|
||||||
Err(Error::RequiredMethodUnsupported("engine_newPayload"))
|
Err(Error::RequiredMethodUnsupported("engine_newPayload"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExecutionPayload::Deneb(execution_payload_deneb) => {
|
NewPayloadRequest::Deneb(new_payload_request_deneb) => {
|
||||||
let Some(versioned_hashes) = versioned_hashes_opt else {
|
|
||||||
return Err(Error::IncorrectStateVariant);
|
|
||||||
};
|
|
||||||
if engine_capabilities.new_payload_v3 {
|
if engine_capabilities.new_payload_v3 {
|
||||||
self.new_payload_v3(execution_payload_deneb, versioned_hashes)
|
self.new_payload_v3(new_payload_request_deneb).await
|
||||||
.await
|
|
||||||
} else {
|
} else {
|
||||||
Err(Error::RequiredMethodUnsupported("engine_newPayloadV3"))
|
Err(Error::RequiredMethodUnsupported("engine_newPayloadV3"))
|
||||||
}
|
}
|
||||||
@ -1121,14 +1142,41 @@ impl HttpJsonRpc {
|
|||||||
pub async fn forkchoice_updated(
|
pub async fn forkchoice_updated(
|
||||||
&self,
|
&self,
|
||||||
forkchoice_state: ForkchoiceState,
|
forkchoice_state: ForkchoiceState,
|
||||||
payload_attributes: Option<PayloadAttributes>,
|
maybe_payload_attributes: Option<PayloadAttributes>,
|
||||||
) -> Result<ForkchoiceUpdatedResponse, Error> {
|
) -> Result<ForkchoiceUpdatedResponse, Error> {
|
||||||
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
||||||
|
if let Some(payload_attributes) = maybe_payload_attributes.as_ref() {
|
||||||
|
match payload_attributes {
|
||||||
|
PayloadAttributes::V1(_) | PayloadAttributes::V2(_) => {
|
||||||
if engine_capabilities.forkchoice_updated_v2 {
|
if engine_capabilities.forkchoice_updated_v2 {
|
||||||
self.forkchoice_updated_v2(forkchoice_state, payload_attributes)
|
self.forkchoice_updated_v2(forkchoice_state, maybe_payload_attributes)
|
||||||
.await
|
.await
|
||||||
} else if engine_capabilities.forkchoice_updated_v1 {
|
} else if engine_capabilities.forkchoice_updated_v1 {
|
||||||
self.forkchoice_updated_v1(forkchoice_state, payload_attributes)
|
self.forkchoice_updated_v1(forkchoice_state, maybe_payload_attributes)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
Err(Error::RequiredMethodUnsupported("engine_forkchoiceUpdated"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PayloadAttributes::V3(_) => {
|
||||||
|
if engine_capabilities.forkchoice_updated_v3 {
|
||||||
|
self.forkchoice_updated_v3(forkchoice_state, maybe_payload_attributes)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
Err(Error::RequiredMethodUnsupported(
|
||||||
|
"engine_forkchoiceUpdatedV3",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if engine_capabilities.forkchoice_updated_v3 {
|
||||||
|
self.forkchoice_updated_v3(forkchoice_state, maybe_payload_attributes)
|
||||||
|
.await
|
||||||
|
} else if engine_capabilities.forkchoice_updated_v2 {
|
||||||
|
self.forkchoice_updated_v2(forkchoice_state, maybe_payload_attributes)
|
||||||
|
.await
|
||||||
|
} else if engine_capabilities.forkchoice_updated_v1 {
|
||||||
|
self.forkchoice_updated_v1(forkchoice_state, maybe_payload_attributes)
|
||||||
.await
|
.await
|
||||||
} else {
|
} else {
|
||||||
Err(Error::RequiredMethodUnsupported("engine_forkchoiceUpdated"))
|
Err(Error::RequiredMethodUnsupported("engine_forkchoiceUpdated"))
|
||||||
|
@ -100,10 +100,10 @@ pub struct JsonExecutionPayload<T: EthSpec> {
|
|||||||
pub withdrawals: VariableList<JsonWithdrawal, T::MaxWithdrawalsPerPayload>,
|
pub withdrawals: VariableList<JsonWithdrawal, T::MaxWithdrawalsPerPayload>,
|
||||||
#[superstruct(only(V3))]
|
#[superstruct(only(V3))]
|
||||||
#[serde(with = "serde_utils::u64_hex_be")]
|
#[serde(with = "serde_utils::u64_hex_be")]
|
||||||
pub data_gas_used: u64,
|
pub blob_gas_used: u64,
|
||||||
#[superstruct(only(V3))]
|
#[superstruct(only(V3))]
|
||||||
#[serde(with = "serde_utils::u64_hex_be")]
|
#[serde(with = "serde_utils::u64_hex_be")]
|
||||||
pub excess_data_gas: u64,
|
pub excess_blob_gas: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> From<ExecutionPayloadMerge<T>> for JsonExecutionPayloadV1<T> {
|
impl<T: EthSpec> From<ExecutionPayloadMerge<T>> for JsonExecutionPayloadV1<T> {
|
||||||
@ -175,8 +175,8 @@ impl<T: EthSpec> From<ExecutionPayloadDeneb<T>> for JsonExecutionPayloadV3<T> {
|
|||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into(),
|
.into(),
|
||||||
data_gas_used: payload.data_gas_used,
|
blob_gas_used: payload.blob_gas_used,
|
||||||
excess_data_gas: payload.excess_data_gas,
|
excess_blob_gas: payload.excess_blob_gas,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,8 +260,8 @@ impl<T: EthSpec> From<JsonExecutionPayloadV3<T>> for ExecutionPayloadDeneb<T> {
|
|||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into(),
|
.into(),
|
||||||
data_gas_used: payload.data_gas_used,
|
blob_gas_used: payload.blob_gas_used,
|
||||||
excess_data_gas: payload.excess_data_gas,
|
excess_blob_gas: payload.excess_blob_gas,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,6 +298,8 @@ pub struct JsonGetPayloadResponse<T: EthSpec> {
|
|||||||
pub block_value: Uint256,
|
pub block_value: Uint256,
|
||||||
#[superstruct(only(V3))]
|
#[superstruct(only(V3))]
|
||||||
pub blobs_bundle: JsonBlobsBundleV1<T>,
|
pub blobs_bundle: JsonBlobsBundleV1<T>,
|
||||||
|
#[superstruct(only(V3))]
|
||||||
|
pub should_override_builder: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> From<JsonGetPayloadResponse<T>> for GetPayloadResponse<T> {
|
impl<T: EthSpec> From<JsonGetPayloadResponse<T>> for GetPayloadResponse<T> {
|
||||||
@ -320,6 +322,7 @@ impl<T: EthSpec> From<JsonGetPayloadResponse<T>> for GetPayloadResponse<T> {
|
|||||||
execution_payload: response.execution_payload.into(),
|
execution_payload: response.execution_payload.into(),
|
||||||
block_value: response.block_value,
|
block_value: response.block_value,
|
||||||
blobs_bundle: response.blobs_bundle.into(),
|
blobs_bundle: response.blobs_bundle.into(),
|
||||||
|
should_override_builder: response.should_override_builder,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -361,7 +364,7 @@ impl From<JsonWithdrawal> for Withdrawal {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[superstruct(
|
#[superstruct(
|
||||||
variants(V1, V2),
|
variants(V1, V2, V3),
|
||||||
variant_attributes(
|
variant_attributes(
|
||||||
derive(Debug, Clone, PartialEq, Serialize, Deserialize),
|
derive(Debug, Clone, PartialEq, Serialize, Deserialize),
|
||||||
serde(rename_all = "camelCase")
|
serde(rename_all = "camelCase")
|
||||||
@ -376,8 +379,10 @@ pub struct JsonPayloadAttributes {
|
|||||||
pub timestamp: u64,
|
pub timestamp: u64,
|
||||||
pub prev_randao: Hash256,
|
pub prev_randao: Hash256,
|
||||||
pub suggested_fee_recipient: Address,
|
pub suggested_fee_recipient: Address,
|
||||||
#[superstruct(only(V2))]
|
#[superstruct(only(V2, V3))]
|
||||||
pub withdrawals: Vec<JsonWithdrawal>,
|
pub withdrawals: Vec<JsonWithdrawal>,
|
||||||
|
#[superstruct(only(V3))]
|
||||||
|
pub parent_beacon_block_root: Hash256,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PayloadAttributes> for JsonPayloadAttributes {
|
impl From<PayloadAttributes> for JsonPayloadAttributes {
|
||||||
@ -394,6 +399,13 @@ impl From<PayloadAttributes> for JsonPayloadAttributes {
|
|||||||
suggested_fee_recipient: pa.suggested_fee_recipient,
|
suggested_fee_recipient: pa.suggested_fee_recipient,
|
||||||
withdrawals: pa.withdrawals.into_iter().map(Into::into).collect(),
|
withdrawals: pa.withdrawals.into_iter().map(Into::into).collect(),
|
||||||
}),
|
}),
|
||||||
|
PayloadAttributes::V3(pa) => Self::V3(JsonPayloadAttributesV3 {
|
||||||
|
timestamp: pa.timestamp,
|
||||||
|
prev_randao: pa.prev_randao,
|
||||||
|
suggested_fee_recipient: pa.suggested_fee_recipient,
|
||||||
|
withdrawals: pa.withdrawals.into_iter().map(Into::into).collect(),
|
||||||
|
parent_beacon_block_root: pa.parent_beacon_block_root,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,6 +424,13 @@ impl From<JsonPayloadAttributes> for PayloadAttributes {
|
|||||||
suggested_fee_recipient: jpa.suggested_fee_recipient,
|
suggested_fee_recipient: jpa.suggested_fee_recipient,
|
||||||
withdrawals: jpa.withdrawals.into_iter().map(Into::into).collect(),
|
withdrawals: jpa.withdrawals.into_iter().map(Into::into).collect(),
|
||||||
}),
|
}),
|
||||||
|
JsonPayloadAttributes::V3(jpa) => Self::V3(PayloadAttributesV3 {
|
||||||
|
timestamp: jpa.timestamp,
|
||||||
|
prev_randao: jpa.prev_randao,
|
||||||
|
suggested_fee_recipient: jpa.suggested_fee_recipient,
|
||||||
|
withdrawals: jpa.withdrawals.into_iter().map(Into::into).collect(),
|
||||||
|
parent_beacon_block_root: jpa.parent_beacon_block_root,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,8 @@ use types::beacon_block_body::KzgCommitments;
|
|||||||
use types::blob_sidecar::Blobs;
|
use types::blob_sidecar::Blobs;
|
||||||
use types::KzgProofs;
|
use types::KzgProofs;
|
||||||
use types::{
|
use types::{
|
||||||
AbstractExecPayload, BeaconStateError, ExecPayload, ExecutionPayloadDeneb, VersionedHash,
|
AbstractExecPayload, BeaconStateError, BlindedPayload, BlockType, ChainSpec, Epoch,
|
||||||
};
|
ExecPayload, ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadMerge,
|
||||||
use types::{
|
|
||||||
BlindedPayload, BlockType, ChainSpec, Epoch, ExecutionPayloadCapella, ExecutionPayloadMerge,
|
|
||||||
};
|
};
|
||||||
use types::{ProposerPreparationData, PublicKeyBytes, Signature, Slot, Transaction};
|
use types::{ProposerPreparationData, PublicKeyBytes, Signature, Slot, Transaction};
|
||||||
|
|
||||||
@ -300,6 +298,7 @@ struct Inner<E: EthSpec> {
|
|||||||
builder_profit_threshold: Uint256,
|
builder_profit_threshold: Uint256,
|
||||||
log: Logger,
|
log: Logger,
|
||||||
always_prefer_builder_payload: bool,
|
always_prefer_builder_payload: bool,
|
||||||
|
ignore_builder_override_suggestion_threshold: f32,
|
||||||
/// Track whether the last `newPayload` call errored.
|
/// Track whether the last `newPayload` call errored.
|
||||||
///
|
///
|
||||||
/// This is used *only* in the informational sync status endpoint, so that a VC using this
|
/// This is used *only* in the informational sync status endpoint, so that a VC using this
|
||||||
@ -330,6 +329,7 @@ pub struct Config {
|
|||||||
pub builder_profit_threshold: u128,
|
pub builder_profit_threshold: u128,
|
||||||
pub execution_timeout_multiplier: Option<u32>,
|
pub execution_timeout_multiplier: Option<u32>,
|
||||||
pub always_prefer_builder_payload: bool,
|
pub always_prefer_builder_payload: bool,
|
||||||
|
pub ignore_builder_override_suggestion_threshold: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Provides access to one execution engine and provides a neat interface for consumption by the
|
/// Provides access to one execution engine and provides a neat interface for consumption by the
|
||||||
@ -339,6 +339,40 @@ pub struct ExecutionLayer<T: EthSpec> {
|
|||||||
inner: Arc<Inner<T>>,
|
inner: Arc<Inner<T>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This function will return the percentage difference between 2 U256 values, using `base_value`
|
||||||
|
/// as the denominator. It is accurate to 7 decimal places which is about the precision of
|
||||||
|
/// an f32.
|
||||||
|
///
|
||||||
|
/// If some error is encountered in the calculation, None will be returned.
|
||||||
|
fn percentage_difference_u256(base_value: Uint256, comparison_value: Uint256) -> Option<f32> {
|
||||||
|
if base_value == Uint256::zero() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// this is the total supply of ETH in WEI
|
||||||
|
let max_value = Uint256::from(12u8) * Uint256::exp10(25);
|
||||||
|
if base_value > max_value || comparison_value > max_value {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we should be able to calculate the difference without division by zero or overflow
|
||||||
|
const PRECISION: usize = 7;
|
||||||
|
let precision_factor = Uint256::exp10(PRECISION);
|
||||||
|
let scaled_difference = if base_value <= comparison_value {
|
||||||
|
(comparison_value - base_value) * precision_factor
|
||||||
|
} else {
|
||||||
|
(base_value - comparison_value) * precision_factor
|
||||||
|
};
|
||||||
|
let scaled_proportion = scaled_difference / base_value;
|
||||||
|
// max value of scaled difference is 1.2 * 10^33, well below the max value of a u128 / f64 / f32
|
||||||
|
let percentage =
|
||||||
|
100.0f64 * scaled_proportion.low_u128() as f64 / precision_factor.low_u128() as f64;
|
||||||
|
if base_value <= comparison_value {
|
||||||
|
Some(percentage as f32)
|
||||||
|
} else {
|
||||||
|
Some(-percentage as f32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> ExecutionLayer<T> {
|
impl<T: EthSpec> ExecutionLayer<T> {
|
||||||
/// Instantiate `Self` with an Execution engine specified in `Config`, using JSON-RPC via HTTP.
|
/// Instantiate `Self` with an Execution engine specified in `Config`, using JSON-RPC via HTTP.
|
||||||
pub fn from_config(config: Config, executor: TaskExecutor, log: Logger) -> Result<Self, Error> {
|
pub fn from_config(config: Config, executor: TaskExecutor, log: Logger) -> Result<Self, Error> {
|
||||||
@ -354,6 +388,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
builder_profit_threshold,
|
builder_profit_threshold,
|
||||||
execution_timeout_multiplier,
|
execution_timeout_multiplier,
|
||||||
always_prefer_builder_payload,
|
always_prefer_builder_payload,
|
||||||
|
ignore_builder_override_suggestion_threshold,
|
||||||
} = config;
|
} = config;
|
||||||
|
|
||||||
if urls.len() > 1 {
|
if urls.len() > 1 {
|
||||||
@ -433,6 +468,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
builder_profit_threshold: Uint256::from(builder_profit_threshold),
|
builder_profit_threshold: Uint256::from(builder_profit_threshold),
|
||||||
log,
|
log,
|
||||||
always_prefer_builder_payload,
|
always_prefer_builder_payload,
|
||||||
|
ignore_builder_override_suggestion_threshold,
|
||||||
last_new_payload_errored: RwLock::new(false),
|
last_new_payload_errored: RwLock::new(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -755,7 +791,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
current_fork,
|
current_fork,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map(ProvenancedPayload::Local)
|
.map(|get_payload_response| ProvenancedPayload::Local(get_payload_response.into()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -824,7 +860,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
.await
|
.await
|
||||||
}),
|
}),
|
||||||
timed_future(metrics::GET_BLINDED_PAYLOAD_LOCAL, async {
|
timed_future(metrics::GET_BLINDED_PAYLOAD_LOCAL, async {
|
||||||
self.get_full_payload_caching::<Payload>(
|
self.get_full_payload_caching(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
payload_attributes,
|
payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
@ -844,7 +880,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
},
|
},
|
||||||
"relay_response_ms" => relay_duration.as_millis(),
|
"relay_response_ms" => relay_duration.as_millis(),
|
||||||
"local_fee_recipient" => match &local_result {
|
"local_fee_recipient" => match &local_result {
|
||||||
Ok(proposal_contents) => format!("{:?}", proposal_contents.payload().fee_recipient()),
|
Ok(get_payload_response) => format!("{:?}", get_payload_response.fee_recipient()),
|
||||||
Err(_) => "request failed".to_string()
|
Err(_) => "request failed".to_string()
|
||||||
},
|
},
|
||||||
"local_response_ms" => local_duration.as_millis(),
|
"local_response_ms" => local_duration.as_millis(),
|
||||||
@ -858,20 +894,20 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"Builder error when requesting payload";
|
"Builder error when requesting payload";
|
||||||
"info" => "falling back to local execution client",
|
"info" => "falling back to local execution client",
|
||||||
"relay_error" => ?e,
|
"relay_error" => ?e,
|
||||||
"local_block_hash" => ?local.payload().block_hash(),
|
"local_block_hash" => ?local.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
Ok(ProvenancedPayload::Local(local))
|
Ok(ProvenancedPayload::Local(local.into()))
|
||||||
}
|
}
|
||||||
(Ok(None), Ok(local)) => {
|
(Ok(None), Ok(local)) => {
|
||||||
info!(
|
info!(
|
||||||
self.log(),
|
self.log(),
|
||||||
"Builder did not return a payload";
|
"Builder did not return a payload";
|
||||||
"info" => "falling back to local execution client",
|
"info" => "falling back to local execution client",
|
||||||
"local_block_hash" => ?local.payload().block_hash(),
|
"local_block_hash" => ?local.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
Ok(ProvenancedPayload::Local(local))
|
Ok(ProvenancedPayload::Local(local.into()))
|
||||||
}
|
}
|
||||||
(Ok(Some(relay)), Ok(local)) => {
|
(Ok(Some(relay)), Ok(local)) => {
|
||||||
let header = &relay.data.message.header;
|
let header = &relay.data.message.header;
|
||||||
@ -880,12 +916,13 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
self.log(),
|
self.log(),
|
||||||
"Received local and builder payloads";
|
"Received local and builder payloads";
|
||||||
"relay_block_hash" => ?header.block_hash(),
|
"relay_block_hash" => ?header.block_hash(),
|
||||||
"local_block_hash" => ?local.payload().block_hash(),
|
"local_block_hash" => ?local.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
|
|
||||||
let relay_value = relay.data.message.value;
|
let relay_value = relay.data.message.value;
|
||||||
let local_value = *local.block_value();
|
let local_value = *local.block_value();
|
||||||
|
|
||||||
if !self.inner.always_prefer_builder_payload {
|
if !self.inner.always_prefer_builder_payload {
|
||||||
if local_value >= relay_value {
|
if local_value >= relay_value {
|
||||||
info!(
|
info!(
|
||||||
@ -894,7 +931,24 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"local_block_value" => %local_value,
|
"local_block_value" => %local_value,
|
||||||
"relay_value" => %relay_value
|
"relay_value" => %relay_value
|
||||||
);
|
);
|
||||||
return Ok(ProvenancedPayload::Local(local));
|
return Ok(ProvenancedPayload::Local(local.into()));
|
||||||
|
} else if local.should_override_builder().unwrap_or(false) {
|
||||||
|
let percentage_difference =
|
||||||
|
percentage_difference_u256(local_value, relay_value);
|
||||||
|
if percentage_difference.map_or(false, |percentage| {
|
||||||
|
percentage
|
||||||
|
< self
|
||||||
|
.inner
|
||||||
|
.ignore_builder_override_suggestion_threshold
|
||||||
|
}) {
|
||||||
|
info!(
|
||||||
|
self.log(),
|
||||||
|
"Using local payload because execution engine suggested we ignore builder payload";
|
||||||
|
"local_block_value" => %local_value,
|
||||||
|
"relay_value" => %relay_value
|
||||||
|
);
|
||||||
|
return Ok(ProvenancedPayload::Local(local.into()));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!(
|
||||||
self.log(),
|
self.log(),
|
||||||
@ -909,7 +963,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
&relay,
|
&relay,
|
||||||
parent_hash,
|
parent_hash,
|
||||||
payload_attributes,
|
payload_attributes,
|
||||||
Some(local.payload().block_number()),
|
Some(local.block_number()),
|
||||||
self.inner.builder_profit_threshold,
|
self.inner.builder_profit_threshold,
|
||||||
current_fork,
|
current_fork,
|
||||||
spec,
|
spec,
|
||||||
@ -929,7 +983,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"relay_block_hash" => ?header.block_hash(),
|
"relay_block_hash" => ?header.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
Ok(ProvenancedPayload::Local(local))
|
Ok(ProvenancedPayload::Local(local.into()))
|
||||||
}
|
}
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
metrics::inc_counter_vec(
|
metrics::inc_counter_vec(
|
||||||
@ -944,7 +998,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
"relay_block_hash" => ?header.block_hash(),
|
"relay_block_hash" => ?header.block_hash(),
|
||||||
"parent_hash" => ?parent_hash,
|
"parent_hash" => ?parent_hash,
|
||||||
);
|
);
|
||||||
Ok(ProvenancedPayload::Local(local))
|
Ok(ProvenancedPayload::Local(local.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1049,17 +1103,17 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
current_fork,
|
current_fork,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map(ProvenancedPayload::Local)
|
.map(|get_payload_response| ProvenancedPayload::Local(get_payload_response.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a full payload without caching its result in the execution layer's payload cache.
|
/// Get a full payload without caching its result in the execution layer's payload cache.
|
||||||
async fn get_full_payload<Payload: AbstractExecPayload<T>>(
|
async fn get_full_payload(
|
||||||
&self,
|
&self,
|
||||||
parent_hash: ExecutionBlockHash,
|
parent_hash: ExecutionBlockHash,
|
||||||
payload_attributes: &PayloadAttributes,
|
payload_attributes: &PayloadAttributes,
|
||||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||||
current_fork: ForkName,
|
current_fork: ForkName,
|
||||||
) -> Result<BlockProposalContents<T, Payload>, Error> {
|
) -> Result<GetPayloadResponse<T>, Error> {
|
||||||
self.get_full_payload_with(
|
self.get_full_payload_with(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
payload_attributes,
|
payload_attributes,
|
||||||
@ -1071,13 +1125,13 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get a full payload and cache its result in the execution layer's payload cache.
|
/// Get a full payload and cache its result in the execution layer's payload cache.
|
||||||
async fn get_full_payload_caching<Payload: AbstractExecPayload<T>>(
|
async fn get_full_payload_caching(
|
||||||
&self,
|
&self,
|
||||||
parent_hash: ExecutionBlockHash,
|
parent_hash: ExecutionBlockHash,
|
||||||
payload_attributes: &PayloadAttributes,
|
payload_attributes: &PayloadAttributes,
|
||||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||||
current_fork: ForkName,
|
current_fork: ForkName,
|
||||||
) -> Result<BlockProposalContents<T, Payload>, Error> {
|
) -> Result<GetPayloadResponse<T>, Error> {
|
||||||
self.get_full_payload_with(
|
self.get_full_payload_with(
|
||||||
parent_hash,
|
parent_hash,
|
||||||
payload_attributes,
|
payload_attributes,
|
||||||
@ -1088,14 +1142,14 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_full_payload_with<Payload: AbstractExecPayload<T>>(
|
async fn get_full_payload_with(
|
||||||
&self,
|
&self,
|
||||||
parent_hash: ExecutionBlockHash,
|
parent_hash: ExecutionBlockHash,
|
||||||
payload_attributes: &PayloadAttributes,
|
payload_attributes: &PayloadAttributes,
|
||||||
forkchoice_update_params: ForkchoiceUpdateParameters,
|
forkchoice_update_params: ForkchoiceUpdateParameters,
|
||||||
current_fork: ForkName,
|
current_fork: ForkName,
|
||||||
f: fn(&ExecutionLayer<T>, ExecutionPayloadRef<T>) -> Option<ExecutionPayload<T>>,
|
f: fn(&ExecutionLayer<T>, ExecutionPayloadRef<T>) -> Option<ExecutionPayload<T>>,
|
||||||
) -> Result<BlockProposalContents<T, Payload>, Error> {
|
) -> Result<GetPayloadResponse<T>, Error> {
|
||||||
self.engine()
|
self.engine()
|
||||||
.request(move |engine| async move {
|
.request(move |engine| async move {
|
||||||
let payload_id = if let Some(id) = engine
|
let payload_id = if let Some(id) = engine
|
||||||
@ -1181,7 +1235,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(payload_response.into())
|
Ok(payload_response)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
.map_err(Box::new)
|
.map_err(Box::new)
|
||||||
@ -1191,29 +1245,25 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
/// Maps to the `engine_newPayload` JSON-RPC call.
|
/// Maps to the `engine_newPayload` JSON-RPC call.
|
||||||
pub async fn notify_new_payload(
|
pub async fn notify_new_payload(
|
||||||
&self,
|
&self,
|
||||||
execution_payload: &ExecutionPayload<T>,
|
new_payload_request: NewPayloadRequest<T>,
|
||||||
versioned_hashes: Option<Vec<VersionedHash>>,
|
|
||||||
) -> Result<PayloadStatus, Error> {
|
) -> Result<PayloadStatus, Error> {
|
||||||
let _timer = metrics::start_timer_vec(
|
let _timer = metrics::start_timer_vec(
|
||||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||||
&[metrics::NEW_PAYLOAD],
|
&[metrics::NEW_PAYLOAD],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let block_hash = new_payload_request.block_hash();
|
||||||
trace!(
|
trace!(
|
||||||
self.log(),
|
self.log(),
|
||||||
"Issuing engine_newPayload";
|
"Issuing engine_newPayload";
|
||||||
"parent_hash" => ?execution_payload.parent_hash(),
|
"parent_hash" => ?new_payload_request.parent_hash(),
|
||||||
"block_hash" => ?execution_payload.block_hash(),
|
"block_hash" => ?block_hash,
|
||||||
"block_number" => execution_payload.block_number(),
|
"block_number" => ?new_payload_request.block_number(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.engine()
|
.engine()
|
||||||
.request(|engine| {
|
.request(|engine| engine.api.new_payload(new_payload_request))
|
||||||
engine
|
|
||||||
.api
|
|
||||||
.new_payload(execution_payload.clone(), versioned_hashes)
|
|
||||||
})
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Ok(status) = &result {
|
if let Ok(status) = &result {
|
||||||
@ -1224,7 +1274,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
}
|
}
|
||||||
*self.inner.last_new_payload_errored.write().await = result.is_err();
|
*self.inner.last_new_payload_errored.write().await = result.is_err();
|
||||||
|
|
||||||
process_payload_status(execution_payload.block_hash(), result, self.log())
|
process_payload_status(block_hash, result, self.log())
|
||||||
.map_err(Box::new)
|
.map_err(Box::new)
|
||||||
.map_err(Error::EngineError)
|
.map_err(Error::EngineError)
|
||||||
}
|
}
|
||||||
@ -1796,8 +1846,8 @@ impl<T: EthSpec> ExecutionLayer<T> {
|
|||||||
block_hash: deneb_block.block_hash,
|
block_hash: deneb_block.block_hash,
|
||||||
transactions: convert_transactions(deneb_block.transactions)?,
|
transactions: convert_transactions(deneb_block.transactions)?,
|
||||||
withdrawals,
|
withdrawals,
|
||||||
data_gas_used: deneb_block.data_gas_used,
|
blob_gas_used: deneb_block.blob_gas_used,
|
||||||
excess_data_gas: deneb_block.excess_data_gas,
|
excess_blob_gas: deneb_block.excess_blob_gas,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -2297,4 +2347,42 @@ mod test {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn percentage_difference_u256_tests() {
|
||||||
|
// ensure function returns `None` when base value is zero
|
||||||
|
assert_eq!(percentage_difference_u256(0.into(), 1.into()), None);
|
||||||
|
// ensure function returns `None` when either value is greater than 120 Million ETH
|
||||||
|
let max_value = Uint256::from(12u8) * Uint256::exp10(25);
|
||||||
|
assert_eq!(
|
||||||
|
percentage_difference_u256(1u8.into(), max_value + Uint256::from(1u8)),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
percentage_difference_u256(max_value + Uint256::from(1u8), 1u8.into()),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
// it should work up to max value
|
||||||
|
assert_eq!(
|
||||||
|
percentage_difference_u256(max_value, max_value / Uint256::from(2u8)),
|
||||||
|
Some(-50f32)
|
||||||
|
);
|
||||||
|
// should work when base value is greater than comparison value
|
||||||
|
assert_eq!(
|
||||||
|
percentage_difference_u256(4u8.into(), 3u8.into()),
|
||||||
|
Some(-25f32)
|
||||||
|
);
|
||||||
|
// should work when comparison value is greater than base value
|
||||||
|
assert_eq!(
|
||||||
|
percentage_difference_u256(4u8.into(), 5u8.into()),
|
||||||
|
Some(25f32)
|
||||||
|
);
|
||||||
|
// should be accurate to 7 decimal places
|
||||||
|
let result =
|
||||||
|
percentage_difference_u256(Uint256::from(31415926u64), Uint256::from(13371337u64))
|
||||||
|
.expect("should get percentage");
|
||||||
|
// result = -57.4377116
|
||||||
|
assert!(result > -57.43772);
|
||||||
|
assert!(result <= -57.43771);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -556,7 +556,9 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
|||||||
transactions: vec![].into(),
|
transactions: vec![].into(),
|
||||||
withdrawals: pa.withdrawals.clone().into(),
|
withdrawals: pa.withdrawals.clone().into(),
|
||||||
}),
|
}),
|
||||||
ForkName::Deneb => ExecutionPayload::Deneb(ExecutionPayloadDeneb {
|
_ => unreachable!(),
|
||||||
|
},
|
||||||
|
PayloadAttributes::V3(pa) => ExecutionPayload::Deneb(ExecutionPayloadDeneb {
|
||||||
parent_hash: forkchoice_state.head_block_hash,
|
parent_hash: forkchoice_state.head_block_hash,
|
||||||
fee_recipient: pa.suggested_fee_recipient,
|
fee_recipient: pa.suggested_fee_recipient,
|
||||||
receipts_root: Hash256::repeat_byte(42),
|
receipts_root: Hash256::repeat_byte(42),
|
||||||
@ -572,11 +574,9 @@ impl<T: EthSpec> ExecutionBlockGenerator<T> {
|
|||||||
block_hash: ExecutionBlockHash::zero(),
|
block_hash: ExecutionBlockHash::zero(),
|
||||||
transactions: vec![].into(),
|
transactions: vec![].into(),
|
||||||
withdrawals: pa.withdrawals.clone().into(),
|
withdrawals: pa.withdrawals.clone().into(),
|
||||||
data_gas_used: 0,
|
blob_gas_used: 0,
|
||||||
excess_data_gas: 0,
|
excess_blob_gas: 0,
|
||||||
}),
|
}),
|
||||||
_ => unreachable!(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match execution_payload.fork_name() {
|
match execution_payload.fork_name() {
|
||||||
|
@ -306,6 +306,7 @@ pub async fn handle_rpc<T: EthSpec>(
|
|||||||
GENERIC_ERROR_CODE,
|
GENERIC_ERROR_CODE,
|
||||||
))?
|
))?
|
||||||
.into(),
|
.into(),
|
||||||
|
should_override_builder: false,
|
||||||
})
|
})
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
@ -313,7 +314,9 @@ pub async fn handle_rpc<T: EthSpec>(
|
|||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ENGINE_FORKCHOICE_UPDATED_V1 | ENGINE_FORKCHOICE_UPDATED_V2 => {
|
ENGINE_FORKCHOICE_UPDATED_V1
|
||||||
|
| ENGINE_FORKCHOICE_UPDATED_V2
|
||||||
|
| ENGINE_FORKCHOICE_UPDATED_V3 => {
|
||||||
let forkchoice_state: JsonForkchoiceStateV1 =
|
let forkchoice_state: JsonForkchoiceStateV1 =
|
||||||
get_param(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
|
get_param(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
|
||||||
let payload_attributes = match method {
|
let payload_attributes = match method {
|
||||||
@ -351,10 +354,15 @@ pub async fn handle_rpc<T: EthSpec>(
|
|||||||
})
|
})
|
||||||
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?
|
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?
|
||||||
}
|
}
|
||||||
|
ENGINE_FORKCHOICE_UPDATED_V3 => {
|
||||||
|
get_param::<Option<JsonPayloadAttributesV3>>(params, 1)
|
||||||
|
.map(|opt| opt.map(JsonPayloadAttributes::V3))
|
||||||
|
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// validate method called correctly according to shanghai fork time
|
// validate method called correctly according to fork time
|
||||||
if let Some(pa) = payload_attributes.as_ref() {
|
if let Some(pa) = payload_attributes.as_ref() {
|
||||||
match ctx
|
match ctx
|
||||||
.execution_block_generator
|
.execution_block_generator
|
||||||
@ -372,13 +380,22 @@ pub async fn handle_rpc<T: EthSpec>(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ForkName::Capella | ForkName::Deneb => {
|
ForkName::Capella => {
|
||||||
if method == ENGINE_FORKCHOICE_UPDATED_V1 {
|
if method == ENGINE_FORKCHOICE_UPDATED_V1 {
|
||||||
return Err((
|
return Err((
|
||||||
format!("{} called after Capella fork!", method),
|
format!("{} called after Capella fork!", method),
|
||||||
FORK_REQUEST_MISMATCH_ERROR_CODE,
|
FORK_REQUEST_MISMATCH_ERROR_CODE,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
if method == ENGINE_FORKCHOICE_UPDATED_V3 {
|
||||||
|
return Err((
|
||||||
|
format!(
|
||||||
|
"{} called with `JsonPayloadAttributesV3` before Deneb fork!",
|
||||||
|
method
|
||||||
|
),
|
||||||
|
GENERIC_ERROR_CODE,
|
||||||
|
));
|
||||||
|
}
|
||||||
if matches!(pa, JsonPayloadAttributes::V1(_)) {
|
if matches!(pa, JsonPayloadAttributes::V1(_)) {
|
||||||
return Err((
|
return Err((
|
||||||
format!(
|
format!(
|
||||||
@ -389,6 +406,20 @@ pub async fn handle_rpc<T: EthSpec>(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ForkName::Deneb => {
|
||||||
|
if method == ENGINE_FORKCHOICE_UPDATED_V1 {
|
||||||
|
return Err((
|
||||||
|
format!("{} called after Deneb fork!", method),
|
||||||
|
FORK_REQUEST_MISMATCH_ERROR_CODE,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if method == ENGINE_FORKCHOICE_UPDATED_V2 {
|
||||||
|
return Err((
|
||||||
|
format!("{} called after Deneb fork!", method),
|
||||||
|
FORK_REQUEST_MISMATCH_ERROR_CODE,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -36,8 +36,8 @@ use task_executor::TaskExecutor;
|
|||||||
use tempfile::NamedTempFile;
|
use tempfile::NamedTempFile;
|
||||||
use tree_hash::TreeHash;
|
use tree_hash::TreeHash;
|
||||||
use types::{
|
use types::{
|
||||||
Address, BeaconState, BlindedPayload, ChainSpec, EthSpec, ExecPayload, ForkName, Hash256, Slot,
|
Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload,
|
||||||
Uint256,
|
ExecutionPayloadHeader, ForkName, Hash256, Slot, Uint256,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -402,13 +402,23 @@ impl<E: EthSpec> mev_rs::BlindedBlockProvider for MockBuilder<E> {
|
|||||||
let prev_randao = head_state
|
let prev_randao = head_state
|
||||||
.get_randao_mix(head_state.current_epoch())
|
.get_randao_mix(head_state.current_epoch())
|
||||||
.map_err(convert_err)?;
|
.map_err(convert_err)?;
|
||||||
|
let parent_root = head_state.latest_block_header().parent_root;
|
||||||
|
|
||||||
let payload_attributes = match fork {
|
let payload_attributes = match fork {
|
||||||
ForkName::Merge => PayloadAttributes::new(timestamp, *prev_randao, fee_recipient, None),
|
ForkName::Merge => {
|
||||||
// the withdrawals root is filled in by operations
|
PayloadAttributes::new(timestamp, *prev_randao, fee_recipient, None, None)
|
||||||
ForkName::Capella | ForkName::Deneb => {
|
|
||||||
PayloadAttributes::new(timestamp, *prev_randao, fee_recipient, Some(vec![]))
|
|
||||||
}
|
}
|
||||||
|
// the withdrawals root is filled in by operations
|
||||||
|
ForkName::Capella => {
|
||||||
|
PayloadAttributes::new(timestamp, *prev_randao, fee_recipient, Some(vec![]), None)
|
||||||
|
}
|
||||||
|
ForkName::Deneb => PayloadAttributes::new(
|
||||||
|
timestamp,
|
||||||
|
*prev_randao,
|
||||||
|
fee_recipient,
|
||||||
|
Some(vec![]),
|
||||||
|
Some(parent_root),
|
||||||
|
),
|
||||||
ForkName::Base | ForkName::Altair => {
|
ForkName::Base | ForkName::Altair => {
|
||||||
return Err(MevError::InvalidFork);
|
return Err(MevError::InvalidFork);
|
||||||
}
|
}
|
||||||
@ -425,9 +435,9 @@ impl<E: EthSpec> mev_rs::BlindedBlockProvider for MockBuilder<E> {
|
|||||||
finalized_hash: Some(finalized_execution_hash),
|
finalized_hash: Some(finalized_execution_hash),
|
||||||
};
|
};
|
||||||
|
|
||||||
let payload = self
|
let payload: ExecutionPayload<E> = self
|
||||||
.el
|
.el
|
||||||
.get_full_payload_caching::<BlindedPayload<E>>(
|
.get_full_payload_caching(
|
||||||
head_execution_hash,
|
head_execution_hash,
|
||||||
&payload_attributes,
|
&payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
@ -435,10 +445,17 @@ impl<E: EthSpec> mev_rs::BlindedBlockProvider for MockBuilder<E> {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(convert_err)?
|
.map_err(convert_err)?
|
||||||
.to_payload()
|
.into();
|
||||||
.to_execution_payload_header();
|
|
||||||
|
|
||||||
let json_payload = serde_json::to_string(&payload).map_err(convert_err)?;
|
let header: ExecutionPayloadHeader<E> = match payload {
|
||||||
|
ExecutionPayload::Merge(payload) => ExecutionPayloadHeader::Merge((&payload).into()),
|
||||||
|
ExecutionPayload::Capella(payload) => {
|
||||||
|
ExecutionPayloadHeader::Capella((&payload).into())
|
||||||
|
}
|
||||||
|
ExecutionPayload::Deneb(payload) => ExecutionPayloadHeader::Deneb((&payload).into()),
|
||||||
|
};
|
||||||
|
|
||||||
|
let json_payload = serde_json::to_string(&header).map_err(convert_err)?;
|
||||||
let mut message = match fork {
|
let mut message = match fork {
|
||||||
ForkName::Capella => BuilderBid::Capella(BuilderBidCapella {
|
ForkName::Capella => BuilderBid::Capella(BuilderBidCapella {
|
||||||
header: serde_json::from_str(json_payload.as_str()).map_err(convert_err)?,
|
header: serde_json::from_str(json_payload.as_str()).map_err(convert_err)?,
|
||||||
|
@ -110,7 +110,8 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
|||||||
timestamp,
|
timestamp,
|
||||||
prev_randao,
|
prev_randao,
|
||||||
Address::repeat_byte(42),
|
Address::repeat_byte(42),
|
||||||
// FIXME: think about how to handle different forks / withdrawals here..
|
// FIXME: think about how to handle different forks here..
|
||||||
|
None,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -140,7 +141,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
|||||||
};
|
};
|
||||||
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
|
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
|
||||||
let payload_attributes =
|
let payload_attributes =
|
||||||
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None);
|
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
|
||||||
let payload: ExecutionPayload<T> = self
|
let payload: ExecutionPayload<T> = self
|
||||||
.el
|
.el
|
||||||
.get_payload::<FullPayload<T>>(
|
.get_payload::<FullPayload<T>>(
|
||||||
@ -148,7 +149,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
|||||||
&payload_attributes,
|
&payload_attributes,
|
||||||
forkchoice_update_params,
|
forkchoice_update_params,
|
||||||
builder_params,
|
builder_params,
|
||||||
// FIXME: do we need to consider other forks somehow? What about withdrawals?
|
// FIXME: do we need to consider other forks somehow?
|
||||||
ForkName::Merge,
|
ForkName::Merge,
|
||||||
&self.spec,
|
&self.spec,
|
||||||
)
|
)
|
||||||
@ -175,7 +176,7 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
|||||||
};
|
};
|
||||||
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
|
let suggested_fee_recipient = self.el.get_suggested_fee_recipient(validator_index).await;
|
||||||
let payload_attributes =
|
let payload_attributes =
|
||||||
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None);
|
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
|
||||||
let payload_header = self
|
let payload_header = self
|
||||||
.el
|
.el
|
||||||
.get_payload::<BlindedPayload<T>>(
|
.get_payload::<BlindedPayload<T>>(
|
||||||
@ -204,7 +205,12 @@ impl<T: EthSpec> MockExecutionLayer<T> {
|
|||||||
Some(payload.clone())
|
Some(payload.clone())
|
||||||
);
|
);
|
||||||
|
|
||||||
let status = self.el.notify_new_payload(&payload, None).await.unwrap();
|
// TODO: again consider forks
|
||||||
|
let status = self
|
||||||
|
.el
|
||||||
|
.notify_new_payload(payload.try_into().unwrap())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
assert_eq!(status, PayloadStatus::Valid);
|
assert_eq!(status, PayloadStatus::Valid);
|
||||||
|
|
||||||
// Use junk values for slot/head-root to ensure there is no payload supplied.
|
// Use junk values for slot/head-root to ensure there is no payload supplied.
|
||||||
|
@ -44,6 +44,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
|
|||||||
new_payload_v3: true,
|
new_payload_v3: true,
|
||||||
forkchoice_updated_v1: true,
|
forkchoice_updated_v1: true,
|
||||||
forkchoice_updated_v2: true,
|
forkchoice_updated_v2: true,
|
||||||
|
forkchoice_updated_v3: true,
|
||||||
get_payload_bodies_by_hash_v1: true,
|
get_payload_bodies_by_hash_v1: true,
|
||||||
get_payload_bodies_by_range_v1: true,
|
get_payload_bodies_by_range_v1: true,
|
||||||
get_payload_v1: true,
|
get_payload_v1: true,
|
||||||
|
@ -30,7 +30,7 @@ impl<'a, T: EthSpec> AttMaxCover<'a, T> {
|
|||||||
if let BeaconState::Base(ref base_state) = state {
|
if let BeaconState::Base(ref base_state) = state {
|
||||||
Self::new_for_base(att, state, base_state, total_active_balance, spec)
|
Self::new_for_base(att, state, base_state, total_active_balance, spec)
|
||||||
} else {
|
} else {
|
||||||
Self::new_for_altair(att, state, reward_cache, total_active_balance, spec)
|
Self::new_for_altair_deneb(att, state, reward_cache, total_active_balance, spec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ impl<'a, T: EthSpec> AttMaxCover<'a, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Initialise an attestation cover object for Altair or later.
|
/// Initialise an attestation cover object for Altair or later.
|
||||||
pub fn new_for_altair(
|
pub fn new_for_altair_deneb(
|
||||||
att: AttestationRef<'a, T>,
|
att: AttestationRef<'a, T>,
|
||||||
state: &BeaconState<T>,
|
state: &BeaconState<T>,
|
||||||
reward_cache: &'a RewardCache,
|
reward_cache: &'a RewardCache,
|
||||||
|
@ -1852,7 +1852,21 @@ mod release_tests {
|
|||||||
// Sign an exit with the Altair domain and a phase0 epoch. This is a weird type of exit
|
// Sign an exit with the Altair domain and a phase0 epoch. This is a weird type of exit
|
||||||
// that is valid because after the Bellatrix fork we'll use the Altair fork domain to verify
|
// that is valid because after the Bellatrix fork we'll use the Altair fork domain to verify
|
||||||
// all prior epochs.
|
// all prior epochs.
|
||||||
let exit2 = harness.make_voluntary_exit(2, Epoch::new(0));
|
let unsigned_exit = VoluntaryExit {
|
||||||
|
epoch: Epoch::new(0),
|
||||||
|
validator_index: 2,
|
||||||
|
};
|
||||||
|
let exit2 = SignedVoluntaryExit {
|
||||||
|
message: unsigned_exit.clone(),
|
||||||
|
signature: harness.validator_keypairs[2]
|
||||||
|
.sk
|
||||||
|
.sign(unsigned_exit.signing_root(spec.compute_domain(
|
||||||
|
Domain::VoluntaryExit,
|
||||||
|
harness.spec.altair_fork_version,
|
||||||
|
harness.chain.genesis_validators_root,
|
||||||
|
))),
|
||||||
|
};
|
||||||
|
|
||||||
let verified_exit2 = exit2
|
let verified_exit2 = exit2
|
||||||
.clone()
|
.clone()
|
||||||
.validate(&bellatrix_head.beacon_state, &harness.chain.spec)
|
.validate(&bellatrix_head.beacon_state, &harness.chain.spec)
|
||||||
|
@ -1089,6 +1089,23 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
.default_value("0")
|
.default_value("0")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("ignore-builder-override-suggestion-threshold")
|
||||||
|
.long("ignore-builder-override-suggestion-threshold")
|
||||||
|
.value_name("PERCENTAGE")
|
||||||
|
.help("When the EE advises Lighthouse to ignore the builder payload, this flag \
|
||||||
|
specifies a percentage threshold for the difference between the reward from \
|
||||||
|
the builder payload and the local EE's payload. This threshold must be met \
|
||||||
|
for Lighthouse to consider ignoring the EE's suggestion. If the reward from \
|
||||||
|
the builder's payload doesn't exceed the local payload by at least this \
|
||||||
|
percentage, the local payload will be used. The conditions under which the \
|
||||||
|
EE may make this suggestion depend on the EE's implementation, with the \
|
||||||
|
primary intent being to safeguard against potential censorship attacks \
|
||||||
|
from builders. Setting this flag to 0 will cause Lighthouse to always \
|
||||||
|
ignore the EE's suggestion. Default: 10.0 (equivalent to 10%).")
|
||||||
|
.default_value("10.0")
|
||||||
|
.takes_value(true)
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("builder-user-agent")
|
Arg::with_name("builder-user-agent")
|
||||||
.long("builder-user-agent")
|
.long("builder-user-agent")
|
||||||
@ -1160,6 +1177,7 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
|||||||
// to local payloads, therefore it fundamentally conflicts with
|
// to local payloads, therefore it fundamentally conflicts with
|
||||||
// always using the builder.
|
// always using the builder.
|
||||||
.conflicts_with("builder-profit-threshold")
|
.conflicts_with("builder-profit-threshold")
|
||||||
|
.conflicts_with("ignore-builder-override-suggestion-threshold")
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name("invalid-gossip-verified-blocks-path")
|
Arg::with_name("invalid-gossip-verified-blocks-path")
|
||||||
|
@ -345,6 +345,8 @@ pub fn get_config<E: EthSpec>(
|
|||||||
el_config.default_datadir = client_config.data_dir().clone();
|
el_config.default_datadir = client_config.data_dir().clone();
|
||||||
el_config.builder_profit_threshold =
|
el_config.builder_profit_threshold =
|
||||||
clap_utils::parse_required(cli_args, "builder-profit-threshold")?;
|
clap_utils::parse_required(cli_args, "builder-profit-threshold")?;
|
||||||
|
el_config.ignore_builder_override_suggestion_threshold =
|
||||||
|
clap_utils::parse_required(cli_args, "ignore-builder-override-suggestion-threshold")?;
|
||||||
let execution_timeout_multiplier =
|
let execution_timeout_multiplier =
|
||||||
clap_utils::parse_required(cli_args, "execution-timeout-multiplier")?;
|
clap_utils::parse_required(cli_args, "execution-timeout-multiplier")?;
|
||||||
el_config.execution_timeout_multiplier = Some(execution_timeout_multiplier);
|
el_config.execution_timeout_multiplier = Some(execution_timeout_multiplier);
|
||||||
|
@ -926,7 +926,7 @@ pub struct SseLateHead {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[superstruct(
|
#[superstruct(
|
||||||
variants(V1, V2),
|
variants(V1, V2, V3),
|
||||||
variant_attributes(derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize))
|
variant_attributes(derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize))
|
||||||
)]
|
)]
|
||||||
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
|
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
|
||||||
@ -939,8 +939,10 @@ pub struct SsePayloadAttributes {
|
|||||||
pub prev_randao: Hash256,
|
pub prev_randao: Hash256,
|
||||||
#[superstruct(getter(copy))]
|
#[superstruct(getter(copy))]
|
||||||
pub suggested_fee_recipient: Address,
|
pub suggested_fee_recipient: Address,
|
||||||
#[superstruct(only(V2))]
|
#[superstruct(only(V2, V3))]
|
||||||
pub withdrawals: Vec<Withdrawal>,
|
pub withdrawals: Vec<Withdrawal>,
|
||||||
|
#[superstruct(only(V3), partial_getter(copy))]
|
||||||
|
pub parent_beacon_block_root: Hash256,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Deserialize, Serialize, Clone)]
|
#[derive(PartialEq, Debug, Deserialize, Serialize, Clone)]
|
||||||
|
@ -44,9 +44,22 @@ pub fn get_attestation_participation_flag_indices<T: EthSpec>(
|
|||||||
if is_matching_source && inclusion_delay <= T::slots_per_epoch().integer_sqrt() {
|
if is_matching_source && inclusion_delay <= T::slots_per_epoch().integer_sqrt() {
|
||||||
participation_flag_indices.push(TIMELY_SOURCE_FLAG_INDEX);
|
participation_flag_indices.push(TIMELY_SOURCE_FLAG_INDEX);
|
||||||
}
|
}
|
||||||
|
match state {
|
||||||
|
&BeaconState::Base(_)
|
||||||
|
| &BeaconState::Altair(_)
|
||||||
|
| &BeaconState::Merge(_)
|
||||||
|
| &BeaconState::Capella(_) => {
|
||||||
if is_matching_target && inclusion_delay <= T::slots_per_epoch() {
|
if is_matching_target && inclusion_delay <= T::slots_per_epoch() {
|
||||||
participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX);
|
participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
&BeaconState::Deneb(_) => {
|
||||||
|
if is_matching_target {
|
||||||
|
// [Modified in Deneb:EIP7045]
|
||||||
|
participation_flag_indices.push(TIMELY_TARGET_FLAG_INDEX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if is_matching_head && inclusion_delay == spec.min_attestation_inclusion_delay {
|
if is_matching_head && inclusion_delay == spec.min_attestation_inclusion_delay {
|
||||||
participation_flag_indices.push(TIMELY_HEAD_FLAG_INDEX);
|
participation_flag_indices.push(TIMELY_HEAD_FLAG_INDEX);
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,7 @@ pub mod base {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod altair {
|
pub mod altair_deneb {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::common::update_progressive_balances_cache::update_progressive_balances_on_attestation;
|
use crate::common::update_progressive_balances_cache::update_progressive_balances_on_attestation;
|
||||||
use types::consts::altair::TIMELY_TARGET_FLAG_INDEX;
|
use types::consts::altair::TIMELY_TARGET_FLAG_INDEX;
|
||||||
@ -269,7 +269,7 @@ pub fn process_attestations<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
|||||||
| BeaconBlockBodyRef::Merge(_)
|
| BeaconBlockBodyRef::Merge(_)
|
||||||
| BeaconBlockBodyRef::Capella(_)
|
| BeaconBlockBodyRef::Capella(_)
|
||||||
| BeaconBlockBodyRef::Deneb(_) => {
|
| BeaconBlockBodyRef::Deneb(_) => {
|
||||||
altair::process_attestations(
|
altair_deneb::process_attestations(
|
||||||
state,
|
state,
|
||||||
block_body.attestations(),
|
block_body.attestations(),
|
||||||
verify_signatures,
|
verify_signatures,
|
||||||
|
@ -387,12 +387,23 @@ where
|
|||||||
let exit = &signed_exit.message;
|
let exit = &signed_exit.message;
|
||||||
let proposer_index = exit.validator_index as usize;
|
let proposer_index = exit.validator_index as usize;
|
||||||
|
|
||||||
let domain = spec.get_domain(
|
let domain = match state {
|
||||||
|
BeaconState::Base(_)
|
||||||
|
| BeaconState::Altair(_)
|
||||||
|
| BeaconState::Merge(_)
|
||||||
|
| BeaconState::Capella(_) => spec.get_domain(
|
||||||
exit.epoch,
|
exit.epoch,
|
||||||
Domain::VoluntaryExit,
|
Domain::VoluntaryExit,
|
||||||
&state.fork(),
|
&state.fork(),
|
||||||
state.genesis_validators_root(),
|
state.genesis_validators_root(),
|
||||||
);
|
),
|
||||||
|
// EIP-7044
|
||||||
|
BeaconState::Deneb(_) => spec.compute_domain(
|
||||||
|
Domain::VoluntaryExit,
|
||||||
|
spec.capella_fork_version,
|
||||||
|
state.genesis_validators_root(),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
let message = exit.signing_root(domain);
|
let message = exit.signing_root(domain);
|
||||||
|
|
||||||
|
@ -32,6 +32,11 @@ pub fn verify_attestation_for_block_inclusion<'ctxt, T: EthSpec>(
|
|||||||
attestation: data.slot,
|
attestation: data.slot,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
match state {
|
||||||
|
BeaconState::Base(_)
|
||||||
|
| BeaconState::Altair(_)
|
||||||
|
| BeaconState::Merge(_)
|
||||||
|
| BeaconState::Capella(_) => {
|
||||||
verify!(
|
verify!(
|
||||||
state.slot() <= data.slot.safe_add(T::slots_per_epoch())?,
|
state.slot() <= data.slot.safe_add(T::slots_per_epoch())?,
|
||||||
Invalid::IncludedTooLate {
|
Invalid::IncludedTooLate {
|
||||||
@ -39,6 +44,10 @@ pub fn verify_attestation_for_block_inclusion<'ctxt, T: EthSpec>(
|
|||||||
attestation: data.slot,
|
attestation: data.slot,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
// [Modified in Deneb:EIP7045]
|
||||||
|
BeaconState::Deneb(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
verify_attestation_for_state(state, attestation, ctxt, verify_signatures, spec)
|
verify_attestation_for_state(state, attestation, ctxt, verify_signatures, spec)
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,9 @@ use metastruct::metastruct;
|
|||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
#[metastruct(mappings(map_execution_block_header_fields_except_withdrawals(exclude(
|
#[metastruct(mappings(map_execution_block_header_fields_except_withdrawals(exclude(
|
||||||
withdrawals_root,
|
withdrawals_root,
|
||||||
data_gas_used,
|
blob_gas_used,
|
||||||
excess_data_gas
|
excess_blob_gas,
|
||||||
|
parent_beacon_block_root
|
||||||
)),))]
|
)),))]
|
||||||
pub struct ExecutionBlockHeader {
|
pub struct ExecutionBlockHeader {
|
||||||
pub parent_hash: Hash256,
|
pub parent_hash: Hash256,
|
||||||
@ -47,8 +48,9 @@ pub struct ExecutionBlockHeader {
|
|||||||
pub nonce: Hash64,
|
pub nonce: Hash64,
|
||||||
pub base_fee_per_gas: Uint256,
|
pub base_fee_per_gas: Uint256,
|
||||||
pub withdrawals_root: Option<Hash256>,
|
pub withdrawals_root: Option<Hash256>,
|
||||||
pub data_gas_used: Option<u64>,
|
pub blob_gas_used: Option<u64>,
|
||||||
pub excess_data_gas: Option<u64>,
|
pub excess_blob_gas: Option<u64>,
|
||||||
|
pub parent_beacon_block_root: Option<Hash256>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExecutionBlockHeader {
|
impl ExecutionBlockHeader {
|
||||||
@ -57,8 +59,9 @@ impl ExecutionBlockHeader {
|
|||||||
rlp_empty_list_root: Hash256,
|
rlp_empty_list_root: Hash256,
|
||||||
rlp_transactions_root: Hash256,
|
rlp_transactions_root: Hash256,
|
||||||
rlp_withdrawals_root: Option<Hash256>,
|
rlp_withdrawals_root: Option<Hash256>,
|
||||||
rlp_data_gas_used: Option<u64>,
|
rlp_blob_gas_used: Option<u64>,
|
||||||
rlp_excess_data_gas: Option<u64>,
|
rlp_excess_blob_gas: Option<u64>,
|
||||||
|
rlp_parent_beacon_block_root: Option<Hash256>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
// Most of these field mappings are defined in EIP-3675 except for `mixHash`, which is
|
// Most of these field mappings are defined in EIP-3675 except for `mixHash`, which is
|
||||||
// defined in EIP-4399.
|
// defined in EIP-4399.
|
||||||
@ -80,8 +83,9 @@ impl ExecutionBlockHeader {
|
|||||||
nonce: Hash64::zero(),
|
nonce: Hash64::zero(),
|
||||||
base_fee_per_gas: payload.base_fee_per_gas(),
|
base_fee_per_gas: payload.base_fee_per_gas(),
|
||||||
withdrawals_root: rlp_withdrawals_root,
|
withdrawals_root: rlp_withdrawals_root,
|
||||||
data_gas_used: rlp_data_gas_used,
|
blob_gas_used: rlp_blob_gas_used,
|
||||||
excess_data_gas: rlp_excess_data_gas,
|
excess_blob_gas: rlp_excess_blob_gas,
|
||||||
|
parent_beacon_block_root: rlp_parent_beacon_block_root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,14 +83,12 @@ pub struct ExecutionPayload<T: EthSpec> {
|
|||||||
pub transactions: Transactions<T>,
|
pub transactions: Transactions<T>,
|
||||||
#[superstruct(only(Capella, Deneb))]
|
#[superstruct(only(Capella, Deneb))]
|
||||||
pub withdrawals: Withdrawals<T>,
|
pub withdrawals: Withdrawals<T>,
|
||||||
#[superstruct(only(Deneb))]
|
#[superstruct(only(Deneb), partial_getter(copy))]
|
||||||
#[serde(with = "serde_utils::quoted_u64")]
|
#[serde(with = "serde_utils::quoted_u64")]
|
||||||
#[superstruct(getter(copy))]
|
pub blob_gas_used: u64,
|
||||||
pub data_gas_used: u64,
|
#[superstruct(only(Deneb), partial_getter(copy))]
|
||||||
#[superstruct(only(Deneb))]
|
|
||||||
#[serde(with = "serde_utils::quoted_u64")]
|
#[serde(with = "serde_utils::quoted_u64")]
|
||||||
#[superstruct(getter(copy))]
|
pub excess_blob_gas: u64,
|
||||||
pub excess_data_gas: u64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: EthSpec> ExecutionPayloadRef<'a, T> {
|
impl<'a, T: EthSpec> ExecutionPayloadRef<'a, T> {
|
||||||
|
@ -80,11 +80,11 @@ pub struct ExecutionPayloadHeader<T: EthSpec> {
|
|||||||
#[superstruct(only(Deneb))]
|
#[superstruct(only(Deneb))]
|
||||||
#[serde(with = "serde_utils::quoted_u64")]
|
#[serde(with = "serde_utils::quoted_u64")]
|
||||||
#[superstruct(getter(copy))]
|
#[superstruct(getter(copy))]
|
||||||
pub data_gas_used: u64,
|
pub blob_gas_used: u64,
|
||||||
#[superstruct(only(Deneb))]
|
#[superstruct(only(Deneb))]
|
||||||
#[serde(with = "serde_utils::quoted_u64")]
|
#[serde(with = "serde_utils::quoted_u64")]
|
||||||
#[superstruct(getter(copy))]
|
#[superstruct(getter(copy))]
|
||||||
pub excess_data_gas: u64,
|
pub excess_blob_gas: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EthSpec> ExecutionPayloadHeader<T> {
|
impl<T: EthSpec> ExecutionPayloadHeader<T> {
|
||||||
@ -155,8 +155,8 @@ impl<T: EthSpec> ExecutionPayloadHeaderCapella<T> {
|
|||||||
block_hash: self.block_hash,
|
block_hash: self.block_hash,
|
||||||
transactions_root: self.transactions_root,
|
transactions_root: self.transactions_root,
|
||||||
withdrawals_root: self.withdrawals_root,
|
withdrawals_root: self.withdrawals_root,
|
||||||
data_gas_used: 0,
|
blob_gas_used: 0,
|
||||||
excess_data_gas: 0,
|
excess_blob_gas: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,8 +221,8 @@ impl<'a, T: EthSpec> From<&'a ExecutionPayloadDeneb<T>> for ExecutionPayloadHead
|
|||||||
block_hash: payload.block_hash,
|
block_hash: payload.block_hash,
|
||||||
transactions_root: payload.transactions.tree_hash_root(),
|
transactions_root: payload.transactions.tree_hash_root(),
|
||||||
withdrawals_root: payload.withdrawals.tree_hash_root(),
|
withdrawals_root: payload.withdrawals.tree_hash_root(),
|
||||||
data_gas_used: payload.data_gas_used,
|
blob_gas_used: payload.blob_gas_used,
|
||||||
excess_data_gas: payload.excess_data_gas,
|
excess_blob_gas: payload.excess_blob_gas,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
test_utils::TestRandom, ChainSpec, Domain, Epoch, Fork, Hash256, SecretKey, SignedRoot,
|
test_utils::TestRandom, ChainSpec, Domain, Epoch, ForkName, Hash256, SecretKey, SignedRoot,
|
||||||
SignedVoluntaryExit,
|
SignedVoluntaryExit,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,16 +37,20 @@ impl VoluntaryExit {
|
|||||||
pub fn sign(
|
pub fn sign(
|
||||||
self,
|
self,
|
||||||
secret_key: &SecretKey,
|
secret_key: &SecretKey,
|
||||||
fork: &Fork,
|
|
||||||
genesis_validators_root: Hash256,
|
genesis_validators_root: Hash256,
|
||||||
spec: &ChainSpec,
|
spec: &ChainSpec,
|
||||||
) -> SignedVoluntaryExit {
|
) -> SignedVoluntaryExit {
|
||||||
let domain = spec.get_domain(
|
let fork_name = spec.fork_name_at_epoch(self.epoch);
|
||||||
self.epoch,
|
let fork_version = match fork_name {
|
||||||
Domain::VoluntaryExit,
|
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
||||||
fork,
|
spec.fork_version_for_name(fork_name)
|
||||||
genesis_validators_root,
|
}
|
||||||
);
|
// EIP-7044
|
||||||
|
ForkName::Deneb => spec.fork_version_for_name(ForkName::Capella),
|
||||||
|
};
|
||||||
|
let domain =
|
||||||
|
spec.compute_domain(Domain::VoluntaryExit, fork_version, genesis_validators_root);
|
||||||
|
|
||||||
let message = self.signing_root(domain);
|
let message = self.signing_root(domain);
|
||||||
SignedVoluntaryExit {
|
SignedVoluntaryExit {
|
||||||
message: self,
|
message: self,
|
||||||
|
@ -734,6 +734,38 @@ fn builder_fallback_flags() {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
run_payload_builder_flag_test_with_config(
|
||||||
|
"builder",
|
||||||
|
"http://meow.cats",
|
||||||
|
Some("ignore-builder-override-suggestion-threshold"),
|
||||||
|
Some("53.4"),
|
||||||
|
|config| {
|
||||||
|
assert_eq!(
|
||||||
|
config
|
||||||
|
.execution_layer
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.ignore_builder_override_suggestion_threshold,
|
||||||
|
53.4f32
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
run_payload_builder_flag_test_with_config(
|
||||||
|
"builder",
|
||||||
|
"http://meow.cats",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
|config| {
|
||||||
|
assert_eq!(
|
||||||
|
config
|
||||||
|
.execution_layer
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.ignore_builder_override_suggestion_threshold,
|
||||||
|
10.0f32
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
TESTS_TAG := v1.4.0-alpha.2
|
TESTS_TAG := v1.4.0-beta.1
|
||||||
TESTS = general minimal mainnet
|
TESTS = general minimal mainnet
|
||||||
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
TARBALLS = $(patsubst %,%-$(TESTS_TAG).tar.gz,$(TESTS))
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ use state_processing::{
|
|||||||
errors::BlockProcessingError,
|
errors::BlockProcessingError,
|
||||||
process_block_header, process_execution_payload,
|
process_block_header, process_execution_payload,
|
||||||
process_operations::{
|
process_operations::{
|
||||||
altair, base, process_attester_slashings, process_bls_to_execution_changes,
|
altair_deneb, base, process_attester_slashings, process_bls_to_execution_changes,
|
||||||
process_deposits, process_exits, process_proposer_slashings,
|
process_deposits, process_exits, process_proposer_slashings,
|
||||||
},
|
},
|
||||||
process_sync_aggregate, process_withdrawals, VerifyBlockRoot, VerifySignatures,
|
process_sync_aggregate, process_withdrawals, VerifyBlockRoot, VerifySignatures,
|
||||||
@ -103,7 +103,14 @@ impl<E: EthSpec> Operation<E> for Attestation<E> {
|
|||||||
| BeaconState::Capella(_)
|
| BeaconState::Capella(_)
|
||||||
| BeaconState::Deneb(_) => {
|
| BeaconState::Deneb(_) => {
|
||||||
initialize_progressive_balances_cache(state, None, spec)?;
|
initialize_progressive_balances_cache(state, None, spec)?;
|
||||||
altair::process_attestation(state, self, 0, &mut ctxt, VerifySignatures::True, spec)
|
altair_deneb::process_attestation(
|
||||||
|
state,
|
||||||
|
self,
|
||||||
|
0,
|
||||||
|
&mut ctxt,
|
||||||
|
VerifySignatures::True,
|
||||||
|
spec,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -270,7 +270,13 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
head_root,
|
head_root,
|
||||||
proposer_index,
|
proposer_index,
|
||||||
// TODO: think about how to test different forks
|
// TODO: think about how to test different forks
|
||||||
PayloadAttributes::new(timestamp, prev_randao, Address::repeat_byte(42), None),
|
PayloadAttributes::new(
|
||||||
|
timestamp,
|
||||||
|
prev_randao,
|
||||||
|
Address::repeat_byte(42),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -309,7 +315,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
.get_suggested_fee_recipient(proposer_index)
|
.get_suggested_fee_recipient(proposer_index)
|
||||||
.await;
|
.await;
|
||||||
let payload_attributes =
|
let payload_attributes =
|
||||||
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None);
|
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
|
||||||
let valid_payload = self
|
let valid_payload = self
|
||||||
.ee_a
|
.ee_a
|
||||||
.execution_layer
|
.execution_layer
|
||||||
@ -358,10 +364,11 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
* Provide the valid payload back to the EE again.
|
* Provide the valid payload back to the EE again.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: again consider forks here
|
||||||
let status = self
|
let status = self
|
||||||
.ee_a
|
.ee_a
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.notify_new_payload(&valid_payload, None)
|
.notify_new_payload(valid_payload.clone().try_into().unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(status, PayloadStatus::Valid);
|
assert_eq!(status, PayloadStatus::Valid);
|
||||||
@ -409,12 +416,13 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
* Provide an invalidated payload to the EE.
|
* Provide an invalidated payload to the EE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: again think about forks here
|
||||||
let mut invalid_payload = valid_payload.clone();
|
let mut invalid_payload = valid_payload.clone();
|
||||||
*invalid_payload.prev_randao_mut() = Hash256::from_low_u64_be(42);
|
*invalid_payload.prev_randao_mut() = Hash256::from_low_u64_be(42);
|
||||||
let status = self
|
let status = self
|
||||||
.ee_a
|
.ee_a
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.notify_new_payload(&invalid_payload, None)
|
.notify_new_payload(invalid_payload.try_into().unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
@ -449,7 +457,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
.get_suggested_fee_recipient(proposer_index)
|
.get_suggested_fee_recipient(proposer_index)
|
||||||
.await;
|
.await;
|
||||||
let payload_attributes =
|
let payload_attributes =
|
||||||
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None);
|
PayloadAttributes::new(timestamp, prev_randao, suggested_fee_recipient, None, None);
|
||||||
let second_payload = self
|
let second_payload = self
|
||||||
.ee_a
|
.ee_a
|
||||||
.execution_layer
|
.execution_layer
|
||||||
@ -473,10 +481,11 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
* Provide the second payload back to the EE again.
|
* Provide the second payload back to the EE again.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: again consider forks here
|
||||||
let status = self
|
let status = self
|
||||||
.ee_a
|
.ee_a
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.notify_new_payload(&second_payload, None)
|
.notify_new_payload(second_payload.clone().try_into().unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(status, PayloadStatus::Valid);
|
assert_eq!(status, PayloadStatus::Valid);
|
||||||
@ -493,7 +502,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
// To save sending proposer preparation data, just set the fee recipient
|
// To save sending proposer preparation data, just set the fee recipient
|
||||||
// to the fee recipient configured for EE A.
|
// to the fee recipient configured for EE A.
|
||||||
let payload_attributes =
|
let payload_attributes =
|
||||||
PayloadAttributes::new(timestamp, prev_randao, Address::repeat_byte(42), None);
|
PayloadAttributes::new(timestamp, prev_randao, Address::repeat_byte(42), None, None);
|
||||||
let slot = Slot::new(42);
|
let slot = Slot::new(42);
|
||||||
let head_block_root = Hash256::repeat_byte(100);
|
let head_block_root = Hash256::repeat_byte(100);
|
||||||
let validator_index = 0;
|
let validator_index = 0;
|
||||||
@ -520,10 +529,11 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
*
|
*
|
||||||
* Provide the second payload, without providing the first.
|
* Provide the second payload, without providing the first.
|
||||||
*/
|
*/
|
||||||
|
// TODO: again consider forks here
|
||||||
let status = self
|
let status = self
|
||||||
.ee_b
|
.ee_b
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.notify_new_payload(&second_payload, None)
|
.notify_new_payload(second_payload.clone().try_into().unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// TODO: we should remove the `Accepted` status here once Geth fixes it
|
// TODO: we should remove the `Accepted` status here once Geth fixes it
|
||||||
@ -561,10 +571,11 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
* Provide the first payload to the EE.
|
* Provide the first payload to the EE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// TODO: again consider forks here
|
||||||
let status = self
|
let status = self
|
||||||
.ee_b
|
.ee_b
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.notify_new_payload(&valid_payload, None)
|
.notify_new_payload(valid_payload.clone().try_into().unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(status, PayloadStatus::Valid);
|
assert_eq!(status, PayloadStatus::Valid);
|
||||||
@ -578,7 +589,7 @@ impl<E: GenericExecutionEngine> TestRig<E> {
|
|||||||
let status = self
|
let status = self
|
||||||
.ee_b
|
.ee_b
|
||||||
.execution_layer
|
.execution_layer
|
||||||
.notify_new_payload(&second_payload, None)
|
.notify_new_payload(second_payload.clone().try_into().unwrap())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(status, PayloadStatus::Valid);
|
assert_eq!(status, PayloadStatus::Valid);
|
||||||
|
@ -21,12 +21,12 @@ use task_executor::TaskExecutor;
|
|||||||
use types::{
|
use types::{
|
||||||
attestation::Error as AttestationError, graffiti::GraffitiString, AbstractExecPayload, Address,
|
attestation::Error as AttestationError, graffiti::GraffitiString, AbstractExecPayload, Address,
|
||||||
AggregateAndProof, Attestation, BeaconBlock, BlindedPayload, BlobSidecarList, ChainSpec,
|
AggregateAndProof, Attestation, BeaconBlock, BlindedPayload, BlobSidecarList, ChainSpec,
|
||||||
ContributionAndProof, Domain, Epoch, EthSpec, Fork, Graffiti, Hash256, Keypair, PublicKeyBytes,
|
ContributionAndProof, Domain, Epoch, EthSpec, Fork, ForkName, Graffiti, Hash256, Keypair,
|
||||||
SelectionProof, Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedBlobSidecar,
|
PublicKeyBytes, SelectionProof, Signature, SignedAggregateAndProof, SignedBeaconBlock,
|
||||||
SignedBlobSidecarList, SignedContributionAndProof, SignedRoot, SignedValidatorRegistrationData,
|
SignedBlobSidecar, SignedBlobSidecarList, SignedContributionAndProof, SignedRoot,
|
||||||
SignedVoluntaryExit, Slot, SyncAggregatorSelectionData, SyncCommitteeContribution,
|
SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncAggregatorSelectionData,
|
||||||
SyncCommitteeMessage, SyncSelectionProof, SyncSubnetId, ValidatorRegistrationData,
|
SyncCommitteeContribution, SyncCommitteeMessage, SyncSelectionProof, SyncSubnetId,
|
||||||
VoluntaryExit,
|
ValidatorRegistrationData, VoluntaryExit,
|
||||||
};
|
};
|
||||||
use validator_dir::ValidatorDir;
|
use validator_dir::ValidatorDir;
|
||||||
|
|
||||||
@ -371,6 +371,9 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn signing_context(&self, domain: Domain, signing_epoch: Epoch) -> SigningContext {
|
fn signing_context(&self, domain: Domain, signing_epoch: Epoch) -> SigningContext {
|
||||||
|
if domain == Domain::VoluntaryExit {
|
||||||
|
match self.spec.fork_name_at_epoch(signing_epoch) {
|
||||||
|
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
|
||||||
SigningContext {
|
SigningContext {
|
||||||
domain,
|
domain,
|
||||||
epoch: signing_epoch,
|
epoch: signing_epoch,
|
||||||
@ -378,6 +381,27 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
|||||||
genesis_validators_root: self.genesis_validators_root,
|
genesis_validators_root: self.genesis_validators_root,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// EIP-7044
|
||||||
|
ForkName::Deneb => SigningContext {
|
||||||
|
domain,
|
||||||
|
epoch: signing_epoch,
|
||||||
|
fork: Fork {
|
||||||
|
previous_version: self.spec.capella_fork_version,
|
||||||
|
current_version: self.spec.capella_fork_version,
|
||||||
|
epoch: signing_epoch,
|
||||||
|
},
|
||||||
|
genesis_validators_root: self.genesis_validators_root,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SigningContext {
|
||||||
|
domain,
|
||||||
|
epoch: signing_epoch,
|
||||||
|
fork: self.fork(signing_epoch),
|
||||||
|
genesis_validators_root: self.genesis_validators_root,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn randao_reveal(
|
pub async fn randao_reveal(
|
||||||
&self,
|
&self,
|
||||||
|
Loading…
Reference in New Issue
Block a user