diff --git a/Cargo.lock b/Cargo.lock
index 0e5fb4c0e5..ab16ea688e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9297,7 +9297,6 @@ dependencies = [
"byteorder",
"bytes",
"derive_more",
- "k256",
"modular-bitfield",
"once_cell",
"op-alloy-consensus",
diff --git a/crates/primitives-traits/Cargo.toml b/crates/primitives-traits/Cargo.toml
index 14747109eb..f8ee37fb01 100644
--- a/crates/primitives-traits/Cargo.toml
+++ b/crates/primitives-traits/Cargo.toml
@@ -16,7 +16,7 @@ workspace = true
reth-codecs = { workspace = true, optional = true }
# ethereum
-alloy-consensus.workspace = true
+alloy-consensus = { workspace = true, features = ["k256"] }
alloy-eips.workspace = true
alloy-genesis.workspace = true
alloy-primitives = { workspace = true, features = ["k256"] }
@@ -31,7 +31,6 @@ op-alloy-consensus = { workspace = true, optional = true }
# crypto
secp256k1 = { workspace = true, features = ["recovery"], optional = true }
-k256.workspace = true
# misc
auto_impl.workspace = true
@@ -85,7 +84,6 @@ std = [
"alloy-rlp/std",
"bytes/std",
"derive_more/std",
- "k256/std",
"once_cell/std",
"secp256k1?/std",
"thiserror/std",
@@ -96,7 +94,7 @@ std = [
"revm-bytecode/std",
"revm-state/std",
]
-secp256k1 = ["dep:secp256k1"]
+secp256k1 = ["alloy-consensus/secp256k1"]
test-utils = [
"arbitrary",
"reth-codecs?/test-utils",
@@ -137,7 +135,6 @@ serde = [
"revm-primitives/serde",
"revm-primitives/serde",
"op-alloy-consensus?/serde",
- "k256/serde",
"secp256k1?/serde",
"alloy-trie/serde",
"revm-bytecode/serde",
diff --git a/crates/primitives-traits/src/crypto.rs b/crates/primitives-traits/src/crypto.rs
index f80df6b4d2..9e95bd52f2 100644
--- a/crates/primitives-traits/src/crypto.rs
+++ b/crates/primitives-traits/src/crypto.rs
@@ -1,253 +1,3 @@
//! 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.
-pub mod secp256k1 {
- use super::*;
- use revm_primitives::{Address, B256};
-
- #[cfg(not(feature = "secp256k1"))]
- use super::impl_k256 as imp;
- #[cfg(feature = "secp256k1")]
- use super::impl_secp256k1 as imp;
-
- use crate::transaction::signed::RecoveryError;
- pub use imp::{public_key_to_address, sign_message};
-
- /// Recover signer from message hash, _without ensuring that the signature has a low `s`
- /// value_.
- ///
- /// Using this for signature validation will succeed, even if the signature is malleable or not
- /// compliant with EIP-2. This is provided for compatibility with old signatures which have
- /// large `s` values.
- pub fn recover_signer_unchecked(
- signature: &Signature,
- hash: B256,
- ) -> Result
{
- 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;
-
- // NOTE: we are removing error from underlying crypto library as it will restrain primitive
- // errors and we care only if recovery is passing or not.
- imp::recover_signer_unchecked(&sig, &hash.0).map_err(|_| RecoveryError)
- }
-
- /// Recover signer address from message hash. This ensures that the signature S value is
- /// greater than `secp256k1n / 2`, as specified in
- /// [EIP-2](https://eips.ethereum.org/EIPS/eip-2).
- ///
- /// If the S value is too large, then this will return `None`
- pub fn recover_signer(signature: &Signature, hash: B256) -> Result {
- if signature.s() > SECP256K1N_HALF {
- return Err(RecoveryError)
- }
- recover_signer_unchecked(signature, hash)
- }
-}
-
-#[cfg(any(test, 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, 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(crate) fn recover_signer_unchecked(
- sig: &[u8; 65],
- msg: &[u8; 32],
- ) -> Result {
- let sig =
- RecoverableSignature::from_compact(&sig[0..64], RecoveryId::try_from(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"),
- i32::from(rec_id) != 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(crate) 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_08::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.v() 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_08::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 alloy_consensus::crypto::*;
diff --git a/crates/primitives-traits/src/transaction/signed.rs b/crates/primitives-traits/src/transaction/signed.rs
index 1497ad2ac7..79c297edc3 100644
--- a/crates/primitives-traits/src/transaction/signed.rs
+++ b/crates/primitives-traits/src/transaction/signed.rs
@@ -14,6 +14,8 @@ use alloy_primitives::{keccak256, Address, Signature, TxHash, B256};
use alloy_rlp::{Decodable, Encodable};
use core::hash::Hash;
+pub use alloy_consensus::crypto::RecoveryError;
+
/// Helper trait that unifies all behaviour required by block to support full node operations.
pub trait FullSignedTx: SignedTransaction + MaybeCompact + MaybeSerdeBincodeCompat {}
impl FullSignedTx for T where T: SignedTransaction + MaybeCompact + MaybeSerdeBincodeCompat {}
@@ -278,7 +280,3 @@ mod op {
}
}
}
-/// Opaque error type for sender recovery.
-#[derive(Debug, Default, thiserror::Error)]
-#[error("Failed to recover the signer")]
-pub struct RecoveryError;