tempo_primitives/transaction/
tt_authorization.rs1use alloy_eips::eip7702::{Authorization, RecoveredAuthority, RecoveredAuthorization};
2use alloy_primitives::{Address, B256, keccak256};
3use alloy_rlp::{BufMut, Decodable, Encodable, Header, Result as RlpResult, length_of_length};
4use core::ops::Deref;
5use std::sync::OnceLock;
6
7use crate::TempoSignature;
8
9pub const MAGIC: u8 = 0x05;
11
12#[derive(Clone, Debug, Eq, PartialEq, Hash)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
23#[cfg_attr(test, reth_codecs::add_arbitrary_tests(compact, rlp))]
24pub struct TempoSignedAuthorization {
25 #[cfg_attr(feature = "serde", serde(flatten))]
27 inner: Authorization,
28 signature: TempoSignature,
30}
31
32impl TempoSignedAuthorization {
33 pub const fn new_unchecked(inner: Authorization, signature: TempoSignature) -> Self {
37 Self { inner, signature }
38 }
39
40 pub const fn signature(&self) -> &TempoSignature {
44 &self.signature
45 }
46
47 pub fn strip_signature(self) -> Authorization {
49 self.inner
50 }
51
52 pub const fn inner(&self) -> &Authorization {
54 &self.inner
55 }
56
57 #[inline]
62 pub fn signature_hash(&self) -> B256 {
63 let mut buf = Vec::new();
64 buf.push(MAGIC);
65 self.inner.encode(&mut buf);
66 keccak256(buf)
67 }
68
69 pub fn recover_authority(&self) -> Result<Address, alloy_consensus::crypto::RecoveryError> {
75 let sig_hash = self.signature_hash();
76 self.signature.recover_signer(&sig_hash)
77 }
78
79 pub fn into_recovered(self) -> RecoveredAuthorization {
82 let authority_result = self.recover_authority();
83 let authority =
84 authority_result.map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid);
85
86 RecoveredAuthorization::new_unchecked(self.inner, authority)
87 }
88
89 fn decode_fields(buf: &mut &[u8]) -> RlpResult<Self> {
91 Ok(Self {
92 inner: Authorization {
93 chain_id: Decodable::decode(buf)?,
94 address: Decodable::decode(buf)?,
95 nonce: Decodable::decode(buf)?,
96 },
97 signature: Decodable::decode(buf)?,
98 })
99 }
100
101 fn fields_len(&self) -> usize {
103 self.inner.chain_id.length()
104 + self.inner.address.length()
105 + self.inner.nonce.length()
106 + self.signature.length()
107 }
108
109 pub fn size(&self) -> usize {
111 core::mem::size_of::<Authorization>() + self.signature.size()
112 }
113}
114
115impl Decodable for TempoSignedAuthorization {
116 fn decode(buf: &mut &[u8]) -> RlpResult<Self> {
117 let header = Header::decode(buf)?;
118 if !header.list {
119 return Err(alloy_rlp::Error::UnexpectedString);
120 }
121 let started_len = buf.len();
122
123 let this = Self::decode_fields(buf)?;
124
125 let consumed = started_len - buf.len();
126 if consumed != header.payload_length {
127 return Err(alloy_rlp::Error::ListLengthMismatch {
128 expected: header.payload_length,
129 got: consumed,
130 });
131 }
132
133 Ok(this)
134 }
135}
136
137impl Encodable for TempoSignedAuthorization {
138 fn encode(&self, buf: &mut dyn BufMut) {
139 Header {
140 list: true,
141 payload_length: self.fields_len(),
142 }
143 .encode(buf);
144 self.inner.chain_id.encode(buf);
145 self.inner.address.encode(buf);
146 self.inner.nonce.encode(buf);
147 self.signature.encode(buf);
148 }
149
150 fn length(&self) -> usize {
151 let len = self.fields_len();
152 len + length_of_length(len)
153 }
154}
155
156impl Deref for TempoSignedAuthorization {
157 type Target = Authorization;
158
159 fn deref(&self) -> &Self::Target {
160 &self.inner
161 }
162}
163
164#[cfg(feature = "reth-codec")]
166impl reth_codecs::Compact for TempoSignedAuthorization {
167 fn to_compact<B>(&self, buf: &mut B) -> usize
168 where
169 B: alloy_rlp::BufMut + AsMut<[u8]>,
170 {
171 let start_len = buf.remaining_mut();
173 self.encode(buf);
174 start_len - buf.remaining_mut()
175 }
176
177 fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
178 let mut buf_slice = &buf[..len];
179 let auth = Self::decode(&mut buf_slice).expect("valid RLP encoding");
180 (auth, &buf[len..])
181 }
182}
183
184#[derive(Clone, Debug)]
190#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
191pub struct RecoveredTempoAuthorization {
192 signed: TempoSignedAuthorization,
194 #[cfg_attr(feature = "serde", serde(skip))]
196 authority: OnceLock<RecoveredAuthority>,
197}
198
199impl RecoveredTempoAuthorization {
200 pub const fn new(signed: TempoSignedAuthorization) -> Self {
204 Self {
205 signed,
206 authority: OnceLock::new(),
207 }
208 }
209
210 pub fn new_unchecked(signed: TempoSignedAuthorization, authority: RecoveredAuthority) -> Self {
215 Self {
216 signed,
217 authority: authority.into(),
218 }
219 }
220
221 pub fn recover(signed: TempoSignedAuthorization) -> Self {
225 let authority = signed
226 .recover_authority()
227 .map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid);
228 Self::new_unchecked(signed, authority)
229 }
230
231 pub const fn signed(&self) -> &TempoSignedAuthorization {
233 &self.signed
234 }
235
236 pub const fn inner(&self) -> &Authorization {
238 self.signed.inner()
239 }
240
241 pub const fn signature(&self) -> &TempoSignature {
243 self.signed.signature()
244 }
245
246 pub fn authority(&self) -> Option<Address> {
250 match self.authority_status() {
251 RecoveredAuthority::Valid(addr) => Some(*addr),
252 RecoveredAuthority::Invalid => None,
253 }
254 }
255
256 pub fn authority_status(&self) -> &RecoveredAuthority {
260 self.authority.get_or_init(|| {
261 self.signed
262 .recover_authority()
263 .map_or(RecoveredAuthority::Invalid, RecoveredAuthority::Valid)
264 })
265 }
266
267 pub fn into_recovered_authorization(self) -> RecoveredAuthorization {
269 let authority = self.authority_status().clone();
270 RecoveredAuthorization::new_unchecked(self.signed.strip_signature(), authority)
271 }
272}
273
274impl PartialEq for RecoveredTempoAuthorization {
275 fn eq(&self, other: &Self) -> bool {
276 self.signed == other.signed
277 }
278}
279
280impl Eq for RecoveredTempoAuthorization {}
281
282impl core::hash::Hash for RecoveredTempoAuthorization {
283 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
284 self.signed.hash(state);
285 }
286}
287
288impl Deref for RecoveredTempoAuthorization {
289 type Target = Authorization;
290
291 fn deref(&self) -> &Self::Target {
292 self.signed.inner()
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299 use crate::TempoSignature;
300 use alloy_primitives::{U256, address};
301
302 #[test]
303 fn test_aa_signed_auth_encode_decode_roundtrip() {
304 let auth = Authorization {
305 chain_id: U256::from(1),
306 address: address!("0000000000000000000000000000000000000006"),
307 nonce: 1,
308 };
309
310 let signature = TempoSignature::default(); let signed = TempoSignedAuthorization::new_unchecked(auth, signature);
312
313 let mut buf = Vec::new();
314 signed.encode(&mut buf);
315
316 let decoded = TempoSignedAuthorization::decode(&mut buf.as_slice()).unwrap();
317 assert_eq!(buf.len(), signed.length());
318 assert_eq!(decoded, signed);
319 }
320
321 #[test]
322 fn test_signature_hash() {
323 let auth = Authorization {
324 chain_id: U256::from(1),
325 address: address!("0000000000000000000000000000000000000006"),
326 nonce: 1,
327 };
328
329 let signature = TempoSignature::default();
330 let signed = TempoSignedAuthorization::new_unchecked(auth.clone(), signature);
331
332 let expected_hash = {
334 let mut buf = Vec::new();
335 buf.push(MAGIC);
336 auth.encode(&mut buf);
337 keccak256(buf)
338 };
339
340 assert_eq!(signed.signature_hash(), expected_hash);
341 }
342}