1use std::{
2 cmp::min,
3 fmt::Display,
4 num::NonZeroUsize,
5 sync::{LazyLock, Mutex},
6};
7
8use crate::utils::keccak;
9use bytes::Bytes;
10use ethereum_types::{Address, H256, U256};
11use ethrex_crypto::{Crypto, CryptoError, NativeCrypto};
12use lru::LruCache;
13pub use mempool::MempoolTransaction;
14
15const MAX_SIGNER_CACHE_ENTRIES: usize = 100_000;
16
17pub static GLOBAL_SIGNER_CACHE: LazyLock<Mutex<LruCache<H256, Address>>> = LazyLock::new(|| {
28 Mutex::new(LruCache::new(
29 NonZeroUsize::new(MAX_SIGNER_CACHE_ENTRIES).expect("MAX_SIGNER_CACHE_ENTRIES is non-zero"),
30 ))
31});
32use rkyv::{Archive, Deserialize as RDeserialize, Serialize as RSerialize};
33use serde::{Serialize, ser::SerializeStruct};
34pub use serde_impl::{
35 AccessListEntry, AuthorizationTupleEntry, FrameEntry, GenericTransaction,
36 GenericTransactionError,
37};
38
39pub const EIP1559_DEFAULT_SERIALIZED_LENGTH: usize = 15;
41
42use ethrex_rlp::{
43 constants::RLP_NULL,
44 decode::{RLPDecode, decode_bytes, decode_rlp_item},
45 encode::{PayloadRLPEncode, RLPEncode},
46 error::RLPDecodeError,
47 structs::{Decoder, Encoder},
48};
49
50#[cfg(all(feature = "eip-8025", target_arch = "riscv64"))]
51use super::eip8025_cell::OnceCell;
52use crate::types::{
53 AccessList, AuthorizationList, BlobsBundle, constants::VERSIONED_HASH_VERSION_KZG,
54};
55#[cfg(not(all(feature = "eip-8025", target_arch = "riscv64")))]
56use once_cell::sync::OnceCell;
57
58#[derive(Clone, Debug, PartialEq, Eq, Serialize, RSerialize, RDeserialize, Archive)]
67#[serde(untagged)]
68pub enum Transaction {
69 LegacyTransaction(LegacyTransaction),
70 EIP2930Transaction(EIP2930Transaction),
71 EIP1559Transaction(EIP1559Transaction),
72 EIP4844Transaction(EIP4844Transaction),
73 EIP7702Transaction(EIP7702Transaction),
74 PrivilegedL2Transaction(PrivilegedL2Transaction),
75 FeeTokenTransaction(FeeTokenTransaction),
76 FrameTransaction(FrameTransaction),
77}
78
79#[derive(Clone, Debug, PartialEq, Eq)]
83pub enum P2PTransaction {
84 LegacyTransaction(LegacyTransaction),
85 EIP2930Transaction(EIP2930Transaction),
86 EIP1559Transaction(EIP1559Transaction),
87 EIP4844TransactionWithBlobs(WrappedEIP4844Transaction),
88 EIP7702Transaction(EIP7702Transaction),
89 FeeTokenTransaction(FeeTokenTransaction),
90 FrameTransaction(FrameTransaction),
91}
92
93impl TryInto<Transaction> for P2PTransaction {
94 type Error = String;
95
96 fn try_into(self) -> Result<Transaction, Self::Error> {
97 match self {
98 P2PTransaction::LegacyTransaction(itx) => Ok(Transaction::LegacyTransaction(itx)),
99 P2PTransaction::EIP2930Transaction(itx) => Ok(Transaction::EIP2930Transaction(itx)),
100 P2PTransaction::EIP1559Transaction(itx) => Ok(Transaction::EIP1559Transaction(itx)),
101 P2PTransaction::EIP7702Transaction(itx) => Ok(Transaction::EIP7702Transaction(itx)),
102 P2PTransaction::FrameTransaction(itx) => Ok(Transaction::FrameTransaction(itx)),
103 _ => Err("Can't convert blob p2p transaction into regular transaction. Blob bundle would be lost.".to_string()),
104 }
105 }
106}
107
108impl RLPEncode for P2PTransaction {
109 fn encode(&self, buf: &mut dyn bytes::BufMut) {
110 match self {
111 P2PTransaction::LegacyTransaction(t) => t.encode(buf),
112 tx => <[u8] as RLPEncode>::encode(&tx.encode_canonical_to_vec(), buf),
113 };
114 }
115}
116
117impl RLPDecode for P2PTransaction {
118 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
119 let (is_list, payload, remainder) = decode_rlp_item(rlp)?;
120 if !is_list {
121 let tx_type = *payload.first().ok_or(RLPDecodeError::InvalidLength)?;
122 let tx_encoding = payload.get(1..).ok_or(RLPDecodeError::InvalidLength)?;
123 let tx = match EnvelopeTxType::from_type_byte(tx_type)? {
126 EnvelopeTxType::EIP2930 => {
127 P2PTransaction::EIP2930Transaction(EIP2930Transaction::decode(tx_encoding)?)
128 }
129 EnvelopeTxType::EIP1559 => {
130 P2PTransaction::EIP1559Transaction(EIP1559Transaction::decode(tx_encoding)?)
131 }
132 EnvelopeTxType::EIP4844 => P2PTransaction::EIP4844TransactionWithBlobs(
133 WrappedEIP4844Transaction::decode(tx_encoding)?,
134 ),
135 EnvelopeTxType::EIP7702 => {
136 P2PTransaction::EIP7702Transaction(EIP7702Transaction::decode(tx_encoding)?)
137 }
138 EnvelopeTxType::Frame => {
139 P2PTransaction::FrameTransaction(FrameTransaction::decode(tx_encoding)?)
140 }
141 EnvelopeTxType::FeeToken => {
142 P2PTransaction::FeeTokenTransaction(FeeTokenTransaction::decode(tx_encoding)?)
143 }
144 EnvelopeTxType::Privileged => {
146 return Err(RLPDecodeError::Custom(
147 "privileged transactions are not sent over p2p".to_string(),
148 ));
149 }
150 };
151 Ok((tx, remainder))
152 } else {
153 LegacyTransaction::decode_unfinished(rlp)
155 .map(|(tx, rem)| (P2PTransaction::LegacyTransaction(tx), rem))
156 }
157 }
158}
159
160#[derive(Clone, Debug, PartialEq, Eq)]
161pub struct WrappedEIP4844Transaction {
162 pub tx: EIP4844Transaction,
163 pub wrapper_version: Option<u8>,
164 pub blobs_bundle: BlobsBundle,
165}
166
167impl RLPEncode for WrappedEIP4844Transaction {
168 fn encode(&self, buf: &mut dyn bytes::BufMut) {
169 let encoder = Encoder::new(buf);
170 encoder
171 .encode_field(&self.tx)
172 .encode_optional_field(&self.wrapper_version)
173 .encode_field(&self.blobs_bundle.blobs)
174 .encode_field(&self.blobs_bundle.commitments)
175 .encode_field(&self.blobs_bundle.proofs)
176 .finish();
177 }
178}
179
180impl RLPDecode for WrappedEIP4844Transaction {
181 fn decode_unfinished(rlp: &[u8]) -> Result<(WrappedEIP4844Transaction, &[u8]), RLPDecodeError> {
182 let decoder = Decoder::new(rlp)?;
183 let Ok((tx, decoder)) = decoder.decode_field("tx") else {
184 let (tx, rest) = EIP4844Transaction::decode_unfinished(rlp)?;
186 return Ok((
187 WrappedEIP4844Transaction {
188 tx,
189 wrapper_version: None,
190 blobs_bundle: BlobsBundle::empty(),
192 },
193 rest,
194 ));
195 };
196
197 let (wrapper_version, decoder) = decoder.decode_optional_field();
198 let (blobs, decoder) = decoder.decode_field("blobs")?;
199 let (commitments, decoder) = decoder.decode_field("commitments")?;
200 let (proofs, decoder) = decoder.decode_field("proofs")?;
201
202 let wrapped = WrappedEIP4844Transaction {
203 tx,
204 wrapper_version,
205 blobs_bundle: BlobsBundle {
206 blobs,
207 commitments,
208 proofs,
209 version: wrapper_version.unwrap_or_default(),
210 },
211 };
212 Ok((wrapped, decoder.finish()?))
213 }
214}
215
216#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
217pub struct LegacyTransaction {
218 pub nonce: u64,
219 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
220 pub gas_price: U256,
221 pub gas: u64,
222 pub to: TxKind,
225 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
226 pub value: U256,
227 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
228 pub data: Bytes,
229 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
230 pub v: U256,
231 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
232 pub r: U256,
233 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
234 pub s: U256,
235 #[rkyv(with=rkyv::with::Skip)]
236 pub inner_hash: OnceCell<H256>,
237 #[rkyv(with=rkyv::with::Skip)]
238 pub sender_cache: OnceCell<Address>,
239}
240
241#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
242pub struct EIP2930Transaction {
243 pub chain_id: u64,
244 pub nonce: u64,
245 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
246 pub gas_price: U256,
247 pub gas_limit: u64,
248 pub to: TxKind,
249 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
250 pub value: U256,
251 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
252 pub data: Bytes,
253 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
254 pub access_list: AccessList,
255 pub signature_y_parity: bool,
256 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
257 pub signature_r: U256,
258 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
259 pub signature_s: U256,
260 #[rkyv(with=rkyv::with::Skip)]
261 pub inner_hash: OnceCell<H256>,
262 #[rkyv(with=rkyv::with::Skip)]
263 pub sender_cache: OnceCell<Address>,
264 #[rkyv(with=rkyv::with::Skip)]
265 pub cached_canonical: OnceCell<Vec<u8>>,
266}
267
268#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
269pub struct EIP1559Transaction {
270 pub chain_id: u64,
271 pub nonce: u64,
272 pub max_priority_fee_per_gas: u64,
273 pub max_fee_per_gas: u64,
274 pub gas_limit: u64,
275 pub to: TxKind,
276 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
277 pub value: U256,
278 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
279 pub data: Bytes,
280 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
281 pub access_list: AccessList,
282 pub signature_y_parity: bool,
283 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
284 pub signature_r: U256,
285 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
286 pub signature_s: U256,
287 #[rkyv(with=rkyv::with::Skip)]
288 pub inner_hash: OnceCell<H256>,
289 #[rkyv(with=rkyv::with::Skip)]
290 pub sender_cache: OnceCell<Address>,
291 #[rkyv(with=rkyv::with::Skip)]
292 pub cached_canonical: OnceCell<Vec<u8>>,
293}
294
295#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
296pub struct EIP4844Transaction {
297 pub chain_id: u64,
298 pub nonce: u64,
299 pub max_priority_fee_per_gas: u64,
300 pub max_fee_per_gas: u64,
301 pub gas: u64,
302 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
303 pub to: Address,
304 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
305 pub value: U256,
306 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
307 pub data: Bytes,
308 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
309 pub access_list: AccessList,
310 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
311 pub max_fee_per_blob_gas: U256,
312 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H256Wrapper>)]
313 pub blob_versioned_hashes: Vec<H256>,
314 pub signature_y_parity: bool,
315 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
316 pub signature_r: U256,
317 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
318 pub signature_s: U256,
319 #[rkyv(with=rkyv::with::Skip)]
320 pub inner_hash: OnceCell<H256>,
321 #[rkyv(with=rkyv::with::Skip)]
322 pub sender_cache: OnceCell<Address>,
323 #[rkyv(with=rkyv::with::Skip)]
324 pub cached_canonical: OnceCell<Vec<u8>>,
325}
326
327#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
328pub struct EIP7702Transaction {
329 pub chain_id: u64,
330 pub nonce: u64,
331 pub max_priority_fee_per_gas: u64,
332 pub max_fee_per_gas: u64,
333 pub gas_limit: u64,
334 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
335 pub to: Address,
336 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
337 pub value: U256,
338 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
339 pub data: Bytes,
340 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
341 pub access_list: AccessList,
342 pub authorization_list: AuthorizationList,
343 pub signature_y_parity: bool,
344 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
345 pub signature_r: U256,
346 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
347 pub signature_s: U256,
348 #[rkyv(with=rkyv::with::Skip)]
349 pub inner_hash: OnceCell<H256>,
350 #[rkyv(with=rkyv::with::Skip)]
351 pub sender_cache: OnceCell<Address>,
352 #[rkyv(with=rkyv::with::Skip)]
353 pub cached_canonical: OnceCell<Vec<u8>>,
354}
355#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
356pub struct PrivilegedL2Transaction {
357 pub chain_id: u64,
358 pub nonce: u64,
359 pub max_priority_fee_per_gas: u64,
360 pub max_fee_per_gas: u64,
361 pub gas_limit: u64,
362 pub to: TxKind,
363 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
364 pub value: U256,
365 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
366 pub data: Bytes,
367 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
368 pub access_list: AccessList,
369 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
370 pub from: Address,
371 #[rkyv(with=rkyv::with::Skip)]
372 pub inner_hash: OnceCell<H256>,
373 #[rkyv(with=rkyv::with::Skip)]
374 pub sender_cache: OnceCell<Address>,
375 #[rkyv(with=rkyv::with::Skip)]
376 pub cached_canonical: OnceCell<Vec<u8>>,
377}
378
379#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
380pub enum TxType {
381 #[default]
382 Legacy = 0x00,
383 EIP2930 = 0x01,
384 EIP1559 = 0x02,
385 EIP4844 = 0x03,
386 EIP7702 = 0x04,
387 Frame = 0x06,
388 FeeToken = 0x7d,
389 Privileged = 0x7e,
392}
393
394#[derive(Clone, Copy, Debug, PartialEq, Eq)]
400pub enum EnvelopeTxType {
401 EIP2930,
402 EIP1559,
403 EIP4844,
404 EIP7702,
405 Frame,
406 FeeToken,
407 Privileged,
408}
409
410impl EnvelopeTxType {
411 pub fn from_type_byte(byte: u8) -> Result<Self, RLPDecodeError> {
419 TxType::from_u8(byte)
420 .and_then(TxType::as_envelope)
421 .ok_or_else(|| RLPDecodeError::Custom(format!("Invalid transaction type: {byte}")))
422 }
423}
424
425impl From<TxType> for u8 {
426 fn from(val: TxType) -> Self {
427 match val {
428 TxType::Legacy => 0x00,
429 TxType::EIP2930 => 0x01,
430 TxType::EIP1559 => 0x02,
431 TxType::EIP4844 => 0x03,
432 TxType::EIP7702 => 0x04,
433 TxType::Frame => 0x06,
434 TxType::FeeToken => 0x7d,
435 TxType::Privileged => 0x7e,
436 }
437 }
438}
439
440impl Display for TxType {
441 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
442 match self {
443 TxType::Legacy => write!(f, "Legacy"),
444 TxType::EIP2930 => write!(f, "EIP2930"),
445 TxType::EIP1559 => write!(f, "EIP1559"),
446 TxType::EIP4844 => write!(f, "EIP4844"),
447 TxType::EIP7702 => write!(f, "EIP7702"),
448 TxType::Frame => write!(f, "Frame"),
449 TxType::Privileged => write!(f, "Privileged"),
450 TxType::FeeToken => write!(f, "FeeToken"),
451 }
452 }
453}
454
455impl Transaction {
456 pub fn tx_type(&self) -> TxType {
457 match self {
458 Transaction::LegacyTransaction(_) => TxType::Legacy,
459 Transaction::EIP2930Transaction(_) => TxType::EIP2930,
460 Transaction::EIP1559Transaction(_) => TxType::EIP1559,
461 Transaction::EIP4844Transaction(_) => TxType::EIP4844,
462 Transaction::EIP7702Transaction(_) => TxType::EIP7702,
463 Transaction::FeeTokenTransaction(_) => TxType::FeeToken,
464 Transaction::PrivilegedL2Transaction(_) => TxType::Privileged,
465 Transaction::FrameTransaction(_) => TxType::Frame,
466 }
467 }
468
469 fn calc_effective_gas_price(&self, base_fee_per_gas: Option<u64>) -> Option<U256> {
470 let base_fee = base_fee_per_gas?;
471 let max_fee = self.max_fee_per_gas()?;
472 if max_fee < base_fee {
473 return None;
475 }
476
477 let priority_fee_per_gas = min(self.max_priority_fee()?, max_fee.saturating_sub(base_fee));
478 Some(U256::from(priority_fee_per_gas) + U256::from(base_fee))
479 }
480
481 pub fn effective_gas_price(&self, base_fee_per_gas: Option<u64>) -> Option<U256> {
482 match self.tx_type() {
483 TxType::Legacy => Some(self.gas_price()),
484 TxType::EIP2930 => Some(self.gas_price()),
485 TxType::EIP1559 => self.calc_effective_gas_price(base_fee_per_gas),
486 TxType::EIP4844 => self.calc_effective_gas_price(base_fee_per_gas),
487 TxType::EIP7702 => self.calc_effective_gas_price(base_fee_per_gas),
488 TxType::Frame => self.calc_effective_gas_price(base_fee_per_gas),
489 TxType::FeeToken => self.calc_effective_gas_price(base_fee_per_gas),
490 TxType::Privileged => Some(self.gas_price()),
491 }
492 }
493
494 pub fn cost_without_base_fee(&self) -> Option<U256> {
495 let price = match self.tx_type() {
496 TxType::Legacy => self.gas_price(),
497 TxType::EIP2930 => self.gas_price(),
498 TxType::EIP1559 => U256::from(self.max_fee_per_gas()?),
499 TxType::EIP4844 => U256::from(self.max_fee_per_gas()?),
500 TxType::EIP7702 => U256::from(self.max_fee_per_gas()?),
501 TxType::Frame => U256::from(self.max_fee_per_gas()?),
502 TxType::FeeToken => U256::from(self.max_fee_per_gas()?),
503 TxType::Privileged => self.gas_price(),
504 };
505
506 let base = U256::saturating_add(
507 U256::saturating_mul(price, self.gas_limit().into()),
508 self.value(),
509 );
510
511 if let Transaction::EIP4844Transaction(tx) = self {
515 let blob_gas = U256::from(crate::constants::GAS_PER_BLOB)
516 .saturating_mul(U256::from(tx.blob_versioned_hashes.len() as u64));
517 let blob_cost = blob_gas.saturating_mul(tx.max_fee_per_blob_gas);
518 return Some(base.saturating_add(blob_cost));
519 }
520
521 Some(base)
522 }
523
524 pub fn fee_token(&self) -> Option<Address> {
525 if let Transaction::FeeTokenTransaction(tx) = self {
526 Some(tx.fee_token)
527 } else {
528 None
529 }
530 }
531
532 fn cached_canonical_cell(&self) -> Option<&OnceCell<Vec<u8>>> {
535 match self {
536 Transaction::LegacyTransaction(_) => None,
537 Transaction::EIP2930Transaction(t) => Some(&t.cached_canonical),
538 Transaction::EIP1559Transaction(t) => Some(&t.cached_canonical),
539 Transaction::EIP4844Transaction(t) => Some(&t.cached_canonical),
540 Transaction::EIP7702Transaction(t) => Some(&t.cached_canonical),
541 Transaction::PrivilegedL2Transaction(t) => Some(&t.cached_canonical),
542 Transaction::FeeTokenTransaction(t) => Some(&t.cached_canonical),
543 Transaction::FrameTransaction(t) => Some(&t.cached_canonical),
544 }
545 }
546}
547
548impl RLPEncode for Transaction {
549 fn encode(&self, buf: &mut dyn bytes::BufMut) {
554 match self {
555 Transaction::LegacyTransaction(t) => t.encode(buf),
556 _ => {
557 let canonical = self.encode_canonical_to_vec();
558 <[u8] as RLPEncode>::encode(canonical.as_slice(), buf)
559 }
560 };
561 }
562}
563
564impl RLPDecode for Transaction {
565 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
570 let (is_list, payload, remainder) = decode_rlp_item(rlp)?;
571 if !is_list {
572 let tx_type = *payload.first().ok_or(RLPDecodeError::InvalidLength)?;
573 let tx_encoding = payload.get(1..).ok_or(RLPDecodeError::InvalidLength)?;
574 let tx = match EnvelopeTxType::from_type_byte(tx_type)? {
578 EnvelopeTxType::EIP2930 => {
579 Transaction::EIP2930Transaction(EIP2930Transaction::decode(tx_encoding)?)
580 }
581 EnvelopeTxType::EIP1559 => {
582 Transaction::EIP1559Transaction(EIP1559Transaction::decode(tx_encoding)?)
583 }
584 EnvelopeTxType::EIP4844 => {
585 Transaction::EIP4844Transaction(EIP4844Transaction::decode(tx_encoding)?)
586 }
587 EnvelopeTxType::EIP7702 => {
588 Transaction::EIP7702Transaction(EIP7702Transaction::decode(tx_encoding)?)
589 }
590 EnvelopeTxType::Frame => {
591 Transaction::FrameTransaction(FrameTransaction::decode(tx_encoding)?)
592 }
593 EnvelopeTxType::FeeToken => {
594 Transaction::FeeTokenTransaction(FeeTokenTransaction::decode(tx_encoding)?)
595 }
596 EnvelopeTxType::Privileged => Transaction::PrivilegedL2Transaction(
597 PrivilegedL2Transaction::decode(tx_encoding)?,
598 ),
599 };
600 Ok((tx, remainder))
601 } else {
602 LegacyTransaction::decode_unfinished(rlp)
604 .map(|(tx, rem)| (Transaction::LegacyTransaction(tx), rem))
605 }
606 }
607}
608
609#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
611pub enum TxKind {
612 Call(#[rkyv(with=crate::rkyv_utils::H160Wrapper)] Address),
613 #[default]
614 Create,
615}
616
617impl RLPEncode for TxKind {
618 fn encode(&self, buf: &mut dyn bytes::BufMut) {
619 match self {
620 Self::Call(address) => address.encode(buf),
621 Self::Create => buf.put_u8(RLP_NULL),
622 }
623 }
624}
625
626impl RLPDecode for TxKind {
627 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
628 let first_byte = rlp.first().ok_or(RLPDecodeError::InvalidLength)?;
629 if *first_byte == RLP_NULL {
630 return Ok((Self::Create, &rlp[1..]));
631 }
632 Address::decode_unfinished(rlp).map(|(t, rest)| (Self::Call(t), rest))
633 }
634}
635
636impl RLPEncode for LegacyTransaction {
637 fn encode(&self, buf: &mut dyn bytes::BufMut) {
638 Encoder::new(buf)
639 .encode_field(&self.nonce)
640 .encode_field(&self.gas_price)
641 .encode_field(&self.gas)
642 .encode_field(&self.to)
643 .encode_field(&self.value)
644 .encode_field(&self.data)
645 .encode_field(&self.v)
646 .encode_field(&self.r)
647 .encode_field(&self.s)
648 .finish();
649 }
650}
651
652impl RLPEncode for EIP2930Transaction {
653 fn encode(&self, buf: &mut dyn bytes::BufMut) {
654 Encoder::new(buf)
655 .encode_field(&self.chain_id)
656 .encode_field(&self.nonce)
657 .encode_field(&self.gas_price)
658 .encode_field(&self.gas_limit)
659 .encode_field(&self.to)
660 .encode_field(&self.value)
661 .encode_field(&self.data)
662 .encode_field(&self.access_list)
663 .encode_field(&self.signature_y_parity)
664 .encode_field(&self.signature_r)
665 .encode_field(&self.signature_s)
666 .finish()
667 }
668}
669
670impl RLPEncode for EIP1559Transaction {
671 fn encode(&self, buf: &mut dyn bytes::BufMut) {
672 Encoder::new(buf)
673 .encode_field(&self.chain_id)
674 .encode_field(&self.nonce)
675 .encode_field(&self.max_priority_fee_per_gas)
676 .encode_field(&self.max_fee_per_gas)
677 .encode_field(&self.gas_limit)
678 .encode_field(&self.to)
679 .encode_field(&self.value)
680 .encode_field(&self.data)
681 .encode_field(&self.access_list)
682 .encode_field(&self.signature_y_parity)
683 .encode_field(&self.signature_r)
684 .encode_field(&self.signature_s)
685 .finish()
686 }
687}
688
689impl RLPEncode for EIP4844Transaction {
690 fn encode(&self, buf: &mut dyn bytes::BufMut) {
691 Encoder::new(buf)
692 .encode_field(&self.chain_id)
693 .encode_field(&self.nonce)
694 .encode_field(&self.max_priority_fee_per_gas)
695 .encode_field(&self.max_fee_per_gas)
696 .encode_field(&self.gas)
697 .encode_field(&self.to)
698 .encode_field(&self.value)
699 .encode_field(&self.data)
700 .encode_field(&self.access_list)
701 .encode_field(&self.max_fee_per_blob_gas)
702 .encode_field(&self.blob_versioned_hashes)
703 .encode_field(&self.signature_y_parity)
704 .encode_field(&self.signature_r)
705 .encode_field(&self.signature_s)
706 .finish()
707 }
708}
709
710impl RLPEncode for EIP7702Transaction {
711 fn encode(&self, buf: &mut dyn bytes::BufMut) {
712 Encoder::new(buf)
713 .encode_field(&self.chain_id)
714 .encode_field(&self.nonce)
715 .encode_field(&self.max_priority_fee_per_gas)
716 .encode_field(&self.max_fee_per_gas)
717 .encode_field(&self.gas_limit)
718 .encode_field(&self.to)
719 .encode_field(&self.value)
720 .encode_field(&self.data)
721 .encode_field(&self.access_list)
722 .encode_field(&self.authorization_list)
723 .encode_field(&self.signature_y_parity)
724 .encode_field(&self.signature_r)
725 .encode_field(&self.signature_s)
726 .finish()
727 }
728}
729
730impl RLPEncode for PrivilegedL2Transaction {
731 fn encode(&self, buf: &mut dyn bytes::BufMut) {
732 Encoder::new(buf)
733 .encode_field(&self.chain_id)
734 .encode_field(&self.nonce)
735 .encode_field(&self.max_priority_fee_per_gas)
736 .encode_field(&self.max_fee_per_gas)
737 .encode_field(&self.gas_limit)
738 .encode_field(&self.to)
739 .encode_field(&self.value)
740 .encode_field(&self.data)
741 .encode_field(&self.access_list)
742 .encode_field(&self.from)
743 .finish()
744 }
745}
746
747impl RLPEncode for FeeTokenTransaction {
748 fn encode(&self, buf: &mut dyn bytes::BufMut) {
749 Encoder::new(buf)
750 .encode_field(&self.chain_id)
751 .encode_field(&self.nonce)
752 .encode_field(&self.max_priority_fee_per_gas)
753 .encode_field(&self.max_fee_per_gas)
754 .encode_field(&self.gas_limit)
755 .encode_field(&self.to)
756 .encode_field(&self.value)
757 .encode_field(&self.data)
758 .encode_field(&self.access_list)
759 .encode_field(&self.fee_token)
760 .encode_field(&self.signature_y_parity)
761 .encode_field(&self.signature_r)
762 .encode_field(&self.signature_s)
763 .finish()
764 }
765}
766
767impl PayloadRLPEncode for Transaction {
768 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
769 match self {
770 Transaction::LegacyTransaction(tx) => tx.encode_payload(buf),
771 Transaction::EIP1559Transaction(tx) => tx.encode_payload(buf),
772 Transaction::EIP2930Transaction(tx) => tx.encode_payload(buf),
773 Transaction::EIP4844Transaction(tx) => tx.encode_payload(buf),
774 Transaction::EIP7702Transaction(tx) => tx.encode_payload(buf),
775 Transaction::PrivilegedL2Transaction(tx) => tx.encode_payload(buf),
776 Transaction::FeeTokenTransaction(tx) => tx.encode_payload(buf),
777 Transaction::FrameTransaction(tx) => tx.encode_payload(buf),
778 }
779 }
780}
781
782impl PayloadRLPEncode for LegacyTransaction {
783 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
784 Encoder::new(buf)
785 .encode_field(&self.nonce)
786 .encode_field(&self.gas_price)
787 .encode_field(&self.gas)
788 .encode_field(&self.to)
789 .encode_field(&self.value)
790 .encode_field(&self.data)
791 .finish();
792 }
793}
794
795impl PayloadRLPEncode for EIP1559Transaction {
796 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
797 Encoder::new(buf)
798 .encode_field(&self.chain_id)
799 .encode_field(&self.nonce)
800 .encode_field(&self.max_priority_fee_per_gas)
801 .encode_field(&self.max_fee_per_gas)
802 .encode_field(&self.gas_limit)
803 .encode_field(&self.to)
804 .encode_field(&self.value)
805 .encode_field(&self.data)
806 .encode_field(&self.access_list)
807 .finish();
808 }
809}
810
811impl PayloadRLPEncode for EIP2930Transaction {
812 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
813 Encoder::new(buf)
814 .encode_field(&self.chain_id)
815 .encode_field(&self.nonce)
816 .encode_field(&self.gas_price)
817 .encode_field(&self.gas_limit)
818 .encode_field(&self.to)
819 .encode_field(&self.value)
820 .encode_field(&self.data)
821 .encode_field(&self.access_list)
822 .finish();
823 }
824}
825
826impl PayloadRLPEncode for EIP4844Transaction {
827 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
828 Encoder::new(buf)
829 .encode_field(&self.chain_id)
830 .encode_field(&self.nonce)
831 .encode_field(&self.max_priority_fee_per_gas)
832 .encode_field(&self.max_fee_per_gas)
833 .encode_field(&self.gas)
834 .encode_field(&self.to)
835 .encode_field(&self.value)
836 .encode_field(&self.data)
837 .encode_field(&self.access_list)
838 .encode_field(&self.max_fee_per_blob_gas)
839 .encode_field(&self.blob_versioned_hashes)
840 .finish();
841 }
842}
843
844impl PayloadRLPEncode for EIP7702Transaction {
845 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
846 Encoder::new(buf)
847 .encode_field(&self.chain_id)
848 .encode_field(&self.nonce)
849 .encode_field(&self.max_priority_fee_per_gas)
850 .encode_field(&self.max_fee_per_gas)
851 .encode_field(&self.gas_limit)
852 .encode_field(&self.to)
853 .encode_field(&self.value)
854 .encode_field(&self.data)
855 .encode_field(&self.access_list)
856 .encode_field(&self.authorization_list)
857 .finish();
858 }
859}
860
861impl PayloadRLPEncode for PrivilegedL2Transaction {
862 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
863 Encoder::new(buf)
864 .encode_field(&self.chain_id)
865 .encode_field(&self.nonce)
866 .encode_field(&self.max_priority_fee_per_gas)
867 .encode_field(&self.max_fee_per_gas)
868 .encode_field(&self.gas_limit)
869 .encode_field(&self.to)
870 .encode_field(&self.value)
871 .encode_field(&self.data)
872 .encode_field(&self.access_list)
873 .encode_field(&self.from)
874 .finish();
875 }
876}
877
878impl PayloadRLPEncode for FeeTokenTransaction {
879 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
880 Encoder::new(buf)
881 .encode_field(&self.chain_id)
882 .encode_field(&self.nonce)
883 .encode_field(&self.max_priority_fee_per_gas)
884 .encode_field(&self.max_fee_per_gas)
885 .encode_field(&self.gas_limit)
886 .encode_field(&self.to)
887 .encode_field(&self.value)
888 .encode_field(&self.data)
889 .encode_field(&self.access_list)
890 .encode_field(&self.fee_token)
891 .finish();
892 }
893}
894
895fn decode_nonce_field(decoder: Decoder) -> Result<(u64, Decoder), RLPDecodeError> {
905 let wrap =
909 |err| RLPDecodeError::Custom(format!("Error decoding field 'nonce' of type u64: {err}"));
910 let (item, rest) = decoder.get_encoded_item_ref().map_err(wrap)?;
911 if let Ok((content, _)) = decode_bytes(item)
923 && content.len() > 8
924 && content.first() != Some(&0)
925 {
926 return Err(RLPDecodeError::Custom("Nonce is max".into()));
927 }
928 let (nonce, _) = u64::decode_unfinished(item).map_err(wrap)?;
929 Ok((nonce, rest))
930}
931
932impl RLPDecode for LegacyTransaction {
933 fn decode_unfinished(rlp: &[u8]) -> Result<(LegacyTransaction, &[u8]), RLPDecodeError> {
934 let decoder = Decoder::new(rlp)?;
935 let (nonce, decoder) = decode_nonce_field(decoder)?;
936 let (gas_price, decoder) = decoder.decode_field("gas_price")?;
937 let (gas, decoder) = decoder.decode_field("gas")?;
938 let (to, decoder) = decoder.decode_field("to")?;
939 let (value, decoder) = decoder.decode_field("value")?;
940 let (data, decoder) = decoder.decode_field("data")?;
941 let (v, decoder) = decoder.decode_field("v")?;
942 let (r, decoder) = decoder.decode_field("r")?;
943 let (s, decoder) = decoder.decode_field("s")?;
944 let inner_hash = OnceCell::new();
945 let sender_cache = OnceCell::new();
946
947 let tx = LegacyTransaction {
948 nonce,
949 gas_price,
950 gas,
951 to,
952 value,
953 data,
954 v,
955 r,
956 s,
957 inner_hash,
958 sender_cache,
959 };
960 Ok((tx, decoder.finish()?))
961 }
962}
963
964impl RLPDecode for EIP2930Transaction {
965 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP2930Transaction, &[u8]), RLPDecodeError> {
966 let decoder = Decoder::new(rlp)?;
967 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
968 let (nonce, decoder) = decode_nonce_field(decoder)?;
969 let (gas_price, decoder) = decoder.decode_field("gas_price")?;
970 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
971 let (to, decoder) = decoder.decode_field("to")?;
972 let (value, decoder) = decoder.decode_field("value")?;
973 let (data, decoder) = decoder.decode_field("data")?;
974 let (access_list, decoder) = decoder.decode_field("access_list")?;
975 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
976 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
977 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
978 let inner_hash = OnceCell::new();
979 let sender_cache = OnceCell::new();
980 let cached_canonical = OnceCell::new();
981
982 let tx = EIP2930Transaction {
983 chain_id,
984 nonce,
985 gas_price,
986 gas_limit,
987 to,
988 value,
989 data,
990 access_list,
991 signature_y_parity,
992 signature_r,
993 signature_s,
994 inner_hash,
995 sender_cache,
996 cached_canonical,
997 };
998 Ok((tx, decoder.finish()?))
999 }
1000}
1001
1002impl RLPDecode for EIP1559Transaction {
1003 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP1559Transaction, &[u8]), RLPDecodeError> {
1004 let decoder = Decoder::new(rlp)?;
1005 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1006 let (nonce, decoder) = decode_nonce_field(decoder)?;
1007 let (max_priority_fee_per_gas, decoder) =
1008 decoder.decode_field("max_priority_fee_per_gas")?;
1009 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1010 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1011 let (to, decoder) = decoder.decode_field("to")?;
1012 let (value, decoder) = decoder.decode_field("value")?;
1013 let (data, decoder) = decoder.decode_field("data")?;
1014 let (access_list, decoder) = decoder.decode_field("access_list")?;
1015 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1016 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1017 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1018 let inner_hash = OnceCell::new();
1019 let sender_cache = OnceCell::new();
1020 let cached_canonical = OnceCell::new();
1021
1022 let tx = EIP1559Transaction {
1023 chain_id,
1024 nonce,
1025 max_priority_fee_per_gas,
1026 max_fee_per_gas,
1027 gas_limit,
1028 to,
1029 value,
1030 data,
1031 access_list,
1032 signature_y_parity,
1033 signature_r,
1034 signature_s,
1035 inner_hash,
1036 sender_cache,
1037 cached_canonical,
1038 };
1039 Ok((tx, decoder.finish()?))
1040 }
1041}
1042
1043impl RLPDecode for EIP4844Transaction {
1044 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP4844Transaction, &[u8]), RLPDecodeError> {
1045 let decoder = Decoder::new(rlp)?;
1046 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1047 let (nonce, decoder) = decode_nonce_field(decoder)?;
1048 let (max_priority_fee_per_gas, decoder) =
1049 decoder.decode_field("max_priority_fee_per_gas")?;
1050 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1051 let (gas, decoder) = decoder.decode_field("gas")?;
1052 let (to, decoder) = decoder.decode_field("to")?;
1053 let (value, decoder) = decoder.decode_field("value")?;
1054 let (data, decoder) = decoder.decode_field("data")?;
1055 let (access_list, decoder) = decoder.decode_field("access_list")?;
1056 let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
1057 let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
1058 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1059 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1060 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1061 let inner_hash = OnceCell::new();
1062 let sender_cache = OnceCell::new();
1063 let cached_canonical = OnceCell::new();
1064
1065 let tx = EIP4844Transaction {
1066 chain_id,
1067 nonce,
1068 max_priority_fee_per_gas,
1069 max_fee_per_gas,
1070 gas,
1071 to,
1072 value,
1073 data,
1074 access_list,
1075 max_fee_per_blob_gas,
1076 blob_versioned_hashes,
1077 signature_y_parity,
1078 signature_r,
1079 signature_s,
1080 inner_hash,
1081 sender_cache,
1082 cached_canonical,
1083 };
1084 Ok((tx, decoder.finish()?))
1085 }
1086}
1087
1088impl RLPDecode for EIP7702Transaction {
1089 fn decode_unfinished(rlp: &[u8]) -> Result<(EIP7702Transaction, &[u8]), RLPDecodeError> {
1090 let decoder = Decoder::new(rlp)?;
1091 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1092 let (nonce, decoder) = decode_nonce_field(decoder)?;
1093 let (max_priority_fee_per_gas, decoder) =
1094 decoder.decode_field("max_priority_fee_per_gas")?;
1095 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1096 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1097 let (to, decoder) = decoder.decode_field("to")?;
1098 let (value, decoder) = decoder.decode_field("value")?;
1099 let (data, decoder) = decoder.decode_field("data")?;
1100 let (access_list, decoder) = decoder.decode_field("access_list")?;
1101 let (authorization_list, decoder) = decoder.decode_field("authorization_list")?;
1102 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1103 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1104 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1105 let inner_hash = OnceCell::new();
1106 let sender_cache = OnceCell::new();
1107 let cached_canonical = OnceCell::new();
1108
1109 let tx = EIP7702Transaction {
1110 chain_id,
1111 nonce,
1112 max_priority_fee_per_gas,
1113 max_fee_per_gas,
1114 gas_limit,
1115 to,
1116 value,
1117 data,
1118 access_list,
1119 authorization_list,
1120 signature_y_parity,
1121 signature_r,
1122 signature_s,
1123 inner_hash,
1124 sender_cache,
1125 cached_canonical,
1126 };
1127 Ok((tx, decoder.finish()?))
1128 }
1129}
1130
1131impl RLPDecode for PrivilegedL2Transaction {
1132 fn decode_unfinished(rlp: &[u8]) -> Result<(PrivilegedL2Transaction, &[u8]), RLPDecodeError> {
1133 let decoder = Decoder::new(rlp)?;
1134 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1135 let (nonce, decoder) = decode_nonce_field(decoder)?;
1136 let (max_priority_fee_per_gas, decoder) =
1137 decoder.decode_field("max_priority_fee_per_gas")?;
1138 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1139 let (gas_limit, decoder) = decoder.decode_field::<u64>("gas_limit")?;
1140 let (to, decoder) = decoder.decode_field("to")?;
1141 let (value, decoder) = decoder.decode_field("value")?;
1142 let (data, decoder) = decoder.decode_field("data")?;
1143 let (access_list, decoder) = decoder.decode_field("access_list")?;
1144 let (from, decoder) = decoder.decode_field("from")?;
1145 let inner_hash = OnceCell::new();
1146 let sender_cache = OnceCell::new();
1147 let cached_canonical = OnceCell::new();
1148
1149 let tx = PrivilegedL2Transaction {
1150 chain_id,
1151 nonce,
1152 max_priority_fee_per_gas,
1153 max_fee_per_gas,
1154 gas_limit,
1155 to,
1156 value,
1157 data,
1158 access_list,
1159 from,
1160 inner_hash,
1161 sender_cache,
1162 cached_canonical,
1163 };
1164 Ok((tx, decoder.finish()?))
1165 }
1166}
1167
1168impl RLPDecode for FeeTokenTransaction {
1169 fn decode_unfinished(rlp: &[u8]) -> Result<(FeeTokenTransaction, &[u8]), RLPDecodeError> {
1170 let decoder = Decoder::new(rlp)?;
1171 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
1172 let (nonce, decoder) = decode_nonce_field(decoder)?;
1173 let (max_priority_fee_per_gas, decoder) =
1174 decoder.decode_field("max_priority_fee_per_gas")?;
1175 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
1176 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1177 let (to, decoder) = decoder.decode_field("to")?;
1178 let (value, decoder) = decoder.decode_field("value")?;
1179 let (data, decoder) = decoder.decode_field("data")?;
1180 let (access_list, decoder) = decoder.decode_field("access_list")?;
1181 let (fee_token, decoder) = decoder.decode_field("fee_token")?;
1182 let (signature_y_parity, decoder) = decoder.decode_field("signature_y_parity")?;
1183 let (signature_r, decoder) = decoder.decode_field("signature_r")?;
1184 let (signature_s, decoder) = decoder.decode_field("signature_s")?;
1185 let inner_hash = OnceCell::new();
1186 let sender_cache = OnceCell::new();
1187 let cached_canonical = OnceCell::new();
1188
1189 let tx = FeeTokenTransaction {
1190 chain_id,
1191 nonce,
1192 max_priority_fee_per_gas,
1193 max_fee_per_gas,
1194 gas_limit,
1195 to,
1196 value,
1197 data,
1198 access_list,
1199 fee_token,
1200 signature_y_parity,
1201 signature_r,
1202 signature_s,
1203 inner_hash,
1204 sender_cache,
1205 cached_canonical,
1206 };
1207 Ok((tx, decoder.finish()?))
1208 }
1209}
1210
1211impl Transaction {
1212 pub fn sender(&self, crypto: &dyn Crypto) -> Result<Address, CryptoError> {
1213 if let Transaction::FrameTransaction(tx) = self {
1215 return Ok(tx.sender);
1216 }
1217 let sender_cache = match self {
1218 Transaction::LegacyTransaction(tx) => &tx.sender_cache,
1219 Transaction::EIP2930Transaction(tx) => &tx.sender_cache,
1220 Transaction::EIP1559Transaction(tx) => &tx.sender_cache,
1221 Transaction::EIP4844Transaction(tx) => &tx.sender_cache,
1222 Transaction::EIP7702Transaction(tx) => &tx.sender_cache,
1223 Transaction::PrivilegedL2Transaction(tx) => &tx.sender_cache,
1224 Transaction::FeeTokenTransaction(tx) => &tx.sender_cache,
1225 Transaction::FrameTransaction(_) => unreachable!(),
1226 };
1227 sender_cache
1228 .get_or_try_init(|| {
1229 let tx_hash = self.hash(crypto);
1230 let mut cache = GLOBAL_SIGNER_CACHE
1232 .lock()
1233 .unwrap_or_else(|e| e.into_inner());
1234 if let Some(&addr) = cache.get(&tx_hash) {
1235 return Ok(addr);
1236 }
1237 drop(cache);
1238 let sender = self.compute_sender(crypto)?;
1240 GLOBAL_SIGNER_CACHE
1242 .lock()
1243 .unwrap_or_else(|e| e.into_inner())
1244 .put(tx_hash, sender);
1245 Ok(sender)
1246 })
1247 .copied()
1248 }
1249
1250 fn compute_sender(&self, crypto: &dyn Crypto) -> Result<Address, CryptoError> {
1251 let (buf, sig) = match self {
1252 Transaction::LegacyTransaction(tx) => {
1253 let v = u64::try_from(tx.v).map_err(|_| CryptoError::InvalidSignature)?;
1254 let signature_y_parity = match self.chain_id() {
1259 Some(chain_id) => match v.checked_sub(35 + chain_id * 2) {
1260 Some(0) => false,
1261 Some(1) => true,
1262 _ => return Err(CryptoError::InvalidSignature),
1263 },
1264 None => match v {
1265 27 => false,
1266 28 => true,
1267 _ => return Err(CryptoError::InvalidSignature),
1268 },
1269 };
1270 let mut buf = vec![];
1271 match self.chain_id() {
1272 None => Encoder::new(&mut buf)
1273 .encode_field(&tx.nonce)
1274 .encode_field(&tx.gas_price)
1275 .encode_field(&tx.gas)
1276 .encode_field(&tx.to)
1277 .encode_field(&tx.value)
1278 .encode_field(&tx.data)
1279 .finish(),
1280 Some(chain_id) => Encoder::new(&mut buf)
1281 .encode_field(&tx.nonce)
1282 .encode_field(&tx.gas_price)
1283 .encode_field(&tx.gas)
1284 .encode_field(&tx.to)
1285 .encode_field(&tx.value)
1286 .encode_field(&tx.data)
1287 .encode_field(&chain_id)
1288 .encode_field(&0u8)
1289 .encode_field(&0u8)
1290 .finish(),
1291 }
1292 let mut sig = [0u8; 65];
1293 sig[..32].copy_from_slice(&tx.r.to_big_endian());
1294 sig[32..64].copy_from_slice(&tx.s.to_big_endian());
1295 sig[64] = signature_y_parity as u8;
1296 (buf, sig)
1297 }
1298 Transaction::EIP2930Transaction(tx) => {
1299 let mut buf = vec![self.tx_type() as u8];
1300 Encoder::new(&mut buf)
1301 .encode_field(&tx.chain_id)
1302 .encode_field(&tx.nonce)
1303 .encode_field(&tx.gas_price)
1304 .encode_field(&tx.gas_limit)
1305 .encode_field(&tx.to)
1306 .encode_field(&tx.value)
1307 .encode_field(&tx.data)
1308 .encode_field(&tx.access_list)
1309 .finish();
1310 let mut sig = [0u8; 65];
1311 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1312 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1313 sig[64] = tx.signature_y_parity as u8;
1314 (buf, sig)
1315 }
1316 Transaction::EIP1559Transaction(tx) => {
1317 let mut buf = vec![self.tx_type() as u8];
1318 Encoder::new(&mut buf)
1319 .encode_field(&tx.chain_id)
1320 .encode_field(&tx.nonce)
1321 .encode_field(&tx.max_priority_fee_per_gas)
1322 .encode_field(&tx.max_fee_per_gas)
1323 .encode_field(&tx.gas_limit)
1324 .encode_field(&tx.to)
1325 .encode_field(&tx.value)
1326 .encode_field(&tx.data)
1327 .encode_field(&tx.access_list)
1328 .finish();
1329 let mut sig = [0u8; 65];
1330 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1331 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1332 sig[64] = tx.signature_y_parity as u8;
1333 (buf, sig)
1334 }
1335 Transaction::EIP4844Transaction(tx) => {
1336 let mut buf = vec![self.tx_type() as u8];
1337 Encoder::new(&mut buf)
1338 .encode_field(&tx.chain_id)
1339 .encode_field(&tx.nonce)
1340 .encode_field(&tx.max_priority_fee_per_gas)
1341 .encode_field(&tx.max_fee_per_gas)
1342 .encode_field(&tx.gas)
1343 .encode_field(&tx.to)
1344 .encode_field(&tx.value)
1345 .encode_field(&tx.data)
1346 .encode_field(&tx.access_list)
1347 .encode_field(&tx.max_fee_per_blob_gas)
1348 .encode_field(&tx.blob_versioned_hashes)
1349 .finish();
1350 let mut sig = [0u8; 65];
1351 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1352 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1353 sig[64] = tx.signature_y_parity as u8;
1354 (buf, sig)
1355 }
1356 Transaction::EIP7702Transaction(tx) => {
1357 let mut buf = vec![self.tx_type() as u8];
1358 Encoder::new(&mut buf)
1359 .encode_field(&tx.chain_id)
1360 .encode_field(&tx.nonce)
1361 .encode_field(&tx.max_priority_fee_per_gas)
1362 .encode_field(&tx.max_fee_per_gas)
1363 .encode_field(&tx.gas_limit)
1364 .encode_field(&tx.to)
1365 .encode_field(&tx.value)
1366 .encode_field(&tx.data)
1367 .encode_field(&tx.access_list)
1368 .encode_field(&tx.authorization_list)
1369 .finish();
1370 let mut sig = [0u8; 65];
1371 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1372 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1373 sig[64] = tx.signature_y_parity as u8;
1374 (buf, sig)
1375 }
1376 Transaction::PrivilegedL2Transaction(tx) => return Ok(tx.from),
1377 Transaction::FrameTransaction(tx) => return Ok(tx.sender),
1378 Transaction::FeeTokenTransaction(tx) => {
1379 let mut buf = vec![self.tx_type() as u8];
1380 Encoder::new(&mut buf)
1381 .encode_field(&tx.chain_id)
1382 .encode_field(&tx.nonce)
1383 .encode_field(&tx.max_priority_fee_per_gas)
1384 .encode_field(&tx.max_fee_per_gas)
1385 .encode_field(&tx.gas_limit)
1386 .encode_field(&tx.to)
1387 .encode_field(&tx.value)
1388 .encode_field(&tx.data)
1389 .encode_field(&tx.access_list)
1390 .encode_field(&tx.fee_token)
1391 .finish();
1392 let mut sig = [0u8; 65];
1393 sig[..32].copy_from_slice(&tx.signature_r.to_big_endian());
1394 sig[32..64].copy_from_slice(&tx.signature_s.to_big_endian());
1395 sig[64] = tx.signature_y_parity as u8;
1396 (buf, sig)
1397 }
1398 };
1399 let msg = crypto.keccak256(&buf);
1400 crypto.recover_signer(&sig, &msg)
1401 }
1402
1403 pub fn gas_limit(&self) -> u64 {
1404 match self {
1405 Transaction::LegacyTransaction(tx) => tx.gas,
1406 Transaction::EIP2930Transaction(tx) => tx.gas_limit,
1407 Transaction::EIP1559Transaction(tx) => tx.gas_limit,
1408 Transaction::EIP7702Transaction(tx) => tx.gas_limit,
1409 Transaction::EIP4844Transaction(tx) => tx.gas,
1410 Transaction::PrivilegedL2Transaction(tx) => tx.gas_limit,
1411 Transaction::FeeTokenTransaction(tx) => tx.gas_limit,
1412 Transaction::FrameTransaction(tx) => tx.total_gas_limit(),
1413 }
1414 }
1415
1416 pub fn gas_price(&self) -> U256 {
1418 match self {
1419 Transaction::LegacyTransaction(tx) => tx.gas_price,
1420 Transaction::EIP2930Transaction(tx) => tx.gas_price,
1421 Transaction::EIP1559Transaction(tx) => U256::from(tx.max_fee_per_gas),
1422 Transaction::EIP7702Transaction(tx) => U256::from(tx.max_fee_per_gas),
1423 Transaction::EIP4844Transaction(tx) => U256::from(tx.max_fee_per_gas),
1424 Transaction::PrivilegedL2Transaction(tx) => U256::from(tx.max_fee_per_gas),
1425 Transaction::FeeTokenTransaction(tx) => U256::from(tx.max_fee_per_gas),
1426 Transaction::FrameTransaction(tx) => U256::from(tx.max_fee_per_gas),
1427 }
1428 }
1429
1430 pub fn to(&self) -> TxKind {
1431 match self {
1432 Transaction::LegacyTransaction(tx) => tx.to.clone(),
1433 Transaction::EIP2930Transaction(tx) => tx.to.clone(),
1434 Transaction::EIP1559Transaction(tx) => tx.to.clone(),
1435 Transaction::EIP4844Transaction(tx) => TxKind::Call(tx.to),
1436 Transaction::EIP7702Transaction(tx) => TxKind::Call(tx.to),
1437 Transaction::PrivilegedL2Transaction(tx) => tx.to.clone(),
1438 Transaction::FeeTokenTransaction(tx) => tx.to.clone(),
1439 Transaction::FrameTransaction(tx) => TxKind::Call(tx.sender),
1440 }
1441 }
1442
1443 pub fn value(&self) -> U256 {
1444 match self {
1445 Transaction::LegacyTransaction(tx) => tx.value,
1446 Transaction::EIP2930Transaction(tx) => tx.value,
1447 Transaction::EIP1559Transaction(tx) => tx.value,
1448 Transaction::EIP4844Transaction(tx) => tx.value,
1449 Transaction::EIP7702Transaction(tx) => tx.value,
1450 Transaction::PrivilegedL2Transaction(tx) => tx.value,
1451 Transaction::FeeTokenTransaction(tx) => tx.value,
1452 Transaction::FrameTransaction(_) => U256::zero(),
1453 }
1454 }
1455
1456 pub fn max_priority_fee(&self) -> Option<u64> {
1457 match self {
1458 Transaction::LegacyTransaction(_tx) => None,
1459 Transaction::EIP2930Transaction(_tx) => None,
1460 Transaction::EIP1559Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1461 Transaction::EIP4844Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1462 Transaction::EIP7702Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1463 Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_priority_fee_per_gas),
1464 Transaction::FeeTokenTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1465 Transaction::FrameTransaction(tx) => Some(tx.max_priority_fee_per_gas),
1466 }
1467 }
1468
1469 pub fn chain_id(&self) -> Option<u64> {
1470 match self {
1471 Transaction::LegacyTransaction(tx) => derive_legacy_chain_id(tx.v),
1472 Transaction::EIP2930Transaction(tx) => Some(tx.chain_id),
1473 Transaction::EIP1559Transaction(tx) => Some(tx.chain_id),
1474 Transaction::EIP4844Transaction(tx) => Some(tx.chain_id),
1475 Transaction::EIP7702Transaction(tx) => Some(tx.chain_id),
1476 Transaction::PrivilegedL2Transaction(tx) => Some(tx.chain_id),
1477 Transaction::FeeTokenTransaction(tx) => Some(tx.chain_id),
1478 Transaction::FrameTransaction(tx) => Some(tx.chain_id),
1479 }
1480 }
1481
1482 pub fn access_list(&self) -> &AccessList {
1483 static EMPTY_ACCESS_LIST: AccessList = Vec::new();
1484 match self {
1485 Transaction::LegacyTransaction(_tx) => &EMPTY_ACCESS_LIST,
1486 Transaction::EIP2930Transaction(tx) => &tx.access_list,
1487 Transaction::EIP1559Transaction(tx) => &tx.access_list,
1488 Transaction::EIP4844Transaction(tx) => &tx.access_list,
1489 Transaction::EIP7702Transaction(tx) => &tx.access_list,
1490 Transaction::PrivilegedL2Transaction(tx) => &tx.access_list,
1491 Transaction::FeeTokenTransaction(tx) => &tx.access_list,
1492 Transaction::FrameTransaction(_) => &EMPTY_ACCESS_LIST,
1493 }
1494 }
1495 pub fn authorization_list(&self) -> Option<&AuthorizationList> {
1496 match self {
1497 Transaction::LegacyTransaction(_) => None,
1498 Transaction::EIP2930Transaction(_) => None,
1499 Transaction::EIP1559Transaction(_) => None,
1500 Transaction::EIP4844Transaction(_) => None,
1501 Transaction::EIP7702Transaction(tx) => Some(&tx.authorization_list),
1502 Transaction::PrivilegedL2Transaction(_) => None,
1503 Transaction::FeeTokenTransaction(_) => None,
1504 Transaction::FrameTransaction(_) => None,
1505 }
1506 }
1507
1508 pub fn nonce(&self) -> u64 {
1509 match self {
1510 Transaction::LegacyTransaction(tx) => tx.nonce,
1511 Transaction::EIP2930Transaction(tx) => tx.nonce,
1512 Transaction::EIP1559Transaction(tx) => tx.nonce,
1513 Transaction::EIP4844Transaction(tx) => tx.nonce,
1514 Transaction::EIP7702Transaction(tx) => tx.nonce,
1515 Transaction::PrivilegedL2Transaction(tx) => tx.nonce,
1516 Transaction::FeeTokenTransaction(tx) => tx.nonce,
1517 Transaction::FrameTransaction(tx) => tx.nonce,
1518 }
1519 }
1520
1521 pub fn data(&self) -> &Bytes {
1522 match self {
1523 Transaction::LegacyTransaction(tx) => &tx.data,
1524 Transaction::EIP2930Transaction(tx) => &tx.data,
1525 Transaction::EIP1559Transaction(tx) => &tx.data,
1526 Transaction::EIP4844Transaction(tx) => &tx.data,
1527 Transaction::EIP7702Transaction(tx) => &tx.data,
1528 Transaction::PrivilegedL2Transaction(tx) => &tx.data,
1529 Transaction::FeeTokenTransaction(tx) => &tx.data,
1530 Transaction::FrameTransaction(_) => {
1531 static EMPTY_DATA: Bytes = Bytes::new();
1532 &EMPTY_DATA
1533 }
1534 }
1535 }
1536
1537 pub fn blob_versioned_hashes(&self) -> Vec<H256> {
1538 match self {
1539 Transaction::LegacyTransaction(_) => Vec::new(),
1540 Transaction::EIP2930Transaction(_) => Vec::new(),
1541 Transaction::EIP1559Transaction(_) => Vec::new(),
1542 Transaction::EIP4844Transaction(tx) => tx.blob_versioned_hashes.clone(),
1543 Transaction::EIP7702Transaction(_) => Vec::new(),
1544 Transaction::PrivilegedL2Transaction(_) => Vec::new(),
1545 Transaction::FeeTokenTransaction(_) => Vec::new(),
1546 Transaction::FrameTransaction(tx) => tx.blob_versioned_hashes.clone(),
1547 }
1548 }
1549
1550 pub fn max_fee_per_blob_gas(&self) -> Option<U256> {
1551 match self {
1552 Transaction::LegacyTransaction(_) => None,
1553 Transaction::EIP2930Transaction(_) => None,
1554 Transaction::EIP1559Transaction(_) => None,
1555 Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_blob_gas),
1556 Transaction::EIP7702Transaction(_) => None,
1557 Transaction::PrivilegedL2Transaction(_) => None,
1558 Transaction::FeeTokenTransaction(_) => None,
1559 Transaction::FrameTransaction(tx) => {
1560 if tx.blob_versioned_hashes.is_empty() {
1561 None
1562 } else {
1563 Some(tx.max_fee_per_blob_gas)
1564 }
1565 }
1566 }
1567 }
1568
1569 pub fn is_contract_creation(&self) -> bool {
1570 match &self {
1571 Transaction::LegacyTransaction(t) => matches!(t.to, TxKind::Create),
1572 Transaction::EIP2930Transaction(t) => matches!(t.to, TxKind::Create),
1573 Transaction::EIP1559Transaction(t) => matches!(t.to, TxKind::Create),
1574 Transaction::EIP4844Transaction(_) => false,
1575 Transaction::EIP7702Transaction(_) => false,
1576 Transaction::PrivilegedL2Transaction(t) => matches!(t.to, TxKind::Create),
1577 Transaction::FeeTokenTransaction(t) => matches!(t.to, TxKind::Create),
1578 Transaction::FrameTransaction(_) => false,
1579 }
1580 }
1581
1582 pub fn is_privileged(&self) -> bool {
1583 matches!(self, Transaction::PrivilegedL2Transaction(_))
1584 }
1585
1586 pub fn max_fee_per_gas(&self) -> Option<u64> {
1587 match self {
1588 Transaction::LegacyTransaction(_tx) => None,
1589 Transaction::EIP2930Transaction(_tx) => None,
1590 Transaction::EIP1559Transaction(tx) => Some(tx.max_fee_per_gas),
1591 Transaction::EIP4844Transaction(tx) => Some(tx.max_fee_per_gas),
1592 Transaction::EIP7702Transaction(tx) => Some(tx.max_fee_per_gas),
1593 Transaction::PrivilegedL2Transaction(tx) => Some(tx.max_fee_per_gas),
1594 Transaction::FeeTokenTransaction(tx) => Some(tx.max_fee_per_gas),
1595 Transaction::FrameTransaction(tx) => Some(tx.max_fee_per_gas),
1596 }
1597 }
1598
1599 fn compute_hash(&self, crypto: &dyn Crypto) -> H256 {
1600 if let Transaction::PrivilegedL2Transaction(tx) = self {
1601 return tx.get_privileged_hash().unwrap_or_default();
1602 }
1603 H256(crypto.keccak256(&self.encode_canonical_to_vec()))
1604 }
1605
1606 pub fn hash(&self, crypto: &dyn Crypto) -> H256 {
1607 let inner_hash = match self {
1608 Transaction::LegacyTransaction(tx) => &tx.inner_hash,
1609 Transaction::EIP2930Transaction(tx) => &tx.inner_hash,
1610 Transaction::EIP1559Transaction(tx) => &tx.inner_hash,
1611 Transaction::EIP4844Transaction(tx) => &tx.inner_hash,
1612 Transaction::EIP7702Transaction(tx) => &tx.inner_hash,
1613 Transaction::PrivilegedL2Transaction(tx) => &tx.inner_hash,
1614 Transaction::FeeTokenTransaction(tx) => &tx.inner_hash,
1615 Transaction::FrameTransaction(tx) => &tx.inner_hash,
1616 };
1617
1618 *inner_hash.get_or_init(|| self.compute_hash(crypto))
1619 }
1620
1621 pub fn gas_tip_cap(&self) -> U256 {
1622 self.max_priority_fee()
1623 .map(U256::from)
1624 .unwrap_or_else(|| self.gas_price())
1625 }
1626
1627 pub fn gas_fee_cap(&self) -> U256 {
1628 self.max_fee_per_gas()
1629 .map(U256::from)
1630 .unwrap_or_else(|| self.gas_price())
1631 }
1632
1633 pub fn effective_gas_tip(&self, base_fee: Option<u64>) -> Option<U256> {
1637 let tip_cap = self.gas_tip_cap();
1638 let Some(base_fee) = base_fee else {
1639 return Some(tip_cap);
1640 };
1641 let base_fee = U256::from(base_fee);
1642 let fee_cap = self.gas_fee_cap();
1643 let tip = fee_cap.checked_sub(base_fee)?;
1644 Some(min(tip, tip_cap))
1645 }
1646
1647 pub fn protected(&self) -> bool {
1650 match self {
1651 Transaction::LegacyTransaction(tx) if tx.v.bits() <= 8 => {
1652 let v = tx.v.low_u64();
1653 v != 27 && v != 28 && v != 1 && v != 0
1654 }
1655 _ => true,
1656 }
1657 }
1658}
1659
1660fn derive_legacy_chain_id(v: U256) -> Option<u64> {
1661 let v = u64::try_from(v).ok()?;
1662 if v < 35 { None } else { Some((v - 35) / 2) }
1667}
1668
1669impl TxType {
1670 pub fn from_u8(value: u8) -> Option<Self> {
1671 match value {
1672 0x00 => Some(Self::Legacy),
1673 0x01 => Some(Self::EIP2930),
1674 0x02 => Some(Self::EIP1559),
1675 0x03 => Some(Self::EIP4844),
1676 0x04 => Some(Self::EIP7702),
1677 0x06 => Some(Self::Frame),
1678 0x7d => Some(Self::FeeToken),
1679 0x7e => Some(Self::Privileged),
1680 _ => None,
1681 }
1682 }
1683
1684 pub fn as_envelope(self) -> Option<EnvelopeTxType> {
1689 match self {
1690 TxType::EIP2930 => Some(EnvelopeTxType::EIP2930),
1691 TxType::EIP1559 => Some(EnvelopeTxType::EIP1559),
1692 TxType::EIP4844 => Some(EnvelopeTxType::EIP4844),
1693 TxType::EIP7702 => Some(EnvelopeTxType::EIP7702),
1694 TxType::Frame => Some(EnvelopeTxType::Frame),
1695 TxType::FeeToken => Some(EnvelopeTxType::FeeToken),
1696 TxType::Privileged => Some(EnvelopeTxType::Privileged),
1697 TxType::Legacy => None,
1698 }
1699 }
1700
1701 pub fn is_l2_only(self) -> bool {
1709 match self {
1710 Self::Legacy
1711 | Self::EIP2930
1712 | Self::EIP1559
1713 | Self::EIP4844
1714 | Self::EIP7702
1715 | Self::Frame => false,
1716 Self::FeeToken | Self::Privileged => true,
1717 }
1718 }
1719}
1720
1721impl PrivilegedL2Transaction {
1722 pub fn get_privileged_hash(&self) -> Option<H256> {
1726 let to = match self.to {
1728 TxKind::Call(to) => to,
1729 _ => return None,
1730 };
1731
1732 let value = self.value.to_big_endian();
1733
1734 let u256_nonce = U256::from(self.nonce);
1737 let nonce = u256_nonce.to_big_endian();
1738
1739 Some(crate::utils::keccak(
1740 [
1741 U256::from(self.chain_id).to_big_endian().as_ref(),
1742 self.from.as_bytes(),
1743 to.as_bytes(),
1744 &nonce,
1745 &value,
1746 &U256::from(self.gas_limit).to_big_endian(),
1747 keccak(&self.data).as_bytes(),
1748 ]
1749 .concat(),
1750 ))
1751 }
1752}
1753
1754#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1755pub struct FeeTokenTransaction {
1756 pub chain_id: u64,
1757 pub nonce: u64,
1758 pub max_priority_fee_per_gas: u64,
1759 pub max_fee_per_gas: u64,
1760 pub gas_limit: u64,
1761 pub to: TxKind,
1762 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1763 pub value: U256,
1764 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1765 pub data: Bytes,
1766 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::AccessListItemWrapper>)]
1767 pub access_list: AccessList,
1768 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
1769 pub fee_token: Address,
1770 pub signature_y_parity: bool,
1771 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1772 pub signature_r: U256,
1773 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1774 pub signature_s: U256,
1775 #[rkyv(with=rkyv::with::Skip)]
1776 pub inner_hash: OnceCell<H256>,
1777 #[rkyv(with=rkyv::with::Skip)]
1778 pub sender_cache: OnceCell<Address>,
1779 #[rkyv(with=rkyv::with::Skip)]
1780 pub cached_canonical: OnceCell<Vec<u8>>,
1781}
1782
1783#[derive(Clone, Copy, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1785#[repr(u8)]
1786pub enum FrameMode {
1787 #[default]
1788 Default = 0,
1789 Verify = 1,
1790 Sender = 2,
1791}
1792
1793impl FrameMode {
1794 pub fn from_u8(val: u8) -> Option<Self> {
1797 match val {
1798 0 => Some(FrameMode::Default),
1799 1 => Some(FrameMode::Verify),
1800 2 => Some(FrameMode::Sender),
1801 _ => None,
1802 }
1803 }
1804}
1805
1806impl From<FrameMode> for u8 {
1807 fn from(mode: FrameMode) -> u8 {
1808 match mode {
1809 FrameMode::Default => 0,
1810 FrameMode::Verify => 1,
1811 FrameMode::Sender => 2,
1812 }
1813 }
1814}
1815
1816#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1822pub struct Frame {
1823 pub mode: u8,
1824 pub flags: u8,
1825 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H160Wrapper>)]
1826 pub target: Option<Address>,
1827 pub gas_limit: u64,
1828 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1832 pub value: U256,
1833 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1834 pub data: Bytes,
1835}
1836
1837impl Frame {
1838 pub fn execution_mode(&self) -> FrameMode {
1840 FrameMode::from_u8(self.mode).unwrap_or_default()
1841 }
1842
1843 pub fn scope_restriction(&self) -> u8 {
1845 self.flags & 0x03
1846 }
1847
1848 pub fn is_atomic_batch(&self) -> bool {
1850 (self.flags >> 2) & 1 == 1
1851 }
1852
1853 pub fn is_expiry_verifier(&self) -> bool {
1856 self.execution_mode() == FrameMode::Verify
1857 && self.target == Some(frame_tx_expiry_verifier())
1858 }
1859}
1860
1861impl RLPEncode for Frame {
1862 fn encode(&self, buf: &mut dyn bytes::BufMut) {
1863 let target_kind = match self.target {
1865 Some(addr) => TxKind::Call(addr),
1866 None => TxKind::Create, };
1868 Encoder::new(buf)
1869 .encode_field(&(self.mode as u64))
1870 .encode_field(&(self.flags as u64))
1871 .encode_field(&target_kind)
1872 .encode_field(&self.gas_limit)
1873 .encode_field(&self.value)
1874 .encode_field(&self.data)
1875 .finish();
1876 }
1877}
1878
1879impl RLPDecode for Frame {
1880 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
1881 let decoder = Decoder::new(rlp)?;
1882 let (mode_u64, decoder): (u64, _) = decoder.decode_field("mode")?;
1883 let mode = u8::try_from(mode_u64)
1884 .map_err(|_| RLPDecodeError::Custom(format!("Frame mode too large: {mode_u64}")))?;
1885 let (flags_u64, decoder): (u64, _) = decoder.decode_field("flags")?;
1886 let flags = u8::try_from(flags_u64)
1887 .map_err(|_| RLPDecodeError::Custom(format!("Frame flags too large: {flags_u64}")))?;
1888 let (target_kind, decoder): (TxKind, _) = decoder.decode_field("target")?;
1889 let target = match target_kind {
1890 TxKind::Call(addr) => Some(addr),
1891 TxKind::Create => None,
1892 };
1893 let (gas_limit, decoder) = decoder.decode_field("gas_limit")?;
1894 let (value, decoder): (U256, _) = decoder.decode_field("value")?;
1895 let (data, decoder) = decoder.decode_field("data")?;
1896 let frame = Frame {
1897 mode,
1898 flags,
1899 target,
1900 gas_limit,
1901 value,
1902 data,
1903 };
1904 Ok((frame, decoder.finish()?))
1905 }
1906}
1907
1908#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1916pub struct FrameSignature {
1917 pub scheme: u8,
1918 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H160Wrapper>)]
1919 pub signer: Option<Address>,
1920 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1921 pub msg: Bytes,
1922 #[rkyv(with=crate::rkyv_utils::BytesWrapper)]
1923 pub signature: Bytes,
1924}
1925
1926impl RLPEncode for FrameSignature {
1927 fn encode(&self, buf: &mut dyn bytes::BufMut) {
1928 let signer_kind = match self.signer {
1931 Some(addr) => TxKind::Call(addr),
1932 None => TxKind::Create,
1933 };
1934 Encoder::new(buf)
1935 .encode_field(&self.scheme)
1936 .encode_field(&signer_kind)
1937 .encode_field(&self.msg)
1938 .encode_field(&self.signature)
1939 .finish();
1940 }
1941}
1942
1943impl RLPDecode for FrameSignature {
1944 fn decode_unfinished(rlp: &[u8]) -> Result<(FrameSignature, &[u8]), RLPDecodeError> {
1945 let decoder = Decoder::new(rlp)?;
1946 let (scheme, decoder) = decoder.decode_field("scheme")?;
1947 let (signer_kind, decoder): (TxKind, _) = decoder.decode_field("signer")?;
1948 let signer = match signer_kind {
1949 TxKind::Call(addr) => Some(addr),
1950 TxKind::Create => None,
1951 };
1952 let (msg, decoder) = decoder.decode_field("msg")?;
1953 let (signature, decoder) = decoder.decode_field("signature")?;
1954 Ok((
1955 FrameSignature {
1956 scheme,
1957 signer,
1958 msg,
1959 signature,
1960 },
1961 decoder.finish()?,
1962 ))
1963 }
1964}
1965
1966#[derive(Clone, Debug, PartialEq, Eq, Default, RSerialize, RDeserialize, Archive)]
1970pub struct FrameTransaction {
1971 pub chain_id: u64,
1972 pub nonce: u64,
1973 #[rkyv(with=crate::rkyv_utils::H160Wrapper)]
1974 pub sender: Address,
1975 pub frames: Vec<Frame>,
1976 pub signatures: Vec<FrameSignature>,
1979 pub max_priority_fee_per_gas: u64,
1980 pub max_fee_per_gas: u64,
1981 #[rkyv(with=crate::rkyv_utils::U256Wrapper)]
1982 pub max_fee_per_blob_gas: U256,
1983 #[rkyv(with=rkyv::with::Map<crate::rkyv_utils::H256Wrapper>)]
1984 pub blob_versioned_hashes: Vec<H256>,
1985 #[rkyv(with=rkyv::with::Skip)]
1986 pub inner_hash: OnceCell<H256>,
1987 #[rkyv(with=rkyv::with::Skip)]
1988 pub cached_canonical: OnceCell<Vec<u8>>,
1989}
1990
1991pub const FRAME_TX_INTRINSIC_COST: u64 = 15000;
1993pub const FRAME_TX_PER_FRAME_COST: u64 = 475;
1995pub const FRAME_TX_ENTRY_POINT_U64: u64 = 0xaa;
1997pub const FRAME_TX_MAX_FRAMES: usize = 64;
1999const FRAME_TX_STANDARD_TOKEN_COST: u64 = 4;
2003const FRAME_TX_TOTAL_COST_FLOOR_PER_TOKEN: u64 = 16;
2004pub const FRAME_SIG_SCHEME_ARBITRARY: u8 = 0;
2006pub const FRAME_SIG_SCHEME_SECP256K1: u8 = 1;
2007pub const FRAME_SIG_SCHEME_P256: u8 = 2;
2008pub const FRAME_TX_MAX_VERIFY_GAS: u64 = 100_000;
2014pub const APPROVE_PAYMENT: u8 = 0x1;
2017pub const APPROVE_EXECUTION: u8 = 0x2;
2018pub const APPROVE_EXECUTION_AND_PAYMENT: u8 = 0x3;
2019pub const FRAME_TX_MAX_PENDING_NONCANONICAL_PAYMASTER: u8 = 1;
2024
2025pub fn frame_tx_entry_point() -> Address {
2028 Address::from_low_u64_be(FRAME_TX_ENTRY_POINT_U64)
2029}
2030
2031pub const FRAME_TX_EXPIRY_VERIFIER_U64: u64 = 0x8141;
2033pub const FRAME_TX_EXPIRY_DATA_LENGTH: usize = 8;
2035
2036pub fn frame_tx_expiry_verifier() -> Address {
2038 Address::from_low_u64_be(FRAME_TX_EXPIRY_VERIFIER_U64)
2039}
2040
2041impl FrameTransaction {
2042 pub fn compute_sig_hash(&self) -> H256 {
2048 let mut buf = vec![TxType::Frame as u8];
2049 let elided_signatures: Vec<FrameSignature> = self
2050 .signatures
2051 .iter()
2052 .map(|s| {
2053 if s.msg.is_empty() {
2054 FrameSignature {
2055 scheme: s.scheme,
2056 signer: s.signer,
2057 msg: s.msg.clone(),
2058 signature: Bytes::new(),
2059 }
2060 } else {
2061 s.clone()
2062 }
2063 })
2064 .collect();
2065 Encoder::new(&mut buf)
2067 .encode_field(&self.chain_id)
2068 .encode_field(&self.nonce)
2069 .encode_field(&self.sender)
2070 .encode_field(&self.frames)
2071 .encode_field(&elided_signatures)
2072 .encode_field(&self.max_priority_fee_per_gas)
2073 .encode_field(&self.max_fee_per_gas)
2074 .encode_field(&self.max_fee_per_blob_gas)
2075 .encode_field(&self.blob_versioned_hashes)
2076 .finish();
2077 keccak(&buf)
2078 }
2079
2080 pub fn signature_verification_cost(&self) -> u64 {
2084 self.signatures
2085 .iter()
2086 .map(|s| match s.scheme {
2087 FRAME_SIG_SCHEME_ARBITRARY => 100u64,
2088 FRAME_SIG_SCHEME_SECP256K1 => 2800u64,
2089 FRAME_SIG_SCHEME_P256 => 6700u64,
2090 _ => 0,
2091 })
2092 .sum()
2093 }
2094
2095 fn data_fields(&self) -> impl Iterator<Item = &[u8]> {
2099 const EMPTY: &[u8] = &[];
2100 self.frames
2101 .iter()
2102 .map(|f| f.data.as_ref())
2103 .chain(self.signatures.iter().flat_map(|s| {
2104 [
2105 s.signer.as_ref().map_or(EMPTY, |a| a.as_bytes()),
2106 s.msg.as_ref(),
2107 s.signature.as_ref(),
2108 ]
2109 }))
2110 }
2111
2112 pub fn data_cost(&self) -> u64 {
2115 self.data_fields().flatten().fold(0u64, |acc, byte| {
2116 acc.saturating_add(if *byte == 0 { 4 } else { 16 })
2117 })
2118 }
2119
2120 pub fn calldata_tokens(&self) -> u64 {
2125 self.data_fields()
2126 .fold(0u64, |acc, field| acc.saturating_add(field.len() as u64))
2127 .saturating_mul(FRAME_TX_STANDARD_TOKEN_COST)
2128 }
2129
2130 pub fn calldata_floor_gas(&self) -> u64 {
2133 self.calldata_tokens()
2134 .saturating_mul(FRAME_TX_TOTAL_COST_FLOOR_PER_TOKEN)
2135 }
2136
2137 pub fn mandatory_gas(&self) -> u64 {
2140 FRAME_TX_INTRINSIC_COST
2141 .saturating_add((self.frames.len() as u64).saturating_mul(FRAME_TX_PER_FRAME_COST))
2142 .saturating_add(self.signature_verification_cost())
2143 }
2144
2145 pub fn total_gas_limit(&self) -> u64 {
2148 self.mandatory_gas()
2149 .saturating_add(self.data_cost())
2150 .saturating_add(
2151 self.frames
2152 .iter()
2153 .map(|f| f.gas_limit)
2154 .fold(0u64, |acc, g| acc.saturating_add(g)),
2155 )
2156 }
2157
2158 pub fn expiry_deadline(&self) -> Option<u64> {
2161 self.frames
2162 .iter()
2163 .find(|f| f.is_expiry_verifier())
2164 .and_then(|f| {
2165 let bytes: [u8; FRAME_TX_EXPIRY_DATA_LENGTH] = f.data.as_ref().try_into().ok()?;
2166 Some(u64::from_be_bytes(bytes))
2167 })
2168 }
2169
2170 pub fn validate_static_constraints(&self) -> Result<(), String> {
2173 if self.sender == Address::zero() {
2175 return Err("tx.sender must not be zero address".to_string());
2176 }
2177 if self.frames.is_empty() || self.frames.len() > FRAME_TX_MAX_FRAMES {
2178 return Err(format!(
2179 "Frame count must be between 1 and {FRAME_TX_MAX_FRAMES}"
2180 ));
2181 }
2182 for (i, hash) in self.blob_versioned_hashes.iter().enumerate() {
2185 if hash.0.first() != Some(&VERSIONED_HASH_VERSION_KZG) {
2186 return Err(format!("Blob versioned hash {i}: wrong version byte"));
2187 }
2188 }
2189 if self.blob_versioned_hashes.is_empty() && !self.max_fee_per_blob_gas.is_zero() {
2190 return Err(
2191 "max_fee_per_blob_gas must be zero when the transaction carries no blobs"
2192 .to_string(),
2193 );
2194 }
2195 for (i, sig) in self.signatures.iter().enumerate() {
2198 match sig.scheme {
2199 FRAME_SIG_SCHEME_ARBITRARY => {
2202 if sig.signer.is_some() {
2203 return Err(format!(
2204 "Signature {i}: ARBITRARY signatures must not name a signer"
2205 ));
2206 }
2207 }
2208 FRAME_SIG_SCHEME_SECP256K1 | FRAME_SIG_SCHEME_P256 => {}
2209 other => {
2210 return Err(format!("Signature {i}: unsupported scheme {other}"));
2211 }
2212 }
2213 match sig.msg.len() {
2214 0 => {}
2215 32 => {
2216 if sig.msg.iter().all(|&b| b == 0) {
2217 return Err(format!(
2218 "Signature {i}: explicit msg must not be zero digest"
2219 ));
2220 }
2221 }
2222 other => {
2223 return Err(format!(
2224 "Signature {i}: msg must be empty or 32 bytes, got {other}"
2225 ));
2226 }
2227 }
2228 }
2229 let mut total_frame_gas: u128 = 0;
2232 let mut expiry_frame_count: usize = 0;
2233 for (i, frame) in self.frames.iter().enumerate() {
2234 if frame.mode >= 3 {
2236 return Err(format!("Frame {i}: reserved execution mode {}", frame.mode));
2237 }
2238 if frame.flags >= 8 {
2240 return Err(format!(
2241 "Frame {i}: reserved flag bits must be zero (flags={:#04x})",
2242 frame.flags
2243 ));
2244 }
2245 if frame.is_expiry_verifier() {
2250 expiry_frame_count = expiry_frame_count.saturating_add(1);
2251 if expiry_frame_count > 1 {
2252 return Err(format!("Frame {i}: more than one expiry verifier frame"));
2253 }
2254 if frame.flags != 0 {
2255 return Err(format!(
2256 "Frame {i}: expiry verifier frame must have flags == 0"
2257 ));
2258 }
2259 if frame.data.len() != FRAME_TX_EXPIRY_DATA_LENGTH {
2260 return Err(format!(
2261 "Frame {i}: expiry verifier frame data must be {FRAME_TX_EXPIRY_DATA_LENGTH} bytes"
2262 ));
2263 }
2264 }
2265 if frame.mode != FrameMode::Sender as u8 && !frame.value.is_zero() {
2269 return Err(format!(
2270 "Frame {i}: non-zero value only allowed in SENDER mode (mode={}, value={})",
2271 frame.mode, frame.value
2272 ));
2273 }
2274 if frame.gas_limit > i64::MAX as u64 {
2282 return Err(format!(
2283 "Frame {i}: gas_limit {} exceeds 2**63-1",
2284 frame.gas_limit
2285 ));
2286 }
2287 total_frame_gas = total_frame_gas
2288 .checked_add(frame.gas_limit as u128)
2289 .ok_or_else(|| format!("Frame {i}: cumulative gas_limit overflow"))?;
2290 if total_frame_gas > i64::MAX as u128 {
2291 return Err(format!(
2292 "Frame {i}: cumulative frame gas_limit {} exceeds 2**63-1",
2293 total_frame_gas
2294 ));
2295 }
2296 if frame.flags & APPROVE_EXECUTION != 0
2299 && frame.target.is_some_and(|target| target != self.sender)
2300 {
2301 return Err(format!(
2302 "Frame {i}: APPROVE_EXECUTION requires an empty target or tx.sender"
2303 ));
2304 }
2305 if frame.is_atomic_batch() {
2310 if frame.mode == FrameMode::Verify as u8 {
2311 return Err(format!("Frame {i}: atomic batch flag on a VERIFY frame"));
2312 }
2313 match self.frames.get(i.saturating_add(1)) {
2314 None => return Err(format!("Frame {i}: atomic batch flag on last frame")),
2315 Some(next) if next.mode == FrameMode::Verify as u8 => {
2316 return Err(format!(
2317 "Frame {i}: atomic batch flag followed by a VERIFY frame"
2318 ));
2319 }
2320 Some(_) => {}
2321 }
2322 }
2323 }
2324 let floor_gas = self.calldata_floor_gas();
2328 if self.total_gas_limit() < self.mandatory_gas().saturating_add(floor_gas) {
2329 return Err(format!(
2330 "Total gas limit does not reserve the calldata floor of {floor_gas}"
2331 ));
2332 }
2333 Ok(())
2334 }
2335
2336 pub fn validation_prefix(&self) -> Result<ValidationPrefix, FrameValidationError> {
2352 let non_expiry: Vec<usize> = self
2354 .frames
2355 .iter()
2356 .enumerate()
2357 .filter(|(_, f)| !f.is_expiry_verifier())
2358 .map(|(i, _)| i)
2359 .collect();
2360
2361 let frame = |pos: usize| -> Option<&Frame> {
2363 non_expiry.get(pos).and_then(|&idx| self.frames.get(idx))
2364 };
2365
2366 let is_default = |pos: usize| -> bool {
2367 frame(pos).is_some_and(|f| f.execution_mode() == FrameMode::Default)
2368 };
2369 let is_verify = |pos: usize| -> bool {
2370 frame(pos).is_some_and(|f| f.execution_mode() == FrameMode::Verify)
2371 };
2372 let scope_of = |pos: usize| -> u8 { frame(pos).map_or(0, |f| f.scope_restriction()) };
2373
2374 if non_expiry.is_empty() {
2375 return Err(FrameValidationError::UnrecognizedPrefix);
2376 }
2377
2378 if is_default(0)
2382 && is_verify(1)
2383 && scope_of(1) == APPROVE_EXECUTION
2384 && is_verify(2)
2385 && scope_of(2) == APPROVE_PAYMENT
2386 {
2387 return Ok(ValidationPrefix {
2388 shape: PrefixShape::DeployOnlyVerifyPay,
2389 frame_indices: vec![non_expiry[0], non_expiry[1], non_expiry[2]],
2390 deploy_index: Some(non_expiry[0]),
2391 pay_index: Some(non_expiry[2]),
2392 });
2393 }
2394
2395 if is_default(0) && is_verify(1) && scope_of(1) == APPROVE_EXECUTION_AND_PAYMENT {
2397 return Ok(ValidationPrefix {
2398 shape: PrefixShape::DeploySelfVerify,
2399 frame_indices: vec![non_expiry[0], non_expiry[1]],
2400 deploy_index: Some(non_expiry[0]),
2401 pay_index: Some(non_expiry[1]),
2402 });
2403 }
2404
2405 if is_verify(0)
2407 && scope_of(0) == APPROVE_EXECUTION
2408 && is_verify(1)
2409 && scope_of(1) == APPROVE_PAYMENT
2410 {
2411 return Ok(ValidationPrefix {
2412 shape: PrefixShape::OnlyVerifyPay,
2413 frame_indices: vec![non_expiry[0], non_expiry[1]],
2414 deploy_index: None,
2415 pay_index: Some(non_expiry[1]),
2416 });
2417 }
2418
2419 if is_verify(0) && scope_of(0) == APPROVE_EXECUTION_AND_PAYMENT {
2421 return Ok(ValidationPrefix {
2422 shape: PrefixShape::SelfVerify,
2423 frame_indices: vec![non_expiry[0]],
2424 deploy_index: None,
2425 pay_index: Some(non_expiry[0]),
2426 });
2427 }
2428
2429 Err(FrameValidationError::UnrecognizedPrefix)
2430 }
2431
2432 pub fn validate_prefix_structure(
2446 &self,
2447 prefix: &ValidationPrefix,
2448 ) -> Result<(), FrameValidationError> {
2449 let mut deploy_count = 0usize;
2450
2451 for &idx in &prefix.frame_indices {
2452 let frame = &self.frames[idx];
2453
2454 if frame.is_atomic_batch() {
2455 return Err(FrameValidationError::AtomicBatchInPrefix { frame_index: idx });
2456 }
2457
2458 match prefix.deploy_index {
2459 Some(deploy_idx) if deploy_idx == idx => {
2460 deploy_count += 1;
2462 if deploy_count > 1 {
2463 return Err(FrameValidationError::MultipleDeploys { frame_index: idx });
2464 }
2465 if prefix.frame_indices.first() != Some(&idx) {
2470 return Err(FrameValidationError::DeployNotFirst { frame_index: idx });
2471 }
2472 if frame.execution_mode() != FrameMode::Default {
2473 return Err(FrameValidationError::DeployNotDefaultMode {
2474 frame_index: idx,
2475 });
2476 }
2477 }
2478 _ => {
2479 if frame.execution_mode() != FrameMode::Verify {
2481 return Err(FrameValidationError::VerifyFrameNotVerifyMode {
2482 frame_index: idx,
2483 });
2484 }
2485
2486 let target_ok = match frame.target {
2488 None => true,
2489 Some(addr) => addr == self.sender,
2490 };
2491 if !target_ok {
2492 return Err(FrameValidationError::VerifyTargetNotSender {
2493 frame_index: idx,
2494 });
2495 }
2496
2497 let expected_scope = match prefix.shape {
2499 PrefixShape::SelfVerify | PrefixShape::DeploySelfVerify => {
2500 APPROVE_EXECUTION_AND_PAYMENT
2501 }
2502 PrefixShape::OnlyVerifyPay | PrefixShape::DeployOnlyVerifyPay => {
2503 if prefix.pay_index == Some(idx) {
2505 APPROVE_PAYMENT
2506 } else {
2507 APPROVE_EXECUTION
2508 }
2509 }
2510 };
2511 if frame.scope_restriction() != expected_scope {
2512 return Err(FrameValidationError::WrongScopeRestriction {
2513 frame_index: idx,
2514 expected: expected_scope,
2515 actual: frame.scope_restriction(),
2516 });
2517 }
2518 }
2519 }
2520 }
2521
2522 let prefix_gas: u64 = prefix
2524 .frame_indices
2525 .iter()
2526 .map(|&i| self.frames[i].gas_limit)
2527 .fold(0u64, |acc, g| acc.saturating_add(g));
2528 let total_verify_gas = prefix_gas.saturating_add(self.signature_verification_cost());
2529 if total_verify_gas > FRAME_TX_MAX_VERIFY_GAS {
2530 return Err(FrameValidationError::VerifyGasBudgetExceeded {
2531 actual: total_verify_gas,
2532 limit: FRAME_TX_MAX_VERIFY_GAS,
2533 });
2534 }
2535
2536 Ok(())
2537 }
2538}
2539
2540#[derive(Clone, Debug, PartialEq, Eq)]
2542pub enum PrefixShape {
2543 SelfVerify,
2545 DeploySelfVerify,
2547 OnlyVerifyPay,
2549 DeployOnlyVerifyPay,
2551}
2552
2553#[derive(Clone, Debug, PartialEq, Eq)]
2555pub struct ValidationPrefix {
2556 pub shape: PrefixShape,
2558 pub frame_indices: Vec<usize>,
2561 pub deploy_index: Option<usize>,
2563 pub pay_index: Option<usize>,
2565}
2566
2567#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
2570pub enum FrameValidationError {
2571 #[error("frame transaction validation prefix does not match any recognized shape")]
2572 UnrecognizedPrefix,
2573 #[error("frame {frame_index}: prefix has more than one DEFAULT deploy frame")]
2574 MultipleDeploys { frame_index: usize },
2575 #[error("frame {frame_index}: deploy frame must be at index 0")]
2576 DeployNotFirst { frame_index: usize },
2577 #[error("frame {frame_index}: deploy frame must use DEFAULT execution mode")]
2578 DeployNotDefaultMode { frame_index: usize },
2579 #[error("frame {frame_index}: prefix VERIFY frame does not use VERIFY execution mode")]
2580 VerifyFrameNotVerifyMode { frame_index: usize },
2581 #[error("frame {frame_index}: VERIFY frame target is not tx.sender")]
2582 VerifyTargetNotSender { frame_index: usize },
2583 #[error("frame {frame_index}: scope restriction is {actual:#04x}, expected {expected:#04x}")]
2584 WrongScopeRestriction {
2585 frame_index: usize,
2586 expected: u8,
2587 actual: u8,
2588 },
2589 #[error("frame {frame_index}: prefix frame has atomic-batch flag set")]
2590 AtomicBatchInPrefix { frame_index: usize },
2591 #[error("prefix gas budget exceeded: {actual} > {limit} (MAX_VERIFY_GAS)")]
2592 VerifyGasBudgetExceeded { actual: u64, limit: u64 },
2593}
2594
2595impl RLPEncode for FrameTransaction {
2596 fn encode(&self, buf: &mut dyn bytes::BufMut) {
2597 Encoder::new(buf)
2598 .encode_field(&self.chain_id)
2599 .encode_field(&self.nonce)
2600 .encode_field(&self.sender)
2601 .encode_field(&self.frames)
2602 .encode_field(&self.signatures)
2603 .encode_field(&self.max_priority_fee_per_gas)
2604 .encode_field(&self.max_fee_per_gas)
2605 .encode_field(&self.max_fee_per_blob_gas)
2606 .encode_field(&self.blob_versioned_hashes)
2607 .finish();
2608 }
2609}
2610
2611impl RLPDecode for FrameTransaction {
2612 fn decode_unfinished(rlp: &[u8]) -> Result<(FrameTransaction, &[u8]), RLPDecodeError> {
2613 let decoder = Decoder::new(rlp)?;
2614 let (chain_id, decoder) = decoder.decode_field("chain_id")?;
2615 let (nonce, decoder) = decode_nonce_field(decoder)?;
2616 let (sender, decoder) = decoder.decode_field("sender")?;
2617 let (frames, decoder) = decoder.decode_field("frames")?;
2618 let (signatures, decoder) = decoder.decode_field("signatures")?;
2619 let (max_priority_fee_per_gas, decoder) =
2620 decoder.decode_field("max_priority_fee_per_gas")?;
2621 let (max_fee_per_gas, decoder) = decoder.decode_field("max_fee_per_gas")?;
2622 let (max_fee_per_blob_gas, decoder) = decoder.decode_field("max_fee_per_blob_gas")?;
2623 let (blob_versioned_hashes, decoder) = decoder.decode_field("blob_versioned_hashes")?;
2624 let tx = FrameTransaction {
2625 chain_id,
2626 nonce,
2627 sender,
2628 frames,
2629 signatures,
2630 max_priority_fee_per_gas,
2631 max_fee_per_gas,
2632 max_fee_per_blob_gas,
2633 blob_versioned_hashes,
2634 inner_hash: OnceCell::new(),
2635 cached_canonical: OnceCell::new(),
2636 };
2637 Ok((tx, decoder.finish()?))
2638 }
2639}
2640
2641impl PayloadRLPEncode for FrameTransaction {
2642 fn encode_payload(&self, buf: &mut dyn bytes::BufMut) {
2643 buf.put_u8(TxType::Frame as u8);
2644 self.encode(buf);
2645 }
2646}
2647
2648mod canonic_encoding {
2654 use super::*;
2655
2656 impl Transaction {
2657 pub fn decode_canonical(bytes: &[u8]) -> Result<Self, RLPDecodeError> {
2663 match bytes.first() {
2665 Some(tx_type) if *tx_type < 0x7f => {
2667 let tx_bytes = &bytes[1..];
2671 match EnvelopeTxType::from_type_byte(*tx_type)? {
2672 EnvelopeTxType::EIP2930 => EIP2930Transaction::decode(tx_bytes)
2673 .map(Transaction::EIP2930Transaction),
2674 EnvelopeTxType::EIP1559 => EIP1559Transaction::decode(tx_bytes)
2675 .map(Transaction::EIP1559Transaction),
2676 EnvelopeTxType::EIP4844 => EIP4844Transaction::decode(tx_bytes)
2677 .map(Transaction::EIP4844Transaction),
2678 EnvelopeTxType::EIP7702 => EIP7702Transaction::decode(tx_bytes)
2679 .map(Transaction::EIP7702Transaction),
2680 EnvelopeTxType::Frame => {
2681 FrameTransaction::decode(tx_bytes).map(Transaction::FrameTransaction)
2682 }
2683 EnvelopeTxType::FeeToken => FeeTokenTransaction::decode(tx_bytes)
2684 .map(Transaction::FeeTokenTransaction),
2685 EnvelopeTxType::Privileged => PrivilegedL2Transaction::decode(tx_bytes)
2686 .map(Transaction::PrivilegedL2Transaction),
2687 }
2688 }
2689 _ => LegacyTransaction::decode(bytes).map(Transaction::LegacyTransaction),
2691 }
2692 }
2693
2694 pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
2700 match self {
2701 Transaction::LegacyTransaction(_) => {}
2703 _ => buf.put_u8(self.tx_type() as u8),
2704 }
2705 match self {
2706 Transaction::LegacyTransaction(t) => t.encode(buf),
2707 Transaction::EIP2930Transaction(t) => t.encode(buf),
2708 Transaction::EIP1559Transaction(t) => t.encode(buf),
2709 Transaction::EIP4844Transaction(t) => t.encode(buf),
2710 Transaction::EIP7702Transaction(t) => t.encode(buf),
2711 Transaction::FeeTokenTransaction(t) => t.encode(buf),
2712 Transaction::PrivilegedL2Transaction(t) => t.encode(buf),
2713 Transaction::FrameTransaction(t) => t.encode(buf),
2714 };
2715 }
2716
2717 pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
2723 if let Some(cell) = self.cached_canonical_cell() {
2724 return cell
2725 .get_or_init(|| {
2726 let mut buf = Vec::new();
2727 self.encode_canonical(&mut buf);
2728 buf
2729 })
2730 .clone();
2731 }
2732 let mut buf = Vec::new();
2733 self.encode_canonical(&mut buf);
2734 buf
2735 }
2736
2737 pub fn encode_canonical_len(&self) -> usize {
2742 let prefix_len = match self {
2743 Transaction::LegacyTransaction(_) => 0,
2744 _ => 1,
2745 };
2746 let inner_len = match self {
2747 Transaction::LegacyTransaction(t) => t.length(),
2748 Transaction::EIP2930Transaction(t) => t.length(),
2749 Transaction::EIP1559Transaction(t) => t.length(),
2750 Transaction::EIP4844Transaction(t) => t.length(),
2751 Transaction::EIP7702Transaction(t) => t.length(),
2752 Transaction::FeeTokenTransaction(t) => t.length(),
2753 Transaction::PrivilegedL2Transaction(t) => t.length(),
2754 Transaction::FrameTransaction(t) => t.length(),
2755 };
2756 prefix_len + inner_len
2757 }
2758 }
2759
2760 impl P2PTransaction {
2761 pub fn tx_type(&self) -> TxType {
2762 match self {
2763 P2PTransaction::LegacyTransaction(_) => TxType::Legacy,
2764 P2PTransaction::EIP2930Transaction(_) => TxType::EIP2930,
2765 P2PTransaction::EIP1559Transaction(_) => TxType::EIP1559,
2766 P2PTransaction::EIP4844TransactionWithBlobs(_) => TxType::EIP4844,
2767 P2PTransaction::EIP7702Transaction(_) => TxType::EIP7702,
2768 P2PTransaction::FeeTokenTransaction(_) => TxType::FeeToken,
2769 P2PTransaction::FrameTransaction(_) => TxType::Frame,
2770 }
2771 }
2772
2773 pub fn encode_canonical(&self, buf: &mut dyn bytes::BufMut) {
2774 match self {
2775 P2PTransaction::LegacyTransaction(_) => {}
2777 _ => buf.put_u8(self.tx_type() as u8),
2778 }
2779 match self {
2780 P2PTransaction::LegacyTransaction(t) => t.encode(buf),
2781 P2PTransaction::EIP2930Transaction(t) => t.encode(buf),
2782 P2PTransaction::EIP1559Transaction(t) => t.encode(buf),
2783 P2PTransaction::EIP4844TransactionWithBlobs(t) => t.encode(buf),
2784 P2PTransaction::EIP7702Transaction(t) => t.encode(buf),
2785 P2PTransaction::FeeTokenTransaction(t) => t.encode(buf),
2786 P2PTransaction::FrameTransaction(t) => t.encode(buf),
2787 };
2788 }
2789
2790 pub fn encode_canonical_to_vec(&self) -> Vec<u8> {
2791 let mut buf = Vec::new();
2792 self.encode_canonical(&mut buf);
2793 buf
2794 }
2795
2796 pub fn encode_canonical_len(&self) -> usize {
2801 let prefix_len = match self {
2802 P2PTransaction::LegacyTransaction(_) => 0,
2803 _ => 1,
2804 };
2805 let inner_len = match self {
2806 P2PTransaction::LegacyTransaction(t) => t.length(),
2807 P2PTransaction::EIP2930Transaction(t) => t.length(),
2808 P2PTransaction::EIP1559Transaction(t) => t.length(),
2809 P2PTransaction::EIP4844TransactionWithBlobs(t) => t.length(),
2810 P2PTransaction::EIP7702Transaction(t) => t.length(),
2811 P2PTransaction::FeeTokenTransaction(t) => t.length(),
2812 P2PTransaction::FrameTransaction(t) => t.length(),
2813 };
2814 prefix_len + inner_len
2815 }
2816
2817 pub fn compute_hash(&self) -> H256 {
2818 match self {
2819 P2PTransaction::LegacyTransaction(t) => {
2820 Transaction::LegacyTransaction(t.clone()).compute_hash(&NativeCrypto)
2821 }
2822 P2PTransaction::EIP2930Transaction(t) => {
2823 Transaction::EIP2930Transaction(t.clone()).compute_hash(&NativeCrypto)
2824 }
2825 P2PTransaction::EIP1559Transaction(t) => {
2826 Transaction::EIP1559Transaction(t.clone()).compute_hash(&NativeCrypto)
2827 }
2828 P2PTransaction::EIP4844TransactionWithBlobs(t) => {
2829 Transaction::EIP4844Transaction(t.tx.clone()).compute_hash(&NativeCrypto)
2830 }
2831 P2PTransaction::EIP7702Transaction(t) => {
2832 Transaction::EIP7702Transaction(t.clone()).compute_hash(&NativeCrypto)
2833 }
2834 P2PTransaction::FeeTokenTransaction(t) => {
2835 Transaction::FeeTokenTransaction(t.clone()).compute_hash(&NativeCrypto)
2836 }
2837 P2PTransaction::FrameTransaction(t) => {
2838 Transaction::FrameTransaction(t.clone()).compute_hash(&NativeCrypto)
2839 }
2840 }
2841 }
2842 }
2843}
2844
2845mod serde_impl {
2849 use ethereum_types::H160;
2850 use serde::Deserialize;
2851 use serde::{Deserializer, de::Error};
2852 use serde_json::Value;
2853 use std::{collections::HashMap, str::FromStr};
2854
2855 #[cfg(feature = "c-kzg")]
2856 use crate::types::BYTES_PER_BLOB;
2857 use crate::types::{AccessListItem, AuthorizationTuple, BlobsBundleError};
2858
2859 use super::*;
2860
2861 impl Serialize for TxKind {
2862 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2863 where
2864 S: serde::Serializer,
2865 {
2866 match self {
2867 TxKind::Call(address) => serializer.serialize_str(&format!("{address:#x}")),
2868 TxKind::Create => serializer.serialize_none(),
2869 }
2870 }
2871 }
2872
2873 impl<'de> Deserialize<'de> for TxKind {
2874 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2875 where
2876 D: serde::Deserializer<'de>,
2877 {
2878 let str_option = Option::<String>::deserialize(deserializer)?;
2879 match str_option {
2880 Some(str) if !str.is_empty() => Ok(TxKind::Call(
2881 Address::from_str(str.trim_start_matches("0x")).map_err(|_| {
2882 serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
2883 })?,
2884 )),
2885 _ => Ok(TxKind::Create),
2886 }
2887 }
2888 }
2889
2890 impl Serialize for TxType {
2891 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2892 where
2893 S: serde::Serializer,
2894 {
2895 serializer.serialize_str(&format!("{:#x}", *self as u8))
2896 }
2897 }
2898
2899 impl<'de> Deserialize<'de> for TxType {
2900 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2901 where
2902 D: serde::Deserializer<'de>,
2903 {
2904 let str = String::deserialize(deserializer)?;
2905 let tx_num = u8::from_str_radix(str.trim_start_matches("0x"), 16).map_err(|_| {
2906 serde::de::Error::custom(format!("Failed to deserialize hex value {str}"))
2907 })?;
2908 TxType::from_u8(tx_num).ok_or_else(|| {
2909 serde::de::Error::custom(format!("Invalid transaction type {tx_num}"))
2910 })
2911 }
2912 }
2913
2914 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2915 #[serde(rename_all = "camelCase")]
2916 pub struct AccessListEntry {
2917 pub address: Address,
2918 pub storage_keys: Vec<H256>,
2919 }
2920
2921 impl From<&AccessListItem> for AccessListEntry {
2922 fn from(value: &AccessListItem) -> AccessListEntry {
2923 AccessListEntry {
2924 address: value.0,
2925 storage_keys: value.1.clone(),
2926 }
2927 }
2928 }
2929
2930 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2931 #[serde(rename_all = "camelCase")]
2932 pub struct AuthorizationTupleEntry {
2933 pub chain_id: U256,
2934 pub address: Address,
2935 #[serde(default, with = "crate::serde_utils::u64::hex_str")]
2936 pub nonce: u64,
2937 pub y_parity: U256,
2938 pub r: U256,
2939 pub s: U256,
2940 }
2941
2942 impl From<&AuthorizationTuple> for AuthorizationTupleEntry {
2943 fn from(value: &AuthorizationTuple) -> AuthorizationTupleEntry {
2944 AuthorizationTupleEntry {
2945 chain_id: value.chain_id,
2946 address: value.address,
2947 nonce: value.nonce,
2948 y_parity: value.y_parity,
2949 r: value.r_signature,
2950 s: value.s_signature,
2951 }
2952 }
2953 }
2954
2955 impl From<AuthorizationTupleEntry> for AuthorizationTuple {
2956 fn from(entry: AuthorizationTupleEntry) -> AuthorizationTuple {
2957 AuthorizationTuple {
2958 chain_id: entry.chain_id,
2959 address: entry.address,
2960 nonce: entry.nonce,
2961 y_parity: entry.y_parity,
2962 r_signature: entry.r,
2963 s_signature: entry.s,
2964 }
2965 }
2966 }
2967
2968 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2969 #[serde(rename_all = "camelCase")]
2970 pub struct FrameEntry {
2971 #[serde(with = "crate::serde_utils::u64::hex_str")]
2972 pub mode: u64,
2973 #[serde(with = "crate::serde_utils::u64::hex_str")]
2974 pub flags: u64,
2975 pub to: Option<Address>,
2976 #[serde(with = "crate::serde_utils::u64::hex_str")]
2977 pub gas_limit: u64,
2978 #[serde(
2979 default,
2980 serialize_with = "serialize_u256_hex",
2981 deserialize_with = "crate::serde_utils::u256::deser_hex_str"
2982 )]
2983 pub value: U256,
2984 #[serde(with = "crate::serde_utils::bytes")]
2985 pub data: Bytes,
2986 }
2987
2988 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
2990 #[serde(rename_all = "camelCase")]
2991 pub struct SignatureEntry {
2992 #[serde(with = "crate::serde_utils::u64::hex_str")]
2993 pub scheme: u64,
2994 pub signer: Option<Address>,
2995 #[serde(with = "crate::serde_utils::bytes")]
2996 pub msg: Bytes,
2997 #[serde(with = "crate::serde_utils::bytes")]
2998 pub signature: Bytes,
2999 }
3000
3001 impl From<&FrameSignature> for SignatureEntry {
3002 fn from(value: &FrameSignature) -> SignatureEntry {
3003 SignatureEntry {
3004 scheme: value.scheme as u64,
3005 signer: value.signer,
3006 msg: value.msg.clone(),
3007 signature: value.signature.clone(),
3008 }
3009 }
3010 }
3011
3012 fn serialize_u256_hex<S: serde::Serializer>(value: &U256, s: S) -> Result<S::Ok, S::Error> {
3013 s.serialize_str(&format!("{value:#x}"))
3014 }
3015
3016 impl From<&Frame> for FrameEntry {
3017 fn from(value: &Frame) -> FrameEntry {
3018 FrameEntry {
3019 mode: value.mode as u64,
3020 flags: value.flags as u64,
3021 to: value.target,
3022 gas_limit: value.gas_limit,
3023 value: value.value,
3024 data: value.data.clone(),
3025 }
3026 }
3027 }
3028
3029 impl From<FrameEntry> for Frame {
3030 fn from(entry: FrameEntry) -> Frame {
3031 Frame {
3032 mode: entry.mode as u8,
3033 flags: entry.flags as u8,
3034 target: entry.to,
3035 gas_limit: entry.gas_limit,
3036 value: entry.value,
3037 data: entry.data,
3038 }
3039 }
3040 }
3041
3042 impl Serialize for LegacyTransaction {
3043 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3044 where
3045 S: serde::Serializer,
3046 {
3047 let mut struct_serializer = serializer.serialize_struct("LegacyTransaction", 11)?;
3048 struct_serializer.serialize_field("type", &TxType::Legacy)?;
3049 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3050 struct_serializer.serialize_field("to", &self.to)?;
3051 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
3052 struct_serializer.serialize_field("value", &self.value)?;
3053 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3054 struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
3055 struct_serializer.serialize_field(
3056 "chainId",
3057 &format!("{:#x}", derive_legacy_chain_id(self.v).unwrap_or_default()),
3058 )?;
3059 struct_serializer.serialize_field("v", &self.v)?;
3060 struct_serializer.serialize_field("r", &self.r)?;
3061 struct_serializer.serialize_field("s", &self.s)?;
3062 struct_serializer.end()
3063 }
3064 }
3065
3066 impl Serialize for EIP2930Transaction {
3067 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3068 where
3069 S: serde::Serializer,
3070 {
3071 let mut struct_serializer = serializer.serialize_struct("Eip2930Transaction", 12)?;
3072 struct_serializer.serialize_field("type", &TxType::EIP2930)?;
3073 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3074 struct_serializer.serialize_field("to", &self.to)?;
3075 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3076 struct_serializer.serialize_field("value", &self.value)?;
3077 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3078 struct_serializer.serialize_field("gasPrice", &format!("{:#x}", self.gas_price))?;
3079 struct_serializer.serialize_field(
3080 "accessList",
3081 &self
3082 .access_list
3083 .iter()
3084 .map(AccessListEntry::from)
3085 .collect::<Vec<_>>(),
3086 )?;
3087 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3088 struct_serializer
3089 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3090 struct_serializer
3091 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3093 struct_serializer.serialize_field("s", &self.signature_s)?;
3094 struct_serializer.end()
3095 }
3096 }
3097
3098 impl Serialize for EIP1559Transaction {
3099 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3100 where
3101 S: serde::Serializer,
3102 {
3103 let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
3104 struct_serializer.serialize_field("type", &TxType::EIP1559)?;
3105 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3106 struct_serializer.serialize_field("to", &self.to)?;
3107 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3108 struct_serializer.serialize_field("value", &self.value)?;
3109 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3110 struct_serializer.serialize_field(
3111 "maxPriorityFeePerGas",
3112 &format!("{:#x}", self.max_priority_fee_per_gas),
3113 )?;
3114 struct_serializer
3115 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3116 struct_serializer
3117 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3118 struct_serializer.serialize_field(
3119 "accessList",
3120 &self
3121 .access_list
3122 .iter()
3123 .map(AccessListEntry::from)
3124 .collect::<Vec<_>>(),
3125 )?;
3126 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3127 struct_serializer
3128 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3129 struct_serializer
3130 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3132 struct_serializer.serialize_field("s", &self.signature_s)?;
3133 struct_serializer.end()
3134 }
3135 }
3136
3137 impl Serialize for EIP4844Transaction {
3138 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3139 where
3140 S: serde::Serializer,
3141 {
3142 let mut struct_serializer = serializer.serialize_struct("Eip4844Transaction", 15)?;
3143 struct_serializer.serialize_field("type", &TxType::EIP4844)?;
3144 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3145 struct_serializer.serialize_field("to", &self.to)?;
3146 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas))?;
3147 struct_serializer.serialize_field("value", &self.value)?;
3148 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3149 struct_serializer.serialize_field(
3150 "maxPriorityFeePerGas",
3151 &format!("{:#x}", self.max_priority_fee_per_gas),
3152 )?;
3153 struct_serializer
3154 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3155 struct_serializer
3156 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3157 struct_serializer.serialize_field(
3158 "maxFeePerBlobGas",
3159 &format!("{:#x}", self.max_fee_per_blob_gas),
3160 )?;
3161 struct_serializer.serialize_field(
3162 "accessList",
3163 &self
3164 .access_list
3165 .iter()
3166 .map(AccessListEntry::from)
3167 .collect::<Vec<_>>(),
3168 )?;
3169 struct_serializer
3170 .serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
3171 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3172 struct_serializer
3173 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3174 struct_serializer
3175 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3177 struct_serializer.serialize_field("s", &self.signature_s)?;
3178 struct_serializer.end()
3179 }
3180 }
3181
3182 impl Serialize for EIP7702Transaction {
3183 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3184 where
3185 S: serde::Serializer,
3186 {
3187 let mut struct_serializer = serializer.serialize_struct("Eip7702Transaction", 15)?;
3188 struct_serializer.serialize_field("type", &TxType::EIP7702)?;
3189 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3190 struct_serializer.serialize_field("to", &self.to)?;
3191 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3192 struct_serializer.serialize_field("value", &self.value)?;
3193 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3194 struct_serializer.serialize_field(
3195 "maxPriorityFeePerGas",
3196 &format!("{:#x}", self.max_priority_fee_per_gas),
3197 )?;
3198 struct_serializer
3199 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3200 struct_serializer
3201 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3202 struct_serializer.serialize_field(
3203 "accessList",
3204 &self
3205 .access_list
3206 .iter()
3207 .map(AccessListEntry::from)
3208 .collect::<Vec<_>>(),
3209 )?;
3210 struct_serializer.serialize_field(
3211 "authorizationList",
3212 &self
3213 .authorization_list
3214 .iter()
3215 .map(AuthorizationTupleEntry::from)
3216 .collect::<Vec<_>>(),
3217 )?;
3218 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3219 struct_serializer
3220 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3221 struct_serializer
3222 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?; struct_serializer.serialize_field("r", &self.signature_r)?;
3224 struct_serializer.serialize_field("s", &self.signature_s)?;
3225 struct_serializer.end()
3226 }
3227 }
3228
3229 impl Serialize for PrivilegedL2Transaction {
3230 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3231 where
3232 S: serde::Serializer,
3233 {
3234 let mut struct_serializer = serializer.serialize_struct("Eip1559Transaction", 14)?;
3235 struct_serializer.serialize_field("type", &TxType::Privileged)?;
3236 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3237 struct_serializer.serialize_field("to", &self.to)?;
3238 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3239 struct_serializer.serialize_field("value", &self.value)?;
3240 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3241 struct_serializer.serialize_field(
3242 "maxPriorityFeePerGas",
3243 &format!("{:#x}", self.max_priority_fee_per_gas),
3244 )?;
3245 struct_serializer
3246 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3247 struct_serializer
3248 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3249 struct_serializer.serialize_field(
3250 "accessList",
3251 &self
3252 .access_list
3253 .iter()
3254 .map(AccessListEntry::from)
3255 .collect::<Vec<_>>(),
3256 )?;
3257 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3258 struct_serializer.serialize_field("sender", &self.from)?;
3259 struct_serializer.end()
3260 }
3261 }
3262
3263 impl Serialize for FeeTokenTransaction {
3264 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3265 where
3266 S: serde::Serializer,
3267 {
3268 let mut struct_serializer = serializer.serialize_struct("FeeTokenTransaction", 15)?;
3269 struct_serializer.serialize_field("type", &TxType::FeeToken)?;
3270 struct_serializer.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3271 struct_serializer.serialize_field("to", &self.to)?;
3272 struct_serializer.serialize_field("gas", &format!("{:#x}", self.gas_limit))?;
3273 struct_serializer.serialize_field("value", &self.value)?;
3274 struct_serializer.serialize_field("input", &format!("0x{:x}", self.data))?;
3275 struct_serializer.serialize_field(
3276 "maxPriorityFeePerGas",
3277 &format!("{:#x}", self.max_priority_fee_per_gas),
3278 )?;
3279 struct_serializer
3280 .serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3281 struct_serializer
3282 .serialize_field("gasPrice", &format!("{:#x}", self.max_fee_per_gas))?;
3283 struct_serializer.serialize_field(
3284 "accessList",
3285 &self
3286 .access_list
3287 .iter()
3288 .map(AccessListEntry::from)
3289 .collect::<Vec<_>>(),
3290 )?;
3291 struct_serializer.serialize_field("feeToken", &format!("{:#x}", self.fee_token))?;
3292 struct_serializer.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3293 struct_serializer
3294 .serialize_field("yParity", &format!("{:#x}", self.signature_y_parity as u8))?;
3295 struct_serializer
3296 .serialize_field("v", &format!("{:#x}", self.signature_y_parity as u8))?;
3297 struct_serializer.serialize_field("r", &self.signature_r)?;
3298 struct_serializer.serialize_field("s", &self.signature_s)?;
3299 struct_serializer.end()
3300 }
3301 }
3302
3303 impl Serialize for FrameTransaction {
3304 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3305 where
3306 S: serde::Serializer,
3307 {
3308 let mut s = serializer.serialize_struct("FrameTransaction", 10)?;
3309 s.serialize_field("type", &TxType::Frame)?;
3310 s.serialize_field("chainId", &format!("{:#x}", self.chain_id))?;
3311 s.serialize_field("nonce", &format!("{:#x}", self.nonce))?;
3312 s.serialize_field("sender", &format!("{:#x}", self.sender))?;
3313 s.serialize_field(
3314 "frames",
3315 &self.frames.iter().map(FrameEntry::from).collect::<Vec<_>>(),
3316 )?;
3317 s.serialize_field(
3318 "signatures",
3319 &self
3320 .signatures
3321 .iter()
3322 .map(SignatureEntry::from)
3323 .collect::<Vec<_>>(),
3324 )?;
3325 s.serialize_field(
3326 "maxPriorityFeePerGas",
3327 &format!("{:#x}", self.max_priority_fee_per_gas),
3328 )?;
3329 s.serialize_field("maxFeePerGas", &format!("{:#x}", self.max_fee_per_gas))?;
3330 s.serialize_field("maxFeePerBlobGas", &self.max_fee_per_blob_gas)?;
3331 s.serialize_field("blobVersionedHashes", &self.blob_versioned_hashes)?;
3332 s.end()
3333 }
3334 }
3335
3336 impl<'de> Deserialize<'de> for Transaction {
3337 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3338 where
3339 D: serde::Deserializer<'de>,
3340 {
3341 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3342 let tx_type =
3343 serde_json::from_value::<TxType>(map.remove("type").unwrap_or(Value::default()))
3344 .unwrap_or_else(|_| {
3345 if map.contains_key("tx_type") {
3346 return TxType::Privileged;
3347 }
3348 TxType::EIP1559
3349 });
3350
3351 let iter = map.into_iter();
3352 match tx_type {
3353 TxType::Legacy => {
3354 LegacyTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3355 .map(Transaction::LegacyTransaction)
3356 .map_err(|e| {
3357 serde::de::Error::custom(format!("Couldn't Deserialize Legacy {e}"))
3358 })
3359 }
3360 TxType::EIP2930 => {
3361 EIP2930Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3362 .map(Transaction::EIP2930Transaction)
3363 .map_err(|e| {
3364 serde::de::Error::custom(format!("Couldn't Deserialize EIP2930 {e}"))
3365 })
3366 }
3367 TxType::EIP1559 => {
3368 EIP1559Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3369 .map(Transaction::EIP1559Transaction)
3370 .map_err(|e| {
3371 serde::de::Error::custom(format!("Couldn't Deserialize EIP1559 {e}"))
3372 })
3373 }
3374 TxType::EIP4844 => {
3375 EIP4844Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3376 .map(Transaction::EIP4844Transaction)
3377 .map_err(|e| {
3378 serde::de::Error::custom(format!("Couldn't Deserialize EIP4844 {e}"))
3379 })
3380 }
3381 TxType::EIP7702 => {
3382 EIP7702Transaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3383 .map(Transaction::EIP7702Transaction)
3384 .map_err(|e| {
3385 serde::de::Error::custom(format!("Couldn't Deserialize EIP7702 {e}"))
3386 })
3387 }
3388 TxType::Privileged => PrivilegedL2Transaction::deserialize(
3389 serde::de::value::MapDeserializer::new(iter),
3390 )
3391 .map(Transaction::PrivilegedL2Transaction)
3392 .map_err(|e| {
3393 serde::de::Error::custom(format!("Couldn't Deserialize Privileged: {e}"))
3394 }),
3395 TxType::FeeToken => {
3396 FeeTokenTransaction::deserialize(serde::de::value::MapDeserializer::new(iter))
3397 .map(Transaction::FeeTokenTransaction)
3398 .map_err(|e| {
3399 serde::de::Error::custom(format!("Couldn't Deserialize FeeToken {e}"))
3400 })
3401 }
3402 TxType::Frame => Err(serde::de::Error::custom(
3403 "Frame transaction JSON deserialization not supported",
3404 )),
3405 }
3406 }
3407 }
3408
3409 fn deserialize_input_field(
3410 map: &mut std::collections::HashMap<String, Value>,
3411 ) -> Result<Bytes, serde_json::Error> {
3412 let data_str: String = serde_json::from_value(
3413 map.remove("input")
3414 .ok_or_else(|| serde::de::Error::missing_field("input"))?,
3415 )
3416 .map_err(serde::de::Error::custom)?;
3417 if let Some(stripped) = data_str.strip_prefix("0x") {
3418 match hex::decode(stripped) {
3419 Ok(decoded_bytes) => Ok(Bytes::from(decoded_bytes)),
3420 Err(_) => Err(serde::de::Error::custom(
3421 "Invalid hex format in 'input' field",
3422 ))?,
3423 }
3424 } else {
3425 Err(serde::de::Error::custom(
3426 "'input' field must start with '0x'",
3427 ))?
3428 }
3429 }
3430
3431 fn deserialize_field<'de, T, D>(
3432 map: &mut HashMap<String, serde_json::Value>,
3433 key: &str,
3434 ) -> Result<T, D::Error>
3435 where
3436 D: serde::Deserializer<'de>,
3437 T: serde::de::DeserializeOwned,
3438 {
3439 map.remove(key)
3440 .ok_or_else(|| D::Error::custom(format!("Missing field: {key}")))
3441 .and_then(|value| {
3442 serde_json::from_value(value).map_err(|err| D::Error::custom(err.to_string()))
3443 })
3444 }
3445
3446 fn deserialize_u64_field<'de, D>(
3447 map: &mut HashMap<String, serde_json::Value>,
3448 key: &str,
3449 ) -> Result<u64, D::Error>
3450 where
3451 D: serde::Deserializer<'de>,
3452 {
3453 let value = deserialize_field::<U256, D>(map, key)?;
3454 u64::try_from(value).map_err(|_| D::Error::custom(format!("{key} value overflows u64")))
3455 }
3456
3457 impl<'de> Deserialize<'de> for LegacyTransaction {
3458 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3459 where
3460 D: serde::Deserializer<'de>,
3461 {
3462 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3463
3464 Ok(LegacyTransaction {
3465 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3466 gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
3467 gas: deserialize_u64_field::<D>(&mut map, "gas")?,
3468 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3469 value: deserialize_field::<U256, D>(&mut map, "value")?,
3470 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3471 v: deserialize_field::<U256, D>(&mut map, "v")?,
3472 r: deserialize_field::<U256, D>(&mut map, "r")?,
3473 s: deserialize_field::<U256, D>(&mut map, "s")?,
3474 ..Default::default()
3475 })
3476 }
3477 }
3478
3479 impl<'de> Deserialize<'de> for EIP2930Transaction {
3480 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3481 where
3482 D: serde::Deserializer<'de>,
3483 {
3484 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3485
3486 Ok(EIP2930Transaction {
3487 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3488 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3489 gas_price: deserialize_field::<U256, D>(&mut map, "gasPrice")?,
3490 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3491 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3492 value: deserialize_field::<U256, D>(&mut map, "value")?,
3493 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3494 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3495 .into_iter()
3496 .map(|v| (v.address, v.storage_keys))
3497 .collect::<Vec<_>>(),
3498 signature_y_parity: u8::from_str_radix(
3499 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3500 16,
3501 )
3502 .map_err(serde::de::Error::custom)?
3503 != 0,
3504 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3505 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3506 ..Default::default()
3507 })
3508 }
3509 }
3510
3511 impl<'de> Deserialize<'de> for EIP1559Transaction {
3512 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3513 where
3514 D: serde::Deserializer<'de>,
3515 {
3516 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3517 Ok(EIP1559Transaction {
3518 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3519 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3520 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3521 &mut map,
3522 "maxPriorityFeePerGas",
3523 )?,
3524 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3525 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3526 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3527 value: deserialize_field::<U256, D>(&mut map, "value")?,
3528 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3529 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3530 .into_iter()
3531 .map(|v| (v.address, v.storage_keys))
3532 .collect::<Vec<_>>(),
3533 signature_y_parity: u8::from_str_radix(
3534 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3535 16,
3536 )
3537 .map_err(serde::de::Error::custom)?
3538 != 0,
3539 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3540 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3541 ..Default::default()
3542 })
3543 }
3544 }
3545
3546 impl<'de> Deserialize<'de> for EIP4844Transaction {
3547 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3548 where
3549 D: serde::Deserializer<'de>,
3550 {
3551 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3552
3553 Ok(EIP4844Transaction {
3554 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3555 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3556 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3557 &mut map,
3558 "maxPriorityFeePerGas",
3559 )?,
3560 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3561 gas: deserialize_u64_field::<D>(&mut map, "gas")?,
3562 to: deserialize_field::<Address, D>(&mut map, "to")?,
3563 value: deserialize_field::<U256, D>(&mut map, "value")?,
3564 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3565 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3566 .into_iter()
3567 .map(|v| (v.address, v.storage_keys))
3568 .collect::<Vec<_>>(),
3569 max_fee_per_blob_gas: deserialize_field::<U256, D>(&mut map, "maxFeePerBlobGas")?,
3570 blob_versioned_hashes: deserialize_field::<Vec<H256>, D>(
3571 &mut map,
3572 "blobVersionedHashes",
3573 )?,
3574 signature_y_parity: u8::from_str_radix(
3575 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3576 16,
3577 )
3578 .map_err(serde::de::Error::custom)?
3579 != 0,
3580 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3581 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3582 ..Default::default()
3583 })
3584 }
3585 }
3586
3587 impl<'de> Deserialize<'de> for EIP7702Transaction {
3588 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3589 where
3590 D: serde::Deserializer<'de>,
3591 {
3592 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3593
3594 Ok(EIP7702Transaction {
3595 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3596 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3597 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3598 &mut map,
3599 "maxPriorityFeePerGas",
3600 )?,
3601 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3602 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3603 to: deserialize_field::<Address, D>(&mut map, "to")?,
3604 value: deserialize_field::<U256, D>(&mut map, "value")?,
3605 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3606 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3607 .into_iter()
3608 .map(|v| (v.address, v.storage_keys))
3609 .collect::<Vec<_>>(),
3610 authorization_list: deserialize_field::<Vec<AuthorizationTupleEntry>, D>(
3611 &mut map,
3612 "authorizationList",
3613 )?
3614 .into_iter()
3615 .map(AuthorizationTuple::from)
3616 .collect::<Vec<_>>(),
3617 signature_y_parity: u8::from_str_radix(
3618 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3619 16,
3620 )
3621 .map_err(serde::de::Error::custom)?
3622 != 0,
3623 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3624 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3625 ..Default::default()
3626 })
3627 }
3628 }
3629
3630 impl<'de> Deserialize<'de> for PrivilegedL2Transaction {
3631 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3632 where
3633 D: serde::Deserializer<'de>,
3634 {
3635 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3636
3637 Ok(PrivilegedL2Transaction {
3638 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3639 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3640 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3641 &mut map,
3642 "maxPriorityFeePerGas",
3643 )?,
3644 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3645 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3646 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3647 value: deserialize_field::<U256, D>(&mut map, "value")?,
3648 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3649 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3650 .into_iter()
3651 .map(|v| (v.address, v.storage_keys))
3652 .collect::<Vec<_>>(),
3653 from: deserialize_field::<Address, D>(&mut map, "sender")?,
3654 ..Default::default()
3655 })
3656 }
3657 }
3658
3659 impl<'de> Deserialize<'de> for FeeTokenTransaction {
3660 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3661 where
3662 D: serde::Deserializer<'de>,
3663 {
3664 let mut map = <HashMap<String, serde_json::Value>>::deserialize(deserializer)?;
3665
3666 Ok(FeeTokenTransaction {
3667 chain_id: deserialize_u64_field::<D>(&mut map, "chainId")?,
3668 nonce: deserialize_u64_field::<D>(&mut map, "nonce")?,
3669 max_priority_fee_per_gas: deserialize_u64_field::<D>(
3670 &mut map,
3671 "maxPriorityFeePerGas",
3672 )?,
3673 max_fee_per_gas: deserialize_u64_field::<D>(&mut map, "maxFeePerGas")?,
3674 gas_limit: deserialize_u64_field::<D>(&mut map, "gas")?,
3675 to: deserialize_field::<TxKind, D>(&mut map, "to")?,
3676 value: deserialize_field::<U256, D>(&mut map, "value")?,
3677 data: deserialize_input_field(&mut map).map_err(serde::de::Error::custom)?,
3678 access_list: deserialize_field::<Vec<AccessListEntry>, D>(&mut map, "accessList")?
3679 .into_iter()
3680 .map(|v| (v.address, v.storage_keys))
3681 .collect::<Vec<_>>(),
3682 fee_token: deserialize_field::<Address, D>(&mut map, "feeToken")?,
3683 signature_y_parity: u8::from_str_radix(
3684 deserialize_field::<String, D>(&mut map, "yParity")?.trim_start_matches("0x"),
3685 16,
3686 )
3687 .map_err(serde::de::Error::custom)?
3688 != 0,
3689 signature_r: deserialize_field::<U256, D>(&mut map, "r")?,
3690 signature_s: deserialize_field::<U256, D>(&mut map, "s")?,
3691 ..Default::default()
3692 })
3693 }
3694 }
3695
3696 #[derive(Debug, thiserror::Error)]
3697 pub enum GenericTransactionError {
3698 #[error("Invalid transaction type: {0}")]
3699 InvalidTxType(TxType),
3700 #[error("Blob bundle error: {0}")]
3701 BlobBundleError(#[from] BlobsBundleError),
3702 #[error("Missing field: {0}")]
3703 MissingField(String),
3704 #[error("Invalid field: {0}")]
3705 InvalidField(String),
3706 }
3707
3708 #[derive(Deserialize, Debug, PartialEq, Clone, Default)]
3711 #[serde(rename_all = "camelCase")]
3712 pub struct GenericTransaction {
3713 #[serde(default)]
3714 pub r#type: TxType,
3715 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3716 pub nonce: Option<u64>,
3717 pub to: TxKind,
3718 #[serde(default)]
3719 pub from: Address,
3720 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3721 pub gas: Option<u64>,
3722 #[serde(default)]
3723 pub value: U256,
3724 #[serde(default)]
3725 pub gas_price: U256,
3726 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3727 pub max_priority_fee_per_gas: Option<u64>,
3728 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3729 pub max_fee_per_gas: Option<u64>,
3730 pub max_fee_per_blob_gas: Option<U256>,
3731 #[serde(default)]
3732 pub access_list: Vec<AccessListEntry>,
3733 #[serde(default)]
3734 pub fee_token: Option<Address>,
3735 #[serde(default)]
3736 pub authorization_list: Option<Vec<AuthorizationTupleEntry>>,
3737 #[serde(default)]
3738 pub blob_versioned_hashes: Vec<H256>,
3739 pub wrapper_version: Option<u8>,
3740 #[serde(default, with = "crate::serde_utils::bytes::vec")]
3741 pub blobs: Vec<Bytes>,
3742 #[serde(default, with = "crate::serde_utils::u64::hex_str_opt")]
3743 pub chain_id: Option<u64>,
3744 #[serde(
3746 flatten,
3747 rename = "input_or_data",
3748 deserialize_with = "deserialize_input",
3749 serialize_with = "crate::serde_utils::bytes::serialize"
3750 )]
3751 pub input: Bytes,
3752 }
3753 pub fn deserialize_input<'de, D>(deserializer: D) -> Result<Bytes, D::Error>
3755 where
3756 D: Deserializer<'de>,
3757 {
3758 let variables = HashMap::<String, Value>::deserialize(deserializer)?;
3761 let data = variables.get("data");
3762 let input = variables.get("input");
3763 let value = match (data, input) {
3764 (None, None) => return Ok(Bytes::new()),
3766 (None, Some(val)) => val,
3767 (Some(val), None) => val,
3768 (Some(val_a), Some(val_b)) => {
3769 if val_a == val_b {
3770 val_a
3771 } else {
3772 return Err(D::Error::custom(
3773 "Transaction has both `data` and `input` fields with different values",
3774 ));
3775 }
3776 }
3777 };
3778 let value = String::deserialize(value).map_err(D::Error::custom)?;
3779 let bytes = hex::decode(value.trim_start_matches("0x"))
3780 .map_err(|e| D::Error::custom(e.to_string()))?;
3781 Ok(Bytes::from(bytes))
3782 }
3783
3784 impl From<EIP1559Transaction> for GenericTransaction {
3785 fn from(value: EIP1559Transaction) -> Self {
3786 Self {
3787 r#type: TxType::EIP1559,
3788 nonce: Some(value.nonce),
3789 to: value.to,
3790 gas: Some(value.gas_limit),
3791 value: value.value,
3792 input: value.data.clone(),
3793 gas_price: U256::from(value.max_fee_per_gas),
3794 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3795 max_fee_per_gas: Some(value.max_fee_per_gas),
3796 max_fee_per_blob_gas: None,
3797 access_list: value
3798 .access_list
3799 .iter()
3800 .map(AccessListEntry::from)
3801 .collect(),
3802 fee_token: None,
3803 authorization_list: None,
3804 blob_versioned_hashes: vec![],
3805 blobs: vec![],
3806 wrapper_version: None,
3807 chain_id: Some(value.chain_id),
3808 from: Address::default(),
3809 }
3810 }
3811 }
3812
3813 impl TryFrom<GenericTransaction> for EIP1559Transaction {
3814 type Error = GenericTransactionError;
3815
3816 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3817 if value.r#type != TxType::EIP1559 {
3818 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3819 }
3820
3821 Ok(Self {
3822 nonce: value.nonce.unwrap_or_default(),
3823 to: value.to,
3824 gas_limit: value.gas.unwrap_or_default(),
3825 value: value.value,
3826 data: value.input.clone(),
3827 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3828 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3829 u64::try_from(value.gas_price).map_err(|_| {
3830 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3831 })?,
3832 ),
3833 access_list: value
3834 .access_list
3835 .into_iter()
3836 .map(|v| (v.address, v.storage_keys))
3837 .collect::<Vec<_>>(),
3838 chain_id: value.chain_id.unwrap_or_default(),
3839 ..Default::default()
3840 })
3841 }
3842 }
3843
3844 impl From<EIP4844Transaction> for GenericTransaction {
3845 fn from(value: EIP4844Transaction) -> Self {
3846 Self {
3847 r#type: TxType::EIP4844,
3848 nonce: Some(value.nonce),
3849 to: TxKind::Call(value.to),
3850 gas: Some(value.gas),
3851 value: value.value,
3852 input: value.data.clone(),
3853 gas_price: U256::from(value.max_fee_per_gas),
3854 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3855 max_fee_per_gas: Some(value.max_fee_per_gas),
3856 max_fee_per_blob_gas: Some(value.max_fee_per_blob_gas),
3857 access_list: value
3858 .access_list
3859 .iter()
3860 .map(AccessListEntry::from)
3861 .collect(),
3862 fee_token: None,
3863 authorization_list: None,
3864 blob_versioned_hashes: value.blob_versioned_hashes,
3865 blobs: vec![],
3866 wrapper_version: None,
3867 chain_id: Some(value.chain_id),
3868 from: Address::default(),
3869 }
3870 }
3871 }
3872
3873 #[cfg(feature = "c-kzg")]
3874 impl TryFrom<GenericTransaction> for WrappedEIP4844Transaction {
3875 type Error = GenericTransactionError;
3876
3877 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3878 let blobs = value
3879 .blobs
3880 .iter()
3881 .map(|bytes| {
3882 let slice = bytes.as_ref();
3883 let mut blob = [0u8; BYTES_PER_BLOB];
3884 blob.copy_from_slice(slice);
3885 blob
3886 })
3887 .collect();
3888
3889 let wrapper_version = value.wrapper_version;
3890 Ok(Self {
3891 tx: value.try_into()?,
3892 wrapper_version,
3893 blobs_bundle: BlobsBundle::create_from_blobs(&blobs, wrapper_version)?,
3894 })
3895 }
3896 }
3897
3898 impl TryFrom<GenericTransaction> for EIP4844Transaction {
3899 type Error = GenericTransactionError;
3900
3901 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3902 if value.r#type != TxType::EIP4844 {
3903 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3904 }
3905 Ok(Self {
3906 nonce: value.nonce.unwrap_or_default(),
3907 to: match value.to {
3908 TxKind::Call(to) => to,
3909 _ => H160::default(),
3910 },
3911 gas: value.gas.unwrap_or_default(),
3912 value: value.value,
3913 data: value.input.clone(),
3914 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3915 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3916 u64::try_from(value.gas_price).map_err(|_| {
3917 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3918 })?,
3919 ),
3920 max_fee_per_blob_gas: value.max_fee_per_blob_gas.unwrap_or_default(),
3921 access_list: value
3922 .access_list
3923 .into_iter()
3924 .map(|v| (v.address, v.storage_keys))
3925 .collect::<Vec<_>>(),
3926 blob_versioned_hashes: value.blob_versioned_hashes,
3927 chain_id: value.chain_id.unwrap_or_default(),
3928 ..Default::default()
3929 })
3930 }
3931 }
3932
3933 impl From<EIP7702Transaction> for GenericTransaction {
3934 fn from(value: EIP7702Transaction) -> Self {
3935 Self {
3936 r#type: TxType::EIP7702,
3937 nonce: Some(value.nonce),
3938 to: TxKind::Call(value.to),
3939 gas: Some(value.gas_limit),
3940 value: value.value,
3941 input: value.data.clone(),
3942 gas_price: U256::from(value.max_fee_per_gas),
3943 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
3944 max_fee_per_gas: Some(value.max_fee_per_gas),
3945 max_fee_per_blob_gas: None,
3946 access_list: value
3947 .access_list
3948 .iter()
3949 .map(AccessListEntry::from)
3950 .collect(),
3951 fee_token: None,
3952 authorization_list: Some(
3953 value
3954 .authorization_list
3955 .iter()
3956 .map(AuthorizationTupleEntry::from)
3957 .collect(),
3958 ),
3959 blob_versioned_hashes: vec![],
3960 blobs: vec![],
3961 wrapper_version: None,
3962 chain_id: Some(value.chain_id),
3963 from: Address::default(),
3964 }
3965 }
3966 }
3967
3968 impl TryFrom<GenericTransaction> for EIP7702Transaction {
3969 type Error = GenericTransactionError;
3970
3971 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
3972 if value.r#type != TxType::EIP7702 {
3973 return Err(GenericTransactionError::InvalidTxType(value.r#type));
3974 }
3975 let TxKind::Call(to) = value.to else {
3976 return Err(GenericTransactionError::MissingField("to".to_owned()));
3977 };
3978 Ok(Self {
3979 chain_id: value.chain_id.unwrap_or_default(),
3980 nonce: value.nonce.unwrap_or_default(),
3981 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
3982 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
3983 u64::try_from(value.gas_price).map_err(|_| {
3984 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
3985 })?,
3986 ),
3987 gas_limit: value.gas.unwrap_or_default(),
3988 to,
3989 value: value.value,
3990 data: value.input,
3991 access_list: value
3992 .access_list
3993 .into_iter()
3994 .map(|v| (v.address, v.storage_keys))
3995 .collect::<Vec<_>>(),
3996 authorization_list: value
3997 .authorization_list
3998 .unwrap_or_default()
3999 .into_iter()
4000 .map(AuthorizationTuple::from)
4001 .collect(),
4002 ..Default::default()
4003 })
4004 }
4005 }
4006
4007 impl From<PrivilegedL2Transaction> for GenericTransaction {
4008 fn from(value: PrivilegedL2Transaction) -> Self {
4009 Self {
4010 r#type: TxType::Privileged,
4011 nonce: Some(value.nonce),
4012 to: value.to,
4013 gas: Some(value.gas_limit),
4014 value: value.value,
4015 input: value.data.clone(),
4016 gas_price: U256::from(value.max_fee_per_gas),
4017 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4018 max_fee_per_gas: Some(value.max_fee_per_gas),
4019 max_fee_per_blob_gas: None,
4020 access_list: value
4021 .access_list
4022 .iter()
4023 .map(AccessListEntry::from)
4024 .collect(),
4025 fee_token: None,
4026 authorization_list: None,
4027 blob_versioned_hashes: vec![],
4028 blobs: vec![],
4029 wrapper_version: None,
4030 chain_id: Some(value.chain_id),
4031 from: value.from,
4032 }
4033 }
4034 }
4035
4036 impl TryFrom<GenericTransaction> for PrivilegedL2Transaction {
4037 type Error = GenericTransactionError;
4038
4039 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
4040 if value.r#type != TxType::Privileged {
4041 return Err(GenericTransactionError::InvalidTxType(value.r#type));
4042 }
4043 Ok(Self {
4044 nonce: value.nonce.unwrap_or_default(),
4045 to: value.to,
4046 gas_limit: value.gas.unwrap_or_default(),
4047 value: value.value,
4048 data: value.input.clone(),
4049 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
4050 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
4051 u64::try_from(value.gas_price).map_err(|_| {
4052 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
4053 })?,
4054 ),
4055 access_list: value
4056 .access_list
4057 .into_iter()
4058 .map(|v| (v.address, v.storage_keys))
4059 .collect::<Vec<_>>(),
4060 chain_id: value.chain_id.unwrap_or_default(),
4061 from: value.from,
4062 ..Default::default()
4063 })
4064 }
4065 }
4066
4067 impl From<FeeTokenTransaction> for GenericTransaction {
4068 fn from(value: FeeTokenTransaction) -> Self {
4069 Self {
4070 r#type: TxType::FeeToken,
4071 nonce: Some(value.nonce),
4072 to: value.to,
4073 gas: Some(value.gas_limit),
4074 value: value.value,
4075 input: value.data.clone(),
4076 gas_price: U256::from(value.max_fee_per_gas),
4077 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4078 max_fee_per_gas: Some(value.max_fee_per_gas),
4079 max_fee_per_blob_gas: None,
4080 access_list: value
4081 .access_list
4082 .iter()
4083 .map(AccessListEntry::from)
4084 .collect(),
4085 fee_token: Some(value.fee_token),
4086 authorization_list: None,
4087 blob_versioned_hashes: vec![],
4088 blobs: vec![],
4089 chain_id: Some(value.chain_id),
4090 from: Address::default(),
4091 wrapper_version: None,
4092 }
4093 }
4094 }
4095
4096 impl TryFrom<GenericTransaction> for FeeTokenTransaction {
4097 type Error = GenericTransactionError;
4098
4099 fn try_from(value: GenericTransaction) -> Result<Self, Self::Error> {
4100 if value.r#type != TxType::FeeToken {
4101 return Err(GenericTransactionError::InvalidTxType(value.r#type));
4102 }
4103
4104 Ok(Self {
4105 nonce: value.nonce.unwrap_or_default(),
4106 to: value.to,
4107 gas_limit: value.gas.unwrap_or_default(),
4108 value: value.value,
4109 data: value.input.clone(),
4110 max_priority_fee_per_gas: value.max_priority_fee_per_gas.unwrap_or_default(),
4111 max_fee_per_gas: value.max_fee_per_gas.unwrap_or(
4112 u64::try_from(value.gas_price).map_err(|_| {
4113 GenericTransactionError::InvalidField("gas_price overflows u64".to_owned())
4114 })?,
4115 ),
4116 access_list: value
4117 .access_list
4118 .into_iter()
4119 .map(|v| (v.address, v.storage_keys))
4120 .collect::<Vec<_>>(),
4121 fee_token: value
4122 .fee_token
4123 .ok_or(GenericTransactionError::MissingField(
4124 "fee token".to_owned(),
4125 ))?,
4126 chain_id: value.chain_id.unwrap_or_default(),
4127 ..Default::default()
4128 })
4129 }
4130 }
4131
4132 impl From<LegacyTransaction> for GenericTransaction {
4133 fn from(value: LegacyTransaction) -> Self {
4134 Self {
4135 r#type: TxType::Legacy,
4136 nonce: Some(value.nonce),
4137 to: value.to,
4138 from: Address::default(),
4139 gas: Some(value.gas),
4140 value: value.value,
4141 gas_price: value.gas_price,
4142 max_priority_fee_per_gas: None,
4143 max_fee_per_gas: None,
4144 max_fee_per_blob_gas: None,
4145 access_list: vec![],
4146 fee_token: None,
4147 authorization_list: None,
4148 blob_versioned_hashes: vec![],
4149 blobs: vec![],
4150 wrapper_version: None,
4151 chain_id: None,
4152 input: value.data,
4153 }
4154 }
4155 }
4156
4157 impl From<EIP2930Transaction> for GenericTransaction {
4158 fn from(value: EIP2930Transaction) -> Self {
4159 Self {
4160 r#type: TxType::EIP2930,
4161 nonce: Some(value.nonce),
4162 to: value.to,
4163 from: Address::default(),
4164 gas: Some(value.gas_limit),
4165 value: value.value,
4166 gas_price: value.gas_price,
4167 max_priority_fee_per_gas: None,
4168 max_fee_per_gas: None,
4169 max_fee_per_blob_gas: None,
4170 access_list: value
4171 .access_list
4172 .into_iter()
4173 .map(|(address, storage_keys)| AccessListEntry {
4174 address,
4175 storage_keys,
4176 })
4177 .collect(),
4178 fee_token: None,
4179 authorization_list: None,
4180 blob_versioned_hashes: vec![],
4181 blobs: vec![],
4182 wrapper_version: None,
4183 chain_id: Some(value.chain_id),
4184 input: value.data,
4185 }
4186 }
4187 }
4188
4189 impl From<Transaction> for GenericTransaction {
4190 fn from(value: Transaction) -> Self {
4191 match value {
4192 Transaction::LegacyTransaction(tx) => tx.into(),
4193 Transaction::EIP2930Transaction(tx) => tx.into(),
4194 Transaction::EIP1559Transaction(tx) => tx.into(),
4195 Transaction::EIP4844Transaction(tx) => tx.into(),
4196 Transaction::EIP7702Transaction(tx) => tx.into(),
4197 Transaction::PrivilegedL2Transaction(tx) => tx.into(),
4198 Transaction::FeeTokenTransaction(tx) => tx.into(),
4199 Transaction::FrameTransaction(tx) => tx.into(),
4200 }
4201 }
4202 }
4203
4204 impl From<FrameTransaction> for GenericTransaction {
4205 fn from(value: FrameTransaction) -> Self {
4206 Self {
4207 r#type: TxType::Frame,
4208 nonce: Some(value.nonce),
4209 to: TxKind::Call(value.sender),
4210 from: value.sender,
4211 gas: Some(value.total_gas_limit()),
4212 value: U256::zero(),
4213 gas_price: value.max_fee_per_gas.into(),
4214 max_priority_fee_per_gas: Some(value.max_priority_fee_per_gas),
4215 max_fee_per_gas: Some(value.max_fee_per_gas),
4216 max_fee_per_blob_gas: if value.blob_versioned_hashes.is_empty() {
4217 None
4218 } else {
4219 Some(value.max_fee_per_blob_gas)
4220 },
4221 access_list: vec![],
4222 fee_token: None,
4223 authorization_list: None,
4224 blob_versioned_hashes: value.blob_versioned_hashes,
4225 blobs: vec![],
4226 wrapper_version: None,
4227 chain_id: Some(value.chain_id),
4228 input: Bytes::new(),
4229 }
4230 }
4231 }
4232}
4233
4234mod mempool {
4235 use super::*;
4236 use std::{
4237 cmp::Ordering,
4238 sync::Arc,
4239 time::{SystemTime, UNIX_EPOCH},
4240 };
4241
4242 #[derive(Debug, Clone, PartialEq, Eq)]
4243 pub struct MempoolTransaction {
4244 timestamp: u128,
4246 sender: Address,
4247 inner: Arc<Transaction>,
4248 }
4249
4250 impl MempoolTransaction {
4251 pub fn new(tx: Transaction, sender: Address) -> Self {
4252 Self {
4253 timestamp: SystemTime::now()
4254 .duration_since(UNIX_EPOCH)
4255 .expect("Invalid system time")
4256 .as_micros(),
4257 sender,
4258 inner: Arc::new(tx),
4259 }
4260 }
4261 pub fn time(&self) -> u128 {
4262 self.timestamp
4263 }
4264
4265 pub fn sender(&self) -> Address {
4266 self.sender
4267 }
4268
4269 pub fn transaction(&self) -> &Transaction {
4270 &self.inner
4271 }
4272 }
4273
4274 impl RLPEncode for MempoolTransaction {
4275 fn encode(&self, buf: &mut dyn bytes::BufMut) {
4276 Encoder::new(buf)
4277 .encode_field(&self.timestamp)
4278 .encode_field(&*self.inner)
4279 .finish();
4280 }
4281 }
4282
4283 impl RLPDecode for MempoolTransaction {
4284 fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), RLPDecodeError> {
4285 let decoder = Decoder::new(rlp)?;
4286 let (timestamp, decoder) = decoder.decode_field("timestamp")?;
4287 let (sender, decoder) = decoder.decode_field("sender")?;
4288 let (inner, decoder) = decoder.decode_field("inner")?;
4289 Ok((
4290 Self {
4291 timestamp,
4292 sender,
4293 inner: Arc::new(inner),
4294 },
4295 decoder.finish()?,
4296 ))
4297 }
4298 }
4299
4300 impl std::ops::Deref for MempoolTransaction {
4301 type Target = Transaction;
4302
4303 fn deref(&self) -> &Self::Target {
4304 &self.inner
4305 }
4306 }
4307
4308 impl Ord for MempoolTransaction {
4310 fn cmp(&self, other: &Self) -> Ordering {
4311 match (self.tx_type(), other.tx_type()) {
4312 (TxType::Privileged, TxType::Privileged) => {
4313 return self.nonce().cmp(&other.nonce());
4314 }
4315 (TxType::Privileged, _) => return Ordering::Less,
4316 (_, TxType::Privileged) => return Ordering::Greater,
4317 _ => (),
4318 };
4319 match self.nonce().cmp(&other.nonce()) {
4320 Ordering::Equal => other.time().cmp(&self.time()),
4321 ordering => ordering,
4322 }
4323 }
4324 }
4325
4326 impl PartialOrd for MempoolTransaction {
4327 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
4328 Some(self.cmp(other))
4329 }
4330 }
4331}
4332
4333#[cfg(test)]
4334mod tests {
4335
4336 use super::*;
4337 use crate::types::{
4338 AuthorizationTuple, BlockBody, Receipt, compute_receipts_root, compute_transactions_root,
4339 };
4340 use ethereum_types::H160;
4341 use hex_literal::hex;
4342 use serde_impl::{AccessListEntry, GenericTransaction};
4343 use std::str::FromStr;
4344
4345 #[test]
4346 fn nonce_over_u64_max_maps_to_nonce_is_max() {
4347 let rlp = hex!("ca89010000000000000000");
4350 let decoder = Decoder::new(&rlp).unwrap();
4351 let err = decode_nonce_field(decoder).unwrap_err();
4352 assert!(
4353 err.to_string().contains("Nonce is max"),
4354 "unexpected error: {err}"
4355 );
4356 }
4357
4358 #[test]
4359 fn nonce_at_u64_max_decodes() {
4360 let rlp = hex!("c988ffffffffffffffff");
4362 let decoder = Decoder::new(&rlp).unwrap();
4363 let (nonce, _) = decode_nonce_field(decoder).unwrap();
4364 assert_eq!(nonce, u64::MAX);
4365 }
4366
4367 #[test]
4368 fn legacy_chain_id_handles_out_of_range_v_without_underflow() {
4369 let legacy_chain_id = |v: u64| {
4375 Transaction::LegacyTransaction(LegacyTransaction {
4376 v: U256::from(v),
4377 ..Default::default()
4378 })
4379 .chain_id()
4380 };
4381 for v in [0u64, 1, 26, 27, 28, 34] {
4382 assert_eq!(legacy_chain_id(v), None, "v={v} must yield no chain id");
4383 }
4384 assert_eq!(legacy_chain_id(35), Some(0));
4386 assert_eq!(legacy_chain_id(36), Some(0));
4387 assert_eq!(legacy_chain_id(37), Some(1));
4388 }
4389
4390 #[test]
4391 fn test_compute_transactions_root() {
4392 let mut body = BlockBody::empty();
4393 let tx = LegacyTransaction {
4394 nonce: 0,
4395 gas_price: U256::from(0x0a),
4396 gas: 0x05f5e100,
4397 to: TxKind::Call(hex!("1000000000000000000000000000000000000000").into()),
4398 value: 0.into(),
4399 data: Default::default(),
4400 v: U256::from(0x1b),
4401 r: U256::from_big_endian(&hex!(
4402 "7e09e26678ed4fac08a249ebe8ed680bf9051a5e14ad223e4b2b9d26e0208f37"
4403 )),
4404 s: U256::from_big_endian(&hex!(
4405 "5f6e3f188e3e6eab7d7d3b6568f5eac7d687b08d307d3154ccd8c87b4630509b"
4406 )),
4407 ..Default::default()
4408 };
4409 body.transactions.push(Transaction::LegacyTransaction(tx));
4410 let expected_root =
4411 hex!("8151d548273f6683169524b66ca9fe338b9ce42bc3540046c828fd939ae23bcb");
4412 let result = compute_transactions_root(&body.transactions, ðrex_crypto::NativeCrypto);
4413
4414 assert_eq!(result, expected_root.into());
4415 }
4416 #[test]
4417 fn test_compute_hash() {
4418 let tx_eip2930 = EIP2930Transaction {
4420 chain_id: 3503995874084926u64,
4421 nonce: 7,
4422 gas_price: U256::from(0x2dbf1f9a_u64),
4423 gas_limit: 0x186A0,
4424 to: TxKind::Call(hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into()),
4425 value: 2.into(),
4426 data: Bytes::from(&b"\xdbS\x06$\x8e\x03\x13\xe7emit"[..]),
4427 access_list: vec![(
4428 hex!("7dcd17433742f4c0ca53122ab541d0ba67fc27df").into(),
4429 vec![
4430 hex!("0000000000000000000000000000000000000000000000000000000000000000").into(),
4431 hex!("a3d07a7d68fbd49ec2f8e6befdd86c885f86c272819f6f345f365dec35ae6707").into(),
4432 ],
4433 )],
4434 signature_y_parity: false,
4435 signature_r: U256::from_dec_str(
4436 "75813812796588349127366022588733264074091236448495248199152066031778895768879",
4437 )
4438 .unwrap(),
4439 signature_s: U256::from_dec_str(
4440 "25476208226281085290728123165613764315157904411823916642262684106502155457829",
4441 )
4442 .unwrap(),
4443 ..Default::default()
4444 };
4445 let tx = Transaction::EIP2930Transaction(tx_eip2930);
4446
4447 let expected_hash =
4448 hex!("a0762610d794acddd2dca15fb7c437ada3611c886f3bea675d53d8da8a6c41b2");
4449 let hash = tx.compute_hash(&NativeCrypto);
4450 assert_eq!(hash, expected_hash.into());
4451 }
4452
4453 #[test]
4454 fn test_compute_receipts_root() {
4455 let tx_type = TxType::Legacy;
4458 let succeeded = true;
4459 let cumulative_gas_used = 0x5208;
4460 let logs = vec![];
4461 let receipt = Receipt::new(tx_type, succeeded, cumulative_gas_used, logs);
4462
4463 let result = compute_receipts_root(&[receipt], ðrex_crypto::NativeCrypto);
4464 let expected_root =
4465 hex!("056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2");
4466 assert_eq!(result, expected_root.into());
4467 }
4468
4469 #[test]
4470 fn legacy_tx_rlp_decode() {
4471 let encoded_tx = "f86d80843baa0c4082f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee538000808360306ba0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
4472 let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
4473 let tx = LegacyTransaction::decode(&encoded_tx_bytes).unwrap();
4474 let expected_tx = LegacyTransaction {
4475 nonce: 0,
4476 gas_price: U256::from(1001000000u64),
4477 gas: 63000,
4478 to: TxKind::Call(Address::from_slice(
4479 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4480 )),
4481 value: 3000000000000000_u64.into(),
4482 data: Bytes::new(),
4483 r: U256::from_str_radix(
4484 "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
4485 16,
4486 )
4487 .unwrap(),
4488 s: U256::from_str_radix(
4489 "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
4490 16,
4491 )
4492 .unwrap(),
4493 v: 6303851.into(),
4494 ..Default::default()
4495 };
4496 assert_eq!(tx, expected_tx);
4497 }
4498
4499 #[test]
4500 fn eip1559_tx_rlp_decode() {
4501 let encoded_tx = "f86c8330182480114e82f618946177843db3138ae69679a54b95cf345ed759450d870aa87bee53800080c080a0151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65da064c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4";
4502 let encoded_tx_bytes = hex::decode(encoded_tx).unwrap();
4503 let tx = EIP1559Transaction::decode(&encoded_tx_bytes).unwrap();
4504 let expected_tx = EIP1559Transaction {
4505 nonce: 0,
4506 max_fee_per_gas: 78,
4507 max_priority_fee_per_gas: 17,
4508 to: TxKind::Call(Address::from_slice(
4509 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4510 )),
4511 value: 3000000000000000_u64.into(),
4512 data: Bytes::new(),
4513 signature_r: U256::from_str_radix(
4514 "151ccc02146b9b11adf516e6787b59acae3e76544fdcd75e77e67c6b598ce65d",
4515 16,
4516 )
4517 .unwrap(),
4518 signature_s: U256::from_str_radix(
4519 "64c5dd5aae2fbb535830ebbdad0234975cd7ece3562013b63ea18cc0df6c97d4",
4520 16,
4521 )
4522 .unwrap(),
4523 signature_y_parity: false,
4524 chain_id: 3151908,
4525 gas_limit: 63000,
4526 access_list: vec![],
4527 ..Default::default()
4528 };
4529 assert_eq!(tx, expected_tx);
4530 }
4531
4532 #[test]
4533 fn deserialize_tx_kind() {
4534 let tx_kind_create = r#""""#;
4535 let tx_kind_call = r#""0x6177843db3138ae69679A54b95cf345ED759450d""#;
4536 let deserialized_tx_kind_create = TxKind::Create;
4537 let deserialized_tx_kind_call = TxKind::Call(Address::from_slice(
4538 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4539 ));
4540 assert_eq!(
4541 deserialized_tx_kind_create,
4542 serde_json::from_str(tx_kind_create).unwrap()
4543 );
4544 assert_eq!(
4545 deserialized_tx_kind_call,
4546 serde_json::from_str(tx_kind_call).unwrap()
4547 )
4548 }
4549
4550 #[test]
4551 fn deserialize_tx_type() {
4552 let tx_type_eip2930 = r#""0x01""#;
4553 let tx_type_eip1559 = r#""0x02""#;
4554 let deserialized_tx_type_eip2930 = TxType::EIP2930;
4555 let deserialized_tx_type_eip1559 = TxType::EIP1559;
4556 assert_eq!(
4557 deserialized_tx_type_eip2930,
4558 serde_json::from_str(tx_type_eip2930).unwrap()
4559 );
4560 assert_eq!(
4561 deserialized_tx_type_eip1559,
4562 serde_json::from_str(tx_type_eip1559).unwrap()
4563 )
4564 }
4565
4566 #[test]
4567 fn deserialize_generic_transaction() {
4568 let generic_transaction = r#"{
4569 "type":"0x01",
4570 "nonce":"0x02",
4571 "to":"",
4572 "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
4573 "gas":"0x5208",
4574 "value":"0x01",
4575 "input":"0x010203040506",
4576 "gasPrice":"0x07",
4577 "accessList": [
4578 {
4579 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4580 "storageKeys": [
4581 "0x000000000000000000000000000000000000000000000000000000000000000c",
4582 "0x000000000000000000000000000000000000000000000000000000000000200b"
4583 ]
4584 }
4585 ]
4586 }"#;
4587 let deserialized_generic_transaction = GenericTransaction {
4588 r#type: TxType::EIP2930,
4589 nonce: Some(2),
4590 to: TxKind::Create,
4591 from: Address::from_slice(
4592 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4593 ),
4594 gas: Some(0x5208),
4595 value: U256::from(1),
4596 input: Bytes::from(hex::decode("010203040506").unwrap()),
4597 gas_price: U256::from(7),
4598 max_priority_fee_per_gas: Default::default(),
4599 max_fee_per_gas: Default::default(),
4600 max_fee_per_blob_gas: Default::default(),
4601 access_list: vec![AccessListEntry {
4602 address: Address::from_slice(
4603 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4604 ),
4605 storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4606 }],
4607 fee_token: None,
4608 blob_versioned_hashes: Default::default(),
4609 blobs: Default::default(),
4610 wrapper_version: None,
4611 chain_id: Default::default(),
4612 authorization_list: None,
4613 };
4614 assert_eq!(
4615 deserialized_generic_transaction,
4616 serde_json::from_str(generic_transaction).unwrap()
4617 )
4618 }
4619
4620 #[test]
4621 fn deserialize_generic_transaction_with_data_and_input_fields() {
4622 let generic_transaction = r#"{
4623 "type":"0x01",
4624 "nonce":"0x02",
4625 "to":"",
4626 "from":"0x6177843db3138ae69679A54b95cf345ED759450d",
4627 "gas":"0x5208",
4628 "value":"0x01",
4629 "input":"0x010203040506",
4630 "data":"0x010203040506",
4631 "gasPrice":"0x07",
4632 "accessList": [
4633 {
4634 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4635 "storageKeys": [
4636 "0x000000000000000000000000000000000000000000000000000000000000000c",
4637 "0x000000000000000000000000000000000000000000000000000000000000200b"
4638 ]
4639 }
4640 ]
4641 }"#;
4642 let deserialized_generic_transaction = GenericTransaction {
4643 r#type: TxType::EIP2930,
4644 nonce: Some(2),
4645 to: TxKind::Create,
4646 from: Address::from_slice(
4647 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4648 ),
4649 gas: Some(0x5208),
4650 value: U256::from(1),
4651 input: Bytes::from(hex::decode("010203040506").unwrap()),
4652 gas_price: U256::from(7),
4653 max_priority_fee_per_gas: Default::default(),
4654 max_fee_per_gas: Default::default(),
4655 max_fee_per_blob_gas: Default::default(),
4656 access_list: vec![AccessListEntry {
4657 address: Address::from_slice(
4658 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4659 ),
4660 storage_keys: vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4661 }],
4662 fee_token: None,
4663 blob_versioned_hashes: Default::default(),
4664 blobs: Default::default(),
4665 wrapper_version: None,
4666 chain_id: Default::default(),
4667 authorization_list: None,
4668 };
4669 assert_eq!(
4670 deserialized_generic_transaction,
4671 serde_json::from_str(generic_transaction).unwrap()
4672 )
4673 }
4674
4675 #[test]
4676 fn deserialize_eip4844_transaction() {
4677 let eip4844_transaction = r#"{
4678 "chainId":"0x01",
4679 "nonce":"0x02",
4680 "maxPriorityFeePerGas":"0x01",
4681 "maxFeePerGas":"0x01",
4682 "gas":"0x5208",
4683 "to":"0x6177843db3138ae69679A54b95cf345ED759450d",
4684 "value":"0x01",
4685 "input":"0x3033",
4686 "accessList": [
4687 {
4688 "address": "0x000f3df6d732807ef1319fb7b8bb8522d0beac02",
4689 "storageKeys": [
4690 "0x000000000000000000000000000000000000000000000000000000000000000c",
4691 "0x000000000000000000000000000000000000000000000000000000000000200b"
4692 ]
4693 }
4694 ],
4695 "maxFeePerBlobGas":"0x03",
4696 "blobVersionedHashes": [
4697 "0x0000000000000000000000000000000000000000000000000000000000000001",
4698 "0x0000000000000000000000000000000000000000000000000000000000000002"
4699 ],
4700 "yParity":"0x0",
4701 "r": "0x01",
4702 "s": "0x02"
4703 }"#;
4704 let deserialized_eip4844_transaction = EIP4844Transaction {
4705 chain_id: 0x01,
4706 nonce: 0x02,
4707 to: Address::from_slice(
4708 &hex::decode("6177843db3138ae69679A54b95cf345ED759450d").unwrap(),
4709 ),
4710 max_priority_fee_per_gas: 1,
4711 max_fee_per_gas: 1,
4712 max_fee_per_blob_gas: U256::from(0x03),
4713 gas: 0x5208,
4714 value: U256::from(0x01),
4715 data: Bytes::from_static(b"03"),
4717 access_list: vec![(
4718 Address::from_slice(
4719 &hex::decode("000f3df6d732807ef1319fb7b8bb8522d0beac02").unwrap(),
4720 ),
4721 vec![H256::from_low_u64_be(12), H256::from_low_u64_be(8203)],
4722 )],
4723 blob_versioned_hashes: vec![H256::from_low_u64_be(1), H256::from_low_u64_be(2)],
4724 signature_y_parity: false,
4725 signature_r: U256::from(0x01),
4726 signature_s: U256::from(0x02),
4727 ..Default::default()
4728 };
4729
4730 assert_eq!(
4731 deserialized_eip4844_transaction,
4732 serde_json::from_str(eip4844_transaction).unwrap()
4733 )
4734 }
4735
4736 #[test]
4737 fn serialize_deserialize_transaction() {
4738 let eip1559 = EIP1559Transaction {
4739 chain_id: 65536999,
4740 nonce: 1,
4741 max_priority_fee_per_gas: 1000,
4742 max_fee_per_gas: 2000,
4743 gas_limit: 21000,
4744 to: TxKind::Call(H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap()),
4745 value: U256::from(100000),
4746 data: Bytes::from_static(b"03"),
4747 access_list: vec![(
4748 H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B3").unwrap(),
4749 vec![H256::zero()],
4750 )],
4751 signature_y_parity: true,
4752 signature_r: U256::one(),
4753 signature_s: U256::zero(),
4754 ..Default::default()
4755 };
4756 let tx_to_serialize = Transaction::EIP1559Transaction(eip1559.clone());
4757 let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
4758
4759 let deserialized_tx: Transaction =
4760 serde_json::from_str(&serialized).expect("Failed to deserialize");
4761
4762 assert!(deserialized_tx.tx_type() == TxType::EIP1559);
4763
4764 if let Transaction::EIP1559Transaction(tx) = deserialized_tx {
4765 assert_eq!(tx, eip1559);
4766 }
4767 }
4768
4769 #[test]
4770 fn serialize_deserialize_eip7702transaction() {
4771 let eip7702 = EIP7702Transaction {
4772 chain_id: 65536999,
4773 nonce: 1,
4774 max_priority_fee_per_gas: 1000,
4775 max_fee_per_gas: 2000,
4776 gas_limit: 21000,
4777 to: Address::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B0").unwrap(),
4778 value: U256::from(100000),
4779 data: Bytes::from_static(b"03"),
4780 access_list: vec![],
4781 signature_y_parity: true,
4782 signature_r: U256::one(),
4783 signature_s: U256::zero(),
4784 authorization_list: vec![AuthorizationTuple {
4785 chain_id: U256::from(65536999),
4786 address: H160::from_str("0x000a52D537c4150ec274dcE3962a0d179B7E71B1").unwrap(),
4787 nonce: 2,
4788 y_parity: U256::one(),
4789 r_signature: U256::from(22),
4790 s_signature: U256::from(37),
4791 }],
4792 ..Default::default()
4793 };
4794 let tx_to_serialize = Transaction::EIP7702Transaction(eip7702.clone());
4795 let serialized = serde_json::to_string(&tx_to_serialize).expect("Failed to serialize");
4796
4797 let deserialized_tx: Transaction =
4798 serde_json::from_str(&serialized).expect("Failed to deserialize");
4799
4800 assert!(deserialized_tx.tx_type() == TxType::EIP7702);
4801
4802 if let Transaction::EIP7702Transaction(tx) = deserialized_tx {
4803 assert_eq!(tx, eip7702);
4804 }
4805 }
4806
4807 #[test]
4808 fn serialize_deserialize_privileged_l2_transaction() -> Result<(), RLPDecodeError> {
4809 let privileged_l2 = PrivilegedL2Transaction {
4810 chain_id: 65536999,
4811 nonce: 0,
4812 max_priority_fee_per_gas: 875000000,
4813 max_fee_per_gas: 875000000,
4814 gas_limit: 42000u64,
4815 to: TxKind::Call(
4816 Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
4817 ),
4818 value: U256::from(500000000000000000000000000u128),
4819 data: Bytes::new(),
4820 access_list: vec![],
4821 from: Address::from_str("0x8943545177806ed17b9f23f0a21ee5948ecaa776").unwrap(),
4822 ..Default::default()
4823 };
4824
4825 let encoded = PrivilegedL2Transaction::encode_to_vec(&privileged_l2);
4826 println!("encoded length: {}", encoded.len());
4827 assert_eq!(encoded.len(), privileged_l2.length());
4828
4829 let deserialized_tx = PrivilegedL2Transaction::decode(&encoded)?;
4830
4831 assert_eq!(deserialized_tx, privileged_l2);
4832
4833 Ok(())
4834 }
4835
4836 #[test]
4837 fn test_legacy_transaction_into_generic() {
4838 let legacy_tx = LegacyTransaction {
4839 nonce: 1,
4840 gas_price: U256::from(20_000_000_000u64),
4841 gas: 21000,
4842 to: TxKind::Call(
4843 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4844 ),
4845 value: U256::from(1_000_000_000_000_000_000u64),
4846 data: Bytes::default(),
4847 v: U256::from(27),
4848 r: U256::from(1),
4849 s: U256::from(1),
4850 ..Default::default()
4851 };
4852
4853 let generic_tx: GenericTransaction = legacy_tx.into();
4854 assert_eq!(generic_tx.r#type, TxType::Legacy);
4855 assert_eq!(generic_tx.nonce, Some(1));
4856 assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
4857 assert_eq!(generic_tx.gas, Some(21000));
4858 assert_eq!(generic_tx.max_priority_fee_per_gas, None);
4859 assert_eq!(generic_tx.max_fee_per_gas, None);
4860 assert_eq!(generic_tx.access_list.len(), 0);
4861 assert_eq!(generic_tx.chain_id, None);
4862 }
4863
4864 #[test]
4865 fn test_eip2930_transaction_into_generic() {
4866 let access_list = vec![(
4867 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4868 vec![
4869 H256::from_str(
4870 "0x1234567890123456789012345678901234567890123456789012345678901234",
4871 )
4872 .unwrap(),
4873 ],
4874 )];
4875
4876 let eip2930_tx = EIP2930Transaction {
4877 chain_id: 1,
4878 nonce: 1,
4879 gas_price: U256::from(20_000_000_000u64),
4880 gas_limit: 21000,
4881 to: TxKind::Call(
4882 Address::from_str("0x742d35Cc6634C0532925a3b844Bc454e4438f44e").unwrap(),
4883 ),
4884 value: U256::from(1_000_000_000_000_000_000u64),
4885 data: Bytes::default(),
4886 access_list: access_list.clone(),
4887 signature_y_parity: false,
4888 signature_r: U256::from(1),
4889 signature_s: U256::from(1),
4890 ..Default::default()
4891 };
4892
4893 let generic_tx: GenericTransaction = eip2930_tx.into();
4894 assert_eq!(generic_tx.r#type, TxType::EIP2930);
4895 assert_eq!(generic_tx.nonce, Some(1));
4896 assert_eq!(generic_tx.gas_price, U256::from(20_000_000_000u64));
4897 assert_eq!(generic_tx.gas, Some(21000));
4898 assert_eq!(generic_tx.max_priority_fee_per_gas, None);
4899 assert_eq!(generic_tx.max_fee_per_gas, None);
4900 assert_eq!(generic_tx.chain_id, Some(1));
4901 assert_eq!(generic_tx.access_list.len(), 1);
4902 assert_eq!(generic_tx.access_list[0].address, access_list[0].0);
4903 assert_eq!(generic_tx.access_list[0].storage_keys, access_list[0].1);
4904 }
4905
4906 #[test]
4907 fn recover_address_rejects_high_s_signatures() {
4908 use ethrex_crypto::NativeCrypto;
4909 use k256::ecdsa::SigningKey;
4910
4911 let crypto = NativeCrypto;
4912
4913 let private_key = hex!("4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318");
4916 let signing_key = SigningKey::from_bytes(&private_key.into()).expect("Valid private key");
4917
4918 let msg = b"Test message for high-s signature rejection";
4920 let payload = keccak(msg).to_fixed_bytes();
4922
4923 let (signature, recovery_id) = signing_key
4926 .sign_prehash_recoverable(&payload)
4927 .expect("Signing failed");
4928
4929 let mut sig_bytes = [0u8; 65];
4931 sig_bytes[..64].copy_from_slice(&signature.to_bytes());
4932 sig_bytes[64] = recovery_id.to_byte();
4933
4934 let uncompressed_pub = signing_key.verifying_key().to_encoded_point(false);
4937 let pub_hash = ethrex_crypto::keccak::keccak_hash(&uncompressed_pub.as_bytes()[1..]);
4938 let expected_address = Address::from_slice(&pub_hash[12..]);
4939
4940 let recovered = crypto
4941 .recover_signer(&sig_bytes, &payload)
4942 .expect("Valid low-s signature should recover successfully");
4943 assert_eq!(recovered, expected_address, "Recovered address mismatch");
4944
4945 let n = U256::from_big_endian(&hex!(
4948 "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"
4949 ));
4950 let s = U256::from_big_endian(&sig_bytes[32..64]);
4951
4952 let half_n = n / 2;
4954 assert!(
4955 s <= half_n,
4956 "Generated signature was not low-s, cannot test high-s rejection"
4957 );
4958
4959 let s_high = n - s;
4961
4962 let mut sig_high_bytes = sig_bytes;
4963 sig_high_bytes[32..64].copy_from_slice(&s_high.to_big_endian());
4965 sig_high_bytes[64] ^= 1;
4967
4968 assert!(
4971 crypto.recover_signer(&sig_high_bytes, &payload).is_err(),
4972 "High-s signature should be rejected (EIP-2 compliance)"
4973 );
4974 }
4975
4976 #[test]
4977 fn encode_decode_low_size_tx() {
4978 let tx = Transaction::EIP2930Transaction(EIP2930Transaction::default());
4979 let tx_to_encode = tx.clone();
4982 let encoded = tx_to_encode.encode_to_vec();
4983 let decoded_tx = Transaction::decode(&encoded).unwrap();
4984 assert_eq!(tx, decoded_tx);
4985 }
4986
4987 #[test]
4988 fn test_eip1559_simple_transfer_size() {
4989 let tx = Transaction::EIP1559Transaction(EIP1559Transaction::default());
4990 assert_eq!(tx.encode_to_vec().len(), EIP1559_DEFAULT_SERIALIZED_LENGTH);
4991 }
4992
4993 fn make_test_frame_tx() -> FrameTransaction {
4996 FrameTransaction {
4997 chain_id: 1,
4998 nonce: 42,
4999 sender: Address::from_low_u64_be(0xABCD),
5000 frames: vec![
5001 Frame {
5002 mode: FrameMode::Verify as u8,
5003 flags: 0x03, target: Some(Address::from_low_u64_be(0xABCD)),
5005 gas_limit: 100_000,
5006 value: U256::zero(),
5007 data: Bytes::from_static(b"verify_data"),
5008 },
5009 Frame {
5010 mode: FrameMode::Sender as u8,
5011 flags: 0x00,
5012 target: Some(Address::from_low_u64_be(0x1234)),
5013 gas_limit: 200_000,
5014 value: U256::zero(),
5015 data: Bytes::from_static(b"call_data"),
5016 },
5017 ],
5018 signatures: vec![FrameSignature {
5019 scheme: FRAME_SIG_SCHEME_SECP256K1,
5020 signer: Some(Address::from_low_u64_be(0xABCD)),
5021 msg: Bytes::new(),
5022 signature: Bytes::from(vec![0u8; 65]),
5023 }],
5024 max_priority_fee_per_gas: 1_000_000_000,
5025 max_fee_per_gas: 30_000_000_000,
5026 max_fee_per_blob_gas: U256::zero(),
5027 blob_versioned_hashes: vec![],
5028 inner_hash: OnceCell::new(),
5029 cached_canonical: OnceCell::new(),
5030 }
5031 }
5032
5033 #[test]
5034 fn atomic_batch_flag_valid_on_default_and_sender_frames() {
5035 let mut tx = make_test_frame_tx();
5038 tx.frames = vec![
5039 Frame {
5040 mode: FrameMode::Default as u8,
5041 flags: 0x04, target: Some(Address::from_low_u64_be(0xB0B)),
5043 gas_limit: 21_000,
5044 value: U256::zero(),
5045 data: Bytes::new(),
5046 },
5047 Frame {
5048 mode: FrameMode::Sender as u8,
5049 flags: 0x04, target: Some(Address::from_low_u64_be(0xB0B)),
5051 gas_limit: 21_000,
5052 value: U256::zero(),
5053 data: Bytes::new(),
5054 },
5055 Frame {
5056 mode: FrameMode::Sender as u8,
5057 flags: 0x00, target: Some(Address::from_low_u64_be(0xCAFE)),
5059 gas_limit: 21_000,
5060 value: U256::zero(),
5061 data: Bytes::new(),
5062 },
5063 ];
5064 assert!(tx.validate_static_constraints().is_ok());
5065 }
5066
5067 #[test]
5068 fn atomic_batch_flag_on_last_frame_still_invalid() {
5069 let mut tx = make_test_frame_tx();
5070 tx.frames = vec![Frame {
5071 mode: FrameMode::Sender as u8,
5072 flags: 0x04,
5073 target: Some(Address::from_low_u64_be(0xCAFE)),
5074 gas_limit: 21_000,
5075 value: U256::zero(),
5076 data: Bytes::new(),
5077 }];
5078 let err = tx.validate_static_constraints().unwrap_err();
5079 assert!(err.contains("atomic batch flag on last frame"), "{err}");
5080 }
5081
5082 #[test]
5083 fn test_frame_rlp_roundtrip() {
5084 let frame = Frame {
5085 mode: FrameMode::Verify as u8,
5086 flags: 0x03,
5087 target: Some(Address::from_low_u64_be(0x1234)),
5088 gas_limit: 50_000,
5089 value: U256::zero(),
5090 data: Bytes::from_static(b"hello"),
5091 };
5092 let encoded = frame.encode_to_vec();
5093 let decoded = Frame::decode(&encoded).unwrap();
5094 assert_eq!(frame, decoded);
5095 }
5096
5097 #[test]
5098 fn test_frame_null_target_rlp_roundtrip() {
5099 let frame = Frame {
5100 mode: FrameMode::Default as u8,
5101 flags: 0x00,
5102 target: None,
5103 gas_limit: 10_000,
5104 value: U256::zero(),
5105 data: Bytes::from_static(b"deploy"),
5106 };
5107 let encoded = frame.encode_to_vec();
5108 let decoded = Frame::decode(&encoded).unwrap();
5109 assert_eq!(frame, decoded);
5110 }
5111
5112 #[test]
5113 fn test_frame_mode_with_flags_rlp_roundtrip() {
5114 for mode_val in [0u8, 1, 2] {
5116 let frame = Frame {
5117 mode: mode_val,
5118 flags: if mode_val == 1 { 0x03 } else { 0x00 },
5119 target: Some(Address::from_low_u64_be(0x1234)),
5120 gas_limit: 50_000,
5121 value: U256::zero(),
5122 data: Bytes::new(),
5123 };
5124 let encoded = frame.encode_to_vec();
5125 let decoded = Frame::decode(&encoded).unwrap();
5126 assert_eq!(frame, decoded);
5127 }
5128 let frame = Frame {
5130 mode: 2, flags: 0x01 | 0x04, target: Some(Address::from_low_u64_be(0x1234)),
5133 gas_limit: 50_000,
5134 value: U256::zero(),
5135 data: Bytes::new(),
5136 };
5137 assert_eq!(frame.execution_mode(), FrameMode::Sender);
5138 assert_eq!(frame.scope_restriction(), 1);
5139 assert!(frame.is_atomic_batch());
5140 let encoded = frame.encode_to_vec();
5141 let decoded = Frame::decode(&encoded).unwrap();
5142 assert_eq!(frame, decoded);
5143 }
5144
5145 #[test]
5146 fn frame_rlp_roundtrip_preserves_value() {
5147 let frame = Frame {
5148 mode: FrameMode::Sender as u8,
5149 flags: 0x00,
5150 target: Some(Address::from_low_u64_be(0xCAFE)),
5151 gas_limit: 100_000,
5152 value: U256::from(1_000_000_000_000_000u64), data: Bytes::from_static(b"hello"),
5154 };
5155 let encoded = frame.encode_to_vec();
5156 let decoded = Frame::decode(&encoded).unwrap();
5157 assert_eq!(frame, decoded);
5158 assert_eq!(decoded.value, U256::from(1_000_000_000_000_000u64));
5159 }
5160
5161 #[test]
5162 fn test_frame_transaction_rlp_roundtrip() {
5163 let tx = make_test_frame_tx();
5164 let encoded = tx.encode_to_vec();
5165 let decoded = FrameTransaction::decode(&encoded).unwrap();
5166 assert_eq!(tx, decoded);
5167 }
5168
5169 #[test]
5170 fn test_frame_transaction_canonical_roundtrip() {
5171 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5172 let encoded = tx.encode_canonical_to_vec();
5173 let decoded = Transaction::decode_canonical(&encoded).unwrap();
5174 assert_eq!(
5176 tx.hash(ðrex_crypto::NativeCrypto),
5177 decoded.hash(ðrex_crypto::NativeCrypto)
5178 );
5179 assert_eq!(tx.tx_type(), decoded.tx_type());
5181 assert_eq!(tx.nonce(), decoded.nonce());
5182 assert_eq!(tx.chain_id(), decoded.chain_id());
5183 }
5184
5185 #[test]
5186 fn test_frame_transaction_sig_hash_covers_all_frame_data() {
5187 let tx = make_test_frame_tx();
5189 let hash1 = tx.compute_sig_hash();
5190
5191 let mut tx2 = tx.clone();
5193 tx2.frames[0].data = Bytes::from_static(b"completely_different_verify_data");
5194 let hash2 = tx2.compute_sig_hash();
5195 assert_ne!(hash1, hash2);
5196
5197 let mut tx3 = tx;
5199 tx3.frames[1].data = Bytes::from_static(b"different_call_data");
5200 let hash3 = tx3.compute_sig_hash();
5201 assert_ne!(hash1, hash3);
5202 }
5203
5204 #[test]
5205 fn test_frame_transaction_accessor_methods() {
5206 let frame_tx = make_test_frame_tx();
5207 let tx = Transaction::FrameTransaction(frame_tx);
5208
5209 assert_eq!(tx.tx_type(), TxType::Frame);
5210 assert_eq!(tx.nonce(), 42);
5211 assert_eq!(tx.chain_id(), Some(1));
5212 assert_eq!(tx.to(), TxKind::Call(Address::from_low_u64_be(0xABCD)));
5213 assert_eq!(tx.value(), U256::zero());
5214 assert_eq!(tx.data(), &Bytes::new());
5215 assert!(tx.access_list().is_empty());
5216 assert!(tx.authorization_list().is_none());
5217 assert_eq!(tx.max_priority_fee(), Some(1_000_000_000));
5218 assert_eq!(tx.max_fee_per_gas(), Some(30_000_000_000));
5219 assert_eq!(tx.max_fee_per_blob_gas(), None); assert!(!tx.is_contract_creation());
5221 assert_eq!(
5223 tx.sender(ðrex_crypto::NativeCrypto).unwrap(),
5224 Address::from_low_u64_be(0xABCD)
5225 );
5226 assert!(tx.gas_limit() >= 300_000); }
5229
5230 #[test]
5231 fn test_frame_transaction_hash_uses_oncecell() {
5232 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5233 let h1 = tx.hash(ðrex_crypto::NativeCrypto);
5234 let h2 = tx.hash(ðrex_crypto::NativeCrypto);
5235 assert_eq!(h1, h2);
5236 }
5237
5238 #[test]
5239 fn frame_transaction_rlp_roundtrip_preserves_fields() {
5240 let tx = make_test_frame_tx();
5241 let mut buf = Vec::new();
5242 tx.encode(&mut buf);
5243 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5244 assert!(rest.is_empty());
5245 assert_eq!(decoded.chain_id, tx.chain_id);
5246 assert_eq!(decoded.nonce, tx.nonce);
5247 assert_eq!(decoded.sender, tx.sender);
5248 }
5249
5250 #[test]
5251 fn frame_transaction_variant_is_exposed_on_transaction_enum() {
5252 let tx = Transaction::FrameTransaction(make_test_frame_tx());
5253 assert!(matches!(tx, Transaction::FrameTransaction(_)));
5254 }
5255
5256 #[test]
5257 fn frame_transaction_serializes_signatures_as_array() {
5258 let tx = make_test_frame_tx();
5260 let json = serde_json::to_value(&tx).unwrap();
5261 let sigs = json
5262 .get("signatures")
5263 .expect("signatures field present")
5264 .as_array()
5265 .expect("signatures serialized as an array");
5266 assert_eq!(sigs.len(), tx.signatures.len());
5267 assert_eq!(sigs[0].get("scheme").unwrap(), "0x1");
5268 assert!(sigs[0].get("signer").is_some());
5269 assert!(sigs[0].get("signature").is_some());
5270 assert!(sigs[0].get("msg").is_some());
5271 }
5272
5273 fn make_frame_tx_with_gas_limits(limits: Vec<u64>) -> FrameTransaction {
5274 let frames = limits
5275 .into_iter()
5276 .map(|gl| Frame {
5277 mode: FrameMode::Sender as u8,
5278 flags: 0x00,
5279 target: Some(Address::from_low_u64_be(0x1234)),
5280 gas_limit: gl,
5281 value: U256::zero(),
5282 data: Bytes::new(),
5283 })
5284 .collect();
5285 FrameTransaction {
5286 chain_id: 1,
5287 nonce: 0,
5288 sender: Address::from_low_u64_be(0xABCD),
5289 frames,
5290 signatures: vec![],
5291 max_priority_fee_per_gas: 1_000_000_000,
5292 max_fee_per_gas: 30_000_000_000,
5293 max_fee_per_blob_gas: U256::zero(),
5294 blob_versioned_hashes: vec![],
5295 inner_hash: OnceCell::new(),
5296 cached_canonical: OnceCell::new(),
5297 }
5298 }
5299
5300 #[test]
5301 fn per_frame_gas_limit_above_i64_max_is_rejected() {
5302 let tx = make_frame_tx_with_gas_limits(vec![(i64::MAX as u64) + 1]);
5303 let err = tx.validate_static_constraints().unwrap_err();
5304 assert!(err.contains("exceeds 2**63-1"), "unexpected error: {err}");
5305 }
5306
5307 #[test]
5308 fn cumulative_frame_gas_limit_above_i64_max_is_rejected() {
5309 let half = (i64::MAX as u64) / 2 + 1;
5310 let tx = make_frame_tx_with_gas_limits(vec![half, half]);
5311 let err = tx.validate_static_constraints().unwrap_err();
5312 assert!(err.contains("cumulative"), "unexpected error: {err}");
5313 }
5314
5315 #[test]
5316 fn cumulative_frame_gas_limit_equal_to_i64_max_is_accepted() {
5317 let a = (i64::MAX as u64) / 2;
5318 let b = i64::MAX as u64 - a;
5319 let tx = make_frame_tx_with_gas_limits(vec![a, b]);
5320 tx.validate_static_constraints()
5321 .expect("exact i64::MAX total should be accepted");
5322 }
5323
5324 #[test]
5325 fn empty_frames_list_is_rejected_by_count_check_not_gas_check() {
5326 let tx = make_frame_tx_with_gas_limits(vec![]);
5329 let err = tx.validate_static_constraints().unwrap_err();
5330 assert!(err.contains("between 1 and"), "unexpected error: {err}");
5331 }
5332
5333 #[test]
5334 fn sig_hash_covers_frame_value() {
5335 let tx = make_test_frame_tx();
5338 let baseline = tx.compute_sig_hash();
5339
5340 let mut with_sender_value = tx.clone();
5341 with_sender_value.frames[1].value = U256::from(1u64);
5342 assert_ne!(
5343 baseline,
5344 with_sender_value.compute_sig_hash(),
5345 "sig_hash must change when a SENDER frame's value changes"
5346 );
5347
5348 let mut with_verify_value = tx.clone();
5349 with_verify_value.frames[0].value = U256::from(1u64);
5350 assert_ne!(
5351 baseline,
5352 with_verify_value.compute_sig_hash(),
5353 "sig_hash must change when a VERIFY frame's value changes"
5354 );
5355
5356 let mut with_verify_data = tx;
5358 with_verify_data.frames[0].data = Bytes::from_static(b"different_verify_data");
5359 assert_ne!(
5360 baseline,
5361 with_verify_data.compute_sig_hash(),
5362 "VERIFY.data is now covered by sig_hash (no longer elided)"
5363 );
5364 }
5365
5366 #[test]
5367 fn validate_static_constraints_rejects_nonzero_value_on_non_sender_frames() {
5368 let verify_tx = FrameTransaction {
5370 chain_id: 1,
5371 nonce: 0,
5372 sender: Address::from_low_u64_be(0xABCD),
5373 frames: vec![Frame {
5374 mode: FrameMode::Verify as u8,
5375 flags: 0x01,
5376 target: None,
5377 gas_limit: 50_000,
5378 value: U256::from(1u64),
5379 data: Bytes::new(),
5380 }],
5381 signatures: vec![],
5382 max_priority_fee_per_gas: 1_000_000_000,
5383 max_fee_per_gas: 30_000_000_000,
5384 max_fee_per_blob_gas: U256::zero(),
5385 blob_versioned_hashes: vec![],
5386 inner_hash: OnceCell::new(),
5387 cached_canonical: OnceCell::new(),
5388 };
5389 let err = verify_tx.validate_static_constraints().unwrap_err();
5390 assert!(
5391 err.contains("non-zero value only allowed in SENDER mode"),
5392 "unexpected error for VERIFY: {err}"
5393 );
5394
5395 let default_tx = FrameTransaction {
5397 frames: vec![Frame {
5398 mode: FrameMode::Default as u8,
5399 flags: 0x00,
5400 target: Some(Address::from_low_u64_be(0x1234)),
5401 gas_limit: 50_000,
5402 value: U256::from(1u64),
5403 data: Bytes::new(),
5404 }],
5405 ..verify_tx
5406 };
5407 let err = default_tx.validate_static_constraints().unwrap_err();
5408 assert!(
5409 err.contains("non-zero value only allowed in SENDER mode"),
5410 "unexpected error for DEFAULT: {err}"
5411 );
5412
5413 let sender_tx = FrameTransaction {
5415 frames: vec![Frame {
5416 mode: FrameMode::Sender as u8,
5417 flags: 0x00,
5418 target: Some(Address::from_low_u64_be(0x1234)),
5419 gas_limit: 50_000,
5420 value: U256::from(1u64),
5421 data: Bytes::new(),
5422 }],
5423 ..default_tx
5424 };
5425 sender_tx
5426 .validate_static_constraints()
5427 .expect("SENDER frames may carry non-zero value");
5428 }
5429
5430 fn chain_config_with_hegota(hegota_time: Option<u64>) -> crate::types::ChainConfig {
5433 crate::types::ChainConfig {
5434 hegota_time,
5435 ..Default::default()
5436 }
5437 }
5438
5439 #[test]
5440 fn test_frame_tx_pre_fork_chain_config_rejects() {
5441 let cfg = chain_config_with_hegota(None);
5442 assert!(!cfg.is_hegota_activated(0));
5443 assert!(!cfg.is_hegota_activated(u64::MAX));
5444 }
5445
5446 #[test]
5447 fn test_frame_tx_post_fork_admits() {
5448 let cfg = chain_config_with_hegota(Some(1000));
5449 assert!(cfg.is_hegota_activated(2000));
5450 }
5451
5452 #[test]
5453 fn test_frame_tx_fork_boundary_admits() {
5454 let activation_time = 1_700_000_000u64;
5455 let cfg = chain_config_with_hegota(Some(activation_time));
5456 assert!(cfg.is_hegota_activated(activation_time));
5457 assert!(!cfg.is_hegota_activated(activation_time - 1));
5458 }
5459
5460 #[test]
5461 fn test_frame_tx_devnet_fork_epoch_zero_admits() {
5462 let cfg = chain_config_with_hegota(Some(0));
5463 assert!(cfg.is_hegota_activated(0));
5464 assert!(cfg.is_hegota_activated(1));
5465 assert!(cfg.is_hegota_activated(u64::MAX));
5466 }
5467
5468 #[test]
5469 fn test_frame_tx_pre_fork_rlp_roundtrip_still_decodes() {
5470 let tx = make_test_frame_tx();
5473 let mut buf = Vec::new();
5474 tx.encode(&mut buf);
5475 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5476 assert!(rest.is_empty());
5477 assert_eq!(decoded.chain_id, tx.chain_id);
5478 assert_eq!(decoded.nonce, tx.nonce);
5479 assert_eq!(decoded.sender, tx.sender);
5480 }
5481
5482 fn expiry_frame(deadline: u64) -> Frame {
5485 Frame {
5486 mode: FrameMode::Verify as u8,
5487 flags: 0x00,
5488 target: Some(frame_tx_expiry_verifier()),
5489 gas_limit: 30_000,
5490 value: U256::zero(),
5491 data: Bytes::copy_from_slice(&deadline.to_be_bytes()),
5492 }
5493 }
5494
5495 #[test]
5496 fn expiry_verifier_frame_passes_static_validation() {
5497 let mut tx = make_test_frame_tx();
5498 tx.frames.insert(0, expiry_frame(1_700_000_000));
5499 assert!(tx.validate_static_constraints().is_ok());
5500 }
5501
5502 #[test]
5503 fn expiry_verifier_frame_rejects_bad_data_length() {
5504 let mut tx = make_test_frame_tx();
5505 let mut f = expiry_frame(0);
5506 f.data = Bytes::from_static(b"short");
5507 tx.frames.insert(0, f);
5508 let err = tx.validate_static_constraints().unwrap_err();
5509 assert!(err.contains("8 bytes"), "{err}");
5510 }
5511
5512 #[test]
5513 fn expiry_verifier_frame_rejects_nonzero_flags() {
5514 let mut tx = make_test_frame_tx();
5515 let mut f = expiry_frame(0);
5516 f.flags = 0x01;
5517 tx.frames.insert(0, f);
5518 let err = tx.validate_static_constraints().unwrap_err();
5519 assert!(err.contains("flags == 0"), "{err}");
5520 }
5521
5522 #[test]
5523 fn at_most_one_expiry_verifier_frame() {
5524 let mut tx = make_test_frame_tx();
5525 tx.frames.insert(0, expiry_frame(1));
5526 tx.frames.insert(0, expiry_frame(2));
5527 let err = tx.validate_static_constraints().unwrap_err();
5528 assert!(err.contains("more than one expiry"), "{err}");
5529 }
5530
5531 #[test]
5532 fn verify_frame_with_zero_scope_is_now_statically_valid() {
5533 let mut tx = make_test_frame_tx();
5538 tx.frames[0].flags = 0x00;
5539 assert!(tx.validate_static_constraints().is_ok());
5540 }
5541
5542 #[test]
5543 fn sig_hash_covers_expiry_deadline_and_all_verify_data() {
5544 let mut tx_a = make_test_frame_tx();
5546 tx_a.frames.insert(0, expiry_frame(100));
5547 let mut tx_b = make_test_frame_tx();
5548 tx_b.frames.insert(0, expiry_frame(200));
5549 assert_ne!(tx_a.compute_sig_hash(), tx_b.compute_sig_hash());
5551
5552 let tx_c = make_test_frame_tx();
5554 let mut tx_d = make_test_frame_tx();
5555 tx_d.frames[0].data = Bytes::from_static(b"different_verify_data");
5556 assert_ne!(tx_c.compute_sig_hash(), tx_d.compute_sig_hash());
5557 }
5558
5559 #[test]
5560 fn expiry_deadline_parses_or_none() {
5561 let mut tx = make_test_frame_tx();
5562 assert_eq!(tx.expiry_deadline(), None);
5563 tx.frames.insert(0, expiry_frame(1_700_000_123));
5564 assert_eq!(tx.expiry_deadline(), Some(1_700_000_123));
5565 }
5566
5567 #[test]
5568 fn signature_rlp_roundtrip() {
5569 let sig = FrameSignature {
5570 scheme: FRAME_SIG_SCHEME_P256,
5571 signer: Some(Address::from_low_u64_be(0x1234)),
5572 msg: Bytes::from(vec![7u8; 32]),
5573 signature: Bytes::from(vec![9u8; 128]),
5574 };
5575 let mut buf = Vec::new();
5576 sig.encode(&mut buf);
5577 let (decoded, rest) = FrameSignature::decode_unfinished(&buf).unwrap();
5578 assert!(rest.is_empty());
5579 assert_eq!(decoded, sig);
5580 }
5581
5582 #[test]
5583 fn frame_tx_with_signatures_rlp_roundtrip() {
5584 let tx = make_test_frame_tx();
5585 assert!(!tx.signatures.is_empty());
5586 let mut buf = Vec::new();
5587 tx.encode(&mut buf);
5588 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5589 assert!(rest.is_empty());
5590 assert_eq!(decoded.signatures, tx.signatures);
5591 assert_eq!(decoded.frames, tx.frames);
5592 assert_eq!(decoded.nonce, tx.nonce);
5593 }
5594
5595 #[test]
5596 fn p2p_frame_transaction_rlp_roundtrip() {
5597 let ft = make_test_frame_tx();
5600 assert!(!ft.signatures.is_empty());
5601 assert!(!ft.frames.is_empty());
5602 let original = P2PTransaction::FrameTransaction(ft);
5603
5604 let encoded = original.encode_to_vec();
5605 let (decoded, rest) = P2PTransaction::decode_unfinished(&encoded).unwrap();
5606 assert!(rest.is_empty());
5607 assert_eq!(decoded, original);
5608 assert_eq!(decoded.tx_type(), TxType::Frame);
5609
5610 let as_tx: Transaction = decoded.try_into().unwrap();
5613 assert!(matches!(as_tx, Transaction::FrameTransaction(_)));
5614 }
5615
5616 #[test]
5617 fn static_validation_rejects_unknown_scheme() {
5618 let mut tx = make_test_frame_tx();
5619 tx.signatures[0].scheme = 3;
5620 assert!(
5621 tx.validate_static_constraints()
5622 .unwrap_err()
5623 .contains("unsupported scheme"),
5624 );
5625 }
5626
5627 #[test]
5628 fn static_validation_accepts_arbitrary_with_empty_signer() {
5629 let mut tx = make_test_frame_tx();
5630 tx.signatures[0].scheme = FRAME_SIG_SCHEME_ARBITRARY;
5632 tx.signatures[0].signer = None;
5633 assert!(tx.validate_static_constraints().is_ok());
5634 assert_eq!(tx.signature_verification_cost(), 100);
5635 }
5636
5637 #[test]
5638 fn static_validation_rejects_arbitrary_with_signer() {
5639 let mut tx = make_test_frame_tx();
5640 tx.signatures[0].scheme = FRAME_SIG_SCHEME_ARBITRARY;
5641 tx.signatures[0].signer = Some(Address::from_low_u64_be(0xABCD));
5642 assert!(
5643 tx.validate_static_constraints()
5644 .unwrap_err()
5645 .contains("ARBITRARY signatures must not name a signer"),
5646 );
5647 }
5648
5649 #[test]
5650 fn static_validation_rejects_bad_msg_length() {
5651 let mut tx = make_test_frame_tx();
5652 tx.signatures[0].msg = Bytes::from(vec![1u8; 16]);
5653 assert!(
5654 tx.validate_static_constraints()
5655 .unwrap_err()
5656 .contains("32 bytes"),
5657 );
5658 }
5659
5660 #[test]
5661 fn static_validation_rejects_zero_explicit_msg() {
5662 let mut tx = make_test_frame_tx();
5663 tx.signatures[0].msg = Bytes::from(vec![0u8; 32]);
5664 assert!(
5665 tx.validate_static_constraints()
5666 .unwrap_err()
5667 .contains("zero digest"),
5668 );
5669 }
5670
5671 #[test]
5672 fn sig_hash_elides_empty_msg_signature_bytes() {
5673 let mut a = make_test_frame_tx();
5674 let mut b = make_test_frame_tx();
5675 assert!(a.signatures[0].msg.is_empty());
5677 a.signatures[0].signature = Bytes::from(vec![1u8; 65]);
5678 b.signatures[0].signature = Bytes::from(vec![2u8; 65]);
5679 assert_eq!(a.compute_sig_hash(), b.compute_sig_hash());
5680 }
5681
5682 #[test]
5683 fn sig_hash_covers_frame_data_now() {
5684 let mut a = make_test_frame_tx();
5685 let mut b = make_test_frame_tx();
5686 a.frames[0].data = Bytes::from_static(b"aaaa");
5688 b.frames[0].data = Bytes::from_static(b"bbbb");
5689 assert_ne!(a.compute_sig_hash(), b.compute_sig_hash());
5690 }
5691
5692 #[test]
5693 fn sig_hash_covers_explicit_msg_signature_bytes() {
5694 let mut a = make_test_frame_tx();
5695 let mut b = make_test_frame_tx();
5696 a.signatures[0].msg = Bytes::from(vec![9u8; 32]);
5697 b.signatures[0].msg = Bytes::from(vec![9u8; 32]);
5698 a.signatures[0].signature = Bytes::from(vec![1u8; 65]);
5699 b.signatures[0].signature = Bytes::from(vec![2u8; 65]);
5700 assert_ne!(a.compute_sig_hash(), b.compute_sig_hash());
5702 }
5703
5704 #[test]
5705 fn total_gas_limit_includes_signature_costs() {
5706 let mut tx = make_test_frame_tx();
5707 let base = tx.total_gas_limit();
5708 tx.signatures.push(FrameSignature {
5710 scheme: FRAME_SIG_SCHEME_P256,
5711 signer: Some(Address::from_low_u64_be(1)),
5712 msg: Bytes::new(),
5713 signature: Bytes::from(vec![0u8; 128]),
5714 });
5715 assert!(tx.total_gas_limit() >= base + 6700);
5716 assert_eq!(tx.signature_verification_cost(), 2800 + 6700);
5717 }
5718
5719 #[test]
5720 fn golden_frame_tx_rlp_and_sig_hash() {
5721 let tx = FrameTransaction {
5726 chain_id: 1,
5727 nonce: 7,
5728 sender: Address::from_low_u64_be(0xABCD),
5729 frames: vec![
5730 Frame {
5731 mode: 1,
5732 flags: 3,
5733 target: None,
5734 gas_limit: 0x5208,
5735 value: U256::zero(),
5736 data: Bytes::from_static(&[0x11, 0x22]),
5737 },
5738 Frame {
5739 mode: 2,
5740 flags: 0,
5741 target: Some(Address::from_low_u64_be(0x1234)),
5742 gas_limit: 0x9c40,
5743 value: U256::zero(),
5744 data: Bytes::new(),
5745 },
5746 ],
5747 signatures: vec![FrameSignature {
5748 scheme: FRAME_SIG_SCHEME_SECP256K1,
5749 signer: Some(Address::from_low_u64_be(0xABCD)),
5750 msg: Bytes::new(),
5751 signature: Bytes::from(vec![0x01u8; 65]),
5752 }],
5753 max_priority_fee_per_gas: 0x3b9aca00,
5754 max_fee_per_gas: 0x6fc23ac00,
5755 max_fee_per_blob_gas: U256::zero(),
5756 blob_versioned_hashes: vec![],
5757 inner_hash: OnceCell::new(),
5758 cached_canonical: OnceCell::new(),
5759 };
5760
5761 let mut buf = Vec::new();
5762 tx.encode(&mut buf);
5763 let rlp_hex = hex::encode(&buf);
5764 assert_eq!(
5766 rlp_hex,
5767 "f8ab010794000000000000000000000000000000000000abcde8ca01038082520880821122dc0280940000000000000000000000000000000000001234829c408080f85cf85a0194000000000000000000000000000000000000abcd80b8410101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101843b9aca008506fc23ac0080c0"
5768 );
5769
5770 let (decoded, rest) = FrameTransaction::decode_unfinished(&buf).unwrap();
5772 assert!(rest.is_empty());
5773 assert_eq!(decoded, tx);
5774
5775 let sig_hash = tx.compute_sig_hash();
5776 assert_eq!(
5778 format!("{:#x}", sig_hash),
5779 "0xe7dc3f33413fc69c09f9c690be154ded294954e497aeea6ce0010ba513f2f26d",
5780 );
5781
5782 let mut tx2 = tx.clone();
5784 tx2.signatures[0].signature = Bytes::from(vec![0x02u8; 65]);
5785 assert_eq!(
5786 tx.compute_sig_hash(),
5787 tx2.compute_sig_hash(),
5788 "sig_hash must be independent of empty-msg signature bytes",
5789 );
5790 }
5791
5792 #[test]
5793 fn test_cost_without_base_fee_eip4844_includes_blob_gas() {
5794 use crate::constants::GAS_PER_BLOB;
5798
5799 let max_fee_per_gas: u64 = 100;
5800 let gas: u64 = 21_000;
5801 let value = U256::from(7u64);
5802 let max_fee_per_blob_gas = U256::from(50u64);
5803 let blob_count: usize = 1;
5804
5805 let tx = Transaction::EIP4844Transaction(EIP4844Transaction {
5806 max_fee_per_gas,
5807 gas,
5808 value,
5809 max_fee_per_blob_gas,
5810 blob_versioned_hashes: vec![H256::zero(); blob_count],
5811 ..Default::default()
5812 });
5813
5814 let got = tx.cost_without_base_fee().expect("cost is computable");
5815
5816 let gas_cost = U256::from(max_fee_per_gas) * U256::from(gas);
5817 let blob_gas = U256::from(GAS_PER_BLOB) * U256::from(blob_count as u64);
5818 let blob_cost = blob_gas * max_fee_per_blob_gas;
5819 let expected = gas_cost + blob_cost + value;
5820
5821 assert_eq!(
5822 got, expected,
5823 "blob-gas term missing from cost_without_base_fee() for EIP-4844"
5824 );
5825 }
5826}