Skip to content

Commit

Permalink
feat: implemented process_execution_payload, notify_new_payload, veri…
Browse files Browse the repository at this point in the history
…fy_and_notify_new_payload functions (ReamLabs#139)

* feat: implemented process_execution_payload, notify_new_payload, verify_and_notify_new_payload functions

* fix: fixed requested changes
  • Loading branch information
Kayden-ML authored Feb 25, 2025
1 parent e494815 commit b911dea
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 16 deletions.
67 changes: 62 additions & 5 deletions crates/common/consensus/src/deneb/beacon_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use alloy_primitives::{aliases::B32, Address, B256};
use anyhow::{anyhow, bail, ensure};
use ethereum_hashing::{hash, hash_fixed};
use itertools::Itertools;
use kzg::eth::c_bindings::KZGCommitment;
use ream_bls::{
traits::{Aggregatable, Verifiable},
AggregatePubKey, BLSSignature, PubKey,
Expand Down Expand Up @@ -39,6 +38,7 @@ use crate::{
deposit::Deposit,
deposit_message::DepositMessage,
eth_1_data::Eth1Data,
execution_engine::{new_payload_request::NewPayloadRequest, ExecutionEngine},
fork::Fork,
fork_choice::helpers::constants::{
BASE_REWARD_FACTOR, BLS_WITHDRAWAL_PREFIX, CAPELLA_FORK_VERSION, CHURN_LIMIT_QUOTIENT,
Expand All @@ -49,8 +49,9 @@ use crate::{
EPOCHS_PER_SYNC_COMMITTEE_PERIOD, ETH1_ADDRESS_WITHDRAWAL_PREFIX, FAR_FUTURE_EPOCH,
GENESIS_EPOCH, GENESIS_SLOT, HYSTERESIS_DOWNWARD_MULTIPLIER, HYSTERESIS_QUOTIENT,
HYSTERESIS_UPWARD_MULTIPLIER, INACTIVITY_PENALTY_QUOTIENT_ALTAIR, INACTIVITY_SCORE_BIAS,
INACTIVITY_SCORE_RECOVERY_RATE, JUSTIFICATION_BITS_LENGTH, MAX_COMMITTEES_PER_SLOT,
MAX_DEPOSITS, MAX_EFFECTIVE_BALANCE, MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, MAX_RANDOM_BYTE,
INACTIVITY_SCORE_RECOVERY_RATE, JUSTIFICATION_BITS_LENGTH, MAX_BLOBS_PER_BLOCK,
MAX_COMMITTEES_PER_SLOT, MAX_DEPOSITS, MAX_EFFECTIVE_BALANCE,
MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, MAX_RANDOM_BYTE,
MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP, MAX_WITHDRAWALS_PER_PAYLOAD,
MIN_ATTESTATION_INCLUSION_DELAY, MIN_EPOCHS_TO_INACTIVITY_PENALTY,
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, MIN_GENESIS_TIME, MIN_PER_EPOCH_CHURN_LIMIT,
Expand All @@ -64,7 +65,7 @@ use crate::{
helpers::xor,
historical_summary::HistoricalSummary,
indexed_attestation::IndexedAttestation,
kzg_commitment::VERSIONED_HASH_VERSION_KZG,
kzg_commitment::{KZGCommitment, VERSIONED_HASH_VERSION_KZG},
misc::{
compute_activation_exit_epoch, compute_committee, compute_domain, compute_epoch_at_slot,
compute_shuffled_index, compute_signing_root, compute_start_slot_at_epoch,
Expand Down Expand Up @@ -1689,6 +1690,62 @@ impl BeaconState {
}
Ok(())
}

pub async fn process_execution_payload(
&mut self,
body: BeaconBlockBody,
execution_engine: ExecutionEngine,
) -> anyhow::Result<()> {
let payload = body.execution_payload;

// Verify consistency of the parent hash with respect to the previous execution payload
// header
ensure!(payload.parent_hash == self.latest_execution_payload_header.block_hash);
// Verify prev_randao
ensure!(payload.prev_randao == self.get_randao_mix(self.get_current_epoch()));
// Verify timestamp
ensure!(payload.timestamp == self.compute_timestamp_at_slot(self.slot));
// Verify commitments are under limit
ensure!(body.blob_kzg_commitments.len() <= MAX_BLOBS_PER_BLOCK as usize);

// Verify the execution payload is valid
let mut versioned_hashes = vec![];
for commitment in body.blob_kzg_commitments {
versioned_hashes.push(kzg_commitment_to_versioned_hash(&commitment));
}
ensure!(
execution_engine
.verify_and_notify_new_payload(NewPayloadRequest {
execution_payload: payload.clone(),
versioned_hashes,
parent_beacon_block_root: self.latest_block_header.parent_root,
})
.await?
);

// Cache execution payload header
self.latest_execution_payload_header = ExecutionPayloadHeader {
parent_hash: payload.parent_hash,
fee_recipient: payload.fee_recipient,
state_root: payload.state_root,
receipts_root: payload.receipts_root,
logs_bloom: payload.logs_bloom,
prev_randao: payload.prev_randao,
block_number: payload.block_number,
gas_limit: payload.gas_limit,
gas_used: payload.gas_used,
timestamp: payload.timestamp,
extra_data: payload.extra_data,
base_fee_per_gas: payload.base_fee_per_gas,
block_hash: payload.block_hash,
transactions_root: payload.transactions.tree_hash_root(),
withdrawals_root: payload.withdrawals.tree_hash_root(),
blob_gas_used: payload.blob_gas_used,
excess_blob_gas: payload.excess_blob_gas,
};

Ok(())
}
}

/// Check if ``leaf`` at ``index`` verifies against the Merkle ``root`` and ``branch``.
Expand Down Expand Up @@ -1762,7 +1819,7 @@ pub fn eth_aggregate_pubkeys(pubkeys: &[&PubKey]) -> anyhow::Result<PubKey> {
}

pub fn kzg_commitment_to_versioned_hash(kzg_commitment: &KZGCommitment) -> B256 {
let mut versioned_hash = hash(&kzg_commitment.bytes);
let mut versioned_hash = hash(&kzg_commitment.0);
versioned_hash[0] = VERSIONED_HASH_VERSION_KZG;
B256::from_slice(&versioned_hash)
}
77 changes: 68 additions & 9 deletions crates/common/consensus/src/execution_engine/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ use rpc_types::{
forkchoice_update::{ForkchoiceStateV1, ForkchoiceUpdateResult, PayloadAttributesV3},
get_blobs::BlobsAndProofV1,
get_payload::PayloadV3,
payload_status::PayloadStatusV1,
payload_status::{PayloadStatus, PayloadStatusV1},
};
use serde_json::json;
use ssz_types::VariableList;
use transaction::{BlobTransaction, TransactionType};
use utils::{strip_prefix, Claims, JsonRpcRequest, JsonRpcResponse};

Expand Down Expand Up @@ -54,20 +55,18 @@ impl ExecutionEngine {
/// Return ``True`` if and only if ``execution_payload.block_hash`` is computed correctly.
pub fn is_valid_block_hash(
&self,
execution_payload: ExecutionPayload,
execution_payload: &ExecutionPayload,
parent_beacon_block_root: B256,
) -> bool {
execution_payload.block_hash == execution_payload.header_hash(parent_beacon_block_root)
}

/// Return ``True`` if and only if the version hashes computed by the blob transactions of
/// ``new_payload_request.execution_payload`` matches ``new_payload_request.versioned_hashes``.
pub fn is_valid_versioned_hashes(
pub fn blob_versioned_hashes(
&self,
new_payload_request: NewPayloadRequest,
) -> anyhow::Result<bool> {
execution_payload: &ExecutionPayload,
) -> anyhow::Result<Vec<B256>> {
let mut blob_versioned_hashes = vec![];
for transaction in new_payload_request.execution_payload.transactions {
for transaction in execution_payload.transactions.iter() {
if TransactionType::try_from(&transaction[..])
.map_err(|err| anyhow!("Failed to detect transaction type: {err:?}"))?
== TransactionType::BlobTransaction
Expand All @@ -77,7 +76,67 @@ impl ExecutionEngine {
}
}

Ok(blob_versioned_hashes == new_payload_request.versioned_hashes)
Ok(blob_versioned_hashes)
}

/// Return ``True`` if and only if the version hashes computed by the blob transactions of
/// ``new_payload_request.execution_payload`` matches ``new_payload_request.versioned_hashes``.
pub fn is_valid_versioned_hashes(
&self,
new_payload_request: &NewPayloadRequest,
) -> anyhow::Result<bool> {
Ok(
self.blob_versioned_hashes(&new_payload_request.execution_payload)?
== new_payload_request.versioned_hashes,
)
}

/// Return ``PayloadStatus`` of execution payload``.
pub async fn notify_new_payload(
&self,
new_payload_request: NewPayloadRequest,
) -> anyhow::Result<PayloadStatus> {
let NewPayloadRequest {
execution_payload,
versioned_hashes,
parent_beacon_block_root,
} = new_payload_request;
let payload_status = self
.engine_new_payload_v3(
execution_payload.into(),
versioned_hashes,
parent_beacon_block_root,
)
.await?;
Ok(payload_status.status)
}

/// Return ``True`` if and only if ``new_payload_request`` is valid with respect to
/// ``self.execution_state``.
pub async fn verify_and_notify_new_payload(
&self,
new_payload_request: NewPayloadRequest,
) -> anyhow::Result<bool> {
if new_payload_request
.execution_payload
.transactions
.contains(&VariableList::empty())
{
return Ok(false);
}

if !self.is_valid_block_hash(
&new_payload_request.execution_payload,
new_payload_request.parent_beacon_block_root,
) {
return Ok(false);
}

if !self.is_valid_versioned_hashes(&new_payload_request)? {
return Ok(false);
}

return Ok(self.notify_new_payload(new_payload_request).await? == PayloadStatus::Valid);
}

pub fn build_request(&self, rpc_request: JsonRpcRequest) -> anyhow::Result<Request> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ssz_types::{
};
use tree_hash_derive::TreeHash;

use crate::withdrawal::Withdrawal;
use crate::{deneb::execution_payload::ExecutionPayload, withdrawal::Withdrawal};

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Encode, Decode, TreeHash)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -41,3 +41,27 @@ pub struct ExecutionPayloadV3 {
#[serde(with = "serde_utils::u64_hex_be")]
pub excess_blob_gas: u64,
}

impl From<ExecutionPayload> for ExecutionPayloadV3 {
fn from(value: ExecutionPayload) -> Self {
ExecutionPayloadV3 {
parent_hash: value.parent_hash,
fee_recipient: value.fee_recipient,
state_root: value.state_root,
receipts_root: value.receipts_root,
logs_bloom: value.logs_bloom,
prev_randao: value.prev_randao,
block_number: value.block_number,
gas_limit: value.gas_limit,
gas_used: value.gas_used,
timestamp: value.timestamp,
extra_data: value.extra_data,
base_fee_per_gas: value.base_fee_per_gas,
block_hash: value.block_hash,
transactions: value.transactions,
withdrawals: value.withdrawals,
blob_gas_used: value.blob_gas_used,
excess_blob_gas: value.excess_blob_gas,
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use alloy_primitives::B256;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Debug, Serialize)]
#[derive(Deserialize, Debug, Serialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum PayloadStatus {
Valid,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub const INTERVALS_PER_SLOT: u64 = 3;
pub const INACTIVITY_SCORE_BIAS: u64 = 4;
pub const INACTIVITY_SCORE_RECOVERY_RATE: u64 = 16;
pub const JUSTIFICATION_BITS_LENGTH: u64 = 4;
pub const MAX_BLOBS_PER_BLOCK: u64 = 6;
pub const MAX_COMMITTEES_PER_SLOT: u64 = 64;
pub const MAX_DEPOSITS: u64 = 16;
pub const MAX_SEED_LOOKAHEAD: u64 = 4;
Expand Down

0 comments on commit b911dea

Please sign in to comment.