diff --git a/crates/interpreter/src/gas/calc.rs b/crates/interpreter/src/gas/calc.rs index 8c454185ca..57d3862392 100644 --- a/crates/interpreter/src/gas/calc.rs +++ b/crates/interpreter/src/gas/calc.rs @@ -421,4 +421,4 @@ pub fn validate_initial_tx_gas( } initial_gas -} \ No newline at end of file +} diff --git a/crates/interpreter/src/instructions/bitwise.rs b/crates/interpreter/src/instructions/bitwise.rs index 8a87ee5ae0..371535b413 100644 --- a/crates/interpreter/src/instructions/bitwise.rs +++ b/crates/interpreter/src/instructions/bitwise.rs @@ -18,6 +18,16 @@ pub fn gt(interpreter: &mut Interpreter, _host: &mut H) { *op2 = U256::from(op1 > *op2); } +/// Implements the CLZ instruction - count leading zeros. +pub fn clz(interpreter: &mut Interpreter, _host: &mut H) { + // check!(interpreter, OSAKA); + // gas!(interpreter, gas::LOW); + pop_top!(interpreter, op1); + + let leading_zeros = op1.leading_zeros(); + *op1 = U256::from(leading_zeros); +} + pub fn slt(interpreter: &mut Interpreter, _host: &mut H) { gas!(interpreter, gas::VERYLOW); pop_top!(interpreter, op1, op2); @@ -123,9 +133,9 @@ pub fn sar(interpreter: &mut Interpreter, _host: & #[cfg(test)] mod tests { - use crate::instructions::bitwise::{byte, sar, shl, shr}; + use crate::instructions::bitwise::{byte, clz, sar, shl, shr}; use crate::{Contract, DummyHost, Interpreter}; - use revm_primitives::{uint, Env, LatestSpec, U256}; + use revm_primitives::{uint, Env, LatestSpec, SpecId, U256}; #[test] fn test_shift_left() { @@ -429,4 +439,56 @@ mod tests { assert_eq!(res, test.expected, "Failed at index: {}", test.index); } } + + #[test] + fn test_clz() { + let mut host = DummyHost::new(Env::default()); + let mut interpreter = Interpreter::default(); + struct TestCase { + value: U256, + expected: U256, + } + + uint! { + let test_cases = [ + TestCase { value: 0x0_U256, expected: 256_U256 }, + TestCase { value: 0x1_U256, expected: 255_U256 }, + TestCase { value: 0x2_U256, expected: 254_U256 }, + TestCase { value: 0x3_U256, expected: 254_U256 }, + TestCase { value: 0x4_U256, expected: 253_U256 }, + TestCase { value: 0x7_U256, expected: 253_U256 }, + TestCase { value: 0x8_U256, expected: 252_U256 }, + TestCase { value: 0xff_U256, expected: 248_U256 }, + TestCase { value: 0x100_U256, expected: 247_U256 }, + TestCase { value: 0xffff_U256, expected: 240_U256 }, + TestCase { + value: 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_U256, // U256::MAX + expected: 0_U256, + }, + TestCase { + value: 0x8000000000000000000000000000000000000000000000000000000000000000_U256, // 1 << 255 + expected: 0_U256, + }, + TestCase { // Smallest value with 1 leading zero + value: 0x4000000000000000000000000000000000000000000000000000000000000000_U256, // 1 << 254 + expected: 1_U256, + }, + TestCase { // Value just below 1 << 255 + value: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_U256, + expected: 1_U256, + }, + ]; + } + + for test in test_cases { + push!(interpreter, test.value); + clz(&mut interpreter, &mut host); + let res = interpreter.stack.pop().unwrap(); + assert_eq!( + res, test.expected, + "CLZ for value {:#x} failed. Expected: {}, Got: {}", + test.value, test.expected, res + ); + } + } } diff --git a/crates/interpreter/src/opcode.rs b/crates/interpreter/src/opcode.rs index b9891712e0..c6a1646f50 100644 --- a/crates/interpreter/src/opcode.rs +++ b/crates/interpreter/src/opcode.rs @@ -462,7 +462,7 @@ opcodes! { 0x1B => SHL => bitwise::shl:: => stack_io(2, 1); 0x1C => SHR => bitwise::shr:: => stack_io(2, 1); 0x1D => SAR => bitwise::sar:: => stack_io(2, 1); - // 0x1E + 0x1E => CLZ => bitwise::clz => stack_io(1, 1); // 0x1F 0x20 => KECCAK256 => system::keccak256 => stack_io(2, 1); // 0x21 @@ -792,8 +792,8 @@ mod tests { eof_opcode_num += 1; } } - assert_eq!(opcode_num, 168); - assert_eq!(eof_opcode_num, 152); + assert_eq!(opcode_num, 169); + assert_eq!(eof_opcode_num, 153); } #[test] diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index b16b0f8f28..a8e6a83e20 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -68,6 +68,8 @@ impl Precompiles { PrecompileSpecId::MORPH203 => Self::morph203(), #[cfg(feature = "morph")] PrecompileSpecId::VIRIDIAN => Self::viridian(), + #[cfg(feature = "morph")] + PrecompileSpecId::EMERALD => Self::emerald(), PrecompileSpecId::CANCUN => Self::cancun(), PrecompileSpecId::PRAGUE => Self::prague(), PrecompileSpecId::LATEST => Self::latest(), @@ -247,6 +249,21 @@ impl Precompiles { }) } + /// Returns precompiles for Morph + #[cfg(feature = "morph")] + pub fn emerald() -> &'static Self { + static INSTANCE: OnceBox = OnceBox::new(); + INSTANCE.get_or_init(|| { + let precompiles = Self::viridian().clone(); + precompiles.extend(bls12_381::precompiles()); // add BLS12-381 precompiles + precompiles.extend([ + modexp::OSAKA, // 0x05 + secp256r1::P256VERIFY_OSAKA, + ]); + Box::new(precompiles) + }) + } + /// Returns the precompiles for the latest spec. pub fn latest() -> &'static Self { Self::prague() @@ -352,6 +369,8 @@ pub enum PrecompileSpecId { MORPH203, #[cfg(feature = "morph")] VIRIDIAN, + #[cfg(feature = "morph")] + EMERALD, CANCUN, PRAGUE, LATEST, @@ -383,6 +402,8 @@ impl PrecompileSpecId { MORPH203 => Self::MORPH203, #[cfg(feature = "morph")] VIRIDIAN => Self::VIRIDIAN, + #[cfg(feature = "morph")] + EMERALD => Self::EMERALD, } } } diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp.rs index 53b3194e8e..0417bacce0 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp.rs @@ -18,12 +18,20 @@ pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( pub const BERLIN: PrecompileWithAddress = PrecompileWithAddress(crate::u64_to_address(5), Precompile::Standard(berlin_run)); +/// `modexp` precompile with OSAKA gas rules. +pub const OSAKA: PrecompileWithAddress = + PrecompileWithAddress(crate::u64_to_address(5), Precompile::Standard(osaka_run)); + #[cfg(feature = "morph")] pub const BERNOULLI: PrecompileWithAddress = PrecompileWithAddress( crate::u64_to_address(5), Precompile::Standard(bernoilli_run), ); +#[cfg(feature = "morph")] +pub const OSAKA: PrecompileWithAddress = + PrecompileWithAddress(crate::u64_to_address(5), Precompile::Standard(osaka_run)); + /// See: /// See: pub fn byzantium_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { @@ -38,6 +46,14 @@ pub fn berlin_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { }) } +/// See: +/// Gas cost of berlin is modified from byzantium. +pub fn osaka_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { + run_inner(input, gas_limit, 500, |a, b, c, d| { + osaka_gas_calc(a, b, c, d) + }) +} + #[cfg(feature = "morph")] pub fn bernoilli_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let base_len = U256::from_be_bytes(right_pad_with_offset::<32>(input, 0).into_owned()); @@ -60,7 +76,31 @@ pub fn bernoilli_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { }) } -pub fn calculate_iteration_count(exp_length: u64, exp_highp: &U256) -> u64 { +#[cfg(feature = "morph")] +/// See: +/// Gas cost of berlin is modified from byzantium. +pub fn osaka_run(input: &[u8], gas_limit: u64) -> PrecompileResult { + let base_len = U256::from_be_bytes(right_pad_with_offset::<32>(input, 0).into_owned()); + let exp_len = U256::from_be_bytes(right_pad_with_offset::<32>(input, 32).into_owned()); + let mod_len = U256::from_be_bytes(right_pad_with_offset::<32>(input, 64).into_owned()); + + // modexp temporarily only accepts inputs of 32 bytes (256 bits) or less + if base_len > MORPH_LEN_LIMIT { + return Err(Error::ModexpBaseOverflow.into()); + } + if exp_len > MORPH_LEN_LIMIT { + return Err(Error::ModexpExpOverflow.into()); + } + if mod_len > MORPH_LEN_LIMIT { + return Err(Error::ModexpModOverflow.into()); + } + run_inner::<_, true>(input, gas_limit, 500, |a, b, c, d| { + osaka_gas_calc(a, b, c, d) + }) +} + +/// Calculate the iteration count for the modexp precompile. +pub fn calculate_iteration_count(exp_length: u64, exp_highp: &U256) -> u64 { let mut iteration_count: u64 = 0; if exp_length <= 32 && exp_highp.is_zero() { @@ -68,7 +108,7 @@ pub fn calculate_iteration_count(exp_length: u64, exp_highp: &U256) -> u64 { } else if exp_length <= 32 { iteration_count = exp_highp.bit_len() as u64 - 1; } else if exp_length > 32 { - iteration_count = (8u64.saturating_mul(exp_length - 32)) + iteration_count = (MULTIPLIER.saturating_mul(exp_length - 32)) .saturating_add(max(1, exp_highp.bit_len() as u64) - 1); } @@ -150,50 +190,65 @@ where )) } +/// Calculate the gas cost for the modexp precompile with BYZANTIUM gas rules. pub fn byzantium_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U256) -> u64 { - // output of this function is bounded by 2^128 - fn mul_complexity(x: u64) -> U256 { - if x <= 64 { - U256::from(x * x) - } else if x <= 1_024 { - U256::from(x * x / 4 + 96 * x - 3_072) + gas_calc::<0, 8, 20, _>(base_len, exp_len, mod_len, exp_highp, |max_len| -> U256 { + // Output of this function is bounded by 2^128 + if max_len <= 64 { + U256::from(max_len * max_len) + } else if max_len <= 1_024 { + U256::from(max_len * max_len / 4 + 96 * max_len - 3_072) } else { - // up-cast to avoid overflow - let x = U256::from(x); + // Up-cast to avoid overflow + let x = U256::from(max_len); let x_sq = x * x; // x < 2^64 => x*x < 2^128 < 2^256 (no overflow) x_sq / U256::from(16) + U256::from(480) * x - U256::from(199_680) } - } - - let mul = mul_complexity(core::cmp::max(mod_len, base_len)); - let iter_count = U256::from(calculate_iteration_count(exp_len, exp_highp)); - // mul * iter_count bounded by 2^195 < 2^256 (no overflow) - let gas = (mul * iter_count) / U256::from(20); - gas.saturating_to() + }) } // Calculate gas cost according to EIP 2565: // https://eips.ethereum.org/EIPS/eip-2565 -pub fn berlin_gas_calc( - base_length: u64, - exp_length: u64, - mod_length: u64, - exp_highp: &U256, -) -> u64 { - fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> U256 { - let max_length = max(base_length, mod_length); - let mut words = max_length / 8; - if max_length % 8 > 0 { - words += 1; - } - let words = U256::from(words); +pub fn berlin_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U256) -> u64 { + gas_calc::<200, 8, 3, _>(base_len, exp_len, mod_len, exp_highp, |max_len| -> U256 { + let words = U256::from(max_len.div_ceil(8)); words * words - } + }) +} + +/// Calculate gas cost according to EIP-7883: +/// +/// +/// There are three changes: +/// 1. Increase minimal price from 200 to 500 +/// 2. Increase cost when exponent is larger than 32 bytes +/// 3. Increase cost when base or modulus is larger than 32 bytes +pub fn osaka_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U256) -> u64 { + gas_calc::<500, 16, 1, _>(base_len, exp_len, mod_len, exp_highp, |max_len| -> U256 { + if max_len <= 32 { + return U256::from(16); // multiplication_complexity = 16 + } + + let words = U256::from(max_len.div_ceil(8)); + words * words * U256::from(2) // multiplication_complexity = 2 * words**2 + }) +} - let multiplication_complexity = calculate_multiplication_complexity(base_length, mod_length); - let iteration_count = calculate_iteration_count(exp_length, exp_highp); - let gas = (multiplication_complexity * U256::from(iteration_count)) / U256::from(3); - max(200, gas.saturating_to()) +/// Calculate gas cost. +pub fn gas_calc( + base_len: u64, + exp_len: u64, + mod_len: u64, + exp_highp: &U256, + calculate_multiplication_complexity: F, +) -> u64 +where + F: Fn(u64) -> U256, +{ + let multiplication_complexity = calculate_multiplication_complexity(max(base_len, mod_len)); + let iteration_count = calculate_iteration_count::(exp_len, exp_highp); + let gas = (multiplication_complexity * U256::from(iteration_count)) / U256::from(GAS_DIVISOR); + max(MIN_PRICE, gas.saturating_to()) } #[cfg(test)] diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 46f00b7405..262a2d0889 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -1,26 +1,48 @@ -//! # EIP-7212 secp256r1 Precompile +//! # RIP-7212 secp256r1 Precompile //! -//! This module implements the [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212) precompile for +//! This module implements the [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md) precompile for //! secp256r1 curve support. //! //! The main purpose of this precompile is to verify ECDSA signatures that use the secp256r1, or //! P256 elliptic curve. The [`P256VERIFY`] const represents the implementation of this precompile, //! with the address that it is currently deployed at. -use crate::{u64_to_address, Precompile, PrecompileWithAddress}; -use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}; -use revm_primitives::{Bytes, PrecompileError, PrecompileOutput, PrecompileResult, B256}; +use crate::{ + crypto, u64_to_address, Precompile, PrecompileError, PrecompileId, PrecompileOutput, + PrecompileResult, +}; +use p256::{ + ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}, + EncodedPoint, +}; +use primitives::{alloy_primitives::B512, Bytes, B256}; + +/// Address of secp256r1 precompile. +pub const P256VERIFY_ADDRESS: u64 = 256; /// Base gas fee for secp256r1 p256verify operation. -const P256VERIFY_BASE: u64 = 3450; +pub const P256VERIFY_BASE_GAS_FEE: u64 = 3450; + +/// Base gas fee for secp256r1 p256verify operation post Osaka. +pub const P256VERIFY_BASE_GAS_FEE_OSAKA: u64 = 6900; /// Returns the secp256r1 precompile with its address. -pub fn precompiles() -> impl Iterator { +pub fn precompiles() -> impl Iterator { [P256VERIFY].into_iter() } -/// [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212#specification) secp256r1 precompile. -pub const P256VERIFY: PrecompileWithAddress = - PrecompileWithAddress(u64_to_address(0x100), Precompile::Standard(p256_verify)); +/// [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md#specification) secp256r1 precompile. +pub const P256VERIFY: Precompile = Precompile::new( + PrecompileId::P256Verify, + u64_to_address(P256VERIFY_ADDRESS), + p256_verify, +); + +/// [RIP-7212](https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md#specification) secp256r1 precompile. +pub const P256VERIFY_OSAKA: Precompile = Precompile::new( + PrecompileId::P256Verify, + u64_to_address(P256VERIFY_ADDRESS), + p256_verify_osaka, +); /// secp256r1 precompile logic. It takes the input bytes sent to the precompile /// and the gas limit. The output represents the result of verifying the @@ -31,53 +53,72 @@ pub const P256VERIFY: PrecompileWithAddress = /// | signed message hash | r | s | public key x | public key y | /// | :-----------------: | :-: | :-: | :----------: | :----------: | /// | 32 | 32 | 32 | 32 | 32 | -pub fn p256_verify(input: &Bytes, gas_limit: u64) -> PrecompileResult { - if P256VERIFY_BASE > gas_limit { - return Err(PrecompileError::OutOfGas.into()); +pub fn p256_verify(input: &[u8], gas_limit: u64) -> PrecompileResult { + p256_verify_inner(input, gas_limit, P256VERIFY_BASE_GAS_FEE) +} + +/// secp256r1 precompile logic with Osaka gas cost. It takes the input bytes sent to the precompile +/// and the gas limit. The output represents the result of verifying the +/// secp256r1 signature of the input. +/// +/// The input is encoded as follows: +/// +/// | signed message hash | r | s | public key x | public key y | +/// | :-----------------: | :-: | :-: | :----------: | :----------: | +/// | 32 | 32 | 32 | 32 | 32 | +pub fn p256_verify_osaka(input: &[u8], gas_limit: u64) -> PrecompileResult { + p256_verify_inner(input, gas_limit, P256VERIFY_BASE_GAS_FEE_OSAKA) +} + +fn p256_verify_inner(input: &[u8], gas_limit: u64, gas_cost: u64) -> PrecompileResult { + if gas_cost > gas_limit { + return Err(PrecompileError::OutOfGas); } - let result = if verify_impl(input).is_some() { + let result = if verify_impl(input) { B256::with_last_byte(1).into() } else { Bytes::new() }; - Ok(PrecompileOutput::new(P256VERIFY_BASE, result)) + Ok(PrecompileOutput::new(gas_cost, result)) } /// Returns `Some(())` if the signature included in the input byte slice is /// valid, `None` otherwise. -pub fn verify_impl(input: &[u8]) -> Option<()> { +pub fn verify_impl(input: &[u8]) -> bool { if input.len() != 160 { - return None; + return false; } // msg signed (msg is already the hash of the original message) - let msg = &input[..32]; + let msg = <&B256>::try_from(&input[..32]).unwrap(); // r, s: signature - let sig = &input[32..96]; + let sig = <&B512>::try_from(&input[32..96]).unwrap(); // x, y: public key - let pk = &input[96..160]; + let pk = <&B512>::try_from(&input[96..160]).unwrap(); - // prepend 0x04 to the public key: uncompressed form - let mut uncompressed_pk = [0u8; 65]; - uncompressed_pk[0] = 0x04; - uncompressed_pk[1..].copy_from_slice(pk); + crypto().secp256r1_verify_signature(&msg.0, &sig.0, &pk.0) +} +pub(crate) fn verify_signature(msg: [u8; 32], sig: [u8; 64], pk: [u8; 64]) -> Option<()> { // Can fail only if the input is not exact length. - let signature = Signature::from_slice(sig).ok()?; - // Can fail if the input is not valid, so we have to propagate the error. - let public_key = VerifyingKey::from_sec1_bytes(&uncompressed_pk).ok()?; + let signature = Signature::from_slice(&sig).ok()?; + // Decode the public key bytes (x,y coordinates) using EncodedPoint + let encoded_point = EncodedPoint::from_untagged_bytes(&pk.into()); + // Create VerifyingKey from the encoded point + let public_key = VerifyingKey::from_encoded_point(&encoded_point).ok()?; - public_key.verify_prehash(msg, &signature).ok() + public_key.verify_prehash(&msg, &signature).ok() } #[cfg(test)] mod test { use super::*; - use crate::primitives::{hex::FromHex, PrecompileErrors}; + use crate::PrecompileError; + use primitives::hex::FromHex; use rstest::rstest; #[rstest] - // test vectors from https://github.com/daimo-eth/p256-verifier/tree/master/test-vectors + // Test vectors from https://github.com/daimo-eth/p256-verifier/tree/master/test-vectors #[case::ok_1("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", true)] #[case::ok_2("3fec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5", true)] #[case::ok_3("e775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a", true)] @@ -113,10 +154,7 @@ mod test { let result = p256_verify(&input, target_gas); assert!(result.is_err()); - assert_eq!( - result.err(), - Some(PrecompileErrors::Error(PrecompileError::OutOfGas)) - ); + assert_eq!(result.err(), Some(PrecompileError::OutOfGas)); } #[rstest] @@ -126,6 +164,6 @@ mod test { let input = Bytes::from_hex(input).unwrap(); let result = verify_impl(&input); - assert_eq!(result.is_some(), expect_success); + assert_eq!(result, expect_success); } } diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index f80f98208f..eabb3f812b 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -30,6 +30,7 @@ pub enum SpecId { CANCUN = 17, // Cancun 19426587 (Timestamp: 1710338135) PRAGUE = 18, // Prague TBD PRAGUE_EOF = 19, // Prague+EOF TBD + OSAKA = 20, // Osaka #[default] LATEST = u8::MAX, } @@ -112,9 +113,10 @@ pub enum SpecId { CURIE = 19, MORPH203 = 20, // revert Precompiles: RIPEMD-160, point evaluation, modexp, ecPairing VIRIDIAN = 21, // Support EIP-7702 - CANCUN = 22, - PRAGUE = 23, - PRAGUE_EOF = 24, + EMERALD = 22, // EMERALD upgrade + CANCUN = 23, + PRAGUE = 24, + PRAGUE_EOF = 25, #[default] LATEST = u8::MAX, } @@ -180,6 +182,8 @@ impl From<&str> for SpecId { "Morph203" => SpecId::MORPH203, #[cfg(feature = "morph")] "Viridian" => SpecId::VIRIDIAN, + #[cfg(feature = "morph")] + "Emerald" => SpecId::EMERALD, _ => Self::LATEST, } } @@ -208,6 +212,7 @@ impl From for &'static str { SpecId::CANCUN => "Cancun", SpecId::PRAGUE => "Prague", SpecId::PRAGUE_EOF => "PragueEOF", + SpecId::OSAKA => "Osaka", #[cfg(feature = "optimism")] SpecId::BEDROCK => "Bedrock", #[cfg(feature = "optimism")] @@ -230,6 +235,8 @@ impl From for &'static str { SpecId::MORPH203 => "Morph203", #[cfg(feature = "morph")] SpecId::VIRIDIAN => "Viridian", + #[cfg(feature = "morph")] + SpecId::EMERALD => "Emerald", SpecId::LATEST => "Latest", } } @@ -305,6 +312,8 @@ spec!(MORPH203, Morph203Spec); spec!(CURIE, CurieSpec); #[cfg(feature = "morph")] spec!(VIRIDIAN, ViridianSpec); +#[cfg(feature = "morph")] +spec!(EMERALD, EmeraldSpec); #[cfg(not(any(feature = "optimism", feature = "morph")))] #[macro_export] @@ -560,6 +569,10 @@ macro_rules! spec_to_generic { use $crate::CurieSpec as SPEC; $e } + $crate::SpecId::EMERALD => { + use $crate::EmeraldSpec as SPEC; + $e + } } }}; } @@ -605,6 +618,8 @@ mod tests { spec_to_generic!(CURIE, assert_eq!(SPEC::SPEC_ID, CURIE)); #[cfg(feature = "morph")] spec_to_generic!(VIRIDIAN, assert_eq!(SPEC::SPEC_ID, VIRIDIAN)); + #[cfg(feature = "morph")] + spec_to_generic!(EMERALD, assert_eq!(SPEC::SPEC_ID, EMERALD)); spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); #[cfg(feature = "optimism")] spec_to_generic!(ECOTONE, assert_eq!(SPEC::SPEC_ID, ECOTONE)); diff --git a/crates/revm/src/handler/mainnet/pre_execution.rs b/crates/revm/src/handler/mainnet/pre_execution.rs index dd7d63d784..555d9b4f5a 100644 --- a/crates/revm/src/handler/mainnet/pre_execution.rs +++ b/crates/revm/src/handler/mainnet/pre_execution.rs @@ -8,7 +8,7 @@ use crate::{ db::Database, eip7702, Account, Bytecode, EVMError, Env, Spec, SpecId::{CANCUN, PRAGUE, SHANGHAI}, - TxKind, BLOCKHASH_STORAGE_ADDRESS, U256, KECCAK_EMPTY, + TxKind, BLOCKHASH_STORAGE_ADDRESS, KECCAK_EMPTY, U256, }, Context, ContextPrecompiles, };