mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore: use native conversions for payload to block (#13608)
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -8702,7 +8702,6 @@ dependencies = [
|
|||||||
"reth-chainspec",
|
"reth-chainspec",
|
||||||
"reth-primitives",
|
"reth-primitives",
|
||||||
"reth-primitives-traits",
|
"reth-primitives-traits",
|
||||||
"reth-rpc-types-compat",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -9227,7 +9226,6 @@ dependencies = [
|
|||||||
"alloy-consensus",
|
"alloy-consensus",
|
||||||
"alloy-eips",
|
"alloy-eips",
|
||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"alloy-rlp",
|
|
||||||
"alloy-rpc-types-engine",
|
"alloy-rpc-types-engine",
|
||||||
"alloy-rpc-types-eth",
|
"alloy-rpc-types-eth",
|
||||||
"jsonrpsee-types",
|
"jsonrpsee-types",
|
||||||
|
|||||||
@ -16,7 +16,6 @@ workspace = true
|
|||||||
reth-chainspec.workspace = true
|
reth-chainspec.workspace = true
|
||||||
reth-primitives.workspace = true
|
reth-primitives.workspace = true
|
||||||
reth-primitives-traits.workspace = true
|
reth-primitives-traits.workspace = true
|
||||||
reth-rpc-types-compat.workspace = true
|
|
||||||
|
|
||||||
# alloy
|
# alloy
|
||||||
alloy-rpc-types = { workspace = true, features = ["engine"] }
|
alloy-rpc-types = { workspace = true, features = ["engine"] }
|
||||||
|
|||||||
@ -14,7 +14,6 @@ use alloy_rpc_types::engine::{
|
|||||||
use reth_chainspec::EthereumHardforks;
|
use reth_chainspec::EthereumHardforks;
|
||||||
use reth_primitives::{BlockBody, BlockExt, Header, SealedBlock};
|
use reth_primitives::{BlockBody, BlockExt, Header, SealedBlock};
|
||||||
use reth_primitives_traits::SignedTransaction;
|
use reth_primitives_traits::SignedTransaction;
|
||||||
use reth_rpc_types_compat::engine::payload::try_into_block;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
/// Execution payload validator.
|
/// Execution payload validator.
|
||||||
@ -121,7 +120,7 @@ impl<ChainSpec: EthereumHardforks> ExecutionPayloadValidator<ChainSpec> {
|
|||||||
let expected_hash = payload.block_hash();
|
let expected_hash = payload.block_hash();
|
||||||
|
|
||||||
// First parse the block
|
// First parse the block
|
||||||
let sealed_block = try_into_block(payload, &sidecar)?.seal_slow();
|
let sealed_block = payload.try_into_block_with_sidecar(&sidecar)?.seal_slow();
|
||||||
|
|
||||||
// Ensure the hash included in the payload matches the block hash
|
// Ensure the hash included in the payload matches the block hash
|
||||||
if expected_hash != sealed_block.hash() {
|
if expected_hash != sealed_block.hash() {
|
||||||
|
|||||||
@ -26,9 +26,7 @@ use reth_payload_primitives::{
|
|||||||
};
|
};
|
||||||
use reth_primitives::EthereumHardfork;
|
use reth_primitives::EthereumHardfork;
|
||||||
use reth_rpc_api::EngineApiServer;
|
use reth_rpc_api::EngineApiServer;
|
||||||
use reth_rpc_types_compat::engine::payload::{
|
use reth_rpc_types_compat::engine::payload::convert_to_payload_body_v1;
|
||||||
convert_payload_input_v2_to_payload, convert_to_payload_body_v1,
|
|
||||||
};
|
|
||||||
use reth_storage_api::{BlockReader, HeaderProvider, StateProviderFactory};
|
use reth_storage_api::{BlockReader, HeaderProvider, StateProviderFactory};
|
||||||
use reth_tasks::TaskSpawner;
|
use reth_tasks::TaskSpawner;
|
||||||
use reth_transaction_pool::TransactionPool;
|
use reth_transaction_pool::TransactionPool;
|
||||||
@ -176,7 +174,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
payload: ExecutionPayloadInputV2,
|
payload: ExecutionPayloadInputV2,
|
||||||
) -> EngineApiResult<PayloadStatus> {
|
) -> EngineApiResult<PayloadStatus> {
|
||||||
let payload = convert_payload_input_v2_to_payload(payload);
|
let payload = payload.into_payload();
|
||||||
let payload_or_attrs =
|
let payload_or_attrs =
|
||||||
PayloadOrAttributes::<'_, EngineT::PayloadAttributes>::from_execution_payload(
|
PayloadOrAttributes::<'_, EngineT::PayloadAttributes>::from_execution_payload(
|
||||||
&payload, None,
|
&payload, None,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
//! Some payload tests
|
//! Some payload tests
|
||||||
|
|
||||||
use alloy_eips::eip4895::Withdrawals;
|
use alloy_eips::eip4895::Withdrawals;
|
||||||
use alloy_primitives::{Bytes, U256};
|
use alloy_primitives::Bytes;
|
||||||
use alloy_rlp::{Decodable, Error as RlpError};
|
use alloy_rlp::{Decodable, Error as RlpError};
|
||||||
use alloy_rpc_types_engine::{
|
use alloy_rpc_types_engine::{
|
||||||
ExecutionPayload, ExecutionPayloadBodyV1, ExecutionPayloadSidecar, ExecutionPayloadV1,
|
ExecutionPayload, ExecutionPayloadBodyV1, ExecutionPayloadSidecar, ExecutionPayloadV1,
|
||||||
@ -10,11 +10,10 @@ use alloy_rpc_types_engine::{
|
|||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use reth_primitives::{proofs, Block, SealedBlock, SealedHeader, TransactionSigned};
|
use reth_primitives::{proofs, Block, SealedBlock, SealedHeader, TransactionSigned};
|
||||||
use reth_rpc_types_compat::engine::payload::{
|
use reth_rpc_types_compat::engine::payload::{
|
||||||
block_to_payload, block_to_payload_v1, convert_to_payload_body_v1, try_into_sealed_block,
|
block_to_payload, block_to_payload_v1, convert_to_payload_body_v1,
|
||||||
try_payload_v1_to_block,
|
|
||||||
};
|
};
|
||||||
use reth_testing_utils::generators::{
|
use reth_testing_utils::generators::{
|
||||||
self, random_block, random_block_range, random_header, BlockParams, BlockRangeParams, Rng,
|
self, random_block, random_block_range, BlockParams, BlockRangeParams, Rng,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn transform_block<F: FnOnce(Block) -> Block>(src: SealedBlock, f: F) -> ExecutionPayload {
|
fn transform_block<F: FnOnce(Block) -> Block>(src: SealedBlock, f: F) -> ExecutionPayload {
|
||||||
@ -56,7 +55,7 @@ fn payload_body_roundtrip() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn payload_validation() {
|
fn payload_validation_conversion() {
|
||||||
let mut rng = generators::rng();
|
let mut rng = generators::rng();
|
||||||
let parent = rng.gen();
|
let parent = rng.gen();
|
||||||
let block = random_block(
|
let block = random_block(
|
||||||
@ -77,7 +76,8 @@ fn payload_validation() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
try_into_sealed_block(block_with_valid_extra_data, &ExecutionPayloadSidecar::none()),
|
block_with_valid_extra_data
|
||||||
|
.try_into_block_with_sidecar::<TransactionSigned>(&ExecutionPayloadSidecar::none()),
|
||||||
Ok(_)
|
Ok(_)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -88,7 +88,7 @@ fn payload_validation() {
|
|||||||
b
|
b
|
||||||
});
|
});
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
try_into_sealed_block(invalid_extra_data_block, &ExecutionPayloadSidecar::none()),
|
invalid_extra_data_block.try_into_block_with_sidecar::<TransactionSigned>(&ExecutionPayloadSidecar::none()),
|
||||||
Err(PayloadError::ExtraData(data)) if data == block_with_invalid_extra_data
|
Err(PayloadError::ExtraData(data)) if data == block_with_invalid_extra_data
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -98,52 +98,16 @@ fn payload_validation() {
|
|||||||
b
|
b
|
||||||
});
|
});
|
||||||
assert_matches!(
|
assert_matches!(
|
||||||
try_into_sealed_block(block_with_zero_base_fee, &ExecutionPayloadSidecar::none()),
|
block_with_zero_base_fee.try_into_block_with_sidecar::<TransactionSigned>(&ExecutionPayloadSidecar::none()),
|
||||||
Err(PayloadError::BaseFee(val)) if val.is_zero()
|
Err(PayloadError::BaseFee(val)) if val.is_zero()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Invalid encoded transactions
|
// Invalid encoded transactions
|
||||||
let mut payload_with_invalid_txs: ExecutionPayloadV1 = block_to_payload_v1(block.clone());
|
let mut payload_with_invalid_txs: ExecutionPayloadV1 = block_to_payload_v1(block);
|
||||||
|
|
||||||
payload_with_invalid_txs.transactions.iter_mut().for_each(|tx| {
|
payload_with_invalid_txs.transactions.iter_mut().for_each(|tx| {
|
||||||
*tx = Bytes::new();
|
*tx = Bytes::new();
|
||||||
});
|
});
|
||||||
let payload_with_invalid_txs =
|
let payload_with_invalid_txs = payload_with_invalid_txs.try_into_block::<TransactionSigned>();
|
||||||
try_payload_v1_to_block::<TransactionSigned>(payload_with_invalid_txs);
|
|
||||||
assert_matches!(payload_with_invalid_txs, Err(PayloadError::Decode(RlpError::InputTooShort)));
|
assert_matches!(payload_with_invalid_txs, Err(PayloadError::Decode(RlpError::InputTooShort)));
|
||||||
|
|
||||||
// Non empty ommers
|
|
||||||
let block_with_ommers = transform_block(block.clone(), |mut b| {
|
|
||||||
b.body.ommers.push(random_header(&mut rng, 100, None).unseal());
|
|
||||||
b
|
|
||||||
});
|
|
||||||
assert_matches!(
|
|
||||||
try_into_sealed_block(block_with_ommers.clone(), &ExecutionPayloadSidecar::none()),
|
|
||||||
Err(PayloadError::BlockHash { consensus, .. })
|
|
||||||
if consensus == block_with_ommers.block_hash()
|
|
||||||
);
|
|
||||||
|
|
||||||
// None zero difficulty
|
|
||||||
let block_with_difficulty = transform_block(block.clone(), |mut b| {
|
|
||||||
b.header.difficulty = U256::from(1);
|
|
||||||
b
|
|
||||||
});
|
|
||||||
assert_matches!(
|
|
||||||
try_into_sealed_block(block_with_difficulty.clone(), &ExecutionPayloadSidecar::none()),
|
|
||||||
Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_difficulty.block_hash()
|
|
||||||
);
|
|
||||||
|
|
||||||
// None zero nonce
|
|
||||||
let block_with_nonce = transform_block(block.clone(), |mut b| {
|
|
||||||
b.header.nonce = 1u64.into();
|
|
||||||
b
|
|
||||||
});
|
|
||||||
assert_matches!(
|
|
||||||
try_into_sealed_block(block_with_nonce.clone(), &ExecutionPayloadSidecar::none()),
|
|
||||||
Err(PayloadError::BlockHash { consensus, .. }) if consensus == block_with_nonce.block_hash()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Valid block
|
|
||||||
let valid_block = block;
|
|
||||||
assert_matches!(TryInto::<SealedBlock>::try_into(valid_block), Ok(_));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,6 @@ reth-primitives-traits.workspace = true
|
|||||||
# ethereum
|
# ethereum
|
||||||
alloy-eips.workspace = true
|
alloy-eips.workspace = true
|
||||||
alloy-primitives.workspace = true
|
alloy-primitives.workspace = true
|
||||||
alloy-rlp.workspace = true
|
|
||||||
alloy-rpc-types-eth = { workspace = true, default-features = false, features = ["serde"] }
|
alloy-rpc-types-eth = { workspace = true, default-features = false, features = ["serde"] }
|
||||||
alloy-rpc-types-engine.workspace = true
|
alloy-rpc-types-engine.workspace = true
|
||||||
alloy-consensus.workspace = true
|
alloy-consensus.workspace = true
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
//! Standalone functions for engine specific rpc type conversions
|
//! Standalone functions for engine specific rpc type conversions
|
||||||
pub mod payload;
|
pub mod payload;
|
||||||
pub use payload::{block_to_payload_v1, try_into_sealed_block, try_payload_v1_to_block};
|
pub use payload::block_to_payload_v1;
|
||||||
|
|||||||
@ -1,123 +1,17 @@
|
|||||||
//! Standalone Conversion Functions for Handling Different Versions of Execution Payloads in
|
//! Standalone Conversion Functions for Handling Different Versions of Execution Payloads in
|
||||||
//! Ethereum's Engine
|
//! Ethereum's Engine
|
||||||
|
|
||||||
use alloy_consensus::{constants::MAXIMUM_EXTRA_DATA_SIZE, Header, EMPTY_OMMER_ROOT_HASH};
|
use alloy_consensus::Header;
|
||||||
use alloy_eips::{
|
use alloy_eips::{eip2718::Encodable2718, eip4895::Withdrawals, eip7685::RequestsOrHash};
|
||||||
eip2718::{Decodable2718, Encodable2718},
|
use alloy_primitives::U256;
|
||||||
eip4895::Withdrawals,
|
|
||||||
eip7685::RequestsOrHash,
|
|
||||||
};
|
|
||||||
use alloy_primitives::{B256, U256};
|
|
||||||
use alloy_rlp::BufMut;
|
|
||||||
use alloy_rpc_types_engine::{
|
use alloy_rpc_types_engine::{
|
||||||
payload::{ExecutionPayloadBodyV1, ExecutionPayloadFieldV2, ExecutionPayloadInputV2},
|
payload::{ExecutionPayloadBodyV1, ExecutionPayloadFieldV2, ExecutionPayloadInputV2},
|
||||||
CancunPayloadFields, ExecutionPayload, ExecutionPayloadSidecar, ExecutionPayloadV1,
|
CancunPayloadFields, ExecutionPayload, ExecutionPayloadSidecar, ExecutionPayloadV1,
|
||||||
ExecutionPayloadV2, ExecutionPayloadV3, PayloadError, PraguePayloadFields,
|
ExecutionPayloadV2, ExecutionPayloadV3, PraguePayloadFields,
|
||||||
};
|
|
||||||
use reth_primitives::{
|
|
||||||
proofs::{self},
|
|
||||||
Block, BlockBody, BlockExt, SealedBlock,
|
|
||||||
};
|
};
|
||||||
|
use reth_primitives::{BlockBody, SealedBlock};
|
||||||
use reth_primitives_traits::{BlockBody as _, SignedTransaction};
|
use reth_primitives_traits::{BlockBody as _, SignedTransaction};
|
||||||
|
|
||||||
/// Converts [`ExecutionPayloadV1`] to [`Block`]
|
|
||||||
pub fn try_payload_v1_to_block<T: Decodable2718>(
|
|
||||||
payload: ExecutionPayloadV1,
|
|
||||||
) -> Result<Block<T>, PayloadError> {
|
|
||||||
if payload.extra_data.len() > MAXIMUM_EXTRA_DATA_SIZE {
|
|
||||||
return Err(PayloadError::ExtraData(payload.extra_data))
|
|
||||||
}
|
|
||||||
|
|
||||||
if payload.base_fee_per_gas.is_zero() {
|
|
||||||
return Err(PayloadError::BaseFee(payload.base_fee_per_gas))
|
|
||||||
}
|
|
||||||
|
|
||||||
let transactions = payload
|
|
||||||
.transactions
|
|
||||||
.iter()
|
|
||||||
.map(|tx| {
|
|
||||||
let mut buf = tx.as_ref();
|
|
||||||
|
|
||||||
let tx = T::decode_2718(&mut buf).map_err(alloy_rlp::Error::from)?;
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
return Err(alloy_rlp::Error::UnexpectedLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(tx)
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
|
||||||
|
|
||||||
// Reuse the encoded bytes for root calculation
|
|
||||||
let transactions_root =
|
|
||||||
proofs::ordered_trie_root_with_encoder(&payload.transactions, |item, buf| {
|
|
||||||
buf.put_slice(item)
|
|
||||||
});
|
|
||||||
|
|
||||||
let header = Header {
|
|
||||||
parent_hash: payload.parent_hash,
|
|
||||||
beneficiary: payload.fee_recipient,
|
|
||||||
state_root: payload.state_root,
|
|
||||||
transactions_root,
|
|
||||||
receipts_root: payload.receipts_root,
|
|
||||||
withdrawals_root: None,
|
|
||||||
logs_bloom: payload.logs_bloom,
|
|
||||||
number: payload.block_number,
|
|
||||||
gas_limit: payload.gas_limit,
|
|
||||||
gas_used: payload.gas_used,
|
|
||||||
timestamp: payload.timestamp,
|
|
||||||
mix_hash: payload.prev_randao,
|
|
||||||
// WARNING: It’s allowed for a base fee in EIP1559 to increase unbounded. We assume that
|
|
||||||
// it will fit in an u64. This is not always necessarily true, although it is extremely
|
|
||||||
// unlikely not to be the case, a u64 maximum would have 2^64 which equates to 18 ETH per
|
|
||||||
// gas.
|
|
||||||
base_fee_per_gas: Some(
|
|
||||||
payload
|
|
||||||
.base_fee_per_gas
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| PayloadError::BaseFee(payload.base_fee_per_gas))?,
|
|
||||||
),
|
|
||||||
blob_gas_used: None,
|
|
||||||
excess_blob_gas: None,
|
|
||||||
parent_beacon_block_root: None,
|
|
||||||
requests_hash: None,
|
|
||||||
extra_data: payload.extra_data,
|
|
||||||
// Defaults
|
|
||||||
ommers_hash: EMPTY_OMMER_ROOT_HASH,
|
|
||||||
difficulty: Default::default(),
|
|
||||||
nonce: Default::default(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Block { header, body: BlockBody { transactions, ..Default::default() } })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts [`ExecutionPayloadV2`] to [`Block`]
|
|
||||||
pub fn try_payload_v2_to_block<T: Decodable2718>(
|
|
||||||
payload: ExecutionPayloadV2,
|
|
||||||
) -> Result<Block<T>, PayloadError> {
|
|
||||||
// this performs the same conversion as the underlying V1 payload, but calculates the
|
|
||||||
// withdrawals root and adds withdrawals
|
|
||||||
let mut base_sealed_block = try_payload_v1_to_block(payload.payload_inner)?;
|
|
||||||
let withdrawals_root = proofs::calculate_withdrawals_root(&payload.withdrawals);
|
|
||||||
base_sealed_block.body.withdrawals = Some(payload.withdrawals.into());
|
|
||||||
base_sealed_block.header.withdrawals_root = Some(withdrawals_root);
|
|
||||||
Ok(base_sealed_block)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts [`ExecutionPayloadV3`] to [`Block`]
|
|
||||||
pub fn try_payload_v3_to_block<T: Decodable2718>(
|
|
||||||
payload: ExecutionPayloadV3,
|
|
||||||
) -> Result<Block<T>, PayloadError> {
|
|
||||||
// this performs the same conversion as the underlying V2 payload, but inserts the blob gas
|
|
||||||
// used and excess blob gas
|
|
||||||
let mut base_block = try_payload_v2_to_block(payload.payload_inner)?;
|
|
||||||
|
|
||||||
base_block.header.blob_gas_used = Some(payload.blob_gas_used);
|
|
||||||
base_block.header.excess_blob_gas = Some(payload.excess_blob_gas);
|
|
||||||
|
|
||||||
Ok(base_block)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts [`SealedBlock`] to [`ExecutionPayload`]
|
/// Converts [`SealedBlock`] to [`ExecutionPayload`]
|
||||||
pub fn block_to_payload<T: SignedTransaction>(
|
pub fn block_to_payload<T: SignedTransaction>(
|
||||||
value: SealedBlock<Header, BlockBody<T>>,
|
value: SealedBlock<Header, BlockBody<T>>,
|
||||||
@ -212,42 +106,6 @@ pub fn convert_block_to_payload_field_v2<T: Encodable2718>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts [`ExecutionPayloadFieldV2`] to [`ExecutionPayload`]
|
|
||||||
pub fn convert_payload_field_v2_to_payload(value: ExecutionPayloadFieldV2) -> ExecutionPayload {
|
|
||||||
match value {
|
|
||||||
ExecutionPayloadFieldV2::V1(payload) => ExecutionPayload::V1(payload),
|
|
||||||
ExecutionPayloadFieldV2::V2(payload) => ExecutionPayload::V2(payload),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts [`ExecutionPayloadV2`] to [`ExecutionPayloadInputV2`].
|
|
||||||
///
|
|
||||||
/// An [`ExecutionPayloadInputV2`] should have a [`Some`] withdrawals field if shanghai is active,
|
|
||||||
/// otherwise the withdrawals field should be [`None`], so the `is_shanghai_active` argument is
|
|
||||||
/// provided which will either:
|
|
||||||
/// - include the withdrawals field as [`Some`] if true
|
|
||||||
/// - set the withdrawals field to [`None`] if false
|
|
||||||
pub fn convert_payload_v2_to_payload_input_v2(
|
|
||||||
value: ExecutionPayloadV2,
|
|
||||||
is_shanghai_active: bool,
|
|
||||||
) -> ExecutionPayloadInputV2 {
|
|
||||||
ExecutionPayloadInputV2 {
|
|
||||||
execution_payload: value.payload_inner,
|
|
||||||
withdrawals: is_shanghai_active.then_some(value.withdrawals),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts [`ExecutionPayloadInputV2`] to [`ExecutionPayload`]
|
|
||||||
pub fn convert_payload_input_v2_to_payload(value: ExecutionPayloadInputV2) -> ExecutionPayload {
|
|
||||||
match value.withdrawals {
|
|
||||||
Some(withdrawals) => ExecutionPayload::V2(ExecutionPayloadV2 {
|
|
||||||
payload_inner: value.execution_payload,
|
|
||||||
withdrawals,
|
|
||||||
}),
|
|
||||||
None => ExecutionPayload::V1(value.execution_payload),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts [`SealedBlock`] to [`ExecutionPayloadInputV2`]
|
/// Converts [`SealedBlock`] to [`ExecutionPayloadInputV2`]
|
||||||
pub fn convert_block_to_payload_input_v2(value: SealedBlock) -> ExecutionPayloadInputV2 {
|
pub fn convert_block_to_payload_input_v2(value: SealedBlock) -> ExecutionPayloadInputV2 {
|
||||||
ExecutionPayloadInputV2 {
|
ExecutionPayloadInputV2 {
|
||||||
@ -256,76 +114,7 @@ pub fn convert_block_to_payload_input_v2(value: SealedBlock) -> ExecutionPayload
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to create a new unsealed block from the given payload and payload sidecar.
|
/// Converts a [`reth_primitives_traits::Block`] to [`ExecutionPayloadBodyV1`]
|
||||||
///
|
|
||||||
/// Performs additional validation of `extra_data` and `base_fee_per_gas` fields.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// The log bloom is assumed to be validated during serialization.
|
|
||||||
///
|
|
||||||
/// See <https://github.com/ethereum/go-ethereum/blob/79a478bb6176425c2400e949890e668a3d9a3d05/core/beacon/types.go#L145>
|
|
||||||
pub fn try_into_block<T: Decodable2718>(
|
|
||||||
value: ExecutionPayload,
|
|
||||||
sidecar: &ExecutionPayloadSidecar,
|
|
||||||
) -> Result<Block<T>, PayloadError> {
|
|
||||||
let mut base_payload = match value {
|
|
||||||
ExecutionPayload::V1(payload) => try_payload_v1_to_block(payload)?,
|
|
||||||
ExecutionPayload::V2(payload) => try_payload_v2_to_block(payload)?,
|
|
||||||
ExecutionPayload::V3(payload) => try_payload_v3_to_block(payload)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
base_payload.header.parent_beacon_block_root = sidecar.parent_beacon_block_root();
|
|
||||||
base_payload.header.requests_hash = sidecar.requests_hash();
|
|
||||||
|
|
||||||
Ok(base_payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tries to create a sealed new block from the given payload and payload sidecar.
|
|
||||||
///
|
|
||||||
/// Uses [`try_into_block`] to convert from the [`ExecutionPayload`] to [`Block`] and seals the
|
|
||||||
/// block with its hash.
|
|
||||||
///
|
|
||||||
/// Uses [`validate_block_hash`] to validate the payload block hash and ultimately return the
|
|
||||||
/// [`SealedBlock`].
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
///
|
|
||||||
/// Empty ommers, nonce, difficulty, and execution request values are validated upon computing block
|
|
||||||
/// hash and comparing the value with `payload.block_hash`.
|
|
||||||
pub fn try_into_sealed_block(
|
|
||||||
payload: ExecutionPayload,
|
|
||||||
sidecar: &ExecutionPayloadSidecar,
|
|
||||||
) -> Result<SealedBlock, PayloadError> {
|
|
||||||
let block_hash = payload.block_hash();
|
|
||||||
let base_payload = try_into_block(payload, sidecar)?;
|
|
||||||
|
|
||||||
// validate block hash and return
|
|
||||||
validate_block_hash(block_hash, base_payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes the expected block hash and [`Block`], validating the block and converting it into a
|
|
||||||
/// [`SealedBlock`].
|
|
||||||
///
|
|
||||||
/// If the provided block hash does not match the block hash computed from the provided block, this
|
|
||||||
/// returns [`PayloadError::BlockHash`].
|
|
||||||
#[inline]
|
|
||||||
pub fn validate_block_hash(
|
|
||||||
expected_block_hash: B256,
|
|
||||||
block: Block,
|
|
||||||
) -> Result<SealedBlock, PayloadError> {
|
|
||||||
let sealed_block = block.seal_slow();
|
|
||||||
if expected_block_hash != sealed_block.hash() {
|
|
||||||
return Err(PayloadError::BlockHash {
|
|
||||||
execution: sealed_block.hash(),
|
|
||||||
consensus: expected_block_hash,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(sealed_block)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Converts [`Block`] to [`ExecutionPayloadBodyV1`]
|
|
||||||
pub fn convert_to_payload_body_v1(
|
pub fn convert_to_payload_body_v1(
|
||||||
value: impl reth_primitives_traits::Block,
|
value: impl reth_primitives_traits::Block,
|
||||||
) -> ExecutionPayloadBodyV1 {
|
) -> ExecutionPayloadBodyV1 {
|
||||||
@ -359,9 +148,7 @@ pub fn execution_payload_from_sealed_block(value: SealedBlock) -> ExecutionPaylo
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::block_to_payload_v3;
|
||||||
block_to_payload_v3, try_into_block, try_payload_v3_to_block, validate_block_hash,
|
|
||||||
};
|
|
||||||
use alloy_primitives::{b256, hex, Bytes, U256};
|
use alloy_primitives::{b256, hex, Bytes, U256};
|
||||||
use alloy_rpc_types_engine::{
|
use alloy_rpc_types_engine::{
|
||||||
CancunPayloadFields, ExecutionPayload, ExecutionPayloadSidecar, ExecutionPayloadV1,
|
CancunPayloadFields, ExecutionPayload, ExecutionPayloadSidecar, ExecutionPayloadV1,
|
||||||
@ -398,7 +185,7 @@ mod tests {
|
|||||||
excess_blob_gas: 0x580000,
|
excess_blob_gas: 0x580000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut block: Block = try_payload_v3_to_block(new_payload.clone()).unwrap();
|
let mut block: Block = new_payload.clone().try_into_block().unwrap();
|
||||||
|
|
||||||
// this newPayload came with a parent beacon block root, we need to manually insert it
|
// this newPayload came with a parent beacon block root, we need to manually insert it
|
||||||
// before hashing
|
// before hashing
|
||||||
@ -441,7 +228,8 @@ mod tests {
|
|||||||
excess_blob_gas: 0x580000,
|
excess_blob_gas: 0x580000,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _block = try_payload_v3_to_block::<TransactionSigned>(new_payload)
|
let _block = new_payload
|
||||||
|
.try_into_block::<TransactionSigned>()
|
||||||
.expect_err("execution payload conversion requires typed txs without a rlp header");
|
.expect_err("execution payload conversion requires typed txs without a rlp header");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,9 +372,13 @@ mod tests {
|
|||||||
let cancun_fields = CancunPayloadFields { parent_beacon_block_root, versioned_hashes };
|
let cancun_fields = CancunPayloadFields { parent_beacon_block_root, versioned_hashes };
|
||||||
|
|
||||||
// convert into block
|
// convert into block
|
||||||
let block = try_into_block(payload, &ExecutionPayloadSidecar::v3(cancun_fields)).unwrap();
|
let block = payload
|
||||||
|
.try_into_block_with_sidecar::<TransactionSigned>(&ExecutionPayloadSidecar::v3(
|
||||||
|
cancun_fields,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Ensure the actual hash is calculated if we set the fields to what they should be
|
// Ensure the actual hash is calculated if we set the fields to what they should be
|
||||||
validate_block_hash(block_hash_with_blob_fee_fields, block).unwrap();
|
assert_eq!(block_hash_with_blob_fee_fields, block.header.hash_slow());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user