From c4a2e495c0fb2b40e9238afb0aa7b84850733632 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Wed, 6 Mar 2024 20:44:04 +0100 Subject: [PATCH 01/10] feat: integrate secp256r1 precompile --- Cargo.lock | 42 ++++++++++++++++++++ Cargo.toml | 3 +- crates/node/Cargo.toml | 1 + crates/node/src/evm.rs | 16 ++------ crates/precompile/Cargo.toml | 18 +++++++++ crates/precompile/src/lib.rs | 19 +++++++++ crates/precompile/src/secp256r1.rs | 63 ++++++++++++++++++++++++++++++ 7 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 crates/precompile/Cargo.toml create mode 100644 crates/precompile/src/lib.rs create mode 100644 crates/precompile/src/secp256r1.rs diff --git a/Cargo.lock b/Cargo.lock index b10bf41..03ce5f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -347,11 +347,21 @@ dependencies = [ name = "alphanet-node" version = "0.0.0" dependencies = [ + "alphanet-precompile", "reth", "reth-node-api", "reth-node-optimism", ] +[[package]] +name = "alphanet-precompile" +version = "0.0.0" +dependencies = [ + "p256", + "reth", + "revm-primitives", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -1640,6 +1650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", + "pem-rfc7468", "zeroize", ] @@ -1925,6 +1936,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core 0.6.4", "sec1", @@ -3832,6 +3844,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", +] + [[package]] name = "page_size" version = "0.6.0" @@ -3952,6 +3976,15 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -4125,6 +4158,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" diff --git a/Cargo.toml b/Cargo.toml index 6c19ceb..ab40905 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["bin/alphanet/", "crates/node"] +members = ["bin/alphanet/", "crates/node", "crates/precompile"] default-members = ["bin/alphanet/"] resolver = "2" @@ -43,6 +43,7 @@ incremental = false [workspace.dependencies] # alphanet alphanet-node = { path = "crates/node" } +alphanet-precompile = { path = "crates/precompile" } # tokio tokio = { version = "1.21", default-features = false } diff --git a/crates/node/Cargo.toml b/crates/node/Cargo.toml index 699ac02..82aa642 100644 --- a/crates/node/Cargo.toml +++ b/crates/node/Cargo.toml @@ -10,6 +10,7 @@ keywords.workspace = true categories.workspace = true [dependencies] +alphanet-precompile.workspace = true reth.workspace = true reth-node-api.workspace = true reth-node-optimism.workspace = true diff --git a/crates/node/src/evm.rs b/crates/node/src/evm.rs index 743a853..465a3e5 100644 --- a/crates/node/src/evm.rs +++ b/crates/node/src/evm.rs @@ -1,13 +1,13 @@ +use alphanet_precompile::secp256r1::P256VERIFY; use reth::{ primitives::{ - address, revm::{config::revm_spec, env::fill_op_tx_env}, - revm_primitives::{AnalysisKind, CfgEnvWithHandlerCfg, Env, PrecompileResult, TxEnv}, + revm_primitives::{AnalysisKind, CfgEnvWithHandlerCfg, TxEnv}, Address, Bytes, ChainSpec, Head, Header, Transaction, U256, }, revm::{ handler::register::EvmHandler, - precompile::{Precompile, PrecompileSpecId, Precompiles}, + precompile::{PrecompileSpecId, Precompiles}, Database, Evm, EvmBuilder, }, }; @@ -36,18 +36,10 @@ impl AlphaNetEvmConfig { // install the precompiles handler.pre_execution.load_precompiles = Arc::new(move || { let mut precompiles = Precompiles::new(PrecompileSpecId::from_spec_id(spec_id)).clone(); - precompiles.inner.insert( - address!("0000000000000000000000000000000000000999"), - Precompile::Env(Self::alphanet_precompile), - ); + precompiles.inner.insert(P256VERIFY.0, P256VERIFY.1); precompiles }); } - - /// A custom precompile that does nothing - fn alphanet_precompile(_data: &Bytes, _gas: u64, _env: &Env) -> PrecompileResult { - Ok((0, Bytes::new())) - } } impl ConfigureEvm for AlphaNetEvmConfig { diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml new file mode 100644 index 0000000..fab3fc4 --- /dev/null +++ b/crates/precompile/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "alphanet-precompile" +version.workspace = true +edition.workspace = true +rust-version.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +keywords.workspace = true +categories.workspace = true + +[dependencies] +p256 = { version = "0.13.2", features = ["ecdsa"] } +reth.workspace = true +revm-primitives.workspace = true + +[lints] +workspace = true diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs new file mode 100644 index 0000000..8c8f309 --- /dev/null +++ b/crates/precompile/src/lib.rs @@ -0,0 +1,19 @@ +//! # alphanet-precompile +//! +//! Implementations of EVM precompiled contracts for AlphaNet. + +/// EIP-7212 secp256r1 precompile. +pub mod secp256r1; + +/// Const function for making an address by concatenating the bytes from two given numbers. +/// +/// Note that 32 + 128 = 160 = 20 bytes (the length of an address). This function is used +/// as a convenience for specifying the addresses of the various precompiles. +use revm_primitives::Address; +#[inline] +const fn u64_to_address(x: u64) -> Address { + let x = x.to_be_bytes(); + Address::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], + ]) +} diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs new file mode 100644 index 0000000..072c69f --- /dev/null +++ b/crates/precompile/src/secp256r1.rs @@ -0,0 +1,63 @@ +use reth::revm::precompile::{Precompile, PrecompileWithAddress}; +use revm_primitives::{Bytes, PrecompileError, PrecompileResult, StandardPrecompileFn}; + +/// EIP-7212 secp256r1 precompile. +pub const P256VERIFY: PrecompileWithAddress = PrecompileWithAddress( + crate::u64_to_address(10), + Precompile::Standard(p256_verify as StandardPrecompileFn), +); + +fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { + use core::cmp::min; + use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}; + + const P256VERIFY_BASE: u64 = 3_450; + + if P256VERIFY_BASE > target_gas { + return Err(PrecompileError::OutOfGas); + } + let mut input = [0u8; 160]; + input[..min(i.len(), 160)].copy_from_slice(&i[..min(i.len(), 160)]); + + // msg signed (msg is already the hash of the original message) + let msg: [u8; 32] = input[..32].try_into().unwrap(); + // r, s: signature + let sig: [u8; 64] = input[32..96].try_into().unwrap(); + // x, y: public key + let pk: [u8; 64] = input[96..160].try_into().unwrap(); + // append 0x04 to the public key: uncompressed form + let mut uncompressed_pk = [0u8; 65]; + uncompressed_pk[0] = 0x04; + uncompressed_pk[1..].copy_from_slice(&pk); + + let signature: Signature = Signature::from_slice(&sig).unwrap(); + let public_key: VerifyingKey = VerifyingKey::from_sec1_bytes(&uncompressed_pk).unwrap(); + + let mut result = [0u8; 32]; + + // verify + if public_key.verify_prehash(&msg, &signature).is_ok() { + result[31] = 0x01; + Ok((P256VERIFY_BASE, result.into())) + } else { + Ok((P256VERIFY_BASE, result.into())) + } +} + +#[cfg(test)] +mod test { + use super::p256_verify; + use revm_primitives::Bytes; + + #[test] + #[ignore] + fn proper_sig_verify() { + let input = Bytes::from("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e"); + let target_gas = 3_500u64; + let (gas_used, res) = p256_verify(&input, target_gas).unwrap(); + assert_eq!(gas_used, 3_450u64); + let mut expected_res = [0u8; 32]; + expected_res[31] = 1; + assert_eq!(res, expected_res.to_vec()); + } +} From 5f2b30f542bb94944ab618f0e77ff5850e36b7c7 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 09:33:44 +0100 Subject: [PATCH 02/10] update precompile address --- crates/precompile/src/secp256r1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 072c69f..029f553 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -3,7 +3,7 @@ use revm_primitives::{Bytes, PrecompileError, PrecompileResult, StandardPrecompi /// EIP-7212 secp256r1 precompile. pub const P256VERIFY: PrecompileWithAddress = PrecompileWithAddress( - crate::u64_to_address(10), + crate::u64_to_address(11), /* 0x0b according to https://eips.ethereum.org/EIPS/eip-7212#specification */ Precompile::Standard(p256_verify as StandardPrecompileFn), ); From 9621117f9bce5b3ea99889105588527c3e28be41 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 09:50:04 +0100 Subject: [PATCH 03/10] fix and enable test --- crates/precompile/src/secp256r1.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 029f553..4e535d6 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -47,12 +47,11 @@ fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { #[cfg(test)] mod test { use super::p256_verify; - use revm_primitives::Bytes; + use revm_primitives::{hex::FromHex, Bytes}; #[test] - #[ignore] fn proper_sig_verify() { - let input = Bytes::from("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e"); + let input = Bytes::from_hex("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").unwrap(); let target_gas = 3_500u64; let (gas_used, res) = p256_verify(&input, target_gas).unwrap(); assert_eq!(gas_used, 3_450u64); From 203084d18769ab327eec9a53743747213ae7184c Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 10:54:54 +0100 Subject: [PATCH 04/10] add more tests --- Cargo.lock | 36 +++++++++++++++++++++++++++ Cargo.toml | 3 +++ crates/precompile/Cargo.toml | 3 +++ crates/precompile/src/secp256r1.rs | 40 ++++++++++++++++++++++++------ 4 files changed, 75 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 03ce5f2..84db568 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -360,6 +360,7 @@ dependencies = [ "p256", "reth", "revm-primitives", + "rstest", ] [[package]] @@ -4568,6 +4569,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "relative-path" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" + [[package]] name = "reqwest" version = "0.11.24" @@ -6002,6 +6009,35 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version 0.4.0", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version 0.4.0", + "syn 2.0.52", + "unicode-ident", +] + [[package]] name = "ruint" version = "1.12.0" diff --git a/Cargo.toml b/Cargo.toml index ab40905..8ed723c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -63,3 +63,6 @@ revm-primitives = { version = "2.1.0", features = ["std"], default-features = fa clap = "4" eyre = "0.6.12" tracing = "0.1.0" + +# misc-testing +rstest = "0.18.2" diff --git a/crates/precompile/Cargo.toml b/crates/precompile/Cargo.toml index fab3fc4..4bb4fce 100644 --- a/crates/precompile/Cargo.toml +++ b/crates/precompile/Cargo.toml @@ -14,5 +14,8 @@ p256 = { version = "0.13.2", features = ["ecdsa"] } reth.workspace = true revm-primitives.workspace = true +[dev-dependencies] +rstest.workspace = true + [lints] workspace = true diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 4e535d6..67c8815 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -47,16 +47,42 @@ fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { #[cfg(test)] mod test { use super::p256_verify; - use revm_primitives::{hex::FromHex, Bytes}; + use revm_primitives::{hex::FromHex, Bytes, PrecompileError}; + use rstest::rstest; - #[test] - fn proper_sig_verify() { - let input = Bytes::from_hex("4cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e").unwrap(); + #[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_1("3cee90eb86eaa050036147a12d49004b6b9c72bd725d39d4785011fe190f0b4da73bd4903f0ce3b639bbbf6e8e80d16931ff4bcf5993d58468e8fb19086e8cac36dbcd03009df8c59286b162af3bd7fcc0450c9aa81be5d10d312af6c66b1d604aebd3099c618202fcfe16ae7770b0c49ab5eadf74b754204a3bb6060e44eff37618b065f9832de4ca6ca971a7a1adc826d0f7c00181a5fb2ddf79ae00b4e10e", false)] + #[case::fail_2("afec5769b5cf4e310a7d150508e82fb8e3eda1c2c94c61492d3bd8aea99e06c9e22466e928fdccef0de49e3503d2657d00494a00e764fd437bdafa05f5922b1fbbb77c6817ccf50748419477e843d5bac67e6a70e97dde5a57e0c983b777e1ad31a80482dadf89de6302b1988c82c29544c9c07bb910596158f6062517eb089a2f54c9a0f348752950094d3228d3b940258c75fe2a413cb70baa21dc2e352fc5", false)] + #[case::fail_3("f775723953ead4a90411a02908fd1a629db584bc600664c609061f221ef6bf7c440066c8626b49daaa7bf2bcc0b74be4f7a1e3dcf0e869f1542fe821498cbf2de73ad398194129f635de4424a07ca715838aefe8fe69d1a391cfa70470795a80dd056866e6e1125aff94413921880c437c9e2570a28ced7267c8beef7e9b2d8d1547d76dfcf4bee592f5fefe10ddfb6aeb0991c5b9dbbee6ec80d11b17c0eb1a", false)] + #[case::fail_4("c5a77e7a90aa14e0bf5f337f06f597148676424fae26e175c6e5621c34351955289f319789da424845c9eac935245fcddd805950e2f02506d09be7e411199556d262144475b1fa46ad85250728c600c53dfd10f8b3f4adf140e27241aec3c2da3a81046703fccf468b48b145f939efdbb96c3786db712b3113bb2488ef286cdcef8afe82d200a5bb36b5462166e8ce77f2d831a52ef2135b2af188110beaefb1", false)] + #[case::fail_5("958b991cfd78f16537fe6d1f4afd10273384db08bdfc843562a22b0626766686f6aec8247599f40bfe01bec0e0ecf17b4319559022d4d9bf007fe929943004eb4866760dedf31b7c691f5ce665f8aae0bda895c23595c834fecc2390a5bcc203b04afcacbb4280713287a2d0c37e23f7513fab898f2c1fefa00ec09a924c335d9b629f1d4fb71901c3e59611afbfea354d101324e894c788d1c01f00b3c251b2", 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 mut expected_res = [0u8; 32]; - expected_res[31] = 1; - assert_eq!(res, expected_res.to_vec()); + let expected_result_str = if expect_success { + "0000000000000000000000000000000000000000000000000000000000000001" + } else { + "0000000000000000000000000000000000000000000000000000000000000000" + }; + let expected_result = Bytes::from_hex(expected_result_str).unwrap(); + assert_eq!(res, expected_result.to_vec()); + } + + #[rstest] + fn test_not_enough_gas_fails() { + 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)); } } From b5ef65a527fd4fb20e6d2ef425cfa95db672ea60 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 12:59:31 +0100 Subject: [PATCH 05/10] prevent copying bytes --- crates/precompile/src/secp256r1.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 67c8815..7b0b161 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -20,23 +20,23 @@ fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { input[..min(i.len(), 160)].copy_from_slice(&i[..min(i.len(), 160)]); // msg signed (msg is already the hash of the original message) - let msg: [u8; 32] = input[..32].try_into().unwrap(); + let msg: &[u8; 32] = input[..32].try_into().unwrap(); // r, s: signature - let sig: [u8; 64] = input[32..96].try_into().unwrap(); + let sig: &[u8; 64] = input[32..96].try_into().unwrap(); // x, y: public key - let pk: [u8; 64] = input[96..160].try_into().unwrap(); + let pk: &[u8; 64] = input[96..160].try_into().unwrap(); // append 0x04 to the public key: uncompressed form let mut uncompressed_pk = [0u8; 65]; uncompressed_pk[0] = 0x04; - uncompressed_pk[1..].copy_from_slice(&pk); + uncompressed_pk[1..].copy_from_slice(pk); - let signature: Signature = Signature::from_slice(&sig).unwrap(); + let signature: Signature = Signature::from_slice(sig).unwrap(); let public_key: VerifyingKey = VerifyingKey::from_sec1_bytes(&uncompressed_pk).unwrap(); let mut result = [0u8; 32]; // verify - if public_key.verify_prehash(&msg, &signature).is_ok() { + if public_key.verify_prehash(msg, &signature).is_ok() { result[31] = 0x01; Ok((P256VERIFY_BASE, result.into())) } else { From 8615a7a4bed7dfa859fffdf490fbface89333ab5 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 13:09:59 +0100 Subject: [PATCH 06/10] use pad utils from revm_precompile --- crates/precompile/src/secp256r1.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 7b0b161..068bec6 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -1,4 +1,4 @@ -use reth::revm::precompile::{Precompile, PrecompileWithAddress}; +use reth::revm::precompile::{utilities::right_pad, Precompile, PrecompileWithAddress}; use revm_primitives::{Bytes, PrecompileError, PrecompileResult, StandardPrecompileFn}; /// EIP-7212 secp256r1 precompile. @@ -8,7 +8,6 @@ pub const P256VERIFY: PrecompileWithAddress = PrecompileWithAddress( ); fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { - use core::cmp::min; use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}; const P256VERIFY_BASE: u64 = 3_450; @@ -16,8 +15,7 @@ fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { if P256VERIFY_BASE > target_gas { return Err(PrecompileError::OutOfGas); } - let mut input = [0u8; 160]; - input[..min(i.len(), 160)].copy_from_slice(&i[..min(i.len(), 160)]); + let input = right_pad::<160>(i); // msg signed (msg is already the hash of the original message) let msg: &[u8; 32] = input[..32].try_into().unwrap(); From 2ba34e6fccaecb743cc458d96bcac2afbf1a4f14 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 13:19:00 +0100 Subject: [PATCH 07/10] Update crates/precompile/src/secp256r1.rs Co-authored-by: DaniPopes <57450786+DaniPopes@users.noreply.github.com> --- crates/precompile/src/secp256r1.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 068bec6..90e0f7f 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -3,7 +3,7 @@ use revm_primitives::{Bytes, PrecompileError, PrecompileResult, StandardPrecompi /// EIP-7212 secp256r1 precompile. pub const P256VERIFY: PrecompileWithAddress = PrecompileWithAddress( - crate::u64_to_address(11), /* 0x0b according to https://eips.ethereum.org/EIPS/eip-7212#specification */ + crate::u64_to_address(0x0b), /* 0x0b according to https://eips.ethereum.org/EIPS/eip-7212#specification */ Precompile::Standard(p256_verify as StandardPrecompileFn), ); From b99172fed1b0fe5da33c1517dbcc89da10df3820 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 13:34:10 +0100 Subject: [PATCH 08/10] simplify result return --- crates/precompile/src/secp256r1.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 90e0f7f..48a396b 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -1,5 +1,5 @@ use reth::revm::precompile::{utilities::right_pad, Precompile, PrecompileWithAddress}; -use revm_primitives::{Bytes, PrecompileError, PrecompileResult, StandardPrecompileFn}; +use revm_primitives::{Bytes, PrecompileError, PrecompileResult, StandardPrecompileFn, B256}; /// EIP-7212 secp256r1 precompile. pub const P256VERIFY: PrecompileWithAddress = PrecompileWithAddress( @@ -31,15 +31,8 @@ fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { let signature: Signature = Signature::from_slice(sig).unwrap(); let public_key: VerifyingKey = VerifyingKey::from_sec1_bytes(&uncompressed_pk).unwrap(); - let mut result = [0u8; 32]; - - // verify - if public_key.verify_prehash(msg, &signature).is_ok() { - result[31] = 0x01; - Ok((P256VERIFY_BASE, result.into())) - } else { - Ok((P256VERIFY_BASE, result.into())) - } + let result = public_key.verify_prehash(msg, &signature).is_ok(); + Ok((P256VERIFY_BASE, B256::with_last_byte(result as u8).into())) } #[cfg(test)] From 37f4fd3f0ae2f9963c4ceb934a18dcf7e88e16ef Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 19:07:28 +0100 Subject: [PATCH 09/10] return verification failure on input len < 160 --- crates/precompile/src/secp256r1.rs | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index 48a396b..a9ab358 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -1,4 +1,4 @@ -use reth::revm::precompile::{utilities::right_pad, Precompile, PrecompileWithAddress}; +use reth::revm::precompile::{Precompile, PrecompileWithAddress}; use revm_primitives::{Bytes, PrecompileError, PrecompileResult, StandardPrecompileFn, B256}; /// EIP-7212 secp256r1 precompile. @@ -7,7 +7,7 @@ pub const P256VERIFY: PrecompileWithAddress = PrecompileWithAddress( Precompile::Standard(p256_verify as StandardPrecompileFn), ); -fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { +fn p256_verify(input: &Bytes, target_gas: u64) -> PrecompileResult { use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}; const P256VERIFY_BASE: u64 = 3_450; @@ -15,7 +15,9 @@ fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { if P256VERIFY_BASE > target_gas { return Err(PrecompileError::OutOfGas); } - let input = right_pad::<160>(i); + if input.len() < 160 { + return Ok((P256VERIFY_BASE, B256::ZERO.into())); + } // msg signed (msg is already the hash of the original message) let msg: &[u8; 32] = input[..32].try_into().unwrap(); @@ -38,7 +40,7 @@ fn p256_verify(i: &Bytes, target_gas: u64) -> PrecompileResult { #[cfg(test)] mod test { use super::p256_verify; - use revm_primitives::{hex::FromHex, Bytes, PrecompileError}; + use revm_primitives::{hex::FromHex, Bytes, PrecompileError, B256}; use rstest::rstest; #[rstest] @@ -58,17 +60,12 @@ mod test { let target_gas = 3_500u64; let (gas_used, res) = p256_verify(&input, target_gas).unwrap(); assert_eq!(gas_used, 3_450u64); - let expected_result_str = if expect_success { - "0000000000000000000000000000000000000000000000000000000000000001" - } else { - "0000000000000000000000000000000000000000000000000000000000000000" - }; - let expected_result = Bytes::from_hex(expected_result_str).unwrap(); + let expected_result = B256::with_last_byte(expect_success as u8); assert_eq!(res, expected_result.to_vec()); } #[rstest] - fn test_not_enough_gas_fails() { + 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); @@ -76,4 +73,14 @@ mod test { assert!(result.is_err()); assert_eq!(result.err(), Some(PrecompileError::OutOfGas)); } + + #[rstest] + fn test_smaller_input_fails() { + let input = Bytes::from_hex("4cee90eb86eaa050036147a12d49004b6a").unwrap(); + let target_gas = 3_500u64; + let result = p256_verify(&input, target_gas); + + assert!(result.is_ok()); + assert_eq!(result.ok(), Some((3_450u64, B256::ZERO.into()))); + } } From 4b31c8a57fa431962083fbcb5be9218b962faa11 Mon Sep 17 00:00:00 2001 From: Federico Gimenez Date: Thu, 7 Mar 2024 19:07:55 +0100 Subject: [PATCH 10/10] add input format doc --- crates/precompile/src/secp256r1.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index a9ab358..162abd9 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -7,6 +7,9 @@ pub const P256VERIFY: PrecompileWithAddress = PrecompileWithAddress( Precompile::Standard(p256_verify as StandardPrecompileFn), ); +/// The input is encoded as follows: +/// | signed msg hash | r | s | pk x | pk y | +/// | 32 | 32 | 32 | 32 | 32 | fn p256_verify(input: &Bytes, target_gas: u64) -> PrecompileResult { use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey};