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 14 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
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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 @@ -169,7 +169,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
6 changes: 3 additions & 3 deletions crates/ethereum/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extern crate alloc;
use alloc::{sync::Arc, vec::Vec};
use alloy_primitives::{Address, Bytes, TxKind, U256};
use reth_chainspec::{ChainSpec, Head};
use reth_evm::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes};
use reth_evm::{ConfigureEvm, ConfigureEvmEnv, NextBlockEnvAttributes, NextCfgError};
use reth_primitives::{transaction::FillTxEnv, Header, TransactionSigned};
use revm_primitives::{
AnalysisKind, BlobExcessGasAndPrice, BlockEnv, CfgEnv, CfgEnvWithHandlerCfg, Env, SpecId, TxEnv,
Expand Down Expand Up @@ -131,7 +131,7 @@ impl ConfigureEvmEnv for EthEvmConfig {
&self,
parent: &Self::Header,
attributes: NextBlockEnvAttributes,
) -> (CfgEnvWithHandlerCfg, BlockEnv) {
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), NextCfgError> {
// 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 +179,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
12 changes: 8 additions & 4 deletions crates/ethereum/payload/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use reth_basic_payload_builder::{
use reth_chain_state::ExecutedBlock;
use reth_chainspec::ChainSpec;
use reth_errors::RethError;
use reth_evm::{system_calls::SystemCaller, ConfigureEvm, NextBlockEnvAttributes};
use reth_evm::{system_calls::SystemCaller, ConfigureEvm, NextBlockEnvAttributes, NextCfgError};
use reth_evm_ethereum::{eip6110::parse_deposits_from_receipts, EthEvmConfig};
use reth_execution_types::ExecutionOutcome;
use reth_payload_builder::{EthBuiltPayload, EthPayloadBuilderAttributes};
Expand Down Expand Up @@ -69,7 +69,7 @@ where
&self,
config: &PayloadConfig<EthPayloadBuilderAttributes>,
parent: &Header,
) -> (CfgEnvWithHandlerCfg, BlockEnv) {
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), NextCfgError> {
let next_attributes = NextBlockEnvAttributes {
timestamp: config.attributes.timestamp(),
suggested_fee_recipient: config.attributes.suggested_fee_recipient(),
Expand All @@ -93,7 +93,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)?;
default_ethereum_payload(self.evm_config.clone(), args, cfg_env, block_env)
}

Expand All @@ -111,7 +113,9 @@ where
cancel: Default::default(),
best_payload: 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)?;
default_ethereum_payload(self.evm_config.clone(), args, cfg_env, block_env)?
.into_payload()
.ok_or_else(|| PayloadBuilderError::MissingPayload)
Expand Down
3 changes: 3 additions & 0 deletions crates/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ futures-util.workspace = true
metrics = { workspace = true, optional = true }
parking_lot = { workspace = true, optional = true }

# misc
thiserror.workspace = true

[dev-dependencies]
parking_lot.workspace = true
reth-ethereum-forks.workspace = true
Expand Down
10 changes: 9 additions & 1 deletion crates/evm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,14 @@ pub trait ConfigureEvm: ConfigureEvmEnv {
fn default_external_context<'a>(&self) -> Self::DefaultExternalContext<'a>;
}

#[derive(thiserror::Error, Debug)]
/// Error type for [`ConfigureEvmEnv::next_cfg_and_block_env`].
pub enum NextCfgError {
#[error("Invalid config: {0}")]
/// This is a generic error type that can be used to wrap any error type that implements
InvalidConfigError(#[from] Box<dyn core::error::Error + Send + Sync>),
}

/// This represents the set of methods used to configure the EVM's environment before block
/// execution.
///
Expand Down Expand Up @@ -192,7 +200,7 @@ pub trait ConfigureEvmEnv: Send + Sync + Unpin + Clone + 'static {
&self,
parent: &Self::Header,
attributes: NextBlockEnvAttributes,
) -> (CfgEnvWithHandlerCfg, BlockEnv);
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), NextCfgError>;
}

/// Represents additional attributes required to configure the next block.
Expand Down
1 change: 1 addition & 0 deletions crates/optimism/chainspec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ serde_json.workspace = true
# misc
derive_more.workspace = true
once_cell.workspace = true
thiserror.workspace = true

[dev-dependencies]
reth-chainspec = { workspace = true, features = ["test-utils"] }
Expand Down
165 changes: 164 additions & 1 deletion crates/optimism/chainspec/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ 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};
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,74 @@ 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(Debug, thiserror::Error)]
/// Error type for decoding Holocene 1559 parameters
pub enum DecodeError {
#[error("Insufficient data to decode")]
/// Insufficient data to decode
InsufficientData,
#[error("Invalid denominator parameter")]
/// Invalid denominator parameter
InvalidDenominator,
#[error("Invalid elasticity parameter")]
/// Invalid elasticity parameter
InvalidElasticity,
}

/// 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 +483,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 +999,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()
)
);
}
}
1 change: 1 addition & 0 deletions crates/optimism/evm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ revm-primitives.workspace = true
# misc
derive_more.workspace = true
tracing.workspace = true
thiserror.workspace = true

[dev-dependencies]
reth-evm = { workspace = true, features = ["test-utils"] }
Expand Down
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
Loading
Loading