use alloy_primitives::{Address, B256, Bytes, Signature, U256};
use alloy_rlp::{Decodable, Encodable, Header, RlpDecodable, RlpEncodable};
use crate::{Eip8141Error, FrameAddress, P256_SIGNATURE_LENGTH, SECP256K1_SIGNATURE_LENGTH};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[repr(u8)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
#[cfg_attr(feature = "borsh", borsh(use_discriminant = true))]
pub enum SignatureScheme {
#[default]
Arbitrary = 0x00,
Secp256k1 = 0x01,
P256 = 0x02,
}
impl SignatureScheme {
pub const fn is_arbitrary(self) -> bool {
matches!(self, Self::Arbitrary)
}
pub const fn is_secp256k1(self) -> bool {
matches!(self, Self::Secp256k1)
}
pub const fn is_p256(self) -> bool {
matches!(self, Self::P256)
}
pub const fn try_from_u8(value: u8) -> Option<Self> {
match value {
0x00 => Some(Self::Arbitrary),
0x01 => Some(Self::Secp256k1),
0x02 => Some(Self::P256),
_ => None,
}
}
pub const fn verification_gas(self) -> u64 {
match self {
Self::Arbitrary => 100,
Self::Secp256k1 => 2_800,
Self::P256 => 6_700,
}
}
pub const fn signature_length(self) -> Option<usize> {
match self {
Self::Arbitrary => None,
Self::Secp256k1 => Some(SECP256K1_SIGNATURE_LENGTH),
Self::P256 => Some(P256_SIGNATURE_LENGTH),
}
}
}
impl_u8_discriminant!(SignatureScheme, InvalidScheme, "invalid EIP-8141 signature scheme");
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
pub enum SignatureMessage {
#[default]
TransactionHash,
Explicit(B256),
}
impl SignatureMessage {
pub const fn is_explicit(self) -> bool {
matches!(self, Self::Explicit(_))
}
pub fn explicit(digest: B256) -> Result<Self, Eip8141Error> {
if digest.is_zero() { Err(Eip8141Error::ZeroMessage) } else { Ok(Self::Explicit(digest)) }
}
pub const fn is_transaction_hash(self) -> bool {
matches!(self, Self::TransactionHash)
}
pub const fn digest(self) -> Option<B256> {
match self {
Self::TransactionHash => None,
Self::Explicit(digest) => Some(digest),
}
}
pub const fn as_bytes(&self) -> &[u8] {
match self {
Self::TransactionHash => &[],
Self::Explicit(digest) => digest.as_slice(),
}
}
}
impl TryFrom<&[u8]> for SignatureMessage {
type Error = Eip8141Error;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.is_empty() {
return Ok(Self::TransactionHash);
}
B256::try_from(value)
.map_err(|_| Eip8141Error::InvalidMessageLength(value.len()))
.and_then(Self::explicit)
}
}
impl Encodable for SignatureMessage {
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
match self {
Self::TransactionHash => out.put_u8(alloy_rlp::EMPTY_STRING_CODE),
Self::Explicit(digest) => digest.encode(out),
}
}
fn length(&self) -> usize {
match self {
Self::TransactionHash => 1,
Self::Explicit(digest) => digest.length(),
}
}
}
impl Decodable for SignatureMessage {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
Self::try_from(Header::decode_bytes(buf, false)?).map_err(|err| match err {
Eip8141Error::ZeroMessage => {
alloy_rlp::Error::Custom("EIP-8141 signature message must be nonzero")
}
_ => alloy_rlp::Error::Custom("invalid EIP-8141 signature message length"),
})
}
}
#[cfg(feature = "arbitrary")]
impl<'a> arbitrary::Arbitrary<'a> for SignatureMessage {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
Ok(match u.arbitrary::<Option<B256>>()? {
Some(digest) if !digest.is_zero() => Self::Explicit(digest),
_ => Self::TransactionHash,
})
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for SignatureMessage {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let digest = match self {
Self::TransactionHash => None,
Self::Explicit(digest) if digest.is_zero() => {
return Err(serde::ser::Error::custom(Eip8141Error::ZeroMessage));
}
Self::Explicit(digest) => Some(*digest),
};
crate::serde_utils::serialize_optional_bytes(digest, serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for SignatureMessage {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
crate::serde_utils::deserialize_optional_bytes::<32, D>(deserializer)?
.map_or(Ok(Self::TransactionHash), |digest| {
Self::explicit(digest).map_err(serde::de::Error::custom)
})
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
pub struct FrameSignature {
pub scheme: SignatureScheme,
pub signer: FrameAddress,
pub msg: SignatureMessage,
pub signature: Bytes,
}
impl FrameSignature {
pub const fn new(
scheme: SignatureScheme,
signer: FrameAddress,
msg: SignatureMessage,
signature: Bytes,
) -> Self {
Self { scheme, signer, msg, signature }
}
pub const fn signs_transaction_hash(&self) -> bool {
self.msg.is_transaction_hash()
}
pub const fn explicit_message(&self) -> Option<B256> {
self.msg.digest()
}
pub fn as_signing(&self) -> SigningFrameSignature<'_> {
SigningFrameSignature {
scheme: self.scheme,
signer: self.signer,
msg: self.msg,
signature: if self.signs_transaction_hash() { &[] } else { &self.signature },
}
}
pub const fn signer_address(&self) -> Option<Address> {
match self.scheme {
SignatureScheme::Arbitrary => None,
_ => self.signer.address(),
}
}
pub const fn resolved_signer(&self, sender: Address) -> Result<Option<Address>, Eip8141Error> {
match self.scheme {
SignatureScheme::Arbitrary => {
if self.signer.is_empty() {
Ok(None)
} else {
Err(Eip8141Error::UnexpectedSigner)
}
}
_ => Ok(Some(self.signer.resolve(sender))),
}
}
pub fn validate_structure(&self) -> Result<(), Eip8141Error> {
let (expected, order) = match self.scheme {
SignatureScheme::Arbitrary => {
return if self.signer.is_empty() {
Ok(())
} else {
Err(Eip8141Error::UnexpectedSigner)
};
}
SignatureScheme::Secp256k1 => (SECP256K1_SIGNATURE_LENGTH, crate::SECP256K1N),
SignatureScheme::P256 => (P256_SIGNATURE_LENGTH, crate::SECP256R1N),
};
if self.signature.len() != expected {
return Err(Eip8141Error::InvalidSignatureLength {
expected,
actual: self.signature.len(),
});
}
let offset = if self.scheme == SignatureScheme::Secp256k1 {
if self.signature[0] > 1 {
return Err(Eip8141Error::InvalidParity(self.signature[0]));
}
1
} else {
0
};
let r = U256::from_be_slice(&self.signature[offset..offset + 32]);
let s = U256::from_be_slice(&self.signature[offset + 32..offset + 64]);
if r.is_zero() || r >= order || s.is_zero() || s > order >> 1 {
return Err(Eip8141Error::InvalidSignatureScalar);
}
Ok(())
}
pub fn validate_structure_with_sender(&self, sender: Address) -> Result<(), Eip8141Error> {
self.validate_structure()?;
if let Some(derived) = self.p256_signer_address() {
let expected = self.signer.resolve(sender);
if derived != expected {
return Err(Eip8141Error::P256SignerMismatch { expected, derived });
}
}
Ok(())
}
pub fn secp256k1_signature(&self) -> Option<Signature> {
if self.scheme != SignatureScheme::Secp256k1
|| self.signature.len() != SECP256K1_SIGNATURE_LENGTH
{
return None;
}
let parity = match self.signature[0] {
0 => false,
1 => true,
_ => return None,
};
Some(Signature::from_bytes_and_parity(&self.signature[1..], parity))
}
pub fn p256_signer_address(&self) -> Option<Address> {
(self.scheme == SignatureScheme::P256 && self.signature.len() == P256_SIGNATURE_LENGTH)
.then(|| Address::from_raw_public_key(&self.signature[64..]))
}
pub fn from_secp256k1(
signer: FrameAddress,
msg: SignatureMessage,
signature: Signature,
) -> Result<Self, Eip8141Error> {
let mut bytes = [0u8; SECP256K1_SIGNATURE_LENGTH];
bytes[0] = u8::from(signature.v());
bytes[1..33].copy_from_slice(&signature.r().to_be_bytes::<32>());
bytes[33..].copy_from_slice(&signature.s().to_be_bytes::<32>());
let entry = Self::new(SignatureScheme::Secp256k1, signer, msg, bytes.into());
entry.validate_structure()?;
Ok(entry)
}
pub const fn verification_gas(&self) -> u64 {
self.scheme.verification_gas()
}
}
#[derive(Clone, Copy, Debug, RlpEncodable)]
pub struct SigningFrameSignature<'a> {
scheme: SignatureScheme,
signer: FrameAddress,
msg: SignatureMessage,
signature: &'a [u8],
}
#[derive(Clone, Copy, Debug)]
pub struct SigningFrameSignatures<'a>(&'a [FrameSignature]);
impl<'a> SigningFrameSignatures<'a> {
pub const fn new(signatures: &'a [FrameSignature]) -> Self {
Self(signatures)
}
}
impl Encodable for SigningFrameSignatures<'_> {
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
alloy_rlp::encode_iter(self.0.iter().map(FrameSignature::as_signing), out);
}
fn length(&self) -> usize {
let payload_length = self.0.iter().map(|signature| signature.as_signing().length()).sum();
Header { list: true, payload_length }.length_with_payload()
}
}