diff --git a/Cargo.lock b/Cargo.lock index d069f287..3c2bc0f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2257,6 +2257,18 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.8", +] + [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -2449,6 +2461,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "primitive-types" version = "0.12.2" @@ -2677,6 +2698,12 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "reqwest" version = "0.11.27" @@ -2759,13 +2786,19 @@ dependencies = [ "cometbft-light-client-verifier", "cometbft-proto", "criterion", + "eyre", "k256", "once_cell", + "p256", "prost", "rand", "revm-primitives", "ripemd", + "rstest", "secp256k1", + "serde", + "serde_derive", + "serde_json", "sha2 0.10.8", "substrate-bn", ] @@ -2895,6 +2928,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rstest" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5316d2a1479eeef1ea21e7f9ddc67c191d497abc8fc3ba2467857abbb68330" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version 0.4.0", +] + +[[package]] +name = "rstest_macros" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04a9df72cc1f67020b0d63ad9bfe4a323e459ea7eb68e03bd9824db49f9a4c25" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.0", + "syn 2.0.65", + "unicode-ident", +] + [[package]] name = "ruint" version = "1.12.1" diff --git a/crates/interpreter/src/instructions/opcode.rs b/crates/interpreter/src/instructions/opcode.rs index ef93ea60..c4b35ea0 100644 --- a/crates/interpreter/src/instructions/opcode.rs +++ b/crates/interpreter/src/instructions/opcode.rs @@ -947,11 +947,21 @@ pub const fn spec_opcode_gas(spec_id: SpecId) -> &'static [OpInfo; 256] { const TABLE: &[OpInfo;256] = &make_gas_table(SpecId::ECOTONE); TABLE } + #[cfg(feature = "optimism")] + SpecId::FJORD => { + const TABLE: &[OpInfo;256] = &make_gas_table(SpecId::FJORD); + TABLE + } #[cfg(feature = "opbnb")] SpecId::FERMAT => { const TABLE: &[OpInfo;256] = &make_gas_table(SpecId::FERMAT); TABLE } + #[cfg(feature = "opbnb")] + SpecId::HABER => { + const TABLE: &[OpInfo;256] = &make_gas_table(SpecId::HABER); + TABLE + } } }; } @@ -975,6 +985,7 @@ pub const fn spec_opcode_gas(spec_id: SpecId) -> &'static [OpInfo; 256] { MERGE, SHANGHAI, CANCUN, + PRAGUE, LATEST, ) } diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index 611e336d..b4798943 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -39,9 +39,17 @@ cometbft-light-client = { git = "https://github.com/bnb-chain/greenfield-cometb prost = { version = "0.12.3" } bls_on_arkworks = "0.3.0" +# p256verify precompile +p256 = { version = "0.13.2", optional = true, default-features = false, features = ["ecdsa"] } + [dev-dependencies] criterion = { version = "0.5" } rand = { version = "0.8", features = ["std"] } +eyre = "0.6.12" +rstest = "0.19.0" +serde = "1.0" +serde_json = "1.0" +serde_derive = "1.0" [features] default = ["std", "c-kzg", "secp256k1", "portable"] @@ -56,7 +64,7 @@ std = [ ] asm-keccak = ["revm-primitives/asm-keccak"] -optimism = ["revm-primitives/optimism"] +optimism = ["revm-primitives/optimism", "secp256r1"] # Optimism default handler enabled Optimism handler register by default in EvmBuilder. optimism-default-handler = [ "optimism", @@ -66,6 +74,9 @@ negate-optimism-default-handler = [ "revm-primitives/negate-optimism-default-handler", ] +# Enables the p256verify precompile. +secp256r1 = ["dep:p256"] + # These libraries may not work on all no_std platforms as they depend on C. # Enables the KZG point evaluation precompile. diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index 1afad41c..70e82741 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -20,6 +20,8 @@ pub mod identity; pub mod kzg_point_evaluation; pub mod modexp; pub mod secp256k1; +#[cfg(feature = "secp256r1")] +pub mod secp256r1; pub mod utilities; use core::hash::Hash; @@ -68,6 +70,7 @@ impl Precompiles { PrecompileSpecId::BERLIN => Self::berlin(), PrecompileSpecId::FERMAT => Self::fermat(), PrecompileSpecId::CANCUN => Self::cancun(), + PrecompileSpecId::PRAGUE => Self::prague(), PrecompileSpecId::LATEST => Self::latest(), } } @@ -183,6 +186,24 @@ impl Precompiles { }) } + /// Returns precompiles for Prague spec. + pub fn prague() -> &'static Self { + static INSTANCE: OnceBox = OnceBox::new(); + INSTANCE.get_or_init(|| { + let precompiles = Self::cancun().clone(); + + // Don't include BLS12-381 precompiles in no_std builds. + #[cfg(feature = "blst")] + let precompiles = { + let mut precompiles = precompiles; + precompiles.extend(bls12_381::precompiles()); + precompiles + }; + + Box::new(precompiles) + }) + } + /// Returns the precompiles for the latest spec. pub fn latest() -> &'static Self { Self::cancun() @@ -259,6 +280,7 @@ pub enum PrecompileSpecId { BERLIN, FERMAT, CANCUN, + PRAGUE, LATEST, } @@ -274,13 +296,16 @@ impl PrecompileSpecId { ISTANBUL | MUIR_GLACIER => Self::ISTANBUL, BERLIN | LONDON | ARROW_GLACIER | GRAY_GLACIER | MERGE | SHANGHAI => Self::BERLIN, CANCUN => Self::CANCUN, + PRAGUE => Self::PRAGUE, LATEST => Self::LATEST, #[cfg(feature = "optimism")] BEDROCK | REGOLITH | CANYON => Self::BERLIN, #[cfg(feature = "optimism")] - ECOTONE => Self::CANCUN, + ECOTONE | FJORD => Self::CANCUN, #[cfg(feature = "opbnb")] FERMAT => Self::FERMAT, + #[cfg(feature = "opbnb")] + HABER => Self::CANCUN, } } } diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs new file mode 100644 index 00000000..e2c9951a --- /dev/null +++ b/crates/precompile/src/secp256r1.rs @@ -0,0 +1,128 @@ +//! # EIP-7212 secp256r1 Precompile +//! +//! This module implements the [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212) 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, PrecompileResult, B256}; + +/// Base gas fee for secp256r1 p256verify operation. +const P256VERIFY_BASE: u64 = 3450; + +/// Returns the secp256r1 precompile with its address. +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)); + +/// secp256r1 precompile logic. 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(input: &Bytes, gas_limit: u64) -> PrecompileResult { + if P256VERIFY_BASE > gas_limit { + return Err(PrecompileError::OutOfGas); + } + let result = if verify_impl(input).is_some() { + B256::with_last_byte(1).into() + } else { + Bytes::new() + }; + Ok((P256VERIFY_BASE, result)) +} + +/// Returns `Some(())` if the signature included in the input byte slice is +/// valid, `None` otherwise. +pub fn verify_impl(input: &[u8]) -> Option<()> { + if input.len() != 160 { + return None; + } + + // msg signed (msg is already the hash of the original message) + let msg = &input[..32]; + // r, s: signature + let sig = &input[32..96]; + // x, y: public key + let pk = &input[96..160]; + + // 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); + + // 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()?; + + public_key.verify_prehash(msg, &signature).ok() +} + +#[cfg(test)] +mod test { + use super::*; + use revm_primitives::hex::FromHex; + use rstest::rstest; + + #[rstest] + // 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)] + #[case::ok_4("b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", true)] + #[case::ok_5("858b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2", true)] + #[case::fail_wrong_msg_1("3cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", false)] + #[case::fail_wrong_msg_2("afec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5", false)] + #[case::fail_wrong_msg_3("f775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a", false)] + #[case::fail_wrong_msg_4("c5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", false)] + #[case::fail_wrong_msg_5("958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2", false)] + #[case::fail_short_input_1("4cee90eb86eaa050036147a12d49004b6a", false)] + #[case::fail_short_input_2("4cee90eb86eaa050036147a12d49004b6a958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf319", false)] + #[case::fail_long_input("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e00", false)] + #[case::fail_invalid_sig("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", false)] + #[case::fail_invalid_pubkey("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false)] + fn test_sig_verify(#[case] input: &str, #[case] expect_success: bool) { + let input = Bytes::from_hex(input).unwrap(); + let target_gas = 3_500u64; + let (gas_used, res) = p256_verify(&input, target_gas).unwrap(); + assert_eq!(gas_used, 3_450u64); + let expected_result = if expect_success { + B256::with_last_byte(1).into() + } else { + Bytes::new() + }; + assert_eq!(res, expected_result); + } + + #[rstest] + fn test_not_enough_gas_errors() { + let input = Bytes::from_hex("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").unwrap(); + let target_gas = 2_500u64; + let result = p256_verify(&input, target_gas); + + assert!(result.is_err()); + assert_eq!(result.err(), Some(PrecompileError::OutOfGas)); + } + + #[rstest] + #[case::ok_1("b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", true)] + #[case::fail_1("b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", false)] + fn test_verify_impl(#[case] input: &str, #[case] expect_success: bool) { + let input = Bytes::from_hex(input).unwrap(); + let result = verify_impl(&input); + + assert_eq!(result.is_some(), expect_success); + } +} diff --git a/crates/primitives/src/specification.rs b/crates/primitives/src/specification.rs index 500fe253..4c035f67 100644 --- a/crates/primitives/src/specification.rs +++ b/crates/primitives/src/specification.rs @@ -27,7 +27,8 @@ pub enum SpecId { GRAY_GLACIER = 14, // Gray Glacier 15050000 MERGE = 15, // Paris/Merge 15537394 (TTD: 58750000000000000000000) SHANGHAI = 16, // Shanghai 17034870 (TS: 1681338455) - CANCUN = 17, // Cancun TBD + CANCUN = 17, // Cancun 19426587 (Timestamp: 1710338135) + PRAGUE = 18, // Praque TBD #[default] LATEST = u8::MAX, } @@ -63,6 +64,9 @@ pub enum SpecId { CANYON = 20, CANCUN = 21, ECOTONE = 22, + FJORD = 23, + PRAGUE = 24, + HABER = 25, #[default] LATEST = u8::MAX, } @@ -110,6 +114,10 @@ impl From<&str> for SpecId { "Canyon" => SpecId::CANYON, #[cfg(feature = "optimism")] "Ecotone" => SpecId::ECOTONE, + #[cfg(feature = "optimism")] + "Fjord" => SpecId::FJORD, + #[cfg(feature = "opbnb")] + "Haber" => SpecId::HABER, _ => Self::LATEST, } } @@ -136,16 +144,21 @@ impl From for &'static str { SpecId::MERGE => "Merge", SpecId::SHANGHAI => "Shanghai", SpecId::CANCUN => "Cancun", + SpecId::PRAGUE => "Prague", #[cfg(feature = "optimism")] SpecId::BEDROCK => "Bedrock", #[cfg(feature = "optimism")] SpecId::REGOLITH => "Regolith", #[cfg(feature = "opbnb")] SpecId::FERMAT => "Fermat", + #[cfg(feature = "opbnb")] + SpecId::HABER => "Haber", #[cfg(feature = "optimism")] SpecId::CANYON => "Canyon", #[cfg(feature = "optimism")] SpecId::ECOTONE => "Ecotone", + #[cfg(feature = "optimism")] + SpecId::FJORD => "Fjord", SpecId::LATEST => "Latest", } } @@ -191,6 +204,7 @@ spec!(LONDON, LondonSpec); spec!(MERGE, MergeSpec); spec!(SHANGHAI, ShanghaiSpec); spec!(CANCUN, CancunSpec); +spec!(PRAGUE, PragueSpec); spec!(LATEST, LatestSpec); @@ -205,6 +219,10 @@ spec!(CANYON, CanyonSpec); spec!(ECOTONE, EcotoneSpec); #[cfg(feature = "opbnb")] spec!(FERMAT, FermatSpec); +#[cfg(feature = "opbnb")] +spec!(HABER, HaberSpec); +#[cfg(feature = "optimism")] +spec!(FJORD, FjordSpec); #[macro_export] macro_rules! spec_to_generic { @@ -265,6 +283,10 @@ macro_rules! spec_to_generic { use $crate::LatestSpec as SPEC; $e } + $crate::SpecId::PRAGUE => { + use $crate::PragueSpec as SPEC; + $e + } #[cfg(feature = "optimism")] $crate::SpecId::BEDROCK => { use $crate::BedrockSpec as SPEC; @@ -285,11 +307,21 @@ macro_rules! spec_to_generic { use $crate::EcotoneSpec as SPEC; $e } + #[cfg(feature = "optimism")] + $crate::SpecId::FJORD => { + use $crate::FjordSpec as SPEC; + $e + } #[cfg(feature = "opbnb")] $crate::SpecId::FERMAT => { use $crate::FermatSpec as SPEC; $e } + #[cfg(feature = "opbnb")] + $crate::SpecId::HABER => { + use $crate::HaberSpec as SPEC; + $e + } } }}; } @@ -326,6 +358,11 @@ mod tests { #[cfg(feature = "optimism")] spec_to_generic!(CANYON, assert_eq!(SPEC::SPEC_ID, CANYON)); spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN)); + #[cfg(feature = "optimism")] + spec_to_generic!(ECOTONE, assert_eq!(SPEC::SPEC_ID, ECOTONE)); + #[cfg(feature = "optimism")] + spec_to_generic!(FJORD, assert_eq!(SPEC::SPEC_ID, FJORD)); + spec_to_generic!(PRAGUE, assert_eq!(SPEC::SPEC_ID, PRAGUE)); spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST)); } } @@ -420,4 +457,30 @@ mod optimism_tests { assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::CANYON)); assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::ECOTONE)); } + + #[test] + fn test_fjord_post_merge_hardforks() { + assert!(FjordSpec::enabled(SpecId::MERGE)); + assert!(FjordSpec::enabled(SpecId::SHANGHAI)); + assert!(FjordSpec::enabled(SpecId::CANCUN)); + assert!(!FjordSpec::enabled(SpecId::LATEST)); + assert!(FjordSpec::enabled(SpecId::BEDROCK)); + assert!(FjordSpec::enabled(SpecId::REGOLITH)); + assert!(FjordSpec::enabled(SpecId::CANYON)); + assert!(FjordSpec::enabled(SpecId::ECOTONE)); + assert!(FjordSpec::enabled(SpecId::FJORD)); + } + + #[test] + fn test_fjord_post_merge_hardforks_spec_id() { + assert!(SpecId::enabled(SpecId::FJORD, SpecId::MERGE)); + assert!(SpecId::enabled(SpecId::FJORD, SpecId::SHANGHAI)); + assert!(SpecId::enabled(SpecId::FJORD, SpecId::CANCUN)); + assert!(!SpecId::enabled(SpecId::FJORD, SpecId::LATEST)); + assert!(SpecId::enabled(SpecId::FJORD, SpecId::BEDROCK)); + assert!(SpecId::enabled(SpecId::FJORD, SpecId::REGOLITH)); + assert!(SpecId::enabled(SpecId::FJORD, SpecId::CANYON)); + assert!(SpecId::enabled(SpecId::FJORD, SpecId::ECOTONE)); + assert!(SpecId::enabled(SpecId::FJORD, SpecId::FJORD)); + } } diff --git a/crates/revm/src/optimism.rs b/crates/revm/src/optimism.rs index 3923b0c6..c72272b8 100644 --- a/crates/revm/src/optimism.rs +++ b/crates/revm/src/optimism.rs @@ -4,7 +4,7 @@ mod handler_register; mod l1block; pub use handler_register::{ - deduct_caller, end, last_frame_return, load_accounts, optimism_handle_register, output, - reward_beneficiary, validate_env, validate_tx_against_state, + deduct_caller, end, last_frame_return, load_accounts, load_precompiles, + optimism_handle_register, output, reward_beneficiary, validate_env, validate_tx_against_state, }; pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT}; diff --git a/crates/revm/src/optimism/handler_register.rs b/crates/revm/src/optimism/handler_register.rs index fe6ee94c..b2138423 100644 --- a/crates/revm/src/optimism/handler_register.rs +++ b/crates/revm/src/optimism/handler_register.rs @@ -11,9 +11,10 @@ use crate::{ db::Database, spec_to_generic, Account, EVMError, Env, ExecutionResult, HaltReason, HashMap, InvalidTransaction, ResultAndState, Spec, SpecId, SpecId::REGOLITH, U256, }, - Context, FrameResult, + Context, ContextPrecompiles, FrameResult, }; use core::ops::Mul; +use revm_precompile::{secp256r1, PrecompileSpecId, Precompiles}; use std::string::ToString; use std::sync::Arc; @@ -23,6 +24,8 @@ pub fn optimism_handle_register(handler: &mut EvmHandler<'_, handler.validation.env = Arc::new(validate_env::); // Validate transaction against state. handler.validation.tx_against_state = Arc::new(validate_tx_against_state::); + // Load additional precompiles for the given chain spec. + handler.pre_execution.load_precompiles = Arc::new(load_precompiles::); // load l1 data handler.pre_execution.load_accounts = Arc::new(load_accounts::); // An estimated batch cost is charged from the caller and added to L1 Fee Vault. @@ -138,6 +141,21 @@ pub fn last_frame_return( Ok(()) } +/// Load precompiles for Optimism chain. +#[inline] +pub fn load_precompiles() -> ContextPrecompiles { + let mut precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)).clone(); + + if SPEC::enabled(SpecId::FJORD) || SPEC::enabled(SpecId::HABER) { + precompiles.extend([ + // EIP-7212: secp256r1 P256verify + secp256r1::P256VERIFY, + ]) + } + + precompiles.into() +} + /// Load account (make them warm) and l1 data from database. #[inline] pub fn load_accounts(