feat: continue opchainspec support (#17422)

Co-authored-by: rose2221 <rose.jethani@nethermind.io>
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
This commit is contained in:
ongyimeng
2025-07-18 22:44:28 +08:00
committed by GitHub
parent 8fb0fbba73
commit 537ffeacac
8 changed files with 79 additions and 76 deletions

1
Cargo.lock generated
View File

@@ -9130,6 +9130,7 @@ dependencies = [
"alloy-primitives",
"derive_more",
"miniz_oxide",
"op-alloy-consensus",
"op-alloy-rpc-types",
"paste",
"reth-chainspec",

View File

@@ -44,6 +44,7 @@ miniz_oxide = { workspace = true, features = ["with-alloc"], optional = true }
derive_more.workspace = true
paste = { workspace = true, optional = true }
thiserror = { workspace = true, optional = true }
op-alloy-consensus.workspace = true
[dev-dependencies]
reth-chainspec = { workspace = true, features = ["test-utils"] }
@@ -71,6 +72,7 @@ std = [
"serde?/std",
"miniz_oxide?/std",
"thiserror?/std",
"op-alloy-consensus/std",
]
serde = [
"alloy-chains/serde",
@@ -84,4 +86,5 @@ serde = [
"reth-optimism-forks/serde",
"reth-optimism-primitives/serde",
"reth-primitives-traits/serde",
"op-alloy-consensus/serde",
]

View File

@@ -0,0 +1,29 @@
//! Base fee related utilities for Optimism chains.
use alloy_consensus::BlockHeader;
use op_alloy_consensus::{decode_holocene_extra_data, EIP1559ParamError};
use reth_chainspec::{BaseFeeParams, EthChainSpec};
use reth_optimism_forks::OpHardforks;
/// Extracts the Holocene 1599 parameters from the encoded extra data from the parent header.
///
/// Caution: Caller must ensure that holocene is active in the parent header.
///
/// See also [Base fee computation](https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/holocene/exec-engine.md#base-fee-computation)
pub fn decode_holocene_base_fee<H>(
chain_spec: impl EthChainSpec + OpHardforks,
parent: &H,
timestamp: u64,
) -> Result<u64, EIP1559ParamError>
where
H: BlockHeader,
{
let (elasticity, denominator) = decode_holocene_extra_data(parent.extra_data())?;
let base_fee_params = if elasticity == 0 && denominator == 0 {
chain_spec.base_fee_params_at_timestamp(timestamp)
} else {
BaseFeeParams::new(denominator as u128, elasticity as u128)
};
Ok(parent.next_block_base_fee(base_fee_params).unwrap_or_default())
}

View File

@@ -34,6 +34,7 @@ extern crate alloc;
mod base;
mod base_sepolia;
mod basefee;
pub mod constants;
mod dev;
@@ -47,6 +48,7 @@ pub use superchain::*;
pub use base::BASE_MAINNET;
pub use base_sepolia::BASE_SEPOLIA;
pub use basefee::*;
pub use dev::OP_DEV;
pub use op::OP_MAINNET;
pub use op_sepolia::OP_SEPOLIA;
@@ -56,7 +58,7 @@ pub use reth_optimism_forks::*;
use alloc::{boxed::Box, vec, vec::Vec};
use alloy_chains::Chain;
use alloy_consensus::{proofs::storage_root_unhashed, Header};
use alloy_consensus::{proofs::storage_root_unhashed, BlockHeader, Header};
use alloy_eips::eip7840::BlobParams;
use alloy_genesis::Genesis;
use alloy_hardforks::Hardfork;
@@ -286,6 +288,14 @@ impl EthChainSpec for OpChainSpec {
fn final_paris_total_difficulty(&self) -> Option<U256> {
self.inner.final_paris_total_difficulty()
}
fn next_block_base_fee(&self, parent: &Header, target_timestamp: u64) -> Option<u64> {
if self.is_holocene_active_at_timestamp(parent.timestamp()) {
decode_holocene_base_fee(self, parent, parent.timestamp()).ok()
} else {
self.inner.next_block_base_fee(parent, target_timestamp)
}
}
}
impl Hardforks for OpChainSpec {

View File

@@ -32,11 +32,11 @@ alloy-primitives.workspace = true
alloy-consensus.workspace = true
alloy-trie.workspace = true
revm.workspace = true
op-alloy-consensus.workspace = true
# misc
tracing.workspace = true
thiserror.workspace = true
reth-optimism-chainspec.workspace = true
[dev-dependencies]
reth-provider = { workspace = true, features = ["test-utils"] }
@@ -49,6 +49,7 @@ reth-db-api = { workspace = true, features = ["op"] }
alloy-chains.workspace = true
alloy-primitives.workspace = true
op-alloy-consensus.workspace = true
[features]
@@ -69,10 +70,10 @@ std = [
"alloy-primitives/std",
"alloy-consensus/std",
"alloy-trie/std",
"op-alloy-consensus/std",
"reth-revm/std",
"revm/std",
"tracing/std",
"thiserror/std",
"reth-execution-types/std",
"op-alloy-consensus/std",
]

View File

@@ -34,9 +34,7 @@ mod proof;
pub use proof::calculate_receipt_root_no_memo_optimism;
pub mod validation;
pub use validation::{
canyon, decode_holocene_base_fee, isthmus, next_block_base_fee, validate_block_post_execution,
};
pub use validation::{canyon, isthmus, validate_block_post_execution};
pub mod error;
pub use error::OpConsensusError;
@@ -178,29 +176,11 @@ where
validate_against_parent_timestamp(header.header(), parent.header())?;
}
// EIP1559 base fee validation
// <https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/holocene/exec-engine.md#base-fee-computation>
// > if Holocene is active in parent_header.timestamp, then the parameters from
// > parent_header.extraData are used.
if self.chain_spec.is_holocene_active_at_timestamp(parent.timestamp()) {
let header_base_fee =
header.base_fee_per_gas().ok_or(ConsensusError::BaseFeeMissing)?;
let expected_base_fee =
decode_holocene_base_fee(&self.chain_spec, parent.header(), header.timestamp())
.map_err(|_| ConsensusError::BaseFeeMissing)?;
if expected_base_fee != header_base_fee {
return Err(ConsensusError::BaseFeeDiff(GotExpected {
expected: expected_base_fee,
got: header_base_fee,
}))
}
} else {
validate_against_parent_eip1559_base_fee(
header.header(),
parent.header(),
&self.chain_spec,
)?;
}
validate_against_parent_eip1559_base_fee(
header.header(),
parent.header(),
&self.chain_spec,
)?;
// ensure that the blob gas fields for this block
if let Some(blob_params) = self.chain_spec.blob_params_at_timestamp(header.timestamp()) {

View File

@@ -3,14 +3,15 @@
pub mod canyon;
pub mod isthmus;
// Re-export the decode_holocene_base_fee function for compatibility
pub use reth_optimism_chainspec::decode_holocene_base_fee;
use crate::proof::calculate_receipt_root_optimism;
use alloc::vec::Vec;
use alloy_consensus::{BlockHeader, TxReceipt, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::Encodable2718;
use alloy_primitives::{Bloom, Bytes, B256};
use alloy_trie::EMPTY_ROOT_HASH;
use op_alloy_consensus::{decode_holocene_extra_data, EIP1559ParamError};
use reth_chainspec::{BaseFeeParams, EthChainSpec};
use reth_consensus::ConsensusError;
use reth_optimism_forks::OpHardforks;
use reth_optimism_primitives::DepositReceipt;
@@ -171,51 +172,13 @@ fn compare_receipts_root_and_logs_bloom(
Ok(())
}
/// Extracts the Holocene 1599 parameters from the encoded extra data from the parent header.
///
/// Caution: Caller must ensure that holocene is active in the parent header.
///
/// See also [Base fee computation](https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/holocene/exec-engine.md#base-fee-computation)
pub fn decode_holocene_base_fee(
chain_spec: impl EthChainSpec + OpHardforks,
parent: impl BlockHeader,
timestamp: u64,
) -> Result<u64, EIP1559ParamError> {
let (elasticity, denominator) = decode_holocene_extra_data(parent.extra_data())?;
let base_fee_params = if elasticity == 0 && denominator == 0 {
chain_spec.base_fee_params_at_timestamp(timestamp)
} else {
BaseFeeParams::new(denominator as u128, elasticity as u128)
};
Ok(parent.next_block_base_fee(base_fee_params).unwrap_or_default())
}
/// Read from parent to determine the base fee for the next block
///
/// See also [Base fee computation](https://github.com/ethereum-optimism/specs/blob/main/specs/protocol/holocene/exec-engine.md#base-fee-computation)
pub fn next_block_base_fee<H: BlockHeader>(
chain_spec: impl EthChainSpec<Header = H> + OpHardforks,
parent: &H,
timestamp: u64,
) -> Result<u64, EIP1559ParamError> {
// 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 chain_spec.is_holocene_active_at_timestamp(parent.timestamp()) {
Ok(decode_holocene_base_fee(chain_spec, parent, timestamp)?)
} else {
Ok(chain_spec.next_block_base_fee(parent, timestamp).unwrap_or_default())
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_consensus::Header;
use alloy_primitives::{b256, hex, Bytes, U256};
use op_alloy_consensus::OpTxEnvelope;
use reth_chainspec::{ChainSpec, ForkCondition, Hardfork};
use reth_chainspec::{BaseFeeParams, ChainSpec, EthChainSpec, ForkCondition, Hardfork};
use reth_optimism_chainspec::{OpChainSpec, BASE_SEPOLIA};
use reth_optimism_forks::{OpHardfork, BASE_SEPOLIA_HARDFORKS};
use std::sync::Arc;
@@ -255,7 +218,8 @@ mod tests {
gas_limit: 144000000,
..Default::default()
};
let base_fee = next_block_base_fee(&op_chain_spec, &parent, 0);
let base_fee =
reth_optimism_chainspec::OpChainSpec::next_block_base_fee(&op_chain_spec, &parent, 0);
assert_eq!(
base_fee.unwrap(),
op_chain_spec.next_block_base_fee(&parent, 0).unwrap_or_default()
@@ -273,7 +237,11 @@ mod tests {
extra_data: Bytes::from_static(&[0, 0, 0, 0, 0, 0, 0, 0, 0]),
..Default::default()
};
let base_fee = next_block_base_fee(&op_chain_spec, &parent, 1800000005);
let base_fee = reth_optimism_chainspec::OpChainSpec::next_block_base_fee(
&op_chain_spec,
&parent,
1800000005,
);
assert_eq!(
base_fee.unwrap(),
op_chain_spec.next_block_base_fee(&parent, 0).unwrap_or_default()
@@ -291,7 +259,11 @@ mod tests {
..Default::default()
};
let base_fee = next_block_base_fee(holocene_chainspec(), &parent, 1800000005);
let base_fee = reth_optimism_chainspec::OpChainSpec::next_block_base_fee(
&holocene_chainspec(),
&parent,
1800000005,
);
assert_eq!(
base_fee.unwrap(),
parent
@@ -312,7 +284,12 @@ mod tests {
..Default::default()
};
let base_fee = next_block_base_fee(&*BASE_SEPOLIA, &parent, 1735315546).unwrap();
let base_fee = reth_optimism_chainspec::OpChainSpec::next_block_base_fee(
&*BASE_SEPOLIA,
&parent,
1735315546,
)
.unwrap();
assert_eq!(base_fee, 507);
}

View File

@@ -22,7 +22,6 @@ use op_revm::{OpSpecId, OpTransaction};
use reth_chainspec::EthChainSpec;
use reth_evm::{ConfigureEvm, EvmEnv};
use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_consensus::next_block_base_fee;
use reth_optimism_forks::OpHardforks;
use reth_optimism_primitives::{DepositReceipt, OpPrimitives};
use reth_primitives_traits::{NodePrimitives, SealedBlock, SealedHeader, SignedTransaction};
@@ -187,7 +186,10 @@ where
prevrandao: Some(attributes.prev_randao),
gas_limit: attributes.gas_limit,
// calculate basefee based on parent block's gas usage
basefee: next_block_base_fee(self.chain_spec(), parent, attributes.timestamp)?,
basefee: self
.chain_spec()
.next_block_base_fee(parent, attributes.timestamp)
.unwrap_or_default(),
// calculate excess gas based on parent block's blob gas usage
blob_excess_gas_and_price,
};