Skip to content

Commit 928883c

Browse files
BrianBlandrakita
andauthored
feat(optimism): Add secp256r1 precompile for Fjord (#1436)
* feat(optimism): Add secp256r1 precompile for Fjord * Fix docs * Fix nostd build * Load fjord precompiles via optimism handler register * Remove outdated fjord() precompile spec constructor * Document the secp256r1 feature * Address feedback * Handle invalid signatures * Update crates/precompile/src/secp256r1.rs * Update crates/precompile/src/secp256r1.rs * Blank return on failed signature verification * Add test case for invalid (zero) pubkey --------- Co-authored-by: rakita <rakita@users.noreply.github.com>
1 parent 2ce53cd commit 928883c

File tree

8 files changed

+224
-7
lines changed

8 files changed

+224
-7
lines changed

Cargo.lock

+22
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/precompile/Cargo.toml

+7-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ secp256k1 = { version = "0.29.0", default-features = false, features = [
4444
# BLS12-381 precompiles
4545
blst = { version = "0.3.11", optional = true }
4646

47+
# p256verify precompile
48+
p256 = { version = "0.13.2", optional = true, default-features = false, features = ["ecdsa"] }
49+
4750
[dev-dependencies]
4851
criterion = { version = "0.5" }
4952
rand = { version = "0.8", features = ["std"] }
@@ -67,7 +70,7 @@ std = [
6770
hashbrown = ["revm-primitives/hashbrown"]
6871
asm-keccak = ["revm-primitives/asm-keccak"]
6972

70-
optimism = ["revm-primitives/optimism"]
73+
optimism = ["revm-primitives/optimism", "secp256r1"]
7174
# Optimism default handler enabled Optimism handler register by default in EvmBuilder.
7275
optimism-default-handler = [
7376
"optimism",
@@ -77,6 +80,9 @@ negate-optimism-default-handler = [
7780
"revm-primitives/negate-optimism-default-handler",
7881
]
7982

83+
# Enables the p256verify precompile.
84+
secp256r1 = ["dep:p256"]
85+
8086
# These libraries may not work on all no_std platforms as they depend on C.
8187

8288
# Enables the KZG point evaluation precompile.

crates/precompile/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ pub mod identity;
1818
pub mod kzg_point_evaluation;
1919
pub mod modexp;
2020
pub mod secp256k1;
21+
#[cfg(feature = "secp256r1")]
22+
pub mod secp256r1;
2123
pub mod utilities;
2224

2325
use core::hash::Hash;
@@ -271,7 +273,7 @@ impl PrecompileSpecId {
271273
#[cfg(feature = "optimism")]
272274
BEDROCK | REGOLITH | CANYON => Self::BERLIN,
273275
#[cfg(feature = "optimism")]
274-
ECOTONE => Self::CANCUN,
276+
ECOTONE | FJORD => Self::CANCUN,
275277
}
276278
}
277279
}

crates/precompile/src/secp256r1.rs

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//! # EIP-7212 secp256r1 Precompile
2+
//!
3+
//! This module implements the [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212) precompile for
4+
//! secp256r1 curve support.
5+
//!
6+
//! The main purpose of this precompile is to verify ECDSA signatures that use the secp256r1, or
7+
//! P256 elliptic curve. The [`P256VERIFY`] const represents the implementation of this precompile,
8+
//! with the address that it is currently deployed at.
9+
use crate::{u64_to_address, Precompile, PrecompileWithAddress};
10+
use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey};
11+
use revm_primitives::{Bytes, PrecompileError, PrecompileResult, B256};
12+
13+
/// Base gas fee for secp256r1 p256verify operation.
14+
const P256VERIFY_BASE: u64 = 3450;
15+
16+
/// Returns the secp256r1 precompile with its address.
17+
pub fn precompiles() -> impl Iterator<Item = PrecompileWithAddress> {
18+
[P256VERIFY].into_iter()
19+
}
20+
21+
/// [EIP-7212](https://eips.ethereum.org/EIPS/eip-7212#specification) secp256r1 precompile.
22+
pub const P256VERIFY: PrecompileWithAddress =
23+
PrecompileWithAddress(u64_to_address(0x100), Precompile::Standard(p256_verify));
24+
25+
/// secp256r1 precompile logic. It takes the input bytes sent to the precompile
26+
/// and the gas limit. The output represents the result of verifying the
27+
/// secp256r1 signature of the input.
28+
///
29+
/// The input is encoded as follows:
30+
///
31+
/// | signed message hash | r | s | public key x | public key y |
32+
/// | :-----------------: | :-: | :-: | :----------: | :----------: |
33+
/// | 32 | 32 | 32 | 32 | 32 |
34+
pub fn p256_verify(input: &Bytes, gas_limit: u64) -> PrecompileResult {
35+
if P256VERIFY_BASE > gas_limit {
36+
return Err(PrecompileError::OutOfGas);
37+
}
38+
let result = if verify_impl(input).is_some() {
39+
B256::with_last_byte(1).into()
40+
} else {
41+
Bytes::new()
42+
};
43+
Ok((P256VERIFY_BASE, result))
44+
}
45+
46+
/// Returns `Some(())` if the signature included in the input byte slice is
47+
/// valid, `None` otherwise.
48+
pub fn verify_impl(input: &[u8]) -> Option<()> {
49+
if input.len() != 160 {
50+
return None;
51+
}
52+
53+
// msg signed (msg is already the hash of the original message)
54+
let msg = &input[..32];
55+
// r, s: signature
56+
let sig = &input[32..96];
57+
// x, y: public key
58+
let pk = &input[96..160];
59+
60+
// prepend 0x04 to the public key: uncompressed form
61+
let mut uncompressed_pk = [0u8; 65];
62+
uncompressed_pk[0] = 0x04;
63+
uncompressed_pk[1..].copy_from_slice(pk);
64+
65+
// Can fail only if the input is not exact length.
66+
let signature = Signature::from_slice(sig).ok()?;
67+
// Can fail if the input is not valid, so we have to propagate the error.
68+
let public_key = VerifyingKey::from_sec1_bytes(&uncompressed_pk).ok()?;
69+
70+
public_key.verify_prehash(msg, &signature).ok()
71+
}
72+
73+
#[cfg(test)]
74+
mod test {
75+
use super::*;
76+
use revm_primitives::hex::FromHex;
77+
use rstest::rstest;
78+
79+
#[rstest]
80+
// test vectors from https://github.com/daimo-eth/p256-verifier/tree/master/test-vectors
81+
#[case::ok_1("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", true)]
82+
#[case::ok_2("3fec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5", true)]
83+
#[case::ok_3("e775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a", true)]
84+
#[case::ok_4("b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", true)]
85+
#[case::ok_5("858b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2", true)]
86+
#[case::fail_wrong_msg_1("3cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", false)]
87+
#[case::fail_wrong_msg_2("afec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5", false)]
88+
#[case::fail_wrong_msg_3("f775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a", false)]
89+
#[case::fail_wrong_msg_4("c5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", false)]
90+
#[case::fail_wrong_msg_5("958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2", false)]
91+
#[case::fail_short_input_1("4cee90eb86eaa050036147a12d49004b6a", false)]
92+
#[case::fail_short_input_2("4cee90eb86eaa050036147a12d49004b6a958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf319", false)]
93+
#[case::fail_long_input("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e00", false)]
94+
#[case::fail_invalid_sig("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", false)]
95+
#[case::fail_invalid_pubkey("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d6000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", false)]
96+
fn test_sig_verify(#[case] input: &str, #[case] expect_success: bool) {
97+
let input = Bytes::from_hex(input).unwrap();
98+
let target_gas = 3_500u64;
99+
let (gas_used, res) = p256_verify(&input, target_gas).unwrap();
100+
assert_eq!(gas_used, 3_450u64);
101+
let expected_result = if expect_success {
102+
B256::with_last_byte(1).into()
103+
} else {
104+
Bytes::new()
105+
};
106+
assert_eq!(res, expected_result);
107+
}
108+
109+
#[rstest]
110+
fn test_not_enough_gas_errors() {
111+
let input = Bytes::from_hex("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").unwrap();
112+
let target_gas = 2_500u64;
113+
let result = p256_verify(&input, target_gas);
114+
115+
assert!(result.is_err());
116+
assert_eq!(result.err(), Some(PrecompileError::OutOfGas));
117+
}
118+
119+
#[rstest]
120+
#[case::ok_1("b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", true)]
121+
#[case::fail_1("b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", false)]
122+
fn test_verify_impl(#[case] input: &str, #[case] expect_success: bool) {
123+
let input = Bytes::from_hex(input).unwrap();
124+
let result = verify_impl(&input);
125+
126+
assert_eq!(result.is_some(), expect_success);
127+
}
128+
}

crates/primitives/src/specification.rs

+42-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ pub enum SpecId {
6363
CANYON = 19,
6464
CANCUN = 20,
6565
ECOTONE = 21,
66-
PRAGUE = 22,
66+
FJORD = 22,
67+
PRAGUE = 23,
6768
#[default]
6869
LATEST = u8::MAX,
6970
}
@@ -114,6 +115,8 @@ impl From<&str> for SpecId {
114115
"Canyon" => SpecId::CANYON,
115116
#[cfg(feature = "optimism")]
116117
"Ecotone" => SpecId::ECOTONE,
118+
#[cfg(feature = "optimism")]
119+
"Fjord" => SpecId::FJORD,
117120
_ => Self::LATEST,
118121
}
119122
}
@@ -149,6 +152,8 @@ impl From<SpecId> for &'static str {
149152
SpecId::CANYON => "Canyon",
150153
#[cfg(feature = "optimism")]
151154
SpecId::ECOTONE => "Ecotone",
155+
#[cfg(feature = "optimism")]
156+
SpecId::FJORD => "Fjord",
152157
SpecId::LATEST => "Latest",
153158
}
154159
}
@@ -207,6 +212,8 @@ spec!(REGOLITH, RegolithSpec);
207212
spec!(CANYON, CanyonSpec);
208213
#[cfg(feature = "optimism")]
209214
spec!(ECOTONE, EcotoneSpec);
215+
#[cfg(feature = "optimism")]
216+
spec!(FJORD, FjordSpec);
210217

211218
#[cfg(not(feature = "optimism"))]
212219
#[macro_export]
@@ -354,6 +361,10 @@ macro_rules! spec_to_generic {
354361
use $crate::EcotoneSpec as SPEC;
355362
$e
356363
}
364+
$crate::SpecId::FJORD => {
365+
use $crate::FjordSpec as SPEC;
366+
$e
367+
}
357368
}
358369
}};
359370
}
@@ -390,6 +401,10 @@ mod tests {
390401
#[cfg(feature = "optimism")]
391402
spec_to_generic!(CANYON, assert_eq!(SPEC::SPEC_ID, CANYON));
392403
spec_to_generic!(CANCUN, assert_eq!(SPEC::SPEC_ID, CANCUN));
404+
#[cfg(feature = "optimism")]
405+
spec_to_generic!(ECOTONE, assert_eq!(SPEC::SPEC_ID, ECOTONE));
406+
#[cfg(feature = "optimism")]
407+
spec_to_generic!(FJORD, assert_eq!(SPEC::SPEC_ID, FJORD));
393408
spec_to_generic!(PRAGUE, assert_eq!(SPEC::SPEC_ID, PRAGUE));
394409
spec_to_generic!(LATEST, assert_eq!(SPEC::SPEC_ID, LATEST));
395410
}
@@ -485,4 +500,30 @@ mod optimism_tests {
485500
assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::CANYON));
486501
assert!(SpecId::enabled(SpecId::ECOTONE, SpecId::ECOTONE));
487502
}
503+
504+
#[test]
505+
fn test_fjord_post_merge_hardforks() {
506+
assert!(FjordSpec::enabled(SpecId::MERGE));
507+
assert!(FjordSpec::enabled(SpecId::SHANGHAI));
508+
assert!(FjordSpec::enabled(SpecId::CANCUN));
509+
assert!(!FjordSpec::enabled(SpecId::LATEST));
510+
assert!(FjordSpec::enabled(SpecId::BEDROCK));
511+
assert!(FjordSpec::enabled(SpecId::REGOLITH));
512+
assert!(FjordSpec::enabled(SpecId::CANYON));
513+
assert!(FjordSpec::enabled(SpecId::ECOTONE));
514+
assert!(FjordSpec::enabled(SpecId::FJORD));
515+
}
516+
517+
#[test]
518+
fn test_fjord_post_merge_hardforks_spec_id() {
519+
assert!(SpecId::enabled(SpecId::FJORD, SpecId::MERGE));
520+
assert!(SpecId::enabled(SpecId::FJORD, SpecId::SHANGHAI));
521+
assert!(SpecId::enabled(SpecId::FJORD, SpecId::CANCUN));
522+
assert!(!SpecId::enabled(SpecId::FJORD, SpecId::LATEST));
523+
assert!(SpecId::enabled(SpecId::FJORD, SpecId::BEDROCK));
524+
assert!(SpecId::enabled(SpecId::FJORD, SpecId::REGOLITH));
525+
assert!(SpecId::enabled(SpecId::FJORD, SpecId::CANYON));
526+
assert!(SpecId::enabled(SpecId::FJORD, SpecId::ECOTONE));
527+
assert!(SpecId::enabled(SpecId::FJORD, SpecId::FJORD));
528+
}
488529
}

crates/revm/src/db/in_memory_db.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ mod tests {
482482
let serialized = serde_json::to_string(&init_state).unwrap();
483483
let deserialized: CacheDB<EmptyDB> = serde_json::from_str(&serialized).unwrap();
484484

485-
assert!(deserialized.accounts.get(&account).is_some());
485+
assert!(deserialized.accounts.contains_key(&account));
486486
assert_eq!(
487487
deserialized.accounts.get(&account).unwrap().info.nonce,
488488
nonce

crates/revm/src/optimism.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ mod handler_register;
44
mod l1block;
55

66
pub use handler_register::{
7-
deduct_caller, end, last_frame_return, load_accounts, optimism_handle_register, output,
8-
reward_beneficiary, validate_env, validate_tx_against_state,
7+
deduct_caller, end, last_frame_return, load_accounts, load_precompiles,
8+
optimism_handle_register, output, reward_beneficiary, validate_env, validate_tx_against_state,
99
};
1010
pub use l1block::{L1BlockInfo, BASE_FEE_RECIPIENT, L1_BLOCK_CONTRACT, L1_FEE_RECIPIENT};

crates/revm/src/optimism/handler_register.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ use crate::{
1111
db::Database, spec_to_generic, Account, EVMError, Env, ExecutionResult, HaltReason,
1212
HashMap, InvalidTransaction, ResultAndState, Spec, SpecId, SpecId::REGOLITH, U256,
1313
},
14-
Context, FrameResult,
14+
Context, ContextPrecompiles, FrameResult,
1515
};
1616
use core::ops::Mul;
17+
use revm_precompile::{secp256r1, PrecompileSpecId, Precompiles};
1718
use std::string::ToString;
1819
use std::sync::Arc;
1920

@@ -23,6 +24,8 @@ pub fn optimism_handle_register<DB: Database, EXT>(handler: &mut EvmHandler<'_,
2324
handler.validation.env = Arc::new(validate_env::<SPEC, DB>);
2425
// Validate transaction against state.
2526
handler.validation.tx_against_state = Arc::new(validate_tx_against_state::<SPEC, EXT, DB>);
27+
// Load additional precompiles for the given chain spec.
28+
handler.pre_execution.load_precompiles = Arc::new(load_precompiles::<SPEC, EXT, DB>);
2629
// load l1 data
2730
handler.pre_execution.load_accounts = Arc::new(load_accounts::<SPEC, EXT, DB>);
2831
// An estimated batch cost is charged from the caller and added to L1 Fee Vault.
@@ -137,6 +140,21 @@ pub fn last_frame_return<SPEC: Spec, EXT, DB: Database>(
137140
Ok(())
138141
}
139142

143+
/// Load precompiles for Optimism chain.
144+
#[inline]
145+
pub fn load_precompiles<SPEC: Spec, EXT, DB: Database>() -> ContextPrecompiles<DB> {
146+
let mut precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(SPEC::SPEC_ID)).clone();
147+
148+
if SPEC::enabled(SpecId::FJORD) {
149+
precompiles.extend([
150+
// EIP-7212: secp256r1 P256verify
151+
secp256r1::P256VERIFY,
152+
])
153+
}
154+
155+
precompiles.into()
156+
}
157+
140158
/// Load account (make them warm) and l1 data from database.
141159
#[inline]
142160
pub fn load_accounts<SPEC: Spec, EXT, DB: Database>(

0 commit comments

Comments
 (0)