mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
400 lines
16 KiB
Rust
400 lines
16 KiB
Rust
//! Contains types required for building a payload.
|
|
|
|
use alloy_rlp::Encodable;
|
|
use reth_engine_primitives::{BuiltPayload, PayloadBuilderAttributes};
|
|
use reth_primitives::{
|
|
constants::EIP1559_INITIAL_BASE_FEE, revm::config::revm_spec_by_timestamp_after_merge, Address,
|
|
BlobTransactionSidecar, ChainSpec, Hardfork, Header, SealedBlock, Withdrawals, B256, U256,
|
|
};
|
|
use reth_rpc_types::engine::{
|
|
ExecutionPayloadEnvelopeV2, ExecutionPayloadEnvelopeV3, ExecutionPayloadV1, PayloadAttributes,
|
|
PayloadId,
|
|
};
|
|
use reth_rpc_types_compat::engine::payload::{
|
|
block_to_payload_v3, convert_block_to_payload_field_v2,
|
|
convert_standalone_withdraw_to_withdrawal, try_block_to_payload_v1,
|
|
};
|
|
use revm_primitives::{BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, SpecId};
|
|
use std::convert::Infallible;
|
|
|
|
/// Contains the built payload.
|
|
///
|
|
/// According to the [engine API specification](https://github.com/ethereum/execution-apis/blob/main/src/engine/README.md) the execution layer should build the initial version of the payload with an empty transaction set and then keep update it in order to maximize the revenue.
|
|
/// Therefore, the empty-block here is always available and full-block will be set/updated
|
|
/// afterward.
|
|
#[derive(Debug, Clone)]
|
|
pub struct EthBuiltPayload {
|
|
/// Identifier of the payload
|
|
pub(crate) id: PayloadId,
|
|
/// The built block
|
|
pub(crate) block: SealedBlock,
|
|
/// The fees of the block
|
|
pub(crate) fees: U256,
|
|
/// The blobs, proofs, and commitments in the block. If the block is pre-cancun, this will be
|
|
/// empty.
|
|
pub(crate) sidecars: Vec<BlobTransactionSidecar>,
|
|
}
|
|
|
|
// === impl BuiltPayload ===
|
|
|
|
impl EthBuiltPayload {
|
|
/// Initializes the payload with the given initial block.
|
|
pub fn new(id: PayloadId, block: SealedBlock, fees: U256) -> Self {
|
|
Self { id, block, fees, sidecars: Vec::new() }
|
|
}
|
|
|
|
/// Returns the identifier of the payload.
|
|
pub fn id(&self) -> PayloadId {
|
|
self.id
|
|
}
|
|
|
|
/// Returns the built block(sealed)
|
|
pub fn block(&self) -> &SealedBlock {
|
|
&self.block
|
|
}
|
|
|
|
/// Fees of the block
|
|
pub fn fees(&self) -> U256 {
|
|
self.fees
|
|
}
|
|
|
|
/// Adds sidecars to the payload.
|
|
pub fn extend_sidecars(&mut self, sidecars: Vec<BlobTransactionSidecar>) {
|
|
self.sidecars.extend(sidecars)
|
|
}
|
|
}
|
|
|
|
impl BuiltPayload for EthBuiltPayload {
|
|
fn block(&self) -> &SealedBlock {
|
|
&self.block
|
|
}
|
|
|
|
fn fees(&self) -> U256 {
|
|
self.fees
|
|
}
|
|
}
|
|
|
|
impl<'a> BuiltPayload for &'a EthBuiltPayload {
|
|
fn block(&self) -> &SealedBlock {
|
|
(**self).block()
|
|
}
|
|
|
|
fn fees(&self) -> U256 {
|
|
(**self).fees()
|
|
}
|
|
}
|
|
|
|
// V1 engine_getPayloadV1 response
|
|
impl From<EthBuiltPayload> for ExecutionPayloadV1 {
|
|
fn from(value: EthBuiltPayload) -> Self {
|
|
try_block_to_payload_v1(value.block)
|
|
}
|
|
}
|
|
|
|
// V2 engine_getPayloadV2 response
|
|
impl From<EthBuiltPayload> for ExecutionPayloadEnvelopeV2 {
|
|
fn from(value: EthBuiltPayload) -> Self {
|
|
let EthBuiltPayload { block, fees, .. } = value;
|
|
|
|
ExecutionPayloadEnvelopeV2 {
|
|
block_value: fees,
|
|
execution_payload: convert_block_to_payload_field_v2(block),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<EthBuiltPayload> for ExecutionPayloadEnvelopeV3 {
|
|
fn from(value: EthBuiltPayload) -> Self {
|
|
let EthBuiltPayload { block, fees, sidecars, .. } = value;
|
|
|
|
ExecutionPayloadEnvelopeV3 {
|
|
execution_payload: block_to_payload_v3(block),
|
|
block_value: fees,
|
|
// From the engine API spec:
|
|
//
|
|
// > Client software **MAY** use any heuristics to decide whether to set
|
|
// `shouldOverrideBuilder` flag or not. If client software does not implement any
|
|
// heuristic this flag **SHOULD** be set to `false`.
|
|
//
|
|
// Spec:
|
|
// <https://github.com/ethereum/execution-apis/blob/fe8e13c288c592ec154ce25c534e26cb7ce0530d/src/engine/cancun.md#specification-2>
|
|
should_override_builder: false,
|
|
blobs_bundle: sidecars.into_iter().map(Into::into).collect::<Vec<_>>().into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Container type for all components required to build a payload.
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub struct EthPayloadBuilderAttributes {
|
|
/// Id of the payload
|
|
pub id: PayloadId,
|
|
/// Parent block to build the payload on top
|
|
pub parent: B256,
|
|
/// Unix timestamp for the generated payload
|
|
///
|
|
/// Number of seconds since the Unix epoch.
|
|
pub timestamp: u64,
|
|
/// Address of the recipient for collecting transaction fee
|
|
pub suggested_fee_recipient: Address,
|
|
/// Randomness value for the generated payload
|
|
pub prev_randao: B256,
|
|
/// Withdrawals for the generated payload
|
|
pub withdrawals: Withdrawals,
|
|
/// Root of the parent beacon block
|
|
pub parent_beacon_block_root: Option<B256>,
|
|
}
|
|
|
|
// === impl EthPayloadBuilderAttributes ===
|
|
|
|
impl EthPayloadBuilderAttributes {
|
|
/// Returns the identifier of the payload.
|
|
pub fn payload_id(&self) -> PayloadId {
|
|
self.id
|
|
}
|
|
|
|
/// Creates a new payload builder for the given parent block and the attributes.
|
|
///
|
|
/// Derives the unique [PayloadId] for the given parent and attributes
|
|
pub fn new(parent: B256, attributes: PayloadAttributes) -> Self {
|
|
let id = payload_id(&parent, &attributes);
|
|
|
|
let withdraw = attributes.withdrawals.map(|withdrawals| {
|
|
Withdrawals::new(
|
|
withdrawals
|
|
.into_iter()
|
|
.map(convert_standalone_withdraw_to_withdrawal) // Removed the parentheses here
|
|
.collect(),
|
|
)
|
|
});
|
|
|
|
Self {
|
|
id,
|
|
parent,
|
|
timestamp: attributes.timestamp,
|
|
suggested_fee_recipient: attributes.suggested_fee_recipient,
|
|
prev_randao: attributes.prev_randao,
|
|
withdrawals: withdraw.unwrap_or_default(),
|
|
parent_beacon_block_root: attributes.parent_beacon_block_root,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PayloadBuilderAttributes for EthPayloadBuilderAttributes {
|
|
type RpcPayloadAttributes = PayloadAttributes;
|
|
type Error = Infallible;
|
|
|
|
/// Creates a new payload builder for the given parent block and the attributes.
|
|
///
|
|
/// Derives the unique [PayloadId] for the given parent and attributes
|
|
fn try_new(parent: B256, attributes: PayloadAttributes) -> Result<Self, Infallible> {
|
|
Ok(Self::new(parent, attributes))
|
|
}
|
|
|
|
fn payload_id(&self) -> PayloadId {
|
|
self.id
|
|
}
|
|
|
|
fn parent(&self) -> B256 {
|
|
self.parent
|
|
}
|
|
|
|
fn timestamp(&self) -> u64 {
|
|
self.timestamp
|
|
}
|
|
|
|
fn parent_beacon_block_root(&self) -> Option<B256> {
|
|
self.parent_beacon_block_root
|
|
}
|
|
|
|
fn suggested_fee_recipient(&self) -> Address {
|
|
self.suggested_fee_recipient
|
|
}
|
|
|
|
fn prev_randao(&self) -> B256 {
|
|
self.prev_randao
|
|
}
|
|
|
|
fn withdrawals(&self) -> &Withdrawals {
|
|
&self.withdrawals
|
|
}
|
|
|
|
fn cfg_and_block_env(
|
|
&self,
|
|
chain_spec: &ChainSpec,
|
|
parent: &Header,
|
|
) -> (CfgEnvWithHandlerCfg, BlockEnv) {
|
|
// configure evm env based on parent block
|
|
let mut cfg = CfgEnv::default();
|
|
cfg.chain_id = chain_spec.chain().id();
|
|
|
|
// ensure we're not missing any timestamp based hardforks
|
|
let spec_id = revm_spec_by_timestamp_after_merge(chain_spec, self.timestamp());
|
|
|
|
// if the parent block did not have excess blob gas (i.e. it was pre-cancun), but it is
|
|
// cancun now, we need to set the excess blob gas to the default value
|
|
let blob_excess_gas_and_price = parent
|
|
.next_block_excess_blob_gas()
|
|
.or_else(|| {
|
|
if spec_id == SpecId::CANCUN {
|
|
// default excess blob gas is zero
|
|
Some(0)
|
|
} else {
|
|
None
|
|
}
|
|
})
|
|
.map(BlobExcessGasAndPrice::new);
|
|
|
|
let mut basefee = parent.next_block_base_fee(chain_spec.base_fee_params(self.timestamp()));
|
|
|
|
let mut gas_limit = U256::from(parent.gas_limit);
|
|
|
|
// If we are on the London fork boundary, we need to multiply the parent's gas limit by the
|
|
// elasticity multiplier to get the new gas limit.
|
|
if chain_spec.fork(Hardfork::London).transitions_at_block(parent.number + 1) {
|
|
let elasticity_multiplier =
|
|
chain_spec.base_fee_params(self.timestamp()).elasticity_multiplier;
|
|
|
|
// multiply the gas limit by the elasticity multiplier
|
|
gas_limit *= U256::from(elasticity_multiplier);
|
|
|
|
// set the base fee to the initial base fee from the EIP-1559 spec
|
|
basefee = Some(EIP1559_INITIAL_BASE_FEE)
|
|
}
|
|
|
|
let block_env = BlockEnv {
|
|
number: U256::from(parent.number + 1),
|
|
coinbase: self.suggested_fee_recipient(),
|
|
timestamp: U256::from(self.timestamp()),
|
|
difficulty: U256::ZERO,
|
|
prevrandao: Some(self.prev_randao()),
|
|
gas_limit,
|
|
// calculate basefee based on parent block's gas usage
|
|
basefee: basefee.map(U256::from).unwrap_or_default(),
|
|
// calculate excess gas based on parent block's blob gas usage
|
|
blob_excess_gas_and_price,
|
|
};
|
|
|
|
(CfgEnvWithHandlerCfg::new_with_spec_id(cfg, spec_id), block_env)
|
|
}
|
|
}
|
|
|
|
/// Generates the payload id for the configured payload from the [PayloadAttributes].
|
|
///
|
|
/// Returns an 8-byte identifier by hashing the payload components with sha256 hash.
|
|
pub(crate) fn payload_id(parent: &B256, attributes: &PayloadAttributes) -> PayloadId {
|
|
use sha2::Digest;
|
|
let mut hasher = sha2::Sha256::new();
|
|
hasher.update(parent.as_slice());
|
|
hasher.update(&attributes.timestamp.to_be_bytes()[..]);
|
|
hasher.update(attributes.prev_randao.as_slice());
|
|
hasher.update(attributes.suggested_fee_recipient.as_slice());
|
|
if let Some(withdrawals) = &attributes.withdrawals {
|
|
let mut buf = Vec::new();
|
|
withdrawals.encode(&mut buf);
|
|
hasher.update(buf);
|
|
}
|
|
|
|
if let Some(parent_beacon_block) = attributes.parent_beacon_block_root {
|
|
hasher.update(parent_beacon_block);
|
|
}
|
|
|
|
let out = hasher.finalize();
|
|
PayloadId::new(out.as_slice()[..8].try_into().expect("sufficient length"))
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use reth_primitives::Genesis;
|
|
|
|
#[test]
|
|
fn ensure_first_london_block_base_fee_is_set() {
|
|
let hive_london = r#"
|
|
{
|
|
"config": {
|
|
"ethash": {},
|
|
"chainId": 7,
|
|
"homesteadBlock": 0,
|
|
"eip150Block": 0,
|
|
"eip155Block": 0,
|
|
"eip158Block": 0,
|
|
"byzantiumBlock": 0,
|
|
"constantinopleBlock": 0,
|
|
"petersburgBlock": 0,
|
|
"istanbulBlock": 0,
|
|
"muirGlacierBlock": 0,
|
|
"berlinBlock": 0,
|
|
"londonBlock": 1,
|
|
"mergeNetsplitBlock": 1,
|
|
"terminalTotalDifficulty": 196608,
|
|
"shanghaiTime": 4662,
|
|
"cancunTime": 4662
|
|
},
|
|
"nonce": "0x0",
|
|
"timestamp": "0x1234",
|
|
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
|
"gasLimit": "0x2fefd8",
|
|
"difficulty": "0x30000",
|
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
|
"alloc": {
|
|
"0000000000000000000000000000000000000314": {
|
|
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029",
|
|
"storage": {
|
|
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x0000000000000000000000000000000000000000000000000000000000001234",
|
|
"0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x0000000000000000000000000000000000000000000000000000000000000001"
|
|
},
|
|
"balance": "0x0"
|
|
},
|
|
"0000000000000000000000000000000000000315": {
|
|
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029",
|
|
"balance": "0x9999999999999999999999999999999"
|
|
},
|
|
"0000000000000000000000000000000000000316": {
|
|
"code": "0x444355",
|
|
"balance": "0x0"
|
|
},
|
|
"0000000000000000000000000000000000000317": {
|
|
"code": "0x600160003555",
|
|
"balance": "0x0"
|
|
},
|
|
"000f3df6d732807ef1319fb7b8bb8522d0beac02": {
|
|
"code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500",
|
|
"balance": "0x0",
|
|
"nonce": "0x1"
|
|
}
|
|
},
|
|
"number": "0x0",
|
|
"gasUsed": "0x0",
|
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
"baseFeePerGas": null,
|
|
"excessBlobGas": null,
|
|
"blobGasUsed": null
|
|
}
|
|
"#;
|
|
|
|
let attributes = r#"{"timestamp":"0x1235","prevRandao":"0xf343b00e02dc34ec0124241f74f32191be28fb370bb48060f5fa4df99bda774c","suggestedFeeRecipient":"0x0000000000000000000000000000000000000000","withdrawals":null,"parentBeaconBlockRoot":null}"#;
|
|
let attributes: PayloadAttributes = serde_json::from_str(attributes).unwrap();
|
|
|
|
// check that it deserializes properly
|
|
let genesis: Genesis = serde_json::from_str(hive_london).unwrap();
|
|
let chainspec = ChainSpec::from(genesis);
|
|
let payload_builder_attributes =
|
|
EthPayloadBuilderAttributes::new(chainspec.genesis_hash(), attributes);
|
|
|
|
// use cfg_and_block_env
|
|
let cfg_and_block_env =
|
|
payload_builder_attributes.cfg_and_block_env(&chainspec, &chainspec.genesis_header());
|
|
|
|
// ensure the base fee is non zero
|
|
assert_eq!(cfg_and_block_env.1.basefee, U256::from(EIP1559_INITIAL_BASE_FEE));
|
|
|
|
// ensure the gas limit is double the previous block's gas limit
|
|
assert_eq!(
|
|
cfg_and_block_env.1.gas_limit,
|
|
U256::from(chainspec.genesis_header().gas_limit * 2)
|
|
);
|
|
}
|
|
}
|