diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 93a3ecc46..255555b2b 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -12,11 +12,6 @@ pub use alloy_primitives as primitives; #[doc(no_inline)] pub use primitives::{hex, uint}; -#[cfg(feature = "unstable-doc")] -#[doc(hidden)] -#[allow(unused_imports)] -pub use primitives::PrivateSignature as _; - #[cfg(feature = "dyn-abi")] #[doc(inline)] pub use alloy_dyn_abi as dyn_abi; diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 21bb1cab5..fa8f87477 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -49,21 +49,7 @@ mod signed; pub use signed::{BigIntConversionError, ParseSignedError, Sign, Signed}; mod signature; -pub use signature::{to_eip155_v, Parity, SignatureError}; - -/// Only available for documentation purposes. -// Without this visible (not `#[doc(hidden)]`) re-export, `rustdoc` will not generate documentation -// for the `Signature` type alias below. -#[cfg(feature = "unstable-doc")] -pub use signature::Signature as PrivateSignature; - -/// An ECDSA Signature, consisting of V, R, and S. -#[cfg(feature = "k256")] -pub type Signature = signature::Signature; - -/// An ECDSA Signature, consisting of V, R, and S. -#[cfg(not(feature = "k256"))] -pub type Signature = signature::Signature<()>; +pub use signature::{to_eip155_v, Parity, Signature, SignatureError}; pub mod utils; pub use utils::{eip191_hash_message, keccak256, Keccak256}; diff --git a/crates/primitives/src/signature/mod.rs b/crates/primitives/src/signature/mod.rs index 5dd8e22a6..4ebd034be 100644 --- a/crates/primitives/src/signature/mod.rs +++ b/crates/primitives/src/signature/mod.rs @@ -5,10 +5,7 @@ mod parity; pub use parity::Parity; mod sig; -#[cfg(feature = "unstable-doc")] pub use sig::Signature; -#[cfg(not(feature = "unstable-doc"))] -pub(crate) use sig::Signature; mod utils; pub use utils::to_eip155_v; diff --git a/crates/primitives/src/signature/sig.rs b/crates/primitives/src/signature/sig.rs index d0cc4f69e..7cc92d608 100644 --- a/crates/primitives/src/signature/sig.rs +++ b/crates/primitives/src/signature/sig.rs @@ -3,59 +3,38 @@ use crate::{ hex, signature::{Parity, SignatureError}, - U256, + uint, U256, }; use alloc::vec::Vec; use core::str::FromStr; +/// The order of the secp256k1 curve +const SECP256K1N_ORDER: U256 = + uint!(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141_U256); + /// An Ethereum ECDSA signature. #[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct Signature { - /// Memoized ecdsa signature (if any) - inner: T, - +pub struct Signature { v: Parity, r: U256, s: U256, } -#[cfg(feature = "k256")] -impl<'a> TryFrom<&'a [u8]> for Signature { +impl<'a> TryFrom<&'a [u8]> for Signature { type Error = SignatureError; /// Parses a raw signature which is expected to be 65 bytes long where /// the first 32 bytes is the `r` value, the second 32 bytes the `s` value /// and the final byte is the `v` value in 'Electrum' notation. - fn try_from(bytes: &'a [u8]) -> Result { - if bytes.len() != 65 { - return Err(k256::ecdsa::Error::new().into()); - } - Self::from_bytes_and_parity(&bytes[..64], bytes[64] as u64) - } -} - -impl<'a> TryFrom<&'a [u8]> for Signature<()> { - type Error = SignatureError; - fn try_from(bytes: &'a [u8]) -> Result { if bytes.len() != 65 { return Err(SignatureError::FromBytes("expected exactly 65 bytes")); } - Self::from_bytes_and_parity(bytes, bytes[64] as u64) - } -} - -#[cfg(feature = "k256")] -impl FromStr for Signature { - type Err = SignatureError; - - fn from_str(s: &str) -> Result { - let bytes = hex::decode(s)?; - Self::try_from(&bytes[..]) + Self::from_bytes_and_parity(&bytes[..64], bytes[64] as u64) } } -impl FromStr for Signature<()> { +impl FromStr for Signature { type Err = SignatureError; fn from_str(s: &str) -> Result { @@ -64,43 +43,52 @@ impl FromStr for Signature<()> { } } -impl From<&crate::Signature> for [u8; 65] { +impl From<&Signature> for [u8; 65] { #[inline] - fn from(value: &crate::Signature) -> [u8; 65] { + fn from(value: &Signature) -> [u8; 65] { value.as_bytes() } } -impl From for [u8; 65] { +impl From for [u8; 65] { #[inline] - fn from(value: crate::Signature) -> [u8; 65] { + fn from(value: Signature) -> [u8; 65] { value.as_bytes() } } -impl From<&crate::Signature> for Vec { +impl From<&Signature> for Vec { #[inline] - fn from(value: &crate::Signature) -> Self { + fn from(value: &Signature) -> Self { value.as_bytes().to_vec() } } -impl From for Vec { +impl From for Vec { #[inline] - fn from(value: crate::Signature) -> Self { + fn from(value: Signature) -> Self { value.as_bytes().to_vec() } } #[cfg(feature = "k256")] -impl From<(k256::ecdsa::Signature, k256::ecdsa::RecoveryId)> for Signature { +impl From<(k256::ecdsa::Signature, k256::ecdsa::RecoveryId)> for Signature { fn from(value: (k256::ecdsa::Signature, k256::ecdsa::RecoveryId)) -> Self { Self::from_signature_and_parity(value.0, value.1).unwrap() } } +#[cfg(feature = "k256")] +impl TryFrom for k256::ecdsa::Signature { + type Error = k256::ecdsa::Error; + + fn try_from(value: Signature) -> Result { + value.to_k256() + } +} + #[cfg(feature = "rlp")] -impl crate::Signature { +impl Signature { /// Decode an RLP-encoded VRS signature. pub fn decode_rlp_vrs(buf: &mut &[u8]) -> Result { use alloy_rlp::Decodable; @@ -114,8 +102,7 @@ impl crate::Signature { } } -#[cfg(feature = "k256")] -impl Signature { +impl Signature { #[doc(hidden)] pub fn test_signature() -> Self { Self::from_scalars_and_parity( @@ -126,47 +113,51 @@ impl Signature { .unwrap() } - /// Instantiate from a signature and recovery id - pub fn from_signature_and_parity, E: Into>( - sig: k256::ecdsa::Signature, - parity: T, - ) -> Result { - let r = U256::from_be_slice(sig.r().to_bytes().as_ref()); - let s = U256::from_be_slice(sig.s().to_bytes().as_ref()); - Ok(Self { inner: sig, v: parity.try_into().map_err(Into::into)?, r, s }) + /// Instantiate a new signature from `r`, `s`, and `v` values. + #[allow(clippy::missing_const_for_fn)] + pub fn new(r: U256, s: U256, v: Parity) -> Self { + Self { r, s, v } } - /// Instantiate from v, r, s. - pub fn from_rs_and_parity, E: Into>( - r: U256, - s: U256, - parity: T, - ) -> Result { - Self::from_scalars_and_parity(r.into(), s.into(), parity) + /// Returns the inner ECDSA signature. + #[cfg(feature = "k256")] + #[deprecated(note = "use `Signature::to_k256` instead")] + #[inline] + pub fn into_inner(self) -> k256::ecdsa::Signature { + self.try_into().expect("signature conversion failed") } - /// Parses a signature from a byte slice, with a v value + /// Returns the inner ECDSA signature. + #[cfg(feature = "k256")] #[inline] - pub fn from_bytes_and_parity, E: Into>( - bytes: &[u8], + pub fn to_k256(self) -> Result { + k256::ecdsa::Signature::from_scalars(self.r.to_be_bytes(), self.s.to_be_bytes()) + } + + /// Instantiate from a signature and recovery id + #[cfg(feature = "k256")] + pub fn from_signature_and_parity, E: Into>( + sig: k256::ecdsa::Signature, parity: T, ) -> Result { - let sig = k256::ecdsa::Signature::from_slice(bytes)?; - Self::from_signature_and_parity(sig, parity) + let r = U256::from_be_slice(sig.r().to_bytes().as_ref()); + let s = U256::from_be_slice(sig.s().to_bytes().as_ref()); + Ok(Self { v: parity.try_into().map_err(Into::into)?, r, s }) } /// Creates a [`Signature`] from the serialized `r` and `s` scalar values, which comprise the /// ECDSA signature, alongside a `v` value, used to determine the recovery ID. - /// - /// See [`k256::ecdsa::Signature::from_scalars`] for more details. #[inline] pub fn from_scalars_and_parity, E: Into>( r: crate::B256, s: crate::B256, parity: T, ) -> Result { - let inner = k256::ecdsa::Signature::from_scalars(r.0, s.0)?; - Self::from_signature_and_parity(inner, parity) + Self::from_rs_and_parity( + U256::from_be_slice(r.as_ref()), + U256::from_be_slice(s.as_ref()), + parity, + ) } /// Normalizes the signature into "low S" form as described in @@ -175,21 +166,23 @@ impl Signature { /// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki #[inline] pub fn normalize_s(&self) -> Option { - // Normalize into "low S" form. See: - // - https://github.com/RustCrypto/elliptic-curves/issues/988 - // - https://github.com/bluealloy/revm/pull/870 - self.inner.normalize_s().map(|normalized| { - let s = U256::from_be_slice(normalized.s().to_bytes().as_ref()); - Self { inner: normalized, v: self.v.inverted(), r: self.r, s } - }) + let s = self.s(); + + if s > SECP256K1N_ORDER >> 1 { + Some(Self { v: self.v.inverted(), r: self.r, s: SECP256K1N_ORDER - s }) + } else { + None + } } /// Returns the recovery ID. + #[cfg(feature = "k256")] #[inline] pub const fn recid(&self) -> k256::ecdsa::RecoveryId { self.v.recid() } + #[cfg(feature = "k256")] #[doc(hidden)] #[deprecated(note = "use `Signature::recid` instead")] pub const fn recovery_id(&self) -> k256::ecdsa::RecoveryId { @@ -200,6 +193,7 @@ impl Signature { /// hashing the message according to [EIP-191](crate::eip191_hash_message). /// /// [`Address`]: crate::Address + #[cfg(feature = "k256")] #[inline] pub fn recover_address_from_msg>( &self, @@ -211,6 +205,7 @@ impl Signature { /// Recovers an [`Address`] from this signature and the given prehashed message. /// /// [`Address`]: crate::Address + #[cfg(feature = "k256")] #[inline] pub fn recover_address_from_prehash( &self, @@ -223,6 +218,7 @@ impl Signature { /// hashing the message according to [EIP-191](crate::eip191_hash_message). /// /// [`VerifyingKey`]: k256::ecdsa::VerifyingKey + #[cfg(feature = "k256")] #[inline] pub fn recover_from_msg>( &self, @@ -234,6 +230,7 @@ impl Signature { /// Recovers a [`VerifyingKey`] from this signature and the given prehashed message. /// /// [`VerifyingKey`]: k256::ecdsa::VerifyingKey + #[cfg(feature = "k256")] #[inline] pub fn recover_from_prehash( &self, @@ -242,14 +239,12 @@ impl Signature { let this = self.normalize_s().unwrap_or(*self); k256::ecdsa::VerifyingKey::recover_from_prehash( prehash.as_slice(), - &this.inner, + &this.to_k256()?, this.recid(), ) .map_err(Into::into) } -} -impl Signature<()> { /// Parses a signature from a byte slice, with a v value /// /// # Panics @@ -271,15 +266,7 @@ impl Signature<()> { s: U256, parity: T, ) -> Result { - Ok(Self { inner: (), v: parity.try_into().map_err(Into::into)?, r, s }) - } -} - -impl Signature { - /// Returns the inner ECDSA signature. - #[inline] - pub const fn into_inner(self) -> S { - self.inner + Ok(Self { v: parity.try_into().map_err(Into::into)?, r, s }) } /// Modifies the recovery ID by applying [EIP-155] to a `v` value. @@ -295,14 +282,6 @@ impl Signature { pub fn with_parity_bool(self) -> Self { self.with_parity(self.v.to_parity_bool()) } -} - -impl Signature { - /// Returns the inner ECDSA signature. - #[inline] - pub const fn inner(&self) -> &S { - &self.inner - } /// Returns the `r` component of this signature. #[inline] @@ -338,7 +317,7 @@ impl Signature { /// Sets the recovery ID by normalizing a `v` value. #[inline] pub fn with_parity>(self, parity: T) -> Self { - Self { inner: self.inner, v: parity.into(), r: self.r, s: self.s } + Self { v: parity.into(), r: self.r, s: self.s } } /// Length of RLP RS field encoding @@ -375,7 +354,7 @@ impl Signature { } #[cfg(feature = "rlp")] -impl alloy_rlp::Encodable for crate::Signature { +impl alloy_rlp::Encodable for Signature { fn encode(&self, out: &mut dyn alloy_rlp::BufMut) { alloy_rlp::Header { list: true, payload_length: self.rlp_vrs_len() }.encode(out); self.write_rlp_vrs(out); @@ -388,7 +367,7 @@ impl alloy_rlp::Encodable for crate::Signature { } #[cfg(feature = "rlp")] -impl alloy_rlp::Decodable for crate::Signature { +impl alloy_rlp::Decodable for Signature { fn decode(buf: &mut &[u8]) -> Result { let header = alloy_rlp::Header::decode(buf)?; let pre_len = buf.len(); @@ -403,7 +382,7 @@ impl alloy_rlp::Decodable for crate::Signature { } #[cfg(feature = "serde")] -impl serde::Serialize for crate::Signature { +impl serde::Serialize for Signature { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, @@ -439,7 +418,7 @@ impl serde::Serialize for crate::Signature { } #[cfg(feature = "serde")] -impl<'de> serde::Deserialize<'de> for crate::Signature { +impl<'de> serde::Deserialize<'de> for Signature { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, @@ -490,7 +469,7 @@ impl<'de> serde::Deserialize<'de> for crate::Signature { struct MapVisitor; impl<'de> serde::de::Visitor<'de> for MapVisitor { - type Value = crate::Signature; + type Value = Signature; fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("a JSON signature object containing r, s, and v or yParity") @@ -538,13 +517,13 @@ impl<'de> serde::Deserialize<'de> for crate::Signature { let r = r.ok_or_else(|| serde::de::Error::missing_field("r"))?; let s = s.ok_or_else(|| serde::de::Error::missing_field("s"))?; - crate::Signature::from_rs_and_parity(r, s, v).map_err(serde::de::Error::custom) + Signature::from_rs_and_parity(r, s, v).map_err(serde::de::Error::custom) } } struct TupleVisitor; impl<'de> serde::de::Visitor<'de> for TupleVisitor { - type Value = crate::Signature; + type Value = Signature; fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { formatter.write_str("a tuple containing r, s, and v") @@ -564,7 +543,7 @@ impl<'de> serde::Deserialize<'de> for crate::Signature { .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(2, &self))?; - crate::Signature::from_rs_and_parity(r, s, v).map_err(serde::de::Error::custom) + Signature::from_rs_and_parity(r, s, v).map_err(serde::de::Error::custom) } } @@ -577,7 +556,7 @@ impl<'de> serde::Deserialize<'de> for crate::Signature { } #[cfg(feature = "arbitrary")] -impl<'a> arbitrary::Arbitrary<'a> for crate::Signature { +impl<'a> arbitrary::Arbitrary<'a> for Signature { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Self::from_rs_and_parity(u.arbitrary()?, u.arbitrary()?, u.arbitrary::()?) .map_err(|_| arbitrary::Error::IncorrectFormat) @@ -585,7 +564,7 @@ impl<'a> arbitrary::Arbitrary<'a> for crate::Signature { } #[cfg(feature = "arbitrary")] -impl proptest::arbitrary::Arbitrary for crate::Signature { +impl proptest::arbitrary::Arbitrary for Signature { type Parameters = (); type Strategy = proptest::strategy::FilterMap< <(U256, U256, Parity) as proptest::arbitrary::Arbitrary>::Strategy, @@ -633,11 +612,11 @@ mod tests { #[test] fn signature_from_str() { - let s1 = crate::Signature::from_str( + let s1 = Signature::from_str( "0xaa231fbe0ed2b5418e6ba7c19bee2522852955ec50996c02a2fe3e71d30ddaf1645baf4823fea7cb4fcc7150842493847cfb6a6d63ab93e8ee928ee3f61f503500" ).expect("could not parse 0x-prefixed signature"); - let s2 = crate::Signature::from_str( + let s2 = Signature::from_str( "aa231fbe0ed2b5418e6ba7c19bee2522852955ec50996c02a2fe3e71d30ddaf1645baf4823fea7cb4fcc7150842493847cfb6a6d63ab93e8ee928ee3f61f503500" ).expect("could not parse non-prefixed signature"); @@ -653,10 +632,9 @@ mod tests { "v":"0x1" }"#; - let signature: crate::Signature = - serde_json::from_str(raw_signature_without_y_parity).unwrap(); + let signature: Signature = serde_json::from_str(raw_signature_without_y_parity).unwrap(); - let expected = crate::Signature::from_rs_and_parity( + let expected = Signature::from_rs_and_parity( U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") .unwrap(), U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") @@ -681,10 +659,9 @@ mod tests { ); println!("{raw_signature_with_y_parity}"); - let signature: crate::Signature = - serde_json::from_value(raw_signature_with_y_parity).unwrap(); + let signature: Signature = serde_json::from_value(raw_signature_with_y_parity).unwrap(); - let expected = crate::Signature::from_rs_and_parity( + let expected = Signature::from_rs_and_parity( U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") .unwrap(), U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") @@ -700,7 +677,7 @@ mod tests { #[test] fn serialize_both_parity() { // this test should be removed if the struct moves to an enum based on tx type - let signature = crate::Signature::from_rs_and_parity( + let signature = Signature::from_rs_and_parity( U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") .unwrap(), U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") @@ -720,7 +697,7 @@ mod tests { #[test] fn serialize_v_only() { // this test should be removed if the struct moves to an enum based on tx type - let signature = crate::Signature::from_rs_and_parity( + let signature = Signature::from_rs_and_parity( U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") .unwrap(), U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") @@ -740,7 +717,7 @@ mod tests { fn serialize_v_hex() { let s = r#"{"r":"0x3d43270611ffb1a10fcab841e636e355a787151969b920cf10fef48d3a61aac3","s":"0x11336489e3050e3ec017079dfe16582ce3d167559bcaa8383b665b3fda4eb963","v":"0x1b"}"#; - let sig = serde_json::from_str::(s).unwrap(); + let sig = serde_json::from_str::(s).unwrap(); let serialized = serde_json::to_string(&sig).unwrap(); assert_eq!(serialized, s); } @@ -748,7 +725,7 @@ mod tests { #[cfg(feature = "serde")] #[test] fn test_bincode_roundtrip() { - let signature = crate::Signature::from_rs_and_parity( + let signature = Signature::from_rs_and_parity( U256::from_str("0xc569c92f176a3be1a6352dd5005bfc751dcb32f57623dd2a23693e64bf4447b0") .unwrap(), U256::from_str("0x1a891b566d369e79b7a66eecab1e008831e22daa15f91a0a0cf4f9f28f47ee05") @@ -758,7 +735,7 @@ mod tests { .unwrap(); let bin = bincode::serialize(&signature).unwrap(); - assert_eq!(bincode::deserialize::(&bin).unwrap(), signature); + assert_eq!(bincode::deserialize::(&bin).unwrap(), signature); } #[cfg(feature = "rlp")]