Skip to content

Commit

Permalink
Optimism Hardfork Support (#8749)
Browse files Browse the repository at this point in the history
optimism hardfork

Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
  • Loading branch information
0xForerunner and klkvr authored Sep 1, 2024
1 parent 818eeb9 commit d75318c
Show file tree
Hide file tree
Showing 12 changed files with 208 additions and 86 deletions.
50 changes: 40 additions & 10 deletions crates/anvil/src/cmd.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use crate::{
config::{ForkChoice, DEFAULT_MNEMONIC},
eth::{backend::db::SerializableState, pool::transactions::TransactionOrder, EthApi},
AccountGenerator, Hardfork, NodeConfig, CHAIN_ID,
hardfork::OptimismHardfork,
AccountGenerator, EthereumHardfork, NodeConfig, CHAIN_ID,
};
use alloy_genesis::Genesis;
use alloy_primitives::{utils::Unit, B256, U256};
Expand Down Expand Up @@ -79,8 +80,8 @@ pub struct NodeArgs {
///
/// Choose the hardfork by name, e.g. `shanghai`, `paris`, `london`, etc...
/// [default: latest]
#[arg(long, value_parser = Hardfork::from_str)]
pub hardfork: Option<Hardfork>,
#[arg(long)]
pub hardfork: Option<String>,

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

impl NodeArgs {
pub fn into_node_config(self) -> NodeConfig {
pub fn into_node_config(self) -> eyre::Result<NodeConfig> {
let genesis_balance = Unit::ETHER.wei().saturating_mul(U256::from(self.balance));
let compute_units_per_second = if self.evm_opts.no_rate_limit {
Some(u64::MAX)
} else {
self.evm_opts.compute_units_per_second
};

NodeConfig::default()
let hardfork = match &self.hardfork {
Some(hf) => {
if self.evm_opts.optimism {
Some(OptimismHardfork::from_str(hf)?.into())
} else {
Some(EthereumHardfork::from_str(hf)?.into())
}
}
None => None,
};

Ok(NodeConfig::default()
.with_gas_limit(self.evm_opts.gas_limit)
.disable_block_gas_limit(self.evm_opts.disable_block_gas_limit)
.with_gas_price(self.evm_opts.gas_price)
.with_hardfork(self.hardfork)
.with_hardfork(hardfork)
.with_blocktime(self.block_time)
.with_no_mining(self.no_mining)
.with_mixed_mining(self.mixed_mining, self.block_time)
Expand Down Expand Up @@ -255,7 +267,7 @@ impl NodeArgs {
.with_alphanet(self.evm_opts.alphanet)
.with_disable_default_create2_deployer(self.evm_opts.disable_default_create2_deployer)
.with_slots_in_an_epoch(self.slots_in_an_epoch)
.with_memory_limit(self.evm_opts.memory_limit)
.with_memory_limit(self.evm_opts.memory_limit))
}

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

let (api, mut handle) = crate::try_spawn(self.into_node_config()).await?;
let (api, mut handle) = crate::try_spawn(self.into_node_config()?).await?;

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

#[cfg(test)]
mod tests {
use crate::EthereumHardfork;

use super::*;
use std::{env, net::Ipv4Addr};

Expand Down Expand Up @@ -773,9 +787,25 @@ mod tests {
}

#[test]
fn can_parse_hardfork() {
fn can_parse_ethereum_hardfork() {
let args: NodeArgs = NodeArgs::parse_from(["anvil", "--hardfork", "berlin"]);
assert_eq!(args.hardfork, Some(Hardfork::Berlin));
let config = args.into_node_config().unwrap();
assert_eq!(config.hardfork, Some(EthereumHardfork::Berlin.into()));
}

#[test]
fn can_parse_optimism_hardfork() {
let args: NodeArgs =
NodeArgs::parse_from(["anvil", "--optimism", "--hardfork", "Regolith"]);
let config = args.into_node_config().unwrap();
assert_eq!(config.hardfork, Some(OptimismHardfork::Regolith.into()));
}

#[test]
fn cant_parse_invalid_hardfork() {
let args: NodeArgs = NodeArgs::parse_from(["anvil", "--hardfork", "Regolith"]);
let config = args.into_node_config();
assert!(config.is_err());
}

#[test]
Expand Down
24 changes: 15 additions & 9 deletions crates/anvil/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ use crate::{
fees::{INITIAL_BASE_FEE, INITIAL_GAS_PRICE},
pool::transactions::{PoolTransaction, TransactionOrder},
},
hardfork::{ChainHardfork, OptimismHardfork},
mem::{self, in_memory_db::MemDb},
FeeManager, Hardfork, PrecompileFactory,
EthereumHardfork, FeeManager, PrecompileFactory,
};
use alloy_genesis::Genesis;
use alloy_network::AnyNetwork;
Expand Down Expand Up @@ -64,7 +65,6 @@ pub const DEFAULT_MNEMONIC: &str = "test test test test test test test test test
/// The default IPC endpoint
pub const DEFAULT_IPC_ENDPOINT: &str =
if cfg!(unix) { "/tmp/anvil.ipc" } else { r"\\.\pipe\anvil.ipc" };

/// `anvil 0.1.0 (f01b232bc 2022-04-13T23:28:39.493201+00:00)`
pub const VERSION_MESSAGE: &str = concat!(
env!("CARGO_PKG_VERSION"),
Expand Down Expand Up @@ -100,7 +100,7 @@ pub struct NodeConfig {
/// Default blob excess gas and price
pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
/// The hardfork to use
pub hardfork: Option<Hardfork>,
pub hardfork: Option<ChainHardfork>,
/// Signer accounts that will be initialised with `genesis_balance` in the genesis block
pub genesis_accounts: Vec<PrivateKeySigner>,
/// Native token balance of every genesis account in the genesis block
Expand Down Expand Up @@ -475,11 +475,17 @@ impl NodeConfig {
}

/// Returns the hardfork to use
pub fn get_hardfork(&self) -> Hardfork {
pub fn get_hardfork(&self) -> ChainHardfork {
if self.alphanet {
return Hardfork::PragueEOF;
return ChainHardfork::Ethereum(EthereumHardfork::PragueEOF);
}
if let Some(hardfork) = self.hardfork {
return hardfork;
}
if self.enable_optimism {
return OptimismHardfork::default().into();
}
self.hardfork.unwrap_or_default()
EthereumHardfork::default().into()
}

/// Sets a custom code size limit
Expand Down Expand Up @@ -621,7 +627,7 @@ impl NodeConfig {

/// Sets the hardfork
#[must_use]
pub fn with_hardfork(mut self, hardfork: Option<Hardfork>) -> Self {
pub fn with_hardfork(mut self, hardfork: Option<ChainHardfork>) -> Self {
self.hardfork = hardfork;
self
}
Expand Down Expand Up @@ -1094,9 +1100,9 @@ impl NodeConfig {
let chain_id =
provider.get_chain_id().await.expect("Failed to fetch network chain ID");
if alloy_chains::NamedChain::Mainnet == chain_id {
let hardfork: Hardfork = fork_block_number.into();
let hardfork: EthereumHardfork = fork_block_number.into();
env.handler_cfg.spec_id = hardfork.into();
self.hardfork = Some(hardfork);
self.hardfork = Some(ChainHardfork::Ethereum(hardfork));
}
Some(U256::from(chain_id))
} else {
Expand Down
141 changes: 107 additions & 34 deletions crates/anvil/src/hardfork.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
use alloy_rpc_types::BlockNumberOrTag;
use eyre::bail;
use foundry_evm::revm::primitives::SpecId;
use std::str::FromStr;

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ChainHardfork {
Ethereum(EthereumHardfork),
Optimism(OptimismHardfork),
}

impl From<EthereumHardfork> for ChainHardfork {
fn from(value: EthereumHardfork) -> Self {
Self::Ethereum(value)
}
}

impl From<OptimismHardfork> for ChainHardfork {
fn from(value: OptimismHardfork) -> Self {
Self::Optimism(value)
}
}

impl From<ChainHardfork> for SpecId {
fn from(fork: ChainHardfork) -> Self {
match fork {
ChainHardfork::Ethereum(hardfork) => hardfork.into(),
ChainHardfork::Optimism(hardfork) => hardfork.into(),
}
}
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Hardfork {
pub enum EthereumHardfork {
Frontier,
Homestead,
Dao,
Expand All @@ -27,7 +55,7 @@ pub enum Hardfork {
Latest,
}

impl Hardfork {
impl EthereumHardfork {
/// Get the first block number of the hardfork.
pub fn fork_block(&self) -> u64 {
match *self {
Expand All @@ -53,8 +81,8 @@ impl Hardfork {
}
}

impl FromStr for Hardfork {
type Err = String;
impl FromStr for EthereumHardfork {
type Err = eyre::Report;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
Expand All @@ -79,40 +107,40 @@ impl FromStr for Hardfork {
"prague" | "18" => Self::Prague,
"pragueeof" | "19" | "prague-eof" => Self::PragueEOF,
"latest" => Self::Latest,
_ => return Err(format!("Unknown hardfork {s}")),
_ => bail!("Unknown hardfork {s}"),
};
Ok(hardfork)
}
}

impl From<Hardfork> for SpecId {
fn from(fork: Hardfork) -> Self {
impl From<EthereumHardfork> for SpecId {
fn from(fork: EthereumHardfork) -> Self {
match fork {
Hardfork::Frontier => Self::FRONTIER,
Hardfork::Homestead => Self::HOMESTEAD,
Hardfork::Dao => Self::HOMESTEAD,
Hardfork::Tangerine => Self::TANGERINE,
Hardfork::SpuriousDragon => Self::SPURIOUS_DRAGON,
Hardfork::Byzantium => Self::BYZANTIUM,
Hardfork::Constantinople => Self::CONSTANTINOPLE,
Hardfork::Petersburg => Self::PETERSBURG,
Hardfork::Istanbul => Self::ISTANBUL,
Hardfork::Muirglacier => Self::MUIR_GLACIER,
Hardfork::Berlin => Self::BERLIN,
Hardfork::London => Self::LONDON,
Hardfork::ArrowGlacier => Self::LONDON,
Hardfork::GrayGlacier => Self::GRAY_GLACIER,
Hardfork::Paris => Self::MERGE,
Hardfork::Shanghai => Self::SHANGHAI,
Hardfork::Cancun | Hardfork::Latest => Self::CANCUN,
Hardfork::Prague => Self::PRAGUE,
EthereumHardfork::Frontier => Self::FRONTIER,
EthereumHardfork::Homestead => Self::HOMESTEAD,
EthereumHardfork::Dao => Self::HOMESTEAD,
EthereumHardfork::Tangerine => Self::TANGERINE,
EthereumHardfork::SpuriousDragon => Self::SPURIOUS_DRAGON,
EthereumHardfork::Byzantium => Self::BYZANTIUM,
EthereumHardfork::Constantinople => Self::CONSTANTINOPLE,
EthereumHardfork::Petersburg => Self::PETERSBURG,
EthereumHardfork::Istanbul => Self::ISTANBUL,
EthereumHardfork::Muirglacier => Self::MUIR_GLACIER,
EthereumHardfork::Berlin => Self::BERLIN,
EthereumHardfork::London => Self::LONDON,
EthereumHardfork::ArrowGlacier => Self::LONDON,
EthereumHardfork::GrayGlacier => Self::GRAY_GLACIER,
EthereumHardfork::Paris => Self::MERGE,
EthereumHardfork::Shanghai => Self::SHANGHAI,
EthereumHardfork::Cancun | EthereumHardfork::Latest => Self::CANCUN,
EthereumHardfork::Prague => Self::PRAGUE,
// TODO: switch to latest after activation
Hardfork::PragueEOF => Self::PRAGUE_EOF,
EthereumHardfork::PragueEOF => Self::PRAGUE_EOF,
}
}
}

impl<T: Into<BlockNumberOrTag>> From<T> for Hardfork {
impl<T: Into<BlockNumberOrTag>> From<T> for EthereumHardfork {
fn from(block: T) -> Self {
let num = match block.into() {
BlockNumberOrTag::Earliest => 0,
Expand Down Expand Up @@ -140,19 +168,64 @@ impl<T: Into<BlockNumberOrTag>> From<T> for Hardfork {
}
}

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum OptimismHardfork {
Bedrock,
Regolith,
Canyon,
Ecotone,
Fjord,
Granite,
#[default]
Latest,
}

impl FromStr for OptimismHardfork {
type Err = eyre::Report;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
let hardfork = match s.as_str() {
"bedrock" => Self::Bedrock,
"regolith" => Self::Regolith,
"canyon" => Self::Canyon,
"ecotone" => Self::Ecotone,
"fjord" => Self::Fjord,
"granite" => Self::Granite,
"latest" => Self::Latest,
_ => bail!("Unknown hardfork {s}"),
};
Ok(hardfork)
}
}

impl From<OptimismHardfork> for SpecId {
fn from(fork: OptimismHardfork) -> Self {
match fork {
OptimismHardfork::Bedrock => Self::BEDROCK,
OptimismHardfork::Regolith => Self::REGOLITH,
OptimismHardfork::Canyon => Self::CANYON,
OptimismHardfork::Ecotone => Self::ECOTONE,
OptimismHardfork::Fjord => Self::FJORD,
OptimismHardfork::Granite => Self::GRANITE,
OptimismHardfork::Latest => Self::LATEST,
}
}
}

#[cfg(test)]
mod tests {
use crate::Hardfork;
use crate::EthereumHardfork;

#[test]
fn test_hardfork_blocks() {
let hf: Hardfork = 12_965_000u64.into();
assert_eq!(hf, Hardfork::London);
let hf: EthereumHardfork = 12_965_000u64.into();
assert_eq!(hf, EthereumHardfork::London);

let hf: Hardfork = 4370000u64.into();
assert_eq!(hf, Hardfork::Byzantium);
let hf: EthereumHardfork = 4370000u64.into();
assert_eq!(hf, EthereumHardfork::Byzantium);

let hf: Hardfork = 12244000u64.into();
assert_eq!(hf, Hardfork::Berlin);
let hf: EthereumHardfork = 12244000u64.into();
assert_eq!(hf, EthereumHardfork::Berlin);
}
}
2 changes: 1 addition & 1 deletion crates/anvil/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ mod config;
pub use config::{AccountGenerator, ForkChoice, NodeConfig, CHAIN_ID, VERSION_MESSAGE};

mod hardfork;
pub use hardfork::Hardfork;
pub use hardfork::EthereumHardfork;

/// ethereum related implementations
pub mod eth;
Expand Down
Loading

0 comments on commit d75318c

Please sign in to comment.