feat(op): opchainspec builder (#11630)

Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
greged93
2024-10-12 17:57:25 +02:00
committed by GitHub
parent c47621754f
commit 53bd6872db
5 changed files with 179 additions and 101 deletions

View File

@ -755,13 +755,19 @@ impl ChainSpecBuilder {
}
/// Add the given fork with the given activation condition to the spec.
pub fn with_fork(mut self, fork: EthereumHardfork, condition: ForkCondition) -> Self {
pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self {
self.hardforks.insert(fork, condition);
self
}
/// Add the given chain hardforks to the spec.
pub fn with_forks(mut self, forks: ChainHardforks) -> Self {
self.hardforks = forks;
self
}
/// Remove the given fork from the spec.
pub fn without_fork(mut self, fork: EthereumHardfork) -> Self {
pub fn without_fork<H: Hardfork>(mut self, fork: H) -> Self {
self.hardforks.remove(fork);
self
}
@ -876,63 +882,6 @@ impl ChainSpecBuilder {
self
}
/// Enable Bedrock at genesis
#[cfg(feature = "optimism")]
pub fn bedrock_activated(mut self) -> Self {
self = self.paris_activated();
self.hardforks
.insert(reth_optimism_forks::OptimismHardfork::Bedrock, ForkCondition::Block(0));
self
}
/// Enable Regolith at genesis
#[cfg(feature = "optimism")]
pub fn regolith_activated(mut self) -> Self {
self = self.bedrock_activated();
self.hardforks
.insert(reth_optimism_forks::OptimismHardfork::Regolith, ForkCondition::Timestamp(0));
self
}
/// Enable Canyon at genesis
#[cfg(feature = "optimism")]
pub fn canyon_activated(mut self) -> Self {
self = self.regolith_activated();
// Canyon also activates changes from L1's Shanghai hardfork
self.hardforks.insert(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0));
self.hardforks
.insert(reth_optimism_forks::OptimismHardfork::Canyon, ForkCondition::Timestamp(0));
self
}
/// Enable Ecotone at genesis
#[cfg(feature = "optimism")]
pub fn ecotone_activated(mut self) -> Self {
self = self.canyon_activated();
self.hardforks.insert(EthereumHardfork::Cancun, ForkCondition::Timestamp(0));
self.hardforks
.insert(reth_optimism_forks::OptimismHardfork::Ecotone, ForkCondition::Timestamp(0));
self
}
/// Enable Fjord at genesis
#[cfg(feature = "optimism")]
pub fn fjord_activated(mut self) -> Self {
self = self.ecotone_activated();
self.hardforks
.insert(reth_optimism_forks::OptimismHardfork::Fjord, ForkCondition::Timestamp(0));
self
}
/// Enable Granite at genesis
#[cfg(feature = "optimism")]
pub fn granite_activated(mut self) -> Self {
self = self.fjord_activated();
self.hardforks
.insert(reth_optimism_forks::OptimismHardfork::Granite, ForkCondition::Timestamp(0));
self
}
/// Build the resulting [`ChainSpec`].
///
/// # Panics

View File

@ -16,25 +16,154 @@ mod dev;
mod op;
mod op_sepolia;
use std::fmt::Display;
use alloy_chains::Chain;
use alloy_genesis::Genesis;
use alloy_primitives::{Parity, Signature, B256, U256};
pub use base::BASE_MAINNET;
pub use base_sepolia::BASE_SEPOLIA;
use derive_more::{Constructor, Deref, From, Into};
pub use dev::OP_DEV;
use once_cell::sync::OnceCell;
pub use op::OP_MAINNET;
pub use op_sepolia::OP_SEPOLIA;
use derive_more::{Constructor, Deref, Into};
use once_cell::sync::OnceCell;
use reth_chainspec::{
BaseFeeParams, BaseFeeParamsKind, ChainSpec, DepositContract, EthChainSpec, EthereumHardforks,
ForkFilter, ForkId, Hardforks, Head,
BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, DepositContract, EthChainSpec,
EthereumHardforks, ForkFilter, ForkId, Hardforks, Head,
};
use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition};
use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition, Hardfork};
use reth_network_peers::NodeRecord;
use reth_primitives_traits::Header;
use std::fmt::Display;
/// Chain spec builder for a OP stack chain.
#[derive(Debug, Default, From)]
pub struct OpChainSpecBuilder {
/// [`ChainSpecBuilder`]
inner: ChainSpecBuilder,
}
impl OpChainSpecBuilder {
/// Construct a new builder from the base mainnet chain spec.
pub fn base_mainnet() -> Self {
let mut inner = ChainSpecBuilder::default()
.chain(BASE_MAINNET.chain)
.genesis(BASE_MAINNET.genesis.clone());
let forks = BASE_MAINNET.hardforks.clone();
inner = inner.with_forks(forks);
Self { inner }
}
/// Construct a new builder from the optimism mainnet chain spec.
pub fn optimism_mainnet() -> Self {
let mut inner =
ChainSpecBuilder::default().chain(OP_MAINNET.chain).genesis(OP_MAINNET.genesis.clone());
let forks = OP_MAINNET.hardforks.clone();
inner = inner.with_forks(forks);
Self { inner }
}
}
impl OpChainSpecBuilder {
/// Set the chain ID
pub fn chain(mut self, chain: Chain) -> Self {
self.inner = self.inner.chain(chain);
self
}
/// Set the genesis block.
pub fn genesis(mut self, genesis: Genesis) -> Self {
self.inner = self.inner.genesis(genesis);
self
}
/// Add the given fork with the given activation condition to the spec.
pub fn with_fork<H: Hardfork>(mut self, fork: H, condition: ForkCondition) -> Self {
self.inner = self.inner.with_fork(fork, condition);
self
}
/// Add the given forks with the given activation condition to the spec.
pub fn with_forks(mut self, forks: ChainHardforks) -> Self {
self.inner = self.inner.with_forks(forks);
self
}
/// Remove the given fork from the spec.
pub fn without_fork(mut self, fork: reth_optimism_forks::OptimismHardfork) -> Self {
self.inner = self.inner.without_fork(fork);
self
}
/// Enable Bedrock at genesis
pub fn bedrock_activated(mut self) -> Self {
self.inner = self.inner.paris_activated();
self.inner = self
.inner
.with_fork(reth_optimism_forks::OptimismHardfork::Bedrock, ForkCondition::Block(0));
self
}
/// Enable Regolith at genesis
pub fn regolith_activated(mut self) -> Self {
self = self.bedrock_activated();
self.inner = self.inner.with_fork(
reth_optimism_forks::OptimismHardfork::Regolith,
ForkCondition::Timestamp(0),
);
self
}
/// Enable Canyon at genesis
pub fn canyon_activated(mut self) -> Self {
self = self.regolith_activated();
// Canyon also activates changes from L1's Shanghai hardfork
self.inner = self.inner.with_fork(EthereumHardfork::Shanghai, ForkCondition::Timestamp(0));
self.inner = self
.inner
.with_fork(reth_optimism_forks::OptimismHardfork::Canyon, ForkCondition::Timestamp(0));
self
}
/// Enable Ecotone at genesis
pub fn ecotone_activated(mut self) -> Self {
self = self.canyon_activated();
self.inner = self.inner.with_fork(EthereumHardfork::Cancun, ForkCondition::Timestamp(0));
self.inner = self
.inner
.with_fork(reth_optimism_forks::OptimismHardfork::Ecotone, ForkCondition::Timestamp(0));
self
}
/// Enable Fjord at genesis
pub fn fjord_activated(mut self) -> Self {
self = self.ecotone_activated();
self.inner = self
.inner
.with_fork(reth_optimism_forks::OptimismHardfork::Fjord, ForkCondition::Timestamp(0));
self
}
/// Enable Granite at genesis
pub fn granite_activated(mut self) -> Self {
self = self.fjord_activated();
self.inner = self
.inner
.with_fork(reth_optimism_forks::OptimismHardfork::Granite, ForkCondition::Timestamp(0));
self
}
/// Build the resulting [`OpChainSpec`].
///
/// # Panics
///
/// This function panics if the chain ID and genesis is not set ([`Self::chain`] and
/// [`Self::genesis`])
pub fn build(self) -> OpChainSpec {
OpChainSpec { inner: self.inner.build() }
}
}
/// OP stack chain spec type.
#[derive(Debug, Clone, Deref, Into, Constructor, PartialEq, Eq)]
@ -286,6 +415,8 @@ mod tests {
#[test]
fn base_mainnet_forkids() {
let base_mainnet = OpChainSpecBuilder::base_mainnet().build();
let _ = base_mainnet.genesis_hash.set(BASE_MAINNET.genesis_hash.get().copied().unwrap());
test_fork_ids(
&BASE_MAINNET,
&[
@ -372,8 +503,12 @@ mod tests {
#[test]
fn op_mainnet_forkids() {
let op_mainnet = OpChainSpecBuilder::optimism_mainnet().build();
// for OP mainnet we have to do this because the genesis header can't be properly computed
// from the genesis.json file
let _ = op_mainnet.genesis_hash.set(OP_MAINNET.genesis_hash());
test_fork_ids(
&OP_MAINNET,
&op_mainnet,
&[
(
Head { number: 0, ..Default::default() },
@ -483,9 +618,19 @@ mod tests {
)
}
#[test]
fn latest_base_mainnet_fork_id_with_builder() {
let base_mainnet = OpChainSpecBuilder::base_mainnet().build();
assert_eq!(
ForkId { hash: ForkHash([0xbc, 0x38, 0xf9, 0xca]), next: 0 },
base_mainnet.latest_fork_id()
)
}
#[test]
fn is_bedrock_active() {
assert!(!OP_MAINNET.is_bedrock_active_at_block(1))
let op_mainnet = OpChainSpecBuilder::optimism_mainnet().build();
assert!(!op_mainnet.is_bedrock_active_at_block(1))
}
#[test]

View File

@ -1,5 +1,5 @@
use reth_chainspec::ChainSpec;
use reth_ethereum_forks::{EthereumHardfork, Head};
use reth_optimism_chainspec::OpChainSpec;
use reth_optimism_forks::OptimismHardfork;
/// Returns the revm [`SpecId`](revm_primitives::SpecId) at the given timestamp.
@ -9,7 +9,7 @@ use reth_optimism_forks::OptimismHardfork;
/// 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: &ChainSpec,
chain_spec: &OpChainSpec,
timestamp: u64,
) -> revm_primitives::SpecId {
if chain_spec.fork(OptimismHardfork::Granite).active_at_timestamp(timestamp) {
@ -28,7 +28,7 @@ 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: &ChainSpec, block: &Head) -> 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) {
revm_primitives::GRANITE
} else if chain_spec.fork(OptimismHardfork::Fjord).active_at_head(block) {
@ -79,12 +79,13 @@ pub fn revm_spec(chain_spec: &ChainSpec, block: &Head) -> revm_primitives::SpecI
mod tests {
use super::*;
use reth_chainspec::ChainSpecBuilder;
use reth_optimism_chainspec::{OpChainSpec, OpChainSpecBuilder};
#[test]
fn test_revm_spec_by_timestamp_after_merge() {
#[inline(always)]
fn op_cs(f: impl FnOnce(ChainSpecBuilder) -> ChainSpecBuilder) -> ChainSpec {
let cs = ChainSpecBuilder::mainnet().chain(reth_chainspec::Chain::from_id(10));
fn op_cs(f: impl FnOnce(OpChainSpecBuilder) -> OpChainSpecBuilder) -> OpChainSpec {
let cs = ChainSpecBuilder::mainnet().chain(reth_chainspec::Chain::from_id(10)).into();
f(cs).build()
}
assert_eq!(
@ -116,8 +117,8 @@ mod tests {
#[test]
fn test_to_revm_spec() {
#[inline(always)]
fn op_cs(f: impl FnOnce(ChainSpecBuilder) -> ChainSpecBuilder) -> ChainSpec {
let cs = ChainSpecBuilder::mainnet().chain(reth_chainspec::Chain::from_id(10));
fn op_cs(f: impl FnOnce(OpChainSpecBuilder) -> OpChainSpecBuilder) -> OpChainSpec {
let cs = ChainSpecBuilder::mainnet().chain(reth_chainspec::Chain::from_id(10)).into();
f(cs).build()
}
assert_eq!(

View File

@ -513,8 +513,8 @@ mod tests {
use crate::OpChainSpec;
use alloy_consensus::TxEip1559;
use alloy_primitives::{b256, Address, StorageKey, StorageValue};
use reth_chainspec::{ChainSpecBuilder, MIN_TRANSACTION_GAS};
use reth_optimism_chainspec::{optimism_deposit_tx_signature, BASE_MAINNET};
use reth_chainspec::MIN_TRANSACTION_GAS;
use reth_optimism_chainspec::{optimism_deposit_tx_signature, OpChainSpecBuilder};
use reth_primitives::{Account, Block, BlockBody, Signature, Transaction, TransactionSigned};
use reth_revm::{
database::StateProviderDatabase, test_utils::StateProviderTest, L1_BLOCK_CONTRACT,
@ -548,8 +548,7 @@ mod tests {
db
}
fn executor_provider(chain_spec: Arc<ChainSpec>) -> OpExecutorProvider<OptimismEvmConfig> {
let chain_spec = Arc::new(OpChainSpec::new(Arc::unwrap_or_clone(chain_spec)));
fn executor_provider(chain_spec: Arc<OpChainSpec>) -> OpExecutorProvider<OptimismEvmConfig> {
OpExecutorProvider { evm_config: OptimismEvmConfig::new(chain_spec.clone()), chain_spec }
}
@ -572,11 +571,7 @@ mod tests {
let account = Account { balance: U256::MAX, ..Account::default() };
db.insert_account(addr, account, None, HashMap::default());
let chain_spec = Arc::new(
ChainSpecBuilder::from(&Arc::new(BASE_MAINNET.inner.clone()))
.regolith_activated()
.build(),
);
let chain_spec = Arc::new(OpChainSpecBuilder::base_mainnet().regolith_activated().build());
let tx = TransactionSigned::from_transaction_and_signature(
Transaction::Eip1559(TxEip1559 {
@ -656,11 +651,7 @@ mod tests {
db.insert_account(addr, account, None, HashMap::default());
let chain_spec = Arc::new(
ChainSpecBuilder::from(&Arc::new(BASE_MAINNET.inner.clone()))
.canyon_activated()
.build(),
);
let chain_spec = Arc::new(OpChainSpecBuilder::base_mainnet().canyon_activated().build());
let tx = TransactionSigned::from_transaction_and_signature(
Transaction::Eip1559(TxEip1559 {

View File

@ -1,15 +1,13 @@
use std::sync::Arc;
use alloy_genesis::Genesis;
use alloy_primitives::{Address, B256};
use reth::{rpc::types::engine::PayloadAttributes, tasks::TaskManager};
use reth_chainspec::ChainSpecBuilder;
use reth_e2e_test_utils::{transaction::TransactionTestContext, wallet::Wallet, NodeHelperType};
use reth_optimism_chainspec::{OpChainSpec, BASE_MAINNET};
use reth_optimism_chainspec::OpChainSpecBuilder;
use reth_optimism_node::{
node::OptimismAddOns, OptimismBuiltPayload, OptimismNode, OptimismPayloadBuilderAttributes,
};
use reth_payload_builder::EthPayloadBuilderAttributes;
use std::sync::Arc;
use tokio::sync::Mutex;
/// Optimism Node Helper type
@ -19,13 +17,7 @@ pub(crate) async fn setup(num_nodes: usize) -> eyre::Result<(Vec<OpNode>, TaskMa
let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json")).unwrap();
reth_e2e_test_utils::setup(
num_nodes,
Arc::new(OpChainSpec::new(
ChainSpecBuilder::default()
.chain(BASE_MAINNET.chain)
.genesis(genesis)
.ecotone_activated()
.build(),
)),
Arc::new(OpChainSpecBuilder::base_mainnet().genesis(genesis).ecotone_activated().build()),
false,
)
.await