Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Eip1559 params in extradata #11887

Merged
merged 26 commits into from
Oct 30, 2024
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/ethereum/engine-primitives/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ impl From<EthBuiltPayload> for ExecutionPayloadEnvelopeV4 {
}

/// Container type for all components required to build a payload.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EthPayloadBuilderAttributes {
/// Id of the payload
pub id: PayloadId,
Expand Down
7 changes: 5 additions & 2 deletions crates/ethereum/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

extern crate alloc;

use core::convert::Infallible;

use alloc::{sync::Arc, vec::Vec};
use alloy_primitives::{Address, Bytes, TxKind, U256};
use reth_chainspec::{ChainSpec, Head};
Expand Down Expand Up @@ -59,6 +61,7 @@ impl EthEvmConfig {

impl ConfigureEvmEnv for EthEvmConfig {
type Header = Header;
type Error = Infallible;

fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
transaction.fill_tx_env(tx_env, sender);
Expand Down Expand Up @@ -131,7 +134,7 @@ impl ConfigureEvmEnv for EthEvmConfig {
&self,
parent: &Self::Header,
attributes: NextBlockEnvAttributes,
) -> (CfgEnvWithHandlerCfg, BlockEnv) {
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), Self::Error> {
// configure evm env based on parent block
let cfg = CfgEnv::default().with_chain_id(self.chain_spec.chain().id());

Expand Down Expand Up @@ -179,7 +182,7 @@ impl ConfigureEvmEnv for EthEvmConfig {
blob_excess_gas_and_price,
};

(CfgEnvWithHandlerCfg::new_with_spec_id(cfg, spec_id), block_env)
Ok((CfgEnvWithHandlerCfg::new_with_spec_id(cfg, spec_id), block_env))
}
}

Expand Down
10 changes: 7 additions & 3 deletions crates/ethereum/payload/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ where
&self,
config: &PayloadConfig<EthPayloadBuilderAttributes>,
parent: &Header,
) -> (CfgEnvWithHandlerCfg, BlockEnv) {
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), EvmConfig::Error> {
let next_attributes = NextBlockEnvAttributes {
timestamp: config.attributes.timestamp(),
suggested_fee_recipient: config.attributes.suggested_fee_recipient(),
Expand All @@ -97,7 +97,9 @@ where
&self,
args: BuildArguments<Pool, Client, EthPayloadBuilderAttributes, EthBuiltPayload>,
) -> Result<BuildOutcome<EthBuiltPayload>, PayloadBuilderError> {
let (cfg_env, block_env) = self.cfg_and_block_env(&args.config, &args.config.parent_block);
let (cfg_env, block_env) = self
.cfg_and_block_env(&args.config, &args.config.parent_block)
.map_err(PayloadBuilderError::other)?;

let pool = args.pool.clone();
default_ethereum_payload(self.evm_config.clone(), args, cfg_env, block_env, |attributes| {
Expand All @@ -120,7 +122,9 @@ where
None,
);

let (cfg_env, block_env) = self.cfg_and_block_env(&args.config, &args.config.parent_block);
let (cfg_env, block_env) = self
.cfg_and_block_env(&args.config, &args.config.parent_block)
.map_err(PayloadBuilderError::other)?;

let pool = args.pool.clone();

Expand Down
16 changes: 8 additions & 8 deletions crates/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,12 @@ std = [
"revm/std",
]
test-utils = [
"dep:parking_lot",
"reth-chainspec/test-utils",
"reth-consensus/test-utils",
"reth-primitives/test-utils",
"reth-primitives-traits/test-utils",
"reth-revm/test-utils",
"revm/test-utils",
"reth-prune-types/test-utils"
"dep:parking_lot",
"reth-chainspec/test-utils",
"reth-consensus/test-utils",
"reth-primitives/test-utils",
"reth-primitives-traits/test-utils",
"reth-revm/test-utils",
"revm/test-utils",
"reth-prune-types/test-utils"
]
5 changes: 4 additions & 1 deletion crates/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static {
/// The header type used by the EVM.
type Header: BlockHeader;

/// The error type that is returned by [`Self::next_cfg_and_block_env`].
type Error: core::error::Error + Send + Sync;

/// Returns a [`TxEnv`] from a [`TransactionSigned`] and [`Address`].
fn tx_env(&self, transaction: &TransactionSigned, signer: Address) -> TxEnv {
let mut tx_env = TxEnv::default();
Expand Down Expand Up @@ -192,7 +195,7 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static {
&self,
parent: &Self::Header,
attributes: NextBlockEnvAttributes,
) -> (CfgEnvWithHandlerCfg, BlockEnv);
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), Self::Error>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imo this is reasonable

}

/// Represents additional attributes required to configure the next block.
Expand Down
174 changes: 172 additions & 2 deletions crates/optimism/chainspec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ mod op_sepolia;
use alloc::{vec, vec::Vec};
use alloy_chains::Chain;
use alloy_genesis::Genesis;
use alloy_primitives::{B256, U256};
use alloy_primitives::{Bytes, Parity, Signature, B256, U256};
pub use base::BASE_MAINNET;
pub use base_sepolia::BASE_SEPOLIA;
use derive_more::{Constructor, Deref, From, Into};
use derive_more::{Constructor, Deref, Display, From, Into};
pub use dev::OP_DEV;
#[cfg(not(feature = "std"))]
pub(crate) use once_cell::sync::Lazy as LazyLock;
Expand Down Expand Up @@ -159,6 +159,16 @@ impl OpChainSpecBuilder {
self
}

/// Enable Holocene at genesis
pub fn holocene_activated(mut self) -> Self {
self = self.granite_activated();
self.inner = self.inner.with_fork(
reth_optimism_forks::OptimismHardfork::Holocene,
ForkCondition::Timestamp(0),
);
self
}

/// Build the resulting [`OpChainSpec`].
///
/// # Panics
Expand All @@ -177,6 +187,81 @@ pub struct OpChainSpec {
pub inner: ChainSpec,
}

impl OpChainSpec {
/// Read from parent to determine the base fee for the next block
pub fn next_block_base_fee(
&self,
parent: &Header,
timestamp: u64,
) -> Result<U256, DecodeError> {
let is_holocene = self.inner.is_fork_active_at_timestamp(
reth_optimism_forks::OptimismHardfork::Holocene,
timestamp,
);
cody-wang-cb marked this conversation as resolved.
Show resolved Hide resolved
// If we are in the Holocene, we need to use the base fee params
// from the parent block's extra data.
// Else, use the base fee params (default values) from chainspec
if is_holocene {
let (denominator, elasticity) = decode_holocene_1559_params(parent.extra_data.clone())?;
if elasticity == 0 && denominator == 0 {
return Ok(U256::from(
parent
.next_block_base_fee(self.base_fee_params_at_timestamp(timestamp))
.unwrap_or_default(),
));
}
let base_fee_params = BaseFeeParams::new(denominator as u128, elasticity as u128);
Ok(U256::from(parent.next_block_base_fee(base_fee_params).unwrap_or_default()))
} else {
Ok(U256::from(
parent
.next_block_base_fee(self.base_fee_params_at_timestamp(timestamp))
.unwrap_or_default(),
))
}
}
}

#[derive(Clone, Debug, Display, Eq, PartialEq)]
/// Error type for decoding Holocene 1559 parameters
pub enum DecodeError {
#[display("Insufficient data to decode")]
/// Insufficient data to decode
InsufficientData,
#[display("Invalid denominator parameter")]
/// Invalid denominator parameter
InvalidDenominator,
#[display("Invalid elasticity parameter")]
/// Invalid elasticity parameter
InvalidElasticity,
}

impl core::error::Error for DecodeError {
fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
// None of the errors have sub-errors
None
}
}

/// Extracts the Holcene 1599 parameters from the encoded form:
/// <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/holocene/exec-engine.md#eip1559params-encoding>
pub fn decode_holocene_1559_params(extra_data: Bytes) -> Result<(u32, u32), DecodeError> {
if extra_data.len() < 9 {
return Err(DecodeError::InsufficientData);
}
let denominator: [u8; 4] =
extra_data[1..5].try_into().map_err(|_| DecodeError::InvalidDenominator)?;
let elasticity: [u8; 4] =
extra_data[5..9].try_into().map_err(|_| DecodeError::InvalidElasticity)?;
Ok((u32::from_be_bytes(denominator), u32::from_be_bytes(elasticity)))
}

/// Returns the signature for the optimism deposit transactions, which don't include a
/// signature.
pub fn optimism_deposit_tx_signature() -> Signature {
Signature::new(U256::ZERO, U256::ZERO, Parity::Parity(false))
}

impl EthChainSpec for OpChainSpec {
fn chain(&self) -> alloy_chains::Chain {
self.inner.chain()
Expand Down Expand Up @@ -405,6 +490,8 @@ impl OptimismGenesisInfo {

#[cfg(test)]
mod tests {
use std::sync::Arc;

use alloy_genesis::{ChainConfig, Genesis};
use alloy_primitives::b256;
use reth_chainspec::{test_fork_ids, BaseFeeParams, BaseFeeParamsKind};
Expand Down Expand Up @@ -919,4 +1006,87 @@ mod tests {
.all(|(expected, actual)| &**expected == *actual));
assert_eq!(expected_hardforks.len(), hardforks.len());
}

#[test]
fn test_get_base_fee_pre_holocene() {
let op_chain_spec = &BASE_SEPOLIA;
let parent = Header {
base_fee_per_gas: Some(1),
gas_used: 15763614,
gas_limit: 144000000,
..Default::default()
};
let base_fee = op_chain_spec.next_block_base_fee(&parent, 0);
assert_eq!(
base_fee.unwrap(),
U256::from(
parent
.next_block_base_fee(op_chain_spec.base_fee_params_at_timestamp(0))
.unwrap_or_default()
)
);
}

fn holocene_chainspec() -> Arc<OpChainSpec> {
let mut hardforks = OptimismHardfork::base_sepolia();
hardforks.insert(OptimismHardfork::Holocene.boxed(), ForkCondition::Timestamp(1800000000));
Arc::new(OpChainSpec {
inner: ChainSpec {
chain: BASE_SEPOLIA.inner.chain,
genesis: BASE_SEPOLIA.inner.genesis.clone(),
genesis_hash: BASE_SEPOLIA.inner.genesis_hash.clone(),
paris_block_and_final_difficulty: Some((0, U256::from(0))),
hardforks,
base_fee_params: BASE_SEPOLIA.inner.base_fee_params.clone(),
max_gas_limit: crate::constants::BASE_SEPOLIA_MAX_GAS_LIMIT,
prune_delete_limit: 10000,
..Default::default()
},
})
}

#[test]
fn test_get_base_fee_holocene_nonce_not_set() {
let op_chain_spec = holocene_chainspec();
let parent = Header {
base_fee_per_gas: Some(1),
gas_used: 15763614,
gas_limit: 144000000,
timestamp: 1800000003,
extra_data: Bytes::from_static(&[0, 0, 0, 0, 0, 0, 0, 0, 0]),
..Default::default()
};
let base_fee = op_chain_spec.next_block_base_fee(&parent, 1800000005);
assert_eq!(
base_fee.unwrap(),
U256::from(
parent
.next_block_base_fee(op_chain_spec.base_fee_params_at_timestamp(0))
.unwrap_or_default()
)
);
}

#[test]
fn test_get_base_fee_holocene_nonce_set() {
let op_chain_spec = holocene_chainspec();
let parent = Header {
base_fee_per_gas: Some(1),
gas_used: 15763614,
gas_limit: 144000000,
extra_data: Bytes::from_static(&[0, 0, 0, 0, 8, 0, 0, 0, 8]),
timestamp: 1800000003,
..Default::default()
};

let base_fee = op_chain_spec.next_block_base_fee(&parent, 1800000005);
assert_eq!(
base_fee.unwrap(),
U256::from(
parent
.next_block_base_fee(BaseFeeParams::new(0x00000008, 0x00000008))
.unwrap_or_default()
)
);
}
}
8 changes: 6 additions & 2 deletions crates/optimism/evm/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ pub fn revm_spec_by_timestamp_after_bedrock(
chain_spec: &OpChainSpec,
timestamp: u64,
) -> revm_primitives::SpecId {
if chain_spec.fork(OptimismHardfork::Granite).active_at_timestamp(timestamp) {
if chain_spec.fork(OptimismHardfork::Holocene).active_at_timestamp(timestamp) {
revm_primitives::HOLOCENE
} else if chain_spec.fork(OptimismHardfork::Granite).active_at_timestamp(timestamp) {
revm_primitives::GRANITE
} else if chain_spec.fork(OptimismHardfork::Fjord).active_at_timestamp(timestamp) {
revm_primitives::FJORD
Expand All @@ -29,7 +31,9 @@ pub fn revm_spec_by_timestamp_after_bedrock(

/// Map the latest active hardfork at the given block to a revm [`SpecId`](revm_primitives::SpecId).
pub fn revm_spec(chain_spec: &OpChainSpec, block: &Head) -> revm_primitives::SpecId {
if chain_spec.fork(OptimismHardfork::Granite).active_at_head(block) {
if chain_spec.fork(OptimismHardfork::Holocene).active_at_head(block) {
revm_primitives::HOLOCENE
} else if chain_spec.fork(OptimismHardfork::Granite).active_at_head(block) {
revm_primitives::GRANITE
} else if chain_spec.fork(OptimismHardfork::Fjord).active_at_head(block) {
revm_primitives::FJORD
Expand Down
15 changes: 5 additions & 10 deletions crates/optimism/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ extern crate alloc;
use alloc::{sync::Arc, vec::Vec};
use alloy_primitives::{Address, U256};
use reth_evm::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes};
use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_chainspec::{DecodeError, OpChainSpec};
use reth_primitives::{
revm_primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv},
transaction::FillTxEnv,
Expand Down Expand Up @@ -56,6 +56,7 @@ impl OptimismEvmConfig {

impl ConfigureEvmEnv for OptimismEvmConfig {
type Header = Header;
type Error = DecodeError;

fn fill_tx_env(&self, tx_env: &mut TxEnv, transaction: &TransactionSigned, sender: Address) {
transaction.fill_tx_env(tx_env, sender);
Expand Down Expand Up @@ -134,7 +135,7 @@ impl ConfigureEvmEnv for OptimismEvmConfig {
&self,
parent: &Self::Header,
attributes: NextBlockEnvAttributes,
) -> (CfgEnvWithHandlerCfg, BlockEnv) {
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), Self::Error> {
// configure evm env based on parent block
let cfg = CfgEnv::default().with_chain_id(self.chain_spec.chain().id());

Expand All @@ -156,13 +157,7 @@ impl ConfigureEvmEnv for OptimismEvmConfig {
prevrandao: Some(attributes.prev_randao),
gas_limit: U256::from(parent.gas_limit),
// calculate basefee based on parent block's gas usage
basefee: U256::from(
parent
.next_block_base_fee(
self.chain_spec.base_fee_params_at_timestamp(attributes.timestamp),
)
.unwrap_or_default(),
),
basefee: self.chain_spec.next_block_base_fee(parent, attributes.timestamp)?,
// calculate excess gas based on parent block's blob gas usage
blob_excess_gas_and_price,
};
Expand All @@ -175,7 +170,7 @@ impl ConfigureEvmEnv for OptimismEvmConfig {
};
}

(cfg_with_handler_cfg, block_env)
Ok((cfg_with_handler_cfg, block_env))
}
}

Expand Down
Loading
Loading