diff --git a/Cargo.lock b/Cargo.lock index cc0d04f62..6c1146640 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2616,6 +2616,7 @@ dependencies = [ "digest 0.10.7", "elliptic-curve", "rfc6979", + "serdect", "signature", "spki", ] @@ -2690,6 +2691,7 @@ dependencies = [ "pkcs8", "rand_core 0.6.4", "sec1", + "serdect", "subtle", "zeroize", ] @@ -4622,6 +4624,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "once_cell", + "serdect", "sha2 0.10.8", "signature", ] @@ -8741,7 +8744,6 @@ dependencies = [ "c-kzg", "criterion", "derive_more", - "k256", "modular-bitfield", "once_cell", "op-alloy-consensus", @@ -8783,6 +8785,7 @@ dependencies = [ "byteorder", "bytes", "derive_more", + "k256", "modular-bitfield", "op-alloy-consensus", "proptest", @@ -8790,6 +8793,7 @@ dependencies = [ "rand 0.8.5", "reth-codecs", "revm-primitives", + "secp256k1", "serde", "serde_json", "serde_with", @@ -10179,6 +10183,7 @@ dependencies = [ "der", "generic-array", "pkcs8", + "serdect", "subtle", "zeroize", ] @@ -10385,6 +10390,16 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "serdect" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a84f14a19e9a014bb9f4512488d9829a68e04ecabffb0f9904cd1ace94598177" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "serial_test" version = "3.2.0" diff --git a/crates/primitives-traits/Cargo.toml b/crates/primitives-traits/Cargo.toml index 459fdbde1..2470f9308 100644 --- a/crates/primitives-traits/Cargo.toml +++ b/crates/primitives-traits/Cargo.toml @@ -26,6 +26,12 @@ revm-primitives.workspace = true # op op-alloy-consensus = { workspace = true, optional = true } +# crypto +secp256k1 = { workspace = true, features = [ + "recovery", +], optional = true } +k256.workspace = true + # misc byteorder = { workspace = true, optional = true } bytes.workspace = true @@ -67,8 +73,11 @@ std = [ "serde_with?/std", "alloy-rlp/std", "bytes/std", - "derive_more/std" + "derive_more/std", + "k256/std", + "secp256k1?/std" ] +secp256k1 = ["dep:secp256k1"] test-utils = [ "arbitrary", "reth-codecs?/test-utils" @@ -102,7 +111,9 @@ serde = [ "reth-codecs?/serde", "revm-primitives/serde", "revm-primitives/serde", - "op-alloy-consensus?/serde" + "op-alloy-consensus?/serde", + "k256/serde", + "secp256k1?/serde" ] reth-codec = [ "dep:reth-codecs", diff --git a/crates/primitives-traits/src/crypto.rs b/crates/primitives-traits/src/crypto.rs new file mode 100644 index 000000000..cc9c8aaf3 --- /dev/null +++ b/crates/primitives-traits/src/crypto.rs @@ -0,0 +1,213 @@ +//! Crypto utilities. + +use crate::transaction::signature::Signature; +use alloy_primitives::U256; + +/// The order of the secp256k1 curve, divided by two. Signatures that should be checked according +/// to EIP-2 should have an S value less than or equal to this. +/// +/// `57896044618658097711785492504343953926418782139537452191302581570759080747168` +pub const SECP256K1N_HALF: U256 = U256::from_be_bytes([ + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0, +]); + +/// Secp256k1 utility functions. +#[cfg(feature = "secp256k1")] +pub mod secp256k1 { + pub use super::impl_secp256k1::*; +} + +/// Secp256k1 utility functions. +#[cfg(not(feature = "secp256k1"))] +pub mod secp256k1 { + pub use super::impl_k256::*; +} + +#[cfg(feature = "secp256k1")] +#[allow(unused)] +mod impl_secp256k1 { + use super::*; + pub(crate) use ::secp256k1::Error; + use ::secp256k1::{ + ecdsa::{RecoverableSignature, RecoveryId}, + Message, PublicKey, SecretKey, SECP256K1, + }; + use alloy_primitives::{keccak256, Address, B256, U256}; + + /// Recovers the address of the sender using secp256k1 pubkey recovery. + /// + /// Converts the public key into an ethereum address by hashing the public key with keccak256. + /// + /// This does not ensure that the `s` value in the signature is low, and _just_ wraps the + /// underlying secp256k1 library. + pub fn recover_signer_unchecked(sig: &[u8; 65], msg: &[u8; 32]) -> Result { + let sig = + RecoverableSignature::from_compact(&sig[0..64], RecoveryId::from_i32(sig[64] as i32)?)?; + + let public = SECP256K1.recover_ecdsa(&Message::from_digest(*msg), &sig)?; + Ok(public_key_to_address(public)) + } + + /// Signs message with the given secret key. + /// Returns the corresponding signature. + pub fn sign_message(secret: B256, message: B256) -> Result { + let sec = SecretKey::from_slice(secret.as_ref())?; + let s = SECP256K1.sign_ecdsa_recoverable(&Message::from_digest(message.0), &sec); + let (rec_id, data) = s.serialize_compact(); + + let signature = Signature::new( + U256::try_from_be_slice(&data[..32]).expect("The slice has at most 32 bytes"), + U256::try_from_be_slice(&data[32..64]).expect("The slice has at most 32 bytes"), + rec_id.to_i32() != 0, + ); + Ok(signature) + } + + /// Converts a public key into an ethereum address by hashing the encoded public key with + /// keccak256. + pub fn public_key_to_address(public: PublicKey) -> Address { + // strip out the first byte because that should be the SECP256K1_TAG_PUBKEY_UNCOMPRESSED + // tag returned by libsecp's uncompressed pubkey serialization + let hash = keccak256(&public.serialize_uncompressed()[1..]); + Address::from_slice(&hash[12..]) + } +} + +#[cfg_attr(feature = "secp256k1", allow(unused, unreachable_pub))] +mod impl_k256 { + use super::*; + use alloy_primitives::{keccak256, Address, B256}; + pub(crate) use k256::ecdsa::Error; + use k256::ecdsa::{RecoveryId, SigningKey, VerifyingKey}; + + /// Recovers the address of the sender using secp256k1 pubkey recovery. + /// + /// Converts the public key into an ethereum address by hashing the public key with keccak256. + /// + /// This does not ensure that the `s` value in the signature is low, and _just_ wraps the + /// underlying secp256k1 library. + pub fn recover_signer_unchecked(sig: &[u8; 65], msg: &[u8; 32]) -> Result { + let mut signature = k256::ecdsa::Signature::from_slice(&sig[0..64])?; + let mut recid = sig[64]; + + // normalize signature and flip recovery id if needed. + if let Some(sig_normalized) = signature.normalize_s() { + signature = sig_normalized; + recid ^= 1; + } + let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid"); + + // recover key + let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &signature, recid)?; + Ok(public_key_to_address(recovered_key)) + } + + /// Signs message with the given secret key. + /// Returns the corresponding signature. + pub fn sign_message(secret: B256, message: B256) -> Result { + let sec = SigningKey::from_slice(secret.as_ref())?; + sec.sign_prehash_recoverable(&message.0).map(Into::into) + } + + /// Converts a public key into an ethereum address by hashing the encoded public key with + /// keccak256. + pub fn public_key_to_address(public: VerifyingKey) -> Address { + let hash = keccak256(&public.to_encoded_point(/* compress = */ false).as_bytes()[1..]); + Address::from_slice(&hash[12..]) + } +} + +#[cfg(test)] +mod tests { + use alloy_primitives::{keccak256, B256}; + + #[cfg(feature = "secp256k1")] + #[test] + fn sanity_ecrecover_call_secp256k1() { + use super::impl_secp256k1::*; + + let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng()); + let signer = public_key_to_address(public); + + let message = b"hello world"; + let hash = keccak256(message); + let signature = + sign_message(B256::from_slice(&secret.secret_bytes()[..]), hash).expect("sign message"); + + let mut sig: [u8; 65] = [0; 65]; + sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>()); + sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>()); + sig[64] = signature.v() as u8; + + assert_eq!(recover_signer_unchecked(&sig, &hash), Ok(signer)); + } + + #[cfg(not(feature = "secp256k1"))] + #[test] + fn sanity_ecrecover_call_k256() { + use super::impl_k256::*; + + let secret = k256::ecdsa::SigningKey::random(&mut rand::thread_rng()); + let public = *secret.verifying_key(); + let signer = public_key_to_address(public); + + let message = b"hello world"; + let hash = keccak256(message); + let signature = + sign_message(B256::from_slice(&secret.to_bytes()[..]), hash).expect("sign message"); + + let mut sig: [u8; 65] = [0; 65]; + sig[0..32].copy_from_slice(&signature.r.to_be_bytes::<32>()); + sig[32..64].copy_from_slice(&signature.s.to_be_bytes::<32>()); + sig[64] = signature.odd_y_parity as u8; + + assert_eq!(recover_signer_unchecked(&sig, &hash).ok(), Some(signer)); + } + + #[test] + fn sanity_secp256k1_k256_compat() { + use super::{impl_k256, impl_secp256k1}; + + let (secp256k1_secret, secp256k1_public) = + secp256k1::generate_keypair(&mut rand::thread_rng()); + let k256_secret = k256::ecdsa::SigningKey::from_slice(&secp256k1_secret.secret_bytes()) + .expect("k256 secret"); + let k256_public = *k256_secret.verifying_key(); + + let secp256k1_signer = impl_secp256k1::public_key_to_address(secp256k1_public); + let k256_signer = impl_k256::public_key_to_address(k256_public); + assert_eq!(secp256k1_signer, k256_signer); + + let message = b"hello world"; + let hash = keccak256(message); + + let secp256k1_signature = impl_secp256k1::sign_message( + B256::from_slice(&secp256k1_secret.secret_bytes()[..]), + hash, + ) + .expect("secp256k1 sign"); + let k256_signature = + impl_k256::sign_message(B256::from_slice(&k256_secret.to_bytes()[..]), hash) + .expect("k256 sign"); + assert_eq!(secp256k1_signature, k256_signature); + + let mut sig: [u8; 65] = [0; 65]; + + sig[0..32].copy_from_slice(&secp256k1_signature.r().to_be_bytes::<32>()); + sig[32..64].copy_from_slice(&secp256k1_signature.s().to_be_bytes::<32>()); + sig[64] = secp256k1_signature.v() as u8; + let secp256k1_recovered = + impl_secp256k1::recover_signer_unchecked(&sig, &hash).expect("secp256k1 recover"); + assert_eq!(secp256k1_recovered, secp256k1_signer); + + sig[0..32].copy_from_slice(&k256_signature.r().to_be_bytes::<32>()); + sig[32..64].copy_from_slice(&k256_signature.s().to_be_bytes::<32>()); + sig[64] = k256_signature.v() as u8; + let k256_recovered = + impl_k256::recover_signer_unchecked(&sig, &hash).expect("k256 recover"); + assert_eq!(k256_recovered, k256_signer); + + assert_eq!(secp256k1_recovered, k256_recovered); + } +} diff --git a/crates/primitives-traits/src/lib.rs b/crates/primitives-traits/src/lib.rs index b620bc535..74d10fc3c 100644 --- a/crates/primitives-traits/src/lib.rs +++ b/crates/primitives-traits/src/lib.rs @@ -1,4 +1,21 @@ -//! Common abstracted types in Reth. +//! Commonly used types and traits in Reth. +//! +//! This crate contains various primitive traits used across reth's components. +//! It provides the [`Block`] trait which is used to represent a block and all its components. +//! A [`Block`] is composed of a [`Header`] and a [`BlockBody`]. In ethereum (and optimism), a block +//! body consists of a list of transactions, a list of uncle headers, and a list of withdrawals. For +//! optimism, uncle headers and withdrawals are always empty lists. +//! +//! ## Feature Flags +//! +//! - `arbitrary`: Adds `proptest` and `arbitrary` support for primitive types. +//! - `op`: Implements the traits for various [op-alloy](https://github.com/alloy-rs/op-alloy) +//! types. +//! - `reth-codec`: Enables db codec support for reth types including zstd compression for certain +//! types. +//! - `serde`: Adds serde support for all types. +//! - `secp256k1`: Adds secp256k1 support for transaction signing/recovery. (By default the no-std +//! friendly `k256` is used) #![doc( html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", @@ -41,6 +58,8 @@ mod encoded; mod withdrawal; pub use encoded::WithEncoded; +pub mod crypto; + mod error; pub use error::{GotExpected, GotExpectedBoxed}; diff --git a/crates/primitives-traits/src/transaction/mod.rs b/crates/primitives-traits/src/transaction/mod.rs index 874fd494f..a75e371e9 100644 --- a/crates/primitives-traits/src/transaction/mod.rs +++ b/crates/primitives-traits/src/transaction/mod.rs @@ -1,6 +1,7 @@ //! Transaction abstraction pub mod execute; +pub mod signature; pub mod signed; pub mod error; diff --git a/crates/primitives-traits/src/transaction/signature.rs b/crates/primitives-traits/src/transaction/signature.rs new file mode 100644 index 000000000..1ff56671b --- /dev/null +++ b/crates/primitives-traits/src/transaction/signature.rs @@ -0,0 +1,4 @@ +//! Signature types and helpers + +/// Re-exported signature type +pub use alloy_primitives::PrimitiveSignature as Signature; diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 109b20ec2..712abd652 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -37,15 +37,15 @@ op-alloy-consensus = { workspace = true, features = [ "serde", ], optional = true } +# for eip-4844 +c-kzg = { workspace = true, features = ["serde"], optional = true } + # crypto secp256k1 = { workspace = true, features = [ "global-context", "recovery", "rand", ], optional = true } -k256.workspace = true -# for eip-4844 -c-kzg = { workspace = true, features = ["serde"], optional = true } # misc bytes.workspace = true @@ -73,6 +73,12 @@ alloy-eips = { workspace = true, features = ["arbitrary"] } alloy-genesis.workspace = true arbitrary = { workspace = true, features = ["derive"] } +secp256k1 = { workspace = true, features = [ + "global-context", + "recovery", + "rand", +] } + assert_matches.workspace = true bincode.workspace = true proptest-arbitrary-interop.workspace = true @@ -98,10 +104,8 @@ std = [ "alloy-genesis/std", "alloy-primitives/std", "alloy-serde?/std", - "k256/std", "once_cell/std", "revm-primitives/std", - "secp256k1?/std", "serde/std", "alloy-trie/std", "serde_with?/std", @@ -109,7 +113,8 @@ std = [ "reth-ethereum-forks/std", "bytes/std", "derive_more/std", - "reth-zstd-compressors?/std" + "reth-zstd-compressors?/std", + "secp256k1?/std" ] reth-codec = [ "dep:reth-codecs", @@ -126,7 +131,6 @@ arbitrary = [ "reth-ethereum-forks/arbitrary", "reth-primitives-traits/arbitrary", "revm-primitives/arbitrary", - "secp256k1", "reth-chainspec/arbitrary", "alloy-consensus/arbitrary", "alloy-primitives/arbitrary", @@ -136,9 +140,12 @@ arbitrary = [ "op-alloy-rpc-types?/arbitrary", "reth-codecs?/arbitrary", "alloy-trie/arbitrary", - "reth-trie-common/arbitrary" + "reth-trie-common/arbitrary", + "dep:secp256k1" +] +secp256k1 = [ + "reth-primitives-traits/secp256k1", ] -secp256k1 = ["dep:secp256k1"] c-kzg = [ "dep:c-kzg", "alloy-consensus/kzg", diff --git a/crates/primitives/src/transaction/signature.rs b/crates/primitives/src/transaction/signature.rs index 6056266ae..f2850aeca 100644 --- a/crates/primitives/src/transaction/signature.rs +++ b/crates/primitives/src/transaction/signature.rs @@ -2,15 +2,7 @@ use crate::transaction::util::secp256k1; use alloy_consensus::transaction::from_eip155_value; use alloy_primitives::{Address, PrimitiveSignature as Signature, B256, U256}; use alloy_rlp::Decodable; - -/// The order of the secp256k1 curve, divided by two. Signatures that should be checked according -/// to EIP-2 should have an S value less than or equal to this. -/// -/// `57896044618658097711785492504343953926418782139537452191302581570759080747168` -const SECP256K1N_HALF: U256 = U256::from_be_bytes([ - 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0, -]); +use reth_primitives_traits::crypto::SECP256K1N_HALF; pub(crate) fn decode_with_eip155_chain_id( buf: &mut &[u8], diff --git a/crates/primitives/src/transaction/util.rs b/crates/primitives/src/transaction/util.rs index 8eb1a639d..98c8468cf 100644 --- a/crates/primitives/src/transaction/util.rs +++ b/crates/primitives/src/transaction/util.rs @@ -1,201 +1,3 @@ //! Utility functions for signature. -use alloy_primitives::{Address, PrimitiveSignature as Signature}; - -/// Secp256k1 utility functions. -#[cfg(feature = "secp256k1")] -pub mod secp256k1 { - pub use super::impl_secp256k1::*; -} - -#[cfg(not(feature = "secp256k1"))] -pub(crate) mod secp256k1 { - pub use super::impl_k256::*; -} - -#[cfg(feature = "secp256k1")] -mod impl_secp256k1 { - use super::*; - pub(crate) use ::secp256k1::Error; - use ::secp256k1::{ - ecdsa::{RecoverableSignature, RecoveryId}, - Message, PublicKey, SecretKey, SECP256K1, - }; - use alloy_primitives::{keccak256, B256, U256}; - - /// Recovers the address of the sender using secp256k1 pubkey recovery. - /// - /// Converts the public key into an ethereum address by hashing the public key with keccak256. - /// - /// This does not ensure that the `s` value in the signature is low, and _just_ wraps the - /// underlying secp256k1 library. - pub fn recover_signer_unchecked(sig: &[u8; 65], msg: &[u8; 32]) -> Result { - let sig = - RecoverableSignature::from_compact(&sig[0..64], RecoveryId::from_i32(sig[64] as i32)?)?; - - let public = SECP256K1.recover_ecdsa(&Message::from_digest(*msg), &sig)?; - Ok(public_key_to_address(public)) - } - - /// Signs message with the given secret key. - /// Returns the corresponding signature. - pub fn sign_message(secret: B256, message: B256) -> Result { - let sec = SecretKey::from_slice(secret.as_ref())?; - let s = SECP256K1.sign_ecdsa_recoverable(&Message::from_digest(message.0), &sec); - let (rec_id, data) = s.serialize_compact(); - - let signature = Signature::new( - U256::try_from_be_slice(&data[..32]).expect("The slice has at most 32 bytes"), - U256::try_from_be_slice(&data[32..64]).expect("The slice has at most 32 bytes"), - rec_id.to_i32() != 0, - ); - Ok(signature) - } - - /// Converts a public key into an ethereum address by hashing the encoded public key with - /// keccak256. - pub fn public_key_to_address(public: PublicKey) -> Address { - // strip out the first byte because that should be the SECP256K1_TAG_PUBKEY_UNCOMPRESSED - // tag returned by libsecp's uncompressed pubkey serialization - let hash = keccak256(&public.serialize_uncompressed()[1..]); - Address::from_slice(&hash[12..]) - } -} - -#[cfg_attr(feature = "secp256k1", allow(unused, unreachable_pub))] -mod impl_k256 { - use super::*; - use alloy_primitives::{keccak256, B256}; - pub(crate) use k256::ecdsa::Error; - use k256::ecdsa::{RecoveryId, SigningKey, VerifyingKey}; - - /// Recovers the address of the sender using secp256k1 pubkey recovery. - /// - /// Converts the public key into an ethereum address by hashing the public key with keccak256. - /// - /// This does not ensure that the `s` value in the signature is low, and _just_ wraps the - /// underlying secp256k1 library. - pub fn recover_signer_unchecked(sig: &[u8; 65], msg: &[u8; 32]) -> Result { - let mut signature = k256::ecdsa::Signature::from_slice(&sig[0..64])?; - let mut recid = sig[64]; - - // normalize signature and flip recovery id if needed. - if let Some(sig_normalized) = signature.normalize_s() { - signature = sig_normalized; - recid ^= 1; - } - let recid = RecoveryId::from_byte(recid).expect("recovery ID is valid"); - - // recover key - let recovered_key = VerifyingKey::recover_from_prehash(&msg[..], &signature, recid)?; - Ok(public_key_to_address(recovered_key)) - } - - /// Signs message with the given secret key. - /// Returns the corresponding signature. - pub fn sign_message(secret: B256, message: B256) -> Result { - let sec = SigningKey::from_slice(secret.as_ref())?; - sec.sign_prehash_recoverable(&message.0).map(Into::into) - } - - /// Converts a public key into an ethereum address by hashing the encoded public key with - /// keccak256. - pub fn public_key_to_address(public: VerifyingKey) -> Address { - let hash = keccak256(&public.to_encoded_point(/* compress = */ false).as_bytes()[1..]); - Address::from_slice(&hash[12..]) - } -} - -#[cfg(test)] -mod tests { - use alloy_primitives::{keccak256, B256}; - - #[cfg(feature = "secp256k1")] - #[test] - fn sanity_ecrecover_call_secp256k1() { - use super::impl_secp256k1::*; - - let (secret, public) = secp256k1::generate_keypair(&mut rand::thread_rng()); - let signer = public_key_to_address(public); - - let message = b"hello world"; - let hash = keccak256(message); - let signature = - sign_message(B256::from_slice(&secret.secret_bytes()[..]), hash).expect("sign message"); - - let mut sig: [u8; 65] = [0; 65]; - sig[0..32].copy_from_slice(&signature.r().to_be_bytes::<32>()); - sig[32..64].copy_from_slice(&signature.s().to_be_bytes::<32>()); - sig[64] = signature.v() as u8; - - assert_eq!(recover_signer_unchecked(&sig, &hash), Ok(signer)); - } - - #[cfg(not(feature = "secp256k1"))] - #[test] - fn sanity_ecrecover_call_k256() { - use super::impl_k256::*; - - let secret = k256::ecdsa::SigningKey::random(&mut rand::thread_rng()); - let public = *secret.verifying_key(); - let signer = public_key_to_address(public); - - let message = b"hello world"; - let hash = keccak256(message); - let signature = - sign_message(B256::from_slice(&secret.to_bytes()[..]), hash).expect("sign message"); - - let mut sig: [u8; 65] = [0; 65]; - sig[0..32].copy_from_slice(&signature.r.to_be_bytes::<32>()); - sig[32..64].copy_from_slice(&signature.s.to_be_bytes::<32>()); - sig[64] = signature.odd_y_parity as u8; - - assert_eq!(recover_signer_unchecked(&sig, &hash).ok(), Some(signer)); - } - - #[test] - fn sanity_secp256k1_k256_compat() { - use super::{impl_k256, impl_secp256k1}; - - let (secp256k1_secret, secp256k1_public) = - secp256k1::generate_keypair(&mut rand::thread_rng()); - let k256_secret = k256::ecdsa::SigningKey::from_slice(&secp256k1_secret.secret_bytes()) - .expect("k256 secret"); - let k256_public = *k256_secret.verifying_key(); - - let secp256k1_signer = impl_secp256k1::public_key_to_address(secp256k1_public); - let k256_signer = impl_k256::public_key_to_address(k256_public); - assert_eq!(secp256k1_signer, k256_signer); - - let message = b"hello world"; - let hash = keccak256(message); - - let secp256k1_signature = impl_secp256k1::sign_message( - B256::from_slice(&secp256k1_secret.secret_bytes()[..]), - hash, - ) - .expect("secp256k1 sign"); - let k256_signature = - impl_k256::sign_message(B256::from_slice(&k256_secret.to_bytes()[..]), hash) - .expect("k256 sign"); - assert_eq!(secp256k1_signature, k256_signature); - - let mut sig: [u8; 65] = [0; 65]; - - sig[0..32].copy_from_slice(&secp256k1_signature.r().to_be_bytes::<32>()); - sig[32..64].copy_from_slice(&secp256k1_signature.s().to_be_bytes::<32>()); - sig[64] = secp256k1_signature.v() as u8; - let secp256k1_recovered = - impl_secp256k1::recover_signer_unchecked(&sig, &hash).expect("secp256k1 recover"); - assert_eq!(secp256k1_recovered, secp256k1_signer); - - sig[0..32].copy_from_slice(&k256_signature.r().to_be_bytes::<32>()); - sig[32..64].copy_from_slice(&k256_signature.s().to_be_bytes::<32>()); - sig[64] = k256_signature.v() as u8; - let k256_recovered = - impl_k256::recover_signer_unchecked(&sig, &hash).expect("k256 recover"); - assert_eq!(k256_recovered, k256_signer); - - assert_eq!(secp256k1_recovered, k256_recovered); - } -} +pub use reth_primitives_traits::crypto::*;