mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
feat: abstract OpBeaconConsensus over primitives and chainspec (#14171)
This commit is contained in:
@ -19,7 +19,7 @@ mod op_sepolia;
|
||||
|
||||
use alloc::{boxed::Box, vec, vec::Vec};
|
||||
use alloy_chains::Chain;
|
||||
use alloy_consensus::{BlockHeader, Header};
|
||||
use alloy_consensus::Header;
|
||||
use alloy_eips::eip7840::BlobParams;
|
||||
use alloy_genesis::Genesis;
|
||||
use alloy_primitives::{B256, U256};
|
||||
@ -28,7 +28,6 @@ pub use base_sepolia::BASE_SEPOLIA;
|
||||
use derive_more::{Constructor, Deref, Display, From, Into};
|
||||
pub use dev::OP_DEV;
|
||||
pub use op::OP_MAINNET;
|
||||
use op_alloy_consensus::{decode_holocene_extra_data, EIP1559ParamError};
|
||||
pub use op_sepolia::OP_SEPOLIA;
|
||||
use reth_chainspec::{
|
||||
BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, DepositContract, EthChainSpec,
|
||||
@ -195,55 +194,6 @@ impl OpChainSpec {
|
||||
pub fn from_genesis(genesis: Genesis) -> Self {
|
||||
genesis.into()
|
||||
}
|
||||
|
||||
/// 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: BlockHeader>(
|
||||
&self,
|
||||
parent: &H,
|
||||
timestamp: u64,
|
||||
) -> Result<u64, EIP1559ParamError> {
|
||||
let (elasticity, denominator) = decode_holocene_extra_data(parent.extra_data())?;
|
||||
let base_fee = if elasticity == 0 && denominator == 0 {
|
||||
parent
|
||||
.next_block_base_fee(self.base_fee_params_at_timestamp(timestamp))
|
||||
.unwrap_or_default()
|
||||
} else {
|
||||
let base_fee_params = BaseFeeParams::new(denominator as u128, elasticity as u128);
|
||||
parent.next_block_base_fee(base_fee_params).unwrap_or_default()
|
||||
};
|
||||
Ok(base_fee)
|
||||
}
|
||||
|
||||
/// 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>(
|
||||
&self,
|
||||
parent: &H,
|
||||
timestamp: u64,
|
||||
) -> Result<U256, EIP1559ParamError> {
|
||||
// > if Holocene is active in parent_header.timestamp, then the parameters from
|
||||
// > parent_header.extraData are used.
|
||||
let is_holocene_activated =
|
||||
self.inner.is_fork_active_at_timestamp(OpHardfork::Holocene, parent.timestamp());
|
||||
|
||||
// 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_activated {
|
||||
Ok(U256::from(self.decode_holocene_base_fee(parent, timestamp)?))
|
||||
} else {
|
||||
Ok(U256::from(
|
||||
parent
|
||||
.next_block_base_fee(self.base_fee_params_at_timestamp(timestamp))
|
||||
.unwrap_or_default(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EthChainSpec for OpChainSpec {
|
||||
@ -483,10 +433,8 @@ impl OpGenesisInfo {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use alloy_genesis::{ChainConfig, Genesis};
|
||||
use alloy_primitives::{b256, hex, Bytes};
|
||||
use alloy_primitives::b256;
|
||||
use reth_chainspec::{test_fork_ids, BaseFeeParams, BaseFeeParamsKind};
|
||||
use reth_ethereum_forks::{EthereumHardfork, ForkCondition, ForkHash, ForkId, Head};
|
||||
use reth_optimism_forks::{OpHardfork, OpHardforks};
|
||||
@ -1026,105 +974,6 @@ mod tests {
|
||||
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 = OpHardfork::base_sepolia();
|
||||
hardforks.insert(OpHardfork::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(),
|
||||
prune_delete_limit: 10000,
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_base_fee_holocene_extra_data_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_extra_data_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()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// <https://sepolia.basescan.org/block/19773628>
|
||||
#[test]
|
||||
fn test_get_base_fee_holocene_extra_data_set_base_sepolia() {
|
||||
let op_chain_spec = BASE_SEPOLIA.clone();
|
||||
let parent = Header {
|
||||
base_fee_per_gas: Some(507),
|
||||
gas_used: 4847634,
|
||||
gas_limit: 60000000,
|
||||
extra_data: hex!("00000000fa0000000a").into(),
|
||||
timestamp: 1735315544,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let base_fee = op_chain_spec.next_block_base_fee(&parent, 1735315546).unwrap();
|
||||
assert_eq!(base_fee, U256::from(507));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_genesis() {
|
||||
let geth_genesis = r#"
|
||||
|
||||
@ -30,12 +30,12 @@ alloy-eips.workspace = true
|
||||
alloy-primitives.workspace = true
|
||||
alloy-consensus.workspace = true
|
||||
alloy-trie.workspace = true
|
||||
op-alloy-consensus.workspace = true
|
||||
|
||||
tracing.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
alloy-primitives.workspace = true
|
||||
op-alloy-consensus.workspace = true
|
||||
reth-optimism-chainspec.workspace = true
|
||||
|
||||
[features]
|
||||
|
||||
@ -12,6 +12,8 @@
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
use core::fmt::Debug;
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use alloy_consensus::{BlockHeader as _, EMPTY_OMMER_ROOT_HASH};
|
||||
use alloy_primitives::{B64, U256};
|
||||
@ -25,45 +27,50 @@ use reth_consensus_common::validation::{
|
||||
validate_body_against_header, validate_cancun_gas, validate_header_base_fee,
|
||||
validate_header_extra_data, validate_header_gas, validate_shanghai_withdrawals,
|
||||
};
|
||||
use reth_optimism_chainspec::OpChainSpec;
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
use reth_optimism_primitives::{OpBlock, OpPrimitives, OpReceipt};
|
||||
use reth_primitives::{GotExpected, RecoveredBlock, SealedHeader};
|
||||
use reth_optimism_primitives::DepositReceipt;
|
||||
use reth_primitives::{GotExpected, NodePrimitives, RecoveredBlock, SealedHeader};
|
||||
|
||||
mod proof;
|
||||
pub use proof::calculate_receipt_root_no_memo_optimism;
|
||||
use reth_primitives_traits::{Block, BlockBody, BlockHeader, SealedBlock};
|
||||
|
||||
mod validation;
|
||||
pub use validation::validate_block_post_execution;
|
||||
pub use validation::{
|
||||
decode_holocene_base_fee, next_block_base_fee, validate_block_post_execution,
|
||||
};
|
||||
|
||||
/// Optimism consensus implementation.
|
||||
///
|
||||
/// Provides basic checks as outlined in the execution specs.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OpBeaconConsensus {
|
||||
pub struct OpBeaconConsensus<ChainSpec> {
|
||||
/// Configuration
|
||||
chain_spec: Arc<OpChainSpec>,
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
}
|
||||
|
||||
impl OpBeaconConsensus {
|
||||
impl<ChainSpec> OpBeaconConsensus<ChainSpec> {
|
||||
/// Create a new instance of [`OpBeaconConsensus`]
|
||||
pub const fn new(chain_spec: Arc<OpChainSpec>) -> Self {
|
||||
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
|
||||
Self { chain_spec }
|
||||
}
|
||||
}
|
||||
|
||||
impl FullConsensus<OpPrimitives> for OpBeaconConsensus {
|
||||
impl<ChainSpec: EthChainSpec + OpHardforks, N: NodePrimitives<Receipt: DepositReceipt>>
|
||||
FullConsensus<N> for OpBeaconConsensus<ChainSpec>
|
||||
{
|
||||
fn validate_block_post_execution(
|
||||
&self,
|
||||
block: &RecoveredBlock<OpBlock>,
|
||||
input: PostExecutionInput<'_, OpReceipt>,
|
||||
block: &RecoveredBlock<N::Block>,
|
||||
input: PostExecutionInput<'_, N::Receipt>,
|
||||
) -> Result<(), ConsensusError> {
|
||||
validate_block_post_execution(block.header(), &self.chain_spec, input.receipts)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> Consensus<B> for OpBeaconConsensus {
|
||||
impl<ChainSpec: EthChainSpec + OpHardforks, B: Block> Consensus<B>
|
||||
for OpBeaconConsensus<ChainSpec>
|
||||
{
|
||||
type Error = ConsensusError;
|
||||
|
||||
fn validate_body_against_header(
|
||||
@ -105,7 +112,9 @@ impl<B: Block> Consensus<B> for OpBeaconConsensus {
|
||||
}
|
||||
}
|
||||
|
||||
impl<H: BlockHeader> HeaderValidator<H> for OpBeaconConsensus {
|
||||
impl<ChainSpec: EthChainSpec + OpHardforks, H: BlockHeader> HeaderValidator<H>
|
||||
for OpBeaconConsensus<ChainSpec>
|
||||
{
|
||||
fn validate_header(&self, header: &SealedHeader<H>) -> Result<(), ConsensusError> {
|
||||
validate_header_gas(header.header())?;
|
||||
validate_header_base_fee(header.header(), &self.chain_spec)
|
||||
@ -129,10 +138,9 @@ impl<H: BlockHeader> HeaderValidator<H> for OpBeaconConsensus {
|
||||
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 = self
|
||||
.chain_spec
|
||||
.decode_holocene_base_fee(parent.header(), header.timestamp())
|
||||
.map_err(|_| 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,
|
||||
|
||||
@ -4,15 +4,14 @@ use alloc::vec::Vec;
|
||||
use alloy_eips::eip2718::Encodable2718;
|
||||
use alloy_primitives::B256;
|
||||
use alloy_trie::root::ordered_trie_root_with_encoder;
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_optimism_forks::{OpHardfork, OpHardforks};
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
use reth_optimism_primitives::DepositReceipt;
|
||||
use reth_primitives::ReceiptWithBloom;
|
||||
|
||||
/// Calculates the receipt root for a header.
|
||||
pub(crate) fn calculate_receipt_root_optimism<R: DepositReceipt>(
|
||||
receipts: &[ReceiptWithBloom<R>],
|
||||
chain_spec: &ChainSpec,
|
||||
chain_spec: impl OpHardforks,
|
||||
timestamp: u64,
|
||||
) -> B256 {
|
||||
// There is a minor bug in op-geth and op-erigon where in the Regolith hardfork,
|
||||
@ -20,8 +19,8 @@ pub(crate) fn calculate_receipt_root_optimism<R: DepositReceipt>(
|
||||
// encoding. In the Regolith Hardfork, we must strip the deposit nonce from the
|
||||
// receipts before calculating the receipt root. This was corrected in the Canyon
|
||||
// hardfork.
|
||||
if chain_spec.is_fork_active_at_timestamp(OpHardfork::Regolith, timestamp) &&
|
||||
!chain_spec.is_fork_active_at_timestamp(OpHardfork::Canyon, timestamp)
|
||||
if chain_spec.is_regolith_active_at_timestamp(timestamp) &&
|
||||
!chain_spec.is_canyon_active_at_timestamp(timestamp)
|
||||
{
|
||||
let receipts = receipts
|
||||
.iter()
|
||||
|
||||
@ -2,8 +2,10 @@ use crate::proof::calculate_receipt_root_optimism;
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::{BlockHeader, TxReceipt};
|
||||
use alloy_primitives::{Bloom, B256};
|
||||
use reth_chainspec::{ChainSpec, EthereumHardforks};
|
||||
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;
|
||||
use reth_primitives::{gas_spent_by_transactions, GotExpected};
|
||||
|
||||
@ -13,7 +15,7 @@ use reth_primitives::{gas_spent_by_transactions, GotExpected};
|
||||
/// - Compares the gas used in the block header to the actual gas usage after execution
|
||||
pub fn validate_block_post_execution<R: DepositReceipt>(
|
||||
header: impl BlockHeader,
|
||||
chain_spec: &ChainSpec,
|
||||
chain_spec: impl OpHardforks,
|
||||
receipts: &[R],
|
||||
) -> Result<(), ConsensusError> {
|
||||
// Before Byzantium, receipts contained state root that would mean that expensive
|
||||
@ -21,7 +23,7 @@ pub fn validate_block_post_execution<R: DepositReceipt>(
|
||||
// transaction This was replaced with is_success flag.
|
||||
// See more about EIP here: https://eips.ethereum.org/EIPS/eip-658
|
||||
if chain_spec.is_byzantium_active_at_block(header.number()) {
|
||||
if let Err(error) = verify_receipts(
|
||||
if let Err(error) = verify_receipts_optimism(
|
||||
header.receipts_root(),
|
||||
header.logs_bloom(),
|
||||
receipts,
|
||||
@ -47,11 +49,11 @@ pub fn validate_block_post_execution<R: DepositReceipt>(
|
||||
}
|
||||
|
||||
/// Verify the calculated receipts root against the expected receipts root.
|
||||
fn verify_receipts<R: DepositReceipt>(
|
||||
fn verify_receipts_optimism<R: DepositReceipt>(
|
||||
expected_receipts_root: B256,
|
||||
expected_logs_bloom: Bloom,
|
||||
receipts: &[R],
|
||||
chain_spec: &ChainSpec,
|
||||
chain_spec: impl OpHardforks,
|
||||
timestamp: u64,
|
||||
) -> Result<(), ConsensusError> {
|
||||
// Calculate receipts root.
|
||||
@ -94,3 +96,145 @@ 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(
|
||||
chain_spec: impl EthChainSpec + OpHardforks,
|
||||
parent: impl BlockHeader,
|
||||
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(parent
|
||||
.next_block_base_fee(chain_spec.base_fee_params_at_timestamp(timestamp))
|
||||
.unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloy_consensus::Header;
|
||||
use alloy_primitives::{hex, Bytes, U256};
|
||||
use reth_chainspec::{ChainSpec, ForkCondition, Hardfork};
|
||||
use reth_optimism_chainspec::{OpChainSpec, BASE_SEPOLIA};
|
||||
use reth_optimism_forks::OpHardfork;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn holocene_chainspec() -> Arc<OpChainSpec> {
|
||||
let mut hardforks = OpHardfork::base_sepolia();
|
||||
hardforks.insert(OpHardfork::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(),
|
||||
prune_delete_limit: 10000,
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_base_fee_pre_holocene() {
|
||||
let op_chain_spec = BASE_SEPOLIA.clone();
|
||||
let parent = Header {
|
||||
base_fee_per_gas: Some(1),
|
||||
gas_used: 15763614,
|
||||
gas_limit: 144000000,
|
||||
..Default::default()
|
||||
};
|
||||
let base_fee = next_block_base_fee(&op_chain_spec, &parent, 0);
|
||||
assert_eq!(
|
||||
base_fee.unwrap(),
|
||||
parent
|
||||
.next_block_base_fee(op_chain_spec.base_fee_params_at_timestamp(0))
|
||||
.unwrap_or_default()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_base_fee_holocene_extra_data_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 = next_block_base_fee(&op_chain_spec, &parent, 1800000005);
|
||||
assert_eq!(
|
||||
base_fee.unwrap(),
|
||||
parent
|
||||
.next_block_base_fee(op_chain_spec.base_fee_params_at_timestamp(0))
|
||||
.unwrap_or_default()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_base_fee_holocene_extra_data_set() {
|
||||
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 = next_block_base_fee(holocene_chainspec(), &parent, 1800000005);
|
||||
assert_eq!(
|
||||
base_fee.unwrap(),
|
||||
parent
|
||||
.next_block_base_fee(BaseFeeParams::new(0x00000008, 0x00000008))
|
||||
.unwrap_or_default()
|
||||
);
|
||||
}
|
||||
|
||||
// <https://sepolia.basescan.org/block/19773628>
|
||||
#[test]
|
||||
fn test_get_base_fee_holocene_extra_data_set_base_sepolia() {
|
||||
let parent = Header {
|
||||
base_fee_per_gas: Some(507),
|
||||
gas_used: 4847634,
|
||||
gas_limit: 60000000,
|
||||
extra_data: hex!("00000000fa0000000a").into(),
|
||||
timestamp: 1735315544,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let base_fee = next_block_base_fee(&*BASE_SEPOLIA, &parent, 1735315546).unwrap();
|
||||
assert_eq!(base_fee, 507);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
use alloy_consensus::Header;
|
||||
use reth_optimism_chainspec::OpChainSpec;
|
||||
use reth_optimism_forks::OpHardfork;
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
|
||||
/// Map the latest active hardfork at the given header to a revm
|
||||
/// [`SpecId`](revm_primitives::SpecId).
|
||||
pub fn revm_spec(chain_spec: &OpChainSpec, header: &Header) -> revm_primitives::SpecId {
|
||||
pub fn revm_spec(chain_spec: impl OpHardforks, header: &Header) -> revm_primitives::SpecId {
|
||||
revm_spec_by_timestamp_after_bedrock(chain_spec, header.timestamp)
|
||||
}
|
||||
|
||||
@ -15,22 +14,22 @@ pub fn revm_spec(chain_spec: &OpChainSpec, header: &Header) -> revm_primitives::
|
||||
/// This is only intended to be used after the Bedrock, when hardforks are activated by
|
||||
/// timestamp.
|
||||
pub fn revm_spec_by_timestamp_after_bedrock(
|
||||
chain_spec: &OpChainSpec,
|
||||
chain_spec: impl OpHardforks,
|
||||
timestamp: u64,
|
||||
) -> revm_primitives::SpecId {
|
||||
if chain_spec.fork(OpHardfork::Isthmus).active_at_timestamp(timestamp) {
|
||||
if chain_spec.is_isthmus_active_at_timestamp(timestamp) {
|
||||
revm_primitives::ISTHMUS
|
||||
} else if chain_spec.fork(OpHardfork::Holocene).active_at_timestamp(timestamp) {
|
||||
} else if chain_spec.is_holocene_active_at_timestamp(timestamp) {
|
||||
revm_primitives::HOLOCENE
|
||||
} else if chain_spec.fork(OpHardfork::Granite).active_at_timestamp(timestamp) {
|
||||
} else if chain_spec.is_granite_active_at_timestamp(timestamp) {
|
||||
revm_primitives::GRANITE
|
||||
} else if chain_spec.fork(OpHardfork::Fjord).active_at_timestamp(timestamp) {
|
||||
} else if chain_spec.is_fjord_active_at_timestamp(timestamp) {
|
||||
revm_primitives::FJORD
|
||||
} else if chain_spec.fork(OpHardfork::Ecotone).active_at_timestamp(timestamp) {
|
||||
} else if chain_spec.is_ecotone_active_at_timestamp(timestamp) {
|
||||
revm_primitives::ECOTONE
|
||||
} else if chain_spec.fork(OpHardfork::Canyon).active_at_timestamp(timestamp) {
|
||||
} else if chain_spec.is_canyon_active_at_timestamp(timestamp) {
|
||||
revm_primitives::CANYON
|
||||
} else if chain_spec.fork(OpHardfork::Regolith).active_at_timestamp(timestamp) {
|
||||
} else if chain_spec.is_regolith_active_at_timestamp(timestamp) {
|
||||
revm_primitives::REGOLITH
|
||||
} else {
|
||||
revm_primitives::BEDROCK
|
||||
@ -51,35 +50,35 @@ mod tests {
|
||||
f(cs).build()
|
||||
}
|
||||
assert_eq!(
|
||||
revm_spec_by_timestamp_after_bedrock(&op_cs(|cs| cs.isthmus_activated()), 0),
|
||||
revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.isthmus_activated()), 0),
|
||||
revm_primitives::ISTHMUS
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec_by_timestamp_after_bedrock(&op_cs(|cs| cs.holocene_activated()), 0),
|
||||
revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.holocene_activated()), 0),
|
||||
revm_primitives::HOLOCENE
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec_by_timestamp_after_bedrock(&op_cs(|cs| cs.granite_activated()), 0),
|
||||
revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.granite_activated()), 0),
|
||||
revm_primitives::GRANITE
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec_by_timestamp_after_bedrock(&op_cs(|cs| cs.fjord_activated()), 0),
|
||||
revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.fjord_activated()), 0),
|
||||
revm_primitives::FJORD
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec_by_timestamp_after_bedrock(&op_cs(|cs| cs.ecotone_activated()), 0),
|
||||
revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.ecotone_activated()), 0),
|
||||
revm_primitives::ECOTONE
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec_by_timestamp_after_bedrock(&op_cs(|cs| cs.canyon_activated()), 0),
|
||||
revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.canyon_activated()), 0),
|
||||
revm_primitives::CANYON
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec_by_timestamp_after_bedrock(&op_cs(|cs| cs.bedrock_activated()), 0),
|
||||
revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.bedrock_activated()), 0),
|
||||
revm_primitives::BEDROCK
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec_by_timestamp_after_bedrock(&op_cs(|cs| cs.regolith_activated()), 0),
|
||||
revm_spec_by_timestamp_after_bedrock(op_cs(|cs| cs.regolith_activated()), 0),
|
||||
revm_primitives::REGOLITH
|
||||
);
|
||||
}
|
||||
@ -92,35 +91,35 @@ mod tests {
|
||||
f(cs).build()
|
||||
}
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.isthmus_activated()), &Default::default()),
|
||||
revm_spec(op_cs(|cs| cs.isthmus_activated()), &Default::default()),
|
||||
revm_primitives::ISTHMUS
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.holocene_activated()), &Default::default()),
|
||||
revm_spec(op_cs(|cs| cs.holocene_activated()), &Default::default()),
|
||||
revm_primitives::HOLOCENE
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.granite_activated()), &Default::default()),
|
||||
revm_spec(op_cs(|cs| cs.granite_activated()), &Default::default()),
|
||||
revm_primitives::GRANITE
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.fjord_activated()), &Default::default()),
|
||||
revm_spec(op_cs(|cs| cs.fjord_activated()), &Default::default()),
|
||||
revm_primitives::FJORD
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.ecotone_activated()), &Default::default()),
|
||||
revm_spec(op_cs(|cs| cs.ecotone_activated()), &Default::default()),
|
||||
revm_primitives::ECOTONE
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.canyon_activated()), &Default::default()),
|
||||
revm_spec(op_cs(|cs| cs.canyon_activated()), &Default::default()),
|
||||
revm_primitives::CANYON
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.bedrock_activated()), &Default::default()),
|
||||
revm_spec(op_cs(|cs| cs.bedrock_activated()), &Default::default()),
|
||||
revm_primitives::BEDROCK
|
||||
);
|
||||
assert_eq!(
|
||||
revm_spec(&op_cs(|cs| cs.regolith_activated()), &Default::default()),
|
||||
revm_spec(op_cs(|cs| cs.regolith_activated()), &Default::default()),
|
||||
revm_primitives::REGOLITH
|
||||
);
|
||||
}
|
||||
|
||||
@ -304,7 +304,7 @@ where
|
||||
receipts: &[N::Receipt],
|
||||
_requests: &Requests,
|
||||
) -> Result<(), ConsensusError> {
|
||||
validate_block_post_execution(block.header(), &self.chain_spec.clone(), receipts)
|
||||
validate_block_post_execution(block.header(), self.chain_spec.clone(), receipts)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,8 @@ use op_alloy_consensus::EIP1559ParamError;
|
||||
use reth_chainspec::EthChainSpec;
|
||||
use reth_evm::{env::EvmEnv, ConfigureEvm, ConfigureEvmEnv, Database, Evm, NextBlockEnvAttributes};
|
||||
use reth_optimism_chainspec::OpChainSpec;
|
||||
use reth_optimism_consensus::next_block_base_fee;
|
||||
use reth_optimism_forks::OpHardforks;
|
||||
use reth_optimism_primitives::OpTransactionSigned;
|
||||
use reth_primitives_traits::FillTxEnv;
|
||||
use reth_revm::{
|
||||
@ -126,24 +128,30 @@ impl<EXT, DB: Database> Evm for OpEvm<'_, EXT, DB> {
|
||||
}
|
||||
|
||||
/// Optimism-related EVM configuration.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OpEvmConfig {
|
||||
chain_spec: Arc<OpChainSpec>,
|
||||
#[derive(Debug)]
|
||||
pub struct OpEvmConfig<ChainSpec = OpChainSpec> {
|
||||
chain_spec: Arc<ChainSpec>,
|
||||
}
|
||||
|
||||
impl OpEvmConfig {
|
||||
impl<ChainSpec> Clone for OpEvmConfig<ChainSpec> {
|
||||
fn clone(&self) -> Self {
|
||||
Self { chain_spec: self.chain_spec.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<ChainSpec> OpEvmConfig<ChainSpec> {
|
||||
/// Creates a new [`OpEvmConfig`] with the given chain spec.
|
||||
pub const fn new(chain_spec: Arc<OpChainSpec>) -> Self {
|
||||
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
|
||||
Self { chain_spec }
|
||||
}
|
||||
|
||||
/// Returns the chain spec associated with this configuration.
|
||||
pub const fn chain_spec(&self) -> &Arc<OpChainSpec> {
|
||||
pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
|
||||
&self.chain_spec
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigureEvmEnv for OpEvmConfig {
|
||||
impl<ChainSpec: EthChainSpec + OpHardforks + 'static> ConfigureEvmEnv for OpEvmConfig<ChainSpec> {
|
||||
type Header = Header;
|
||||
type Transaction = OpTransactionSigned;
|
||||
type Error = EIP1559ParamError;
|
||||
@ -208,7 +216,11 @@ impl ConfigureEvmEnv for OpEvmConfig {
|
||||
prevrandao: Some(attributes.prev_randao),
|
||||
gas_limit: U256::from(attributes.gas_limit),
|
||||
// calculate basefee based on parent block's gas usage
|
||||
basefee: self.chain_spec.next_block_base_fee(parent, attributes.timestamp)?,
|
||||
basefee: U256::from(next_block_base_fee(
|
||||
&self.chain_spec,
|
||||
parent,
|
||||
attributes.timestamp,
|
||||
)?),
|
||||
// calculate excess gas based on parent block's blob gas usage
|
||||
blob_excess_gas_and_price,
|
||||
};
|
||||
@ -225,7 +237,7 @@ impl ConfigureEvmEnv for OpEvmConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigureEvm for OpEvmConfig {
|
||||
impl<ChainSpec: EthChainSpec + OpHardforks + 'static> ConfigureEvm for OpEvmConfig<ChainSpec> {
|
||||
type Evm<'a, DB: Database + 'a, I: 'a> = OpEvm<'a, I, DB>;
|
||||
type EvmError<DBError: core::error::Error + Send + Sync + 'static> = EVMError<DBError>;
|
||||
type HaltReason = HaltReason;
|
||||
|
||||
@ -33,7 +33,7 @@ use reth_optimism_payload_builder::{
|
||||
builder::OpPayloadTransactions,
|
||||
config::{OpBuilderConfig, OpDAConfig},
|
||||
};
|
||||
use reth_optimism_primitives::{OpPrimitives, OpReceipt, OpTransactionSigned};
|
||||
use reth_optimism_primitives::{DepositReceipt, OpPrimitives, OpReceipt, OpTransactionSigned};
|
||||
use reth_optimism_rpc::{
|
||||
miner::{MinerApiExtServer, OpMinerExtApi},
|
||||
witness::{DebugExecutionWitnessApiServer, OpDebugWitnessApi},
|
||||
@ -650,9 +650,14 @@ pub struct OpConsensusBuilder;
|
||||
|
||||
impl<Node> ConsensusBuilder<Node> for OpConsensusBuilder
|
||||
where
|
||||
Node: FullNodeTypes<Types: NodeTypes<ChainSpec = OpChainSpec, Primitives = OpPrimitives>>,
|
||||
Node: FullNodeTypes<
|
||||
Types: NodeTypes<
|
||||
ChainSpec: OpHardforks,
|
||||
Primitives: NodePrimitives<Receipt: DepositReceipt>,
|
||||
>,
|
||||
>,
|
||||
{
|
||||
type Consensus = Arc<OpBeaconConsensus>;
|
||||
type Consensus = Arc<OpBeaconConsensus<<Node::Types as NodeTypes>::ChainSpec>>;
|
||||
|
||||
async fn build_consensus(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::Consensus> {
|
||||
Ok(Arc::new(OpBeaconConsensus::new(ctx.chain_spec())))
|
||||
|
||||
Reference in New Issue
Block a user