diff --git a/Cargo.lock b/Cargo.lock
index 37f31856..ec039398 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1586,7 +1586,7 @@ checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"
dependencies = [
"serde",
"termcolor",
- "unicode-width 0.1.14",
+ "unicode-width 0.2.0",
]
[[package]]
@@ -2791,7 +2791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976"
dependencies = [
"data-encoding",
- "syn 1.0.109",
+ "syn 2.0.101",
]
[[package]]
@@ -7232,7 +7232,7 @@ version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
dependencies = [
- "proc-macro-crate 1.1.3",
+ "proc-macro-crate 3.3.0",
"proc-macro2",
"quote",
"syn 2.0.101",
@@ -7460,6 +7460,18 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+[[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.9",
+]
+
[[package]]
name = "pallet-asset-conversion"
version = "22.0.0"
@@ -8138,6 +8150,17 @@ dependencies = [
"num",
]
+[[package]]
+name = "pallet-evm-precompile-p256verify"
+version = "0.1.0"
+dependencies = [
+ "fp-evm",
+ "frame-support",
+ "hex-literal 0.3.4",
+ "p256",
+ "precompile-utils",
+]
+
[[package]]
name = "pallet-evm-precompile-parachain-staking"
version = "0.0.2"
@@ -9348,6 +9371,7 @@ dependencies = [
"pallet-evm-precompile-bn128",
"pallet-evm-precompile-dispatch",
"pallet-evm-precompile-modexp",
+ "pallet-evm-precompile-p256verify",
"pallet-evm-precompile-parachain-staking",
"pallet-evm-precompile-peaq-did",
"pallet-evm-precompile-peaq-rbac",
@@ -9490,6 +9514,7 @@ dependencies = [
"pallet-evm-precompile-bn128",
"pallet-evm-precompile-dispatch",
"pallet-evm-precompile-modexp",
+ "pallet-evm-precompile-p256verify",
"pallet-evm-precompile-parachain-staking",
"pallet-evm-precompile-peaq-did",
"pallet-evm-precompile-peaq-rbac",
@@ -10103,6 +10128,7 @@ dependencies = [
"pallet-evm-precompile-bn128",
"pallet-evm-precompile-dispatch",
"pallet-evm-precompile-modexp",
+ "pallet-evm-precompile-p256verify",
"pallet-evm-precompile-parachain-staking",
"pallet-evm-precompile-peaq-did",
"pallet-evm-precompile-peaq-rbac",
@@ -11638,6 +11664,15 @@ dependencies = [
"syn 2.0.101",
]
+[[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.13.1"
diff --git a/Cargo.toml b/Cargo.toml
index 0bf0019d..dbbd3670 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,7 +14,8 @@ members = [
"precompiles/batch",
"precompiles/peaq-rbac",
"precompiles/parachain-staking",
- "precompiles/vesting"
+ "precompiles/vesting",
+ "precompiles/p256verify"
]
resolver = "2"
@@ -52,6 +53,7 @@ hex-literal = { version = "0.3.4", default-features = false }
impl-trait-for-tuples = "0.2.2"
jsonrpsee = { version = "0.24.9", default-features = false }
libsecp256k1 = { version = "0.7", default-features = false }
+p256 = { version = "0.13.2", default-features = false, features = [ "ecdsa" ] }
log = { version = "0.4.17", default-features = false }
macrotest = { version = "1.0.9", default-features = false }
num_enum = { version = "0.5.3", default-features = false }
diff --git a/pallets/xc-asset-config/src/tests.rs b/pallets/xc-asset-config/src/tests.rs
index 943476d5..d62eb038 100644
--- a/pallets/xc-asset-config/src/tests.rs
+++ b/pallets/xc-asset-config/src/tests.rs
@@ -366,4 +366,3 @@ fn different_xcm_versions_are_ok() {
assert_eq!(XcAssetConfig::get_xc_asset_location(asset_id), Some(new_asset_location));
})
}
-
diff --git a/precompiles/p256verify/Cargo.toml b/precompiles/p256verify/Cargo.toml
new file mode 100644
index 00000000..f3de6a56
--- /dev/null
+++ b/precompiles/p256verify/Cargo.toml
@@ -0,0 +1,28 @@
+[package]
+name = "pallet-evm-precompile-p256verify"
+authors = [ "peaq" ]
+description = "RIP-7212 secp256r1 (P-256) signature verification precompile for Frontier EVM."
+edition = "2021"
+version = "0.1.0"
+
+[dependencies]
+# Crypto
+p256 = { workspace = true }
+
+# Substrate
+frame-support = { workspace = true, default-features = false }
+
+# Frontier
+fp-evm = { workspace = true, default-features = false }
+
+[dev-dependencies]
+precompile-utils = { workspace = true, features = [ "std", "testing" ] }
+hex-literal = { workspace = true }
+
+[features]
+default = [ "std" ]
+std = [
+ "p256/std",
+ "frame-support/std",
+ "fp-evm/std",
+]
diff --git a/precompiles/p256verify/src/lib.rs b/precompiles/p256verify/src/lib.rs
new file mode 100644
index 00000000..ff154bc6
--- /dev/null
+++ b/precompiles/p256verify/src/lib.rs
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+//
+// RIP-7212 secp256r1 (P-256) signature verification precompile.
+// Ported for peaq from Moonbeam (moonbeam-foundation/moonbeam,
+// precompiles/p256verify). Both projects are GPL-3.0-or-later.
+//
+// Copyright 2025 Moonbeam Foundation.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+// "secp256r1" is a specific elliptic curve, also known as "P-256"
+// and "prime256v1" curves.
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+extern crate alloc;
+
+use alloc::vec::Vec;
+use core::marker::PhantomData;
+use fp_evm::{
+ ExitError, ExitSucceed, Precompile, PrecompileHandle, PrecompileOutput, PrecompileResult,
+};
+use frame_support::{traits::Get, weights::Weight};
+use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey};
+
+pub struct P256Verify, G: Get>(PhantomData<(W, G)>);
+
+impl, G: Get> P256Verify {
+ /// Expected input length (160 bytes)
+ const INPUT_LENGTH: usize = 160;
+
+ /// Handle gas costs
+ #[inline]
+ fn handle_cost(handle: &mut impl PrecompileHandle) -> Result<(), ExitError> {
+ let weight = W::get();
+ handle.record_cost(G::get())?;
+ handle.record_external_cost(Some(weight.ref_time()), None, None)
+ }
+
+ /// (Signed payload) Hash of the original message
+ /// 32 bytes of the signed data hash
+ #[inline]
+ fn message_hash(input: &[u8]) -> &[u8] {
+ &input[..32]
+ }
+
+ /// r and s signature components
+ #[inline]
+ fn signature(input: &[u8]) -> &[u8] {
+ &input[32..96]
+ }
+
+ /// x and y coordinates of the public key
+ #[inline]
+ fn public_key(input: &[u8]) -> &[u8] {
+ &input[96..160]
+ }
+
+ /// Extract and validate signature from input
+ fn verify_from_input(input: &[u8]) -> Option<()> {
+ // Input data: 160 bytes of data including:
+ // - 32 bytes of the signed data hash
+ // - 32 bytes of the r component of the signature
+ // - 32 bytes of the s component of the signature
+ // - 32 bytes of the x coordinate of the public key
+ // - 32 bytes of the y coordinate of the public key
+ if input.len() != Self::INPUT_LENGTH {
+ return None;
+ }
+
+ let message_hash = Self::message_hash(input);
+ let signature = Self::signature(input);
+ let public_key = Self::public_key(input);
+
+ let mut uncompressed_pk = [0u8; 65];
+ // (0x04) prefix indicates the public key is in its uncompressed form
+ uncompressed_pk[0] = 0x04;
+ uncompressed_pk[1..].copy_from_slice(public_key);
+
+ // Will only fail if the signature is not exactly 64 bytes
+ let signature = Signature::from_slice(signature).ok()?;
+
+ let public_key = VerifyingKey::from_sec1_bytes(&uncompressed_pk).ok()?;
+
+ public_key.verify_prehash(message_hash, &signature).ok()
+ }
+}
+
+/// Implements RIP-7212 P256VERIFY precompile.
+/// https://github.com/ethereum/RIPs/blob/master/RIPS/rip-7212.md
+impl, G: Get> Precompile for P256Verify {
+ fn execute(handle: &mut impl PrecompileHandle) -> PrecompileResult {
+ Self::handle_cost(handle)?;
+
+ let result = if Self::verify_from_input(handle.input()).is_some() {
+ // If the signature verification process succeeds, it returns 1 in 32 bytes format.
+ let mut result = [0u8; 32];
+ result[31] = 1;
+
+ result.to_vec()
+ } else {
+ // If the signature verification process fails, it does not return any output data.
+ Vec::new()
+ };
+
+ Ok(PrecompileOutput { exit_status: ExitSucceed::Returned, output: result.to_vec() })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use fp_evm::Context;
+ use frame_support::parameter_types;
+ use hex_literal::hex;
+ use precompile_utils::testing::MockHandle;
+
+ parameter_types! {
+ pub const DummyWeight: Weight = Weight::from_parts(3450, 0);
+ pub const DummyGas: u64 = 3450;
+ }
+
+ fn prepare_handle(input: Vec, cost: u64) -> MockHandle {
+ let context: Context = Context {
+ address: Default::default(),
+ caller: Default::default(),
+ apparent_value: From::from(0),
+ };
+
+ let mut handle = MockHandle::new(Default::default(), context);
+ handle.input = input;
+ handle.gas_limit = cost;
+
+ handle
+ }
+
+ #[test]
+ fn test_valid_signature() {
+ // RIP-7212 test vectors: 2 valid, 3 invalid.
+ let inputs = vec![
+ (
+ true,
+ hex!(
+ "b5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f"
+ "319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d2621444"
+ "75b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fc"
+ "cf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb"
+ "36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1"
+ )
+ .to_vec(),
+ ),
+ (
+ true,
+ hex!(
+ "4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73b"
+ "d4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03"
+ "009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c61"
+ "8202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4"
+ "ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e"
+ )
+ .to_vec(),
+ ),
+ (
+ false,
+ hex!(
+ "afec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466"
+ "e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817cc"
+ "f50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de63"
+ "02b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d32"
+ "28d3b940258c75fe2a413cb70baa21dc2e352fc5"
+ )
+ .to_vec(),
+ ),
+ (
+ false,
+ hex!(
+ "3cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4"
+ "903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009d"
+ "f8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fc"
+ "fe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971"
+ "a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e"
+ )
+ .to_vec(),
+ ),
+ (false, hex!("4cee90eb86eaa050036147a12d49004b6a").to_vec()),
+ ];
+ for input in inputs {
+ let cost = 3450;
+ let mut handle = prepare_handle(input.1.clone(), cost);
+
+ let mut success_result = [0u8; 32];
+ success_result[31] = 1;
+
+ let unsuccessful_result = Vec::::new();
+
+ match (input.0, P256Verify::::execute(&mut handle)) {
+ (true, Ok(result)) => assert_eq!(result.output, success_result.to_vec()),
+ (false, Ok(result)) => assert_eq!(result.output, unsuccessful_result),
+ (_, Err(_)) => panic!("Test not expected to fail for input: {:?}", input),
+ }
+
+ assert_eq!(handle.gas_used, DummyGas::get());
+ }
+ }
+}
diff --git a/runtime/krest/Cargo.toml b/runtime/krest/Cargo.toml
index 23a67269..9ed751b4 100644
--- a/runtime/krest/Cargo.toml
+++ b/runtime/krest/Cargo.toml
@@ -123,6 +123,7 @@ pallet-evm-precompile-xtokens = { path = "../../precompiles/xtokens", default-fe
pallet-evm-precompile-xcm-utils = { path = "../../precompiles/xcm-utils", default-features = false }
pallet-evm-precompile-parachain-staking = { path = "../../precompiles/parachain-staking", default-features = false }
pallet-evm-precompile-vesting = { path = "../../precompiles/vesting", default-features = false }
+pallet-evm-precompile-p256verify = { path = "../../precompiles/p256verify", default-features = false }
pallet-evm-precompile-balances-erc20 = { path = "../../precompiles/balances-erc20", default-features = false}
runtime-common = { path = "../common", default-features = false }
pallet-scheduler = { workspace = true, default-features = false }
@@ -297,6 +298,7 @@ std = [
"pallet-evm-precompile-peaq-rbac/std",
"pallet-evm-precompile-parachain-staking/std",
"pallet-evm-precompile-vesting/std",
+ "pallet-evm-precompile-p256verify/std",
"pallet-xcm/std",
"inflation-manager/std",
"pallet-message-queue/std",
diff --git a/runtime/krest/src/precompiles.rs b/runtime/krest/src/precompiles.rs
index a5a18b17..6184310b 100644
--- a/runtime/krest/src/precompiles.rs
+++ b/runtime/krest/src/precompiles.rs
@@ -1,5 +1,5 @@
use crate::xcm_config::XcmConfig;
-use frame_support::parameter_types;
+use frame_support::{parameter_types, weights::Weight};
use pallet_evm_precompile_assets_erc20::Erc20AssetsPrecompileSet;
use pallet_evm_precompile_assets_factory::AssetsFactoryPrecompile;
use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata};
@@ -7,6 +7,7 @@ use pallet_evm_precompile_batch::BatchPrecompile;
use pallet_evm_precompile_blake2::Blake2F;
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
use pallet_evm_precompile_modexp::Modexp;
+use pallet_evm_precompile_p256verify::P256Verify;
use pallet_evm_precompile_parachain_staking::ParachainStakingPrecompile;
use pallet_evm_precompile_peaq_did::PeaqDIDPrecompile;
use pallet_evm_precompile_peaq_rbac::PeaqRbacPrecompile;
@@ -25,6 +26,24 @@ parameter_types! {
pub EVMAssetPrefix: &'static [u8] = ASSET_PRECOMPILE_ADDRESS_PREFIX;
}
+parameter_types! {
+ /// RIP-7212 P256VERIFY: 3450 EVM gas (spec constant, kept for tooling equivalence).
+ pub const P256VerifyGas: u64 = 3450;
+ /// The block-weight (DoS) meter is charged the REAL verify cost via
+ /// `record_external_cost`, NOT the 3450 gas -- 3450 is far below the cost of the
+ /// elliptic-curve work, so pricing the meter by gas alone would let a caller fill
+ /// blocks with underpriced verification.
+ ///
+ /// 2.5 ms is derived from measuring this exact `p256` verify path compiled to
+ /// wasm32 and executed under Cranelift (the runtime's execution mode): 2.79 ms
+ /// median on a development machine, which the parachain-staking benchmark shows
+ /// to be ~1.25x slower than the reference hardware, giving ~2.24 ms median and
+ /// ~2.58 ms p90 reference-equivalent. Rounded up for DoS headroom. Note this is
+ /// ~1.4x higher than the constant published for the same crate elsewhere; prefer
+ /// a benchmark generated on peaq reference hardware once one exists.
+ pub const P256VerifyWeight: Weight = Weight::from_parts(2_500_000_000, 0);
+}
+
/// ERC20 metadata for the native token.
pub struct NativeErc20Metadata;
@@ -72,6 +91,12 @@ pub type PeaqPrecompiles = PrecompileSetBuilder<
PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>,
PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>,
PrecompileAt, Blake2F, EthereumPrecompilesChecks>,
+ // RIP-7212 secp256r1 (P-256) signature verification.
+ PrecompileAt<
+ AddressU64<256>,
+ P256Verify,
+ EthereumPrecompilesChecks,
+ >,
// Non-Moonbeam specific nor Ethereum precompiles :
PrecompileAt<
AddressU64<1024>,
diff --git a/runtime/peaq-dev/Cargo.toml b/runtime/peaq-dev/Cargo.toml
index 0558e11e..481412b3 100644
--- a/runtime/peaq-dev/Cargo.toml
+++ b/runtime/peaq-dev/Cargo.toml
@@ -124,6 +124,7 @@ pallet-evm-precompile-xtokens = { path = "../../precompiles/xtokens", default-fe
pallet-evm-precompile-xcm-utils = { path = "../../precompiles/xcm-utils", default-features = false }
pallet-evm-precompile-parachain-staking = { path = "../../precompiles/parachain-staking", default-features = false }
pallet-evm-precompile-vesting = { path = "../../precompiles/vesting", default-features = false }
+pallet-evm-precompile-p256verify = { path = "../../precompiles/p256verify", default-features = false }
runtime-common = { path = "../common", default-features = false }
peaq-pallet-mor = { workspace = true, default-features = false }
xc-asset-config = { path = "../../pallets/xc-asset-config", default-features = false }
@@ -299,6 +300,7 @@ std = [
"pallet-evm-precompile-peaq-rbac/std",
"pallet-evm-precompile-parachain-staking/std",
"pallet-evm-precompile-vesting/std",
+ "pallet-evm-precompile-p256verify/std",
"pallet-xcm/std",
"inflation-manager/std",
"pallet-message-queue/std",
diff --git a/runtime/peaq-dev/src/precompiles.rs b/runtime/peaq-dev/src/precompiles.rs
index 38d9c4d6..c7b73d21 100644
--- a/runtime/peaq-dev/src/precompiles.rs
+++ b/runtime/peaq-dev/src/precompiles.rs
@@ -1,5 +1,5 @@
use crate::xcm_config::XcmConfig;
-use frame_support::parameter_types;
+use frame_support::{parameter_types, weights::Weight};
use pallet_evm_precompile_assets_erc20::Erc20AssetsPrecompileSet;
use pallet_evm_precompile_assets_factory::AssetsFactoryPrecompile;
use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata};
@@ -7,6 +7,7 @@ use pallet_evm_precompile_batch::BatchPrecompile;
use pallet_evm_precompile_blake2::Blake2F;
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
use pallet_evm_precompile_modexp::Modexp;
+use pallet_evm_precompile_p256verify::P256Verify;
use pallet_evm_precompile_parachain_staking::ParachainStakingPrecompile;
use pallet_evm_precompile_peaq_did::PeaqDIDPrecompile;
use pallet_evm_precompile_peaq_rbac::PeaqRbacPrecompile;
@@ -51,6 +52,24 @@ parameter_types! {
pub EVMAssetPrefix: &'static [u8] = ASSET_PRECOMPILE_ADDRESS_PREFIX;
}
+parameter_types! {
+ /// RIP-7212 P256VERIFY: 3450 EVM gas (spec constant, kept for tooling equivalence).
+ pub const P256VerifyGas: u64 = 3450;
+ /// The block-weight (DoS) meter is charged the REAL verify cost via
+ /// `record_external_cost`, NOT the 3450 gas -- 3450 is far below the cost of the
+ /// elliptic-curve work, so pricing the meter by gas alone would let a caller fill
+ /// blocks with underpriced verification.
+ ///
+ /// 2.5 ms is derived from measuring this exact `p256` verify path compiled to
+ /// wasm32 and executed under Cranelift (the runtime's execution mode): 2.79 ms
+ /// median on a development machine, which the parachain-staking benchmark shows
+ /// to be ~1.25x slower than the reference hardware, giving ~2.24 ms median and
+ /// ~2.58 ms p90 reference-equivalent. Rounded up for DoS headroom. Note this is
+ /// ~1.4x higher than the constant published for the same crate elsewhere; prefer
+ /// a benchmark generated on peaq reference hardware once one exists.
+ pub const P256VerifyWeight: Weight = Weight::from_parts(2_500_000_000, 0);
+}
+
/// The following distribution has been decided for the precompiles
/// 0-1023: Ethereum Mainnet Precompiles
/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither Peaq-dev specific
@@ -72,6 +91,12 @@ pub type PeaqPrecompiles = PrecompileSetBuilder<
PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>,
PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>,
PrecompileAt, Blake2F, EthereumPrecompilesChecks>,
+ // RIP-7212 secp256r1 (P-256) signature verification.
+ PrecompileAt<
+ AddressU64<256>,
+ P256Verify,
+ EthereumPrecompilesChecks,
+ >,
// Non-Moonbeam specific nor Ethereum precompiles :
PrecompileAt<
AddressU64<1024>,
diff --git a/runtime/peaq/Cargo.toml b/runtime/peaq/Cargo.toml
index 9b38660e..34fd6a48 100644
--- a/runtime/peaq/Cargo.toml
+++ b/runtime/peaq/Cargo.toml
@@ -122,6 +122,7 @@ pallet-evm-precompile-xtokens = { path = "../../precompiles/xtokens", default-fe
pallet-evm-precompile-xcm-utils = { path = "../../precompiles/xcm-utils", default-features = false }
pallet-evm-precompile-parachain-staking = { path = "../../precompiles/parachain-staking", default-features = false }
pallet-evm-precompile-vesting = { path = "../../precompiles/vesting", default-features = false }
+pallet-evm-precompile-p256verify = { path = "../../precompiles/p256verify", default-features = false }
pallet-evm-precompile-balances-erc20 = { path = "../../precompiles/balances-erc20", default-features = false}
runtime-common = { path = "../common", default-features = false }
pallet-scheduler = { workspace = true, default-features = false }
@@ -294,6 +295,7 @@ std = [
"pallet-evm-precompile-peaq-rbac/std",
"pallet-evm-precompile-parachain-staking/std",
"pallet-evm-precompile-vesting/std",
+ "pallet-evm-precompile-p256verify/std",
"pallet-xcm/std",
"inflation-manager/std",
"pallet-message-queue/std",
diff --git a/runtime/peaq/src/precompiles.rs b/runtime/peaq/src/precompiles.rs
index 9512f1df..7f7981a5 100644
--- a/runtime/peaq/src/precompiles.rs
+++ b/runtime/peaq/src/precompiles.rs
@@ -1,5 +1,5 @@
use crate::xcm_config::XcmConfig;
-use frame_support::parameter_types;
+use frame_support::{parameter_types, weights::Weight};
use pallet_evm_precompile_assets_erc20::Erc20AssetsPrecompileSet;
use pallet_evm_precompile_assets_factory::AssetsFactoryPrecompile;
use pallet_evm_precompile_balances_erc20::{Erc20BalancesPrecompile, Erc20Metadata};
@@ -7,6 +7,7 @@ use pallet_evm_precompile_batch::BatchPrecompile;
use pallet_evm_precompile_blake2::Blake2F;
use pallet_evm_precompile_bn128::{Bn128Add, Bn128Mul, Bn128Pairing};
use pallet_evm_precompile_modexp::Modexp;
+use pallet_evm_precompile_p256verify::P256Verify;
use pallet_evm_precompile_parachain_staking::ParachainStakingPrecompile;
use pallet_evm_precompile_peaq_did::PeaqDIDPrecompile;
use pallet_evm_precompile_peaq_rbac::PeaqRbacPrecompile;
@@ -25,6 +26,24 @@ parameter_types! {
pub EVMAssetPrefix: &'static [u8] = ASSET_PRECOMPILE_ADDRESS_PREFIX;
}
+parameter_types! {
+ /// RIP-7212 P256VERIFY: 3450 EVM gas (spec constant, kept for tooling equivalence).
+ pub const P256VerifyGas: u64 = 3450;
+ /// The block-weight (DoS) meter is charged the REAL verify cost via
+ /// `record_external_cost`, NOT the 3450 gas -- 3450 is far below the cost of the
+ /// elliptic-curve work, so pricing the meter by gas alone would let a caller fill
+ /// blocks with underpriced verification.
+ ///
+ /// 2.5 ms is derived from measuring this exact `p256` verify path compiled to
+ /// wasm32 and executed under Cranelift (the runtime's execution mode): 2.79 ms
+ /// median on a development machine, which the parachain-staking benchmark shows
+ /// to be ~1.25x slower than the reference hardware, giving ~2.24 ms median and
+ /// ~2.58 ms p90 reference-equivalent. Rounded up for DoS headroom. Note this is
+ /// ~1.4x higher than the constant published for the same crate elsewhere; prefer
+ /// a benchmark generated on peaq reference hardware once one exists.
+ pub const P256VerifyWeight: Weight = Weight::from_parts(2_500_000_000, 0);
+}
+
/// ERC20 metadata for the native token.
pub struct NativeErc20Metadata;
@@ -72,6 +91,12 @@ pub type PeaqPrecompiles = PrecompileSetBuilder<
PrecompileAt, Bn128Mul, EthereumPrecompilesChecks>,
PrecompileAt, Bn128Pairing, EthereumPrecompilesChecks>,
PrecompileAt, Blake2F, EthereumPrecompilesChecks>,
+ // RIP-7212 secp256r1 (P-256) signature verification.
+ PrecompileAt<
+ AddressU64<256>,
+ P256Verify,
+ EthereumPrecompilesChecks,
+ >,
// Non-Moonbeam specific nor Ethereum precompiles :
PrecompileAt<
AddressU64<1024>,