1use alloy_primitives::{Address, B256, Bytes, Signature, U256};
2use alloy_rlp::{Decodable, Encodable, Header, RlpDecodable, RlpEncodable};
3
4use crate::{Eip8141Error, FrameAddress, P256_SIGNATURE_LENGTH, SECP256K1_SIGNATURE_LENGTH};
5
6#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
8#[repr(u8)]
9#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
10#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
11#[cfg_attr(feature = "borsh", borsh(use_discriminant = true))]
12pub enum SignatureScheme {
13 #[default]
15 Arbitrary = 0x00,
16 Secp256k1 = 0x01,
18 P256 = 0x02,
20}
21
22impl SignatureScheme {
23 pub const fn is_arbitrary(self) -> bool {
25 matches!(self, Self::Arbitrary)
26 }
27
28 pub const fn is_secp256k1(self) -> bool {
30 matches!(self, Self::Secp256k1)
31 }
32
33 pub const fn is_p256(self) -> bool {
35 matches!(self, Self::P256)
36 }
37
38 pub const fn try_from_u8(value: u8) -> Option<Self> {
40 match value {
41 0x00 => Some(Self::Arbitrary),
42 0x01 => Some(Self::Secp256k1),
43 0x02 => Some(Self::P256),
44 _ => None,
45 }
46 }
47
48 pub const fn verification_gas(self) -> u64 {
50 match self {
51 Self::Arbitrary => 100,
52 Self::Secp256k1 => 2_800,
53 Self::P256 => 6_700,
54 }
55 }
56
57 pub const fn signature_length(self) -> Option<usize> {
61 match self {
62 Self::Arbitrary => None,
63 Self::Secp256k1 => Some(SECP256K1_SIGNATURE_LENGTH),
64 Self::P256 => Some(P256_SIGNATURE_LENGTH),
65 }
66 }
67}
68
69impl_u8_discriminant!(SignatureScheme, InvalidScheme, "invalid EIP-8141 signature scheme");
70
71#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
78#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
79pub enum SignatureMessage {
80 #[default]
82 TransactionHash,
83 Explicit(B256),
87}
88
89impl SignatureMessage {
90 pub const fn is_explicit(self) -> bool {
92 matches!(self, Self::Explicit(_))
93 }
94
95 pub fn explicit(digest: B256) -> Result<Self, Eip8141Error> {
97 if digest.is_zero() { Err(Eip8141Error::ZeroMessage) } else { Ok(Self::Explicit(digest)) }
98 }
99
100 pub const fn is_transaction_hash(self) -> bool {
102 matches!(self, Self::TransactionHash)
103 }
104
105 pub const fn digest(self) -> Option<B256> {
107 match self {
108 Self::TransactionHash => None,
109 Self::Explicit(digest) => Some(digest),
110 }
111 }
112
113 pub const fn as_bytes(&self) -> &[u8] {
115 match self {
116 Self::TransactionHash => &[],
117 Self::Explicit(digest) => digest.as_slice(),
118 }
119 }
120}
121
122impl TryFrom<&[u8]> for SignatureMessage {
123 type Error = Eip8141Error;
124
125 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
126 if value.is_empty() {
127 return Ok(Self::TransactionHash);
128 }
129 B256::try_from(value)
130 .map_err(|_| Eip8141Error::InvalidMessageLength(value.len()))
131 .and_then(Self::explicit)
132 }
133}
134
135impl Encodable for SignatureMessage {
136 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
137 match self {
138 Self::TransactionHash => out.put_u8(alloy_rlp::EMPTY_STRING_CODE),
139 Self::Explicit(digest) => digest.encode(out),
140 }
141 }
142
143 fn length(&self) -> usize {
144 match self {
145 Self::TransactionHash => 1,
146 Self::Explicit(digest) => digest.length(),
147 }
148 }
149}
150
151impl Decodable for SignatureMessage {
152 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
153 Self::try_from(Header::decode_bytes(buf, false)?).map_err(|err| match err {
154 Eip8141Error::ZeroMessage => {
155 alloy_rlp::Error::Custom("EIP-8141 signature message must be nonzero")
156 }
157 _ => alloy_rlp::Error::Custom("invalid EIP-8141 signature message length"),
158 })
159 }
160}
161
162#[cfg(feature = "arbitrary")]
163impl<'a> arbitrary::Arbitrary<'a> for SignatureMessage {
164 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
165 Ok(match u.arbitrary::<Option<B256>>()? {
166 Some(digest) if !digest.is_zero() => Self::Explicit(digest),
167 _ => Self::TransactionHash,
168 })
169 }
170}
171
172#[cfg(feature = "serde")]
173impl serde::Serialize for SignatureMessage {
174 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
175 let digest = match self {
176 Self::TransactionHash => None,
177 Self::Explicit(digest) if digest.is_zero() => {
178 return Err(serde::ser::Error::custom(Eip8141Error::ZeroMessage));
179 }
180 Self::Explicit(digest) => Some(*digest),
181 };
182 crate::serde_utils::serialize_optional_bytes(digest, serializer)
183 }
184}
185
186#[cfg(feature = "serde")]
187impl<'de> serde::Deserialize<'de> for SignatureMessage {
188 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
189 crate::serde_utils::deserialize_optional_bytes::<32, D>(deserializer)?
190 .map_or(Ok(Self::TransactionHash), |digest| {
191 Self::explicit(digest).map_err(serde::de::Error::custom)
192 })
193 }
194}
195
196#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable)]
198#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
199#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
200#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
201#[cfg_attr(feature = "borsh", derive(borsh::BorshSerialize, borsh::BorshDeserialize))]
202pub struct FrameSignature {
203 pub scheme: SignatureScheme,
205 pub signer: FrameAddress,
207 pub msg: SignatureMessage,
209 pub signature: Bytes,
211}
212
213impl FrameSignature {
214 pub const fn new(
219 scheme: SignatureScheme,
220 signer: FrameAddress,
221 msg: SignatureMessage,
222 signature: Bytes,
223 ) -> Self {
224 Self { scheme, signer, msg, signature }
225 }
226
227 pub const fn signs_transaction_hash(&self) -> bool {
229 self.msg.is_transaction_hash()
230 }
231
232 pub const fn explicit_message(&self) -> Option<B256> {
234 self.msg.digest()
235 }
236
237 pub fn as_signing(&self) -> SigningFrameSignature<'_> {
243 SigningFrameSignature {
244 scheme: self.scheme,
245 signer: self.signer,
246 msg: self.msg,
247 signature: if self.signs_transaction_hash() { &[] } else { &self.signature },
248 }
249 }
250
251 pub const fn signer_address(&self) -> Option<Address> {
257 match self.scheme {
258 SignatureScheme::Arbitrary => None,
259 _ => self.signer.address(),
260 }
261 }
262
263 pub const fn resolved_signer(&self, sender: Address) -> Result<Option<Address>, Eip8141Error> {
265 match self.scheme {
266 SignatureScheme::Arbitrary => {
267 if self.signer.is_empty() {
268 Ok(None)
269 } else {
270 Err(Eip8141Error::UnexpectedSigner)
271 }
272 }
273 _ => Ok(Some(self.signer.resolve(sender))),
274 }
275 }
276
277 pub fn validate_structure(&self) -> Result<(), Eip8141Error> {
283 let (expected, order) = match self.scheme {
284 SignatureScheme::Arbitrary => {
285 return if self.signer.is_empty() {
286 Ok(())
287 } else {
288 Err(Eip8141Error::UnexpectedSigner)
289 };
290 }
291 SignatureScheme::Secp256k1 => (SECP256K1_SIGNATURE_LENGTH, crate::SECP256K1N),
292 SignatureScheme::P256 => (P256_SIGNATURE_LENGTH, crate::SECP256R1N),
293 };
294 if self.signature.len() != expected {
295 return Err(Eip8141Error::InvalidSignatureLength {
296 expected,
297 actual: self.signature.len(),
298 });
299 }
300 let offset = if self.scheme == SignatureScheme::Secp256k1 {
301 if self.signature[0] > 1 {
302 return Err(Eip8141Error::InvalidParity(self.signature[0]));
303 }
304 1
305 } else {
306 0
307 };
308 let r = U256::from_be_slice(&self.signature[offset..offset + 32]);
309 let s = U256::from_be_slice(&self.signature[offset + 32..offset + 64]);
310 if r.is_zero() || r >= order || s.is_zero() || s > order >> 1 {
311 return Err(Eip8141Error::InvalidSignatureScalar);
312 }
313 Ok(())
314 }
315
316 pub fn validate_structure_with_sender(&self, sender: Address) -> Result<(), Eip8141Error> {
322 self.validate_structure()?;
323 if let Some(derived) = self.p256_signer_address() {
324 let expected = self.signer.resolve(sender);
325 if derived != expected {
326 return Err(Eip8141Error::P256SignerMismatch { expected, derived });
327 }
328 }
329 Ok(())
330 }
331
332 pub fn secp256k1_signature(&self) -> Option<Signature> {
337 if self.scheme != SignatureScheme::Secp256k1
338 || self.signature.len() != SECP256K1_SIGNATURE_LENGTH
339 {
340 return None;
341 }
342 let parity = match self.signature[0] {
343 0 => false,
344 1 => true,
345 _ => return None,
346 };
347 Some(Signature::from_bytes_and_parity(&self.signature[1..], parity))
348 }
349
350 pub fn p256_signer_address(&self) -> Option<Address> {
354 (self.scheme == SignatureScheme::P256 && self.signature.len() == P256_SIGNATURE_LENGTH)
355 .then(|| Address::from_raw_public_key(&self.signature[64..]))
356 }
357
358 pub fn from_secp256k1(
362 signer: FrameAddress,
363 msg: SignatureMessage,
364 signature: Signature,
365 ) -> Result<Self, Eip8141Error> {
366 let mut bytes = [0u8; SECP256K1_SIGNATURE_LENGTH];
367 bytes[0] = u8::from(signature.v());
368 bytes[1..33].copy_from_slice(&signature.r().to_be_bytes::<32>());
369 bytes[33..].copy_from_slice(&signature.s().to_be_bytes::<32>());
370 let entry = Self::new(SignatureScheme::Secp256k1, signer, msg, bytes.into());
371 entry.validate_structure()?;
372 Ok(entry)
373 }
374
375 pub const fn verification_gas(&self) -> u64 {
377 self.scheme.verification_gas()
378 }
379}
380
381#[derive(Clone, Copy, Debug, RlpEncodable)]
386pub struct SigningFrameSignature<'a> {
387 scheme: SignatureScheme,
388 signer: FrameAddress,
389 msg: SignatureMessage,
390 signature: &'a [u8],
391}
392
393#[derive(Clone, Copy, Debug)]
409pub struct SigningFrameSignatures<'a>(&'a [FrameSignature]);
410
411impl<'a> SigningFrameSignatures<'a> {
412 pub const fn new(signatures: &'a [FrameSignature]) -> Self {
414 Self(signatures)
415 }
416}
417
418impl Encodable for SigningFrameSignatures<'_> {
419 fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
420 alloy_rlp::encode_iter(self.0.iter().map(FrameSignature::as_signing), out);
421 }
422
423 fn length(&self) -> usize {
424 let payload_length = self.0.iter().map(|signature| signature.as_signing().length()).sum();
425 Header { list: true, payload_length }.length_with_payload()
426 }
427}