Skip to content

Commit d75318c

Browse files
0xForerunnerklkvr
andauthored
Optimism Hardfork Support (#8749)
optimism hardfork Co-authored-by: Arsenii Kulikov <[email protected]>
1 parent 818eeb9 commit d75318c

File tree

12 files changed

+208
-86
lines changed

12 files changed

+208
-86
lines changed

crates/anvil/src/cmd.rs

Lines changed: 40 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use crate::{
22
config::{ForkChoice, DEFAULT_MNEMONIC},
33
eth::{backend::db::SerializableState, pool::transactions::TransactionOrder, EthApi},
4-
AccountGenerator, Hardfork, NodeConfig, CHAIN_ID,
4+
hardfork::OptimismHardfork,
5+
AccountGenerator, EthereumHardfork, NodeConfig, CHAIN_ID,
56
};
67
use alloy_genesis::Genesis;
78
use alloy_primitives::{utils::Unit, B256, U256};
@@ -79,8 +80,8 @@ pub struct NodeArgs {
7980
///
8081
/// Choose the hardfork by name, e.g. `shanghai`, `paris`, `london`, etc...
8182
/// [default: latest]
82-
#[arg(long, value_parser = Hardfork::from_str)]
83-
pub hardfork: Option<Hardfork>,
83+
#[arg(long)]
84+
pub hardfork: Option<String>,
8485

8586
/// Block time in seconds for interval mining.
8687
#[arg(short, long, visible_alias = "blockTime", value_name = "SECONDS", value_parser = duration_from_secs_f64)]
@@ -196,19 +197,30 @@ const IPC_HELP: &str = "Launch an ipc server at the given path or default path =
196197
const DEFAULT_DUMP_INTERVAL: Duration = Duration::from_secs(60);
197198

198199
impl NodeArgs {
199-
pub fn into_node_config(self) -> NodeConfig {
200+
pub fn into_node_config(self) -> eyre::Result<NodeConfig> {
200201
let genesis_balance = Unit::ETHER.wei().saturating_mul(U256::from(self.balance));
201202
let compute_units_per_second = if self.evm_opts.no_rate_limit {
202203
Some(u64::MAX)
203204
} else {
204205
self.evm_opts.compute_units_per_second
205206
};
206207

207-
NodeConfig::default()
208+
let hardfork = match &self.hardfork {
209+
Some(hf) => {
210+
if self.evm_opts.optimism {
211+
Some(OptimismHardfork::from_str(hf)?.into())
212+
} else {
213+
Some(EthereumHardfork::from_str(hf)?.into())
214+
}
215+
}
216+
None => None,
217+
};
218+
219+
Ok(NodeConfig::default()
208220
.with_gas_limit(self.evm_opts.gas_limit)
209221
.disable_block_gas_limit(self.evm_opts.disable_block_gas_limit)
210222
.with_gas_price(self.evm_opts.gas_price)
211-
.with_hardfork(self.hardfork)
223+
.with_hardfork(hardfork)
212224
.with_blocktime(self.block_time)
213225
.with_no_mining(self.no_mining)
214226
.with_mixed_mining(self.mixed_mining, self.block_time)
@@ -255,7 +267,7 @@ impl NodeArgs {
255267
.with_alphanet(self.evm_opts.alphanet)
256268
.with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer)
257269
.with_slots_in_an_epoch(self.slots_in_an_epoch)
258-
.with_memory_limit(self.evm_opts.memory_limit)
270+
.with_memory_limit(self.evm_opts.memory_limit))
259271
}
260272

261273
fn account_generator(&self) -> AccountGenerator {
@@ -295,7 +307,7 @@ impl NodeArgs {
295307
let dump_interval =
296308
self.state_interval.map(Duration::from_secs).unwrap_or(DEFAULT_DUMP_INTERVAL);
297309

298-
let (api, mut handle) = crate::try_spawn(self.into_node_config()).await?;
310+
let (api, mut handle) = crate::try_spawn(self.into_node_config()?).await?;
299311

300312
// sets the signal handler to gracefully shutdown.
301313
let mut fork = api.get_fork();
@@ -739,6 +751,8 @@ fn duration_from_secs_f64(s: &str) -> Result<Duration, String> {
739751

740752
#[cfg(test)]
741753
mod tests {
754+
use crate::EthereumHardfork;
755+
742756
use super::*;
743757
use std::{env, net::Ipv4Addr};
744758

@@ -773,9 +787,25 @@ mod tests {
773787
}
774788

775789
#[test]
776-
fn can_parse_hardfork() {
790+
fn can_parse_ethereum_hardfork() {
777791
let args: NodeArgs = NodeArgs::parse_from(["anvil", "--hardfork", "berlin"]);
778-
assert_eq!(args.hardfork, Some(Hardfork::Berlin));
792+
let config = args.into_node_config().unwrap();
793+
assert_eq!(config.hardfork, Some(EthereumHardfork::Berlin.into()));
794+
}
795+
796+
#[test]
797+
fn can_parse_optimism_hardfork() {
798+
let args: NodeArgs =
799+
NodeArgs::parse_from(["anvil", "--optimism", "--hardfork", "Regolith"]);
800+
let config = args.into_node_config().unwrap();
801+
assert_eq!(config.hardfork, Some(OptimismHardfork::Regolith.into()));
802+
}
803+
804+
#[test]
805+
fn cant_parse_invalid_hardfork() {
806+
let args: NodeArgs = NodeArgs::parse_from(["anvil", "--hardfork", "Regolith"]);
807+
let config = args.into_node_config();
808+
assert!(config.is_err());
779809
}
780810

781811
#[test]

crates/anvil/src/config.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ use crate::{
1111
fees::{INITIAL_BASE_FEE, INITIAL_GAS_PRICE},
1212
pool::transactions::{PoolTransaction, TransactionOrder},
1313
},
14+
hardfork::{ChainHardfork, OptimismHardfork},
1415
mem::{self, in_memory_db::MemDb},
15-
FeeManager, Hardfork, PrecompileFactory,
16+
EthereumHardfork, FeeManager, PrecompileFactory,
1617
};
1718
use alloy_genesis::Genesis;
1819
use alloy_network::AnyNetwork;
@@ -64,7 +65,6 @@ pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test
6465
/// The default IPC endpoint
6566
pub const DEFAULT_IPC_ENDPOINT: &str =
6667
if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" };
67-
6868
/// `anvil 0.1.0 (f01b232bc 2022-04-13T23:28:39.493201+00:00)`
6969
pub const VERSION_MESSAGE: &str = concat!(
7070
env!("CARGO_PKG_VERSION"),
@@ -100,7 +100,7 @@ pub struct NodeConfig {
100100
/// Default blob excess gas and price
101101
pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
102102
/// The hardfork to use
103-
pub hardfork: Option<Hardfork>,
103+
pub hardfork: Option<ChainHardfork>,
104104
/// Signer accounts that will be initialised with `genesis_balance` in the genesis block
105105
pub genesis_accounts: Vec<PrivateKeySigner>,
106106
/// Native token balance of every genesis account in the genesis block
@@ -475,11 +475,17 @@ impl NodeConfig {
475475
}
476476

477477
/// Returns the hardfork to use
478-
pub fn get_hardfork(&self) -> Hardfork {
478+
pub fn get_hardfork(&self) -> ChainHardfork {
479479
if self.alphanet {
480-
return Hardfork::PragueEOF;
480+
return ChainHardfork::Ethereum(EthereumHardfork::PragueEOF);
481+
}
482+
if let Some(hardfork) = self.hardfork {
483+
return hardfork;
484+
}
485+
if self.enable_optimism {
486+
return OptimismHardfork::default().into();
481487
}
482-
self.hardfork.unwrap_or_default()
488+
EthereumHardfork::default().into()
483489
}
484490

485491
/// Sets a custom code size limit
@@ -621,7 +627,7 @@ impl NodeConfig {
621627

622628
/// Sets the hardfork
623629
#[must_use]
624-
pub fn with_hardfork(mut self, hardfork: Option<Hardfork>) -> Self {
630+
pub fn with_hardfork(mut self, hardfork: Option<ChainHardfork>) -> Self {
625631
self.hardfork = hardfork;
626632
self
627633
}
@@ -1094,9 +1100,9 @@ impl NodeConfig {
10941100
let chain_id =
10951101
provider.get_chain_id().await.expect("Failed to fetch network chain ID");
10961102
if alloy_chains::NamedChain::Mainnet == chain_id {
1097-
let hardfork: Hardfork = fork_block_number.into();
1103+
let hardfork: EthereumHardfork = fork_block_number.into();
10981104
env.handler_cfg.spec_id = hardfork.into();
1099-
self.hardfork = Some(hardfork);
1105+
self.hardfork = Some(ChainHardfork::Ethereum(hardfork));
11001106
}
11011107
Some(U256::from(chain_id))
11021108
} else {

crates/anvil/src/hardfork.rs

Lines changed: 107 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
11
use alloy_rpc_types::BlockNumberOrTag;
2+
use eyre::bail;
23
use foundry_evm::revm::primitives::SpecId;
34
use std::str::FromStr;
45

6+
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
7+
pub enum ChainHardfork {
8+
Ethereum(EthereumHardfork),
9+
Optimism(OptimismHardfork),
10+
}
11+
12+
impl From<EthereumHardfork> for ChainHardfork {
13+
fn from(value: EthereumHardfork) -> Self {
14+
Self::Ethereum(value)
15+
}
16+
}
17+
18+
impl From<OptimismHardfork> for ChainHardfork {
19+
fn from(value: OptimismHardfork) -> Self {
20+
Self::Optimism(value)
21+
}
22+
}
23+
24+
impl From<ChainHardfork> for SpecId {
25+
fn from(fork: ChainHardfork) -> Self {
26+
match fork {
27+
ChainHardfork::Ethereum(hardfork) => hardfork.into(),
28+
ChainHardfork::Optimism(hardfork) => hardfork.into(),
29+
}
30+
}
31+
}
32+
533
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
6-
pub enum Hardfork {
34+
pub enum EthereumHardfork {
735
Frontier,
836
Homestead,
937
Dao,
@@ -27,7 +55,7 @@ pub enum Hardfork {
2755
Latest,
2856
}
2957

30-
impl Hardfork {
58+
impl EthereumHardfork {
3159
/// Get the first block number of the hardfork.
3260
pub fn fork_block(&self) -> u64 {
3361
match *self {
@@ -53,8 +81,8 @@ impl Hardfork {
5381
}
5482
}
5583

56-
impl FromStr for Hardfork {
57-
type Err = String;
84+
impl FromStr for EthereumHardfork {
85+
type Err = eyre::Report;
5886

5987
fn from_str(s: &str) -> Result<Self, Self::Err> {
6088
let s = s.to_lowercase();
@@ -79,40 +107,40 @@ impl FromStr for Hardfork {
79107
"prague" | "18" => Self::Prague,
80108
"pragueeof" | "19" | "prague-eof" => Self::PragueEOF,
81109
"latest" => Self::Latest,
82-
_ => return Err(format!("Unknown hardfork {s}")),
110+
_ => bail!("Unknown hardfork {s}"),
83111
};
84112
Ok(hardfork)
85113
}
86114
}
87115

88-
impl From<Hardfork> for SpecId {
89-
fn from(fork: Hardfork) -> Self {
116+
impl From<EthereumHardfork> for SpecId {
117+
fn from(fork: EthereumHardfork) -> Self {
90118
match fork {
91-
Hardfork::Frontier => Self::FRONTIER,
92-
Hardfork::Homestead => Self::HOMESTEAD,
93-
Hardfork::Dao => Self::HOMESTEAD,
94-
Hardfork::Tangerine => Self::TANGERINE,
95-
Hardfork::SpuriousDragon => Self::SPURIOUS_DRAGON,
96-
Hardfork::Byzantium => Self::BYZANTIUM,
97-
Hardfork::Constantinople => Self::CONSTANTINOPLE,
98-
Hardfork::Petersburg => Self::PETERSBURG,
99-
Hardfork::Istanbul => Self::ISTANBUL,
100-
Hardfork::Muirglacier => Self::MUIR_GLACIER,
101-
Hardfork::Berlin => Self::BERLIN,
102-
Hardfork::London => Self::LONDON,
103-
Hardfork::ArrowGlacier => Self::LONDON,
104-
Hardfork::GrayGlacier => Self::GRAY_GLACIER,
105-
Hardfork::Paris => Self::MERGE,
106-
Hardfork::Shanghai => Self::SHANGHAI,
107-
Hardfork::Cancun | Hardfork::Latest => Self::CANCUN,
108-
Hardfork::Prague => Self::PRAGUE,
119+
EthereumHardfork::Frontier => Self::FRONTIER,
120+
EthereumHardfork::Homestead => Self::HOMESTEAD,
121+
EthereumHardfork::Dao => Self::HOMESTEAD,
122+
EthereumHardfork::Tangerine => Self::TANGERINE,
123+
EthereumHardfork::SpuriousDragon => Self::SPURIOUS_DRAGON,
124+
EthereumHardfork::Byzantium => Self::BYZANTIUM,
125+
EthereumHardfork::Constantinople => Self::CONSTANTINOPLE,
126+
EthereumHardfork::Petersburg => Self::PETERSBURG,
127+
EthereumHardfork::Istanbul => Self::ISTANBUL,
128+
EthereumHardfork::Muirglacier => Self::MUIR_GLACIER,
129+
EthereumHardfork::Berlin => Self::BERLIN,
130+
EthereumHardfork::London => Self::LONDON,
131+
EthereumHardfork::ArrowGlacier => Self::LONDON,
132+
EthereumHardfork::GrayGlacier => Self::GRAY_GLACIER,
133+
EthereumHardfork::Paris => Self::MERGE,
134+
EthereumHardfork::Shanghai => Self::SHANGHAI,
135+
EthereumHardfork::Cancun | EthereumHardfork::Latest => Self::CANCUN,
136+
EthereumHardfork::Prague => Self::PRAGUE,
109137
// TODO: switch to latest after activation
110-
Hardfork::PragueEOF => Self::PRAGUE_EOF,
138+
EthereumHardfork::PragueEOF => Self::PRAGUE_EOF,
111139
}
112140
}
113141
}
114142

115-
impl<T: Into<BlockNumberOrTag>> From<T> for Hardfork {
143+
impl<T: Into<BlockNumberOrTag>> From<T> for EthereumHardfork {
116144
fn from(block: T) -> Self {
117145
let num = match block.into() {
118146
BlockNumberOrTag::Earliest => 0,
@@ -140,19 +168,64 @@ impl<T: Into<BlockNumberOrTag>> From<T> for Hardfork {
140168
}
141169
}
142170

171+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
172+
pub enum OptimismHardfork {
173+
Bedrock,
174+
Regolith,
175+
Canyon,
176+
Ecotone,
177+
Fjord,
178+
Granite,
179+
#[default]
180+
Latest,
181+
}
182+
183+
impl FromStr for OptimismHardfork {
184+
type Err = eyre::Report;
185+
186+
fn from_str(s: &str) -> Result<Self, Self::Err> {
187+
let s = s.to_lowercase();
188+
let hardfork = match s.as_str() {
189+
"bedrock" => Self::Bedrock,
190+
"regolith" => Self::Regolith,
191+
"canyon" => Self::Canyon,
192+
"ecotone" => Self::Ecotone,
193+
"fjord" => Self::Fjord,
194+
"granite" => Self::Granite,
195+
"latest" => Self::Latest,
196+
_ => bail!("Unknown hardfork {s}"),
197+
};
198+
Ok(hardfork)
199+
}
200+
}
201+
202+
impl From<OptimismHardfork> for SpecId {
203+
fn from(fork: OptimismHardfork) -> Self {
204+
match fork {
205+
OptimismHardfork::Bedrock => Self::BEDROCK,
206+
OptimismHardfork::Regolith => Self::REGOLITH,
207+
OptimismHardfork::Canyon => Self::CANYON,
208+
OptimismHardfork::Ecotone => Self::ECOTONE,
209+
OptimismHardfork::Fjord => Self::FJORD,
210+
OptimismHardfork::Granite => Self::GRANITE,
211+
OptimismHardfork::Latest => Self::LATEST,
212+
}
213+
}
214+
}
215+
143216
#[cfg(test)]
144217
mod tests {
145-
use crate::Hardfork;
218+
use crate::EthereumHardfork;
146219

147220
#[test]
148221
fn test_hardfork_blocks() {
149-
let hf: Hardfork = 12_965_000u64.into();
150-
assert_eq!(hf, Hardfork::London);
222+
let hf: EthereumHardfork = 12_965_000u64.into();
223+
assert_eq!(hf, EthereumHardfork::London);
151224

152-
let hf: Hardfork = 4370000u64.into();
153-
assert_eq!(hf, Hardfork::Byzantium);
225+
let hf: EthereumHardfork = 4370000u64.into();
226+
assert_eq!(hf, EthereumHardfork::Byzantium);
154227

155-
let hf: Hardfork = 12244000u64.into();
156-
assert_eq!(hf, Hardfork::Berlin);
228+
let hf: EthereumHardfork = 12244000u64.into();
229+
assert_eq!(hf, EthereumHardfork::Berlin);
157230
}
158231
}

crates/anvil/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ mod config;
4848
pub use config::{AccountGenerator, ForkChoice, NodeConfig, CHAIN_ID, VERSION_MESSAGE};
4949

5050
mod hardfork;
51-
pub use hardfork::Hardfork;
51+
pub use hardfork::EthereumHardfork;
5252

5353
/// ethereum related implementations
5454
pub mod eth;

0 commit comments

Comments
 (0)