1mod authorization;
8pub mod eth_transaction;
9pub mod native_transaction;
10
11pub use authorization::{
12 extract_7702_payload, AuthorizationList, AuthorizationListItem, AUTH_MAGIC,
13 CODE_PREFIX_7702,
14};
15pub use eth_transaction::{
16 Eip1559Transaction, Eip155Transaction, Eip2930Transaction,
17 Eip7702Transaction, EthereumTransaction,
18};
19pub use native_transaction::{
20 Cip1559Transaction, Cip2930Transaction, NativeTransaction,
21 TypedNativeTransaction,
22};
23use rlp_derive::{RlpDecodable, RlpEncodable};
24
25use crate::{
26 bytes::Bytes,
27 hash::keccak,
28 keylib::{
29 self, public_to_address, recover, verify_public, Public, Secret,
30 Signature,
31 },
32};
33use cfx_types::{
34 cal_contract_address_with_space, Address, AddressSpaceUtil,
35 AddressWithSpace, BigEndianHash, CreateContractAddressType, Space, H160,
36 H256, U256,
37};
38use eth_transaction::eip155_signature;
39use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
40use rlp::{self, Decodable, DecoderError, Encodable, Rlp, RlpStream};
41use serde::{Deserialize, Serialize};
42use std::{
43 error, fmt,
44 ops::{Deref, DerefMut},
45};
46use unexpected::OutOfBounds;
47
48pub const UNSIGNED_SENDER: Address = H160([0xff; 20]);
50
51pub const TYPED_NATIVE_TX_PREFIX: &[u8; 3] = b"cfx";
52pub const TYPED_NATIVE_TX_PREFIX_BYTE: u8 = TYPED_NATIVE_TX_PREFIX[0];
53pub const LEGACY_TX_TYPE: u8 = 0x00;
54pub const EIP2930_TYPE: u8 = 0x01;
55pub const EIP1559_TYPE: u8 = 0x02;
56pub const EIP7702_TYPE: u8 = 0x04;
57pub const CIP2930_TYPE: u8 = 0x01;
58pub const CIP1559_TYPE: u8 = 0x02;
59
60pub type TxShortId = u64;
63
64pub type TxPropagateId = u32;
65
66#[derive(Debug, PartialEq, Clone, Eq)]
70pub enum TransactionError {
72 AlreadyImported,
74 ChainIdMismatch {
76 expected: u32,
77 got: u32,
78 space: Space,
79 },
80 EpochHeightOutOfBound {
82 block_height: u64,
83 set: u64,
84 transaction_epoch_bound: u64,
85 },
86 NotEnoughBaseGas {
88 required: U256,
90 got: U256,
92 },
93 Stale,
95 TooCheapToReplace,
99 LimitReached,
102 InsufficientGasPrice {
104 minimal: U256,
106 got: U256,
108 },
109 InsufficientGas {
111 minimal: U256,
113 got: U256,
115 },
116 InsufficientBalance {
118 balance: U256,
120 cost: U256,
122 },
123 GasLimitExceeded {
125 limit: U256,
127 got: U256,
129 },
130 InvalidGasLimit(OutOfBounds<U256>),
132 InvalidSignature(String),
134 TooBig,
136 InvalidRlp(String),
138 ZeroGasPrice,
139 FutureTransactionType {
141 tx_type: u8,
142 enable_height: u64,
143 current_height: u64,
144 },
145 CreateInitCodeSizeLimit,
147 InvalidReceiver,
149 TooLargeNonce,
151 EmptyAuthorizationList,
153 PriortyGreaterThanMaxFee,
155}
156
157impl From<keylib::Error> for TransactionError {
158 fn from(err: keylib::Error) -> Self {
159 TransactionError::InvalidSignature(format!("{}", err))
160 }
161}
162
163impl From<rlp::DecoderError> for TransactionError {
164 fn from(err: rlp::DecoderError) -> Self {
165 TransactionError::InvalidRlp(format!("{}", err))
166 }
167}
168
169impl fmt::Display for TransactionError {
170 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
171 use self::TransactionError::*;
172 let msg = match *self {
173 AlreadyImported => "Already imported".into(),
174 ChainIdMismatch { expected, got, space } => {
175 format!("Chain id mismatch, expected {}, got {}, space {:?}", expected, got, space)
176 }
177 EpochHeightOutOfBound {
178 block_height,
179 set,
180 transaction_epoch_bound,
181 } => format!(
182 "EpochHeight out of bound:\
183 block_height {}, transaction epoch_height {}, transaction_epoch_bound {}",
184 block_height, set, transaction_epoch_bound
185 ),
186 NotEnoughBaseGas { got, required } => format!(
187 "Transaction gas {} less than intrinsic gas {}",
188 got, required
189 ),
190 Stale => "No longer valid".into(),
191 TooCheapToReplace => "Gas price too low to replace".into(),
192 LimitReached => "Transaction limit reached".into(),
193 InsufficientGasPrice { minimal, got } => format!(
194 "Insufficient gas price. Min={}, Given={}",
195 minimal, got
196 ),
197 InsufficientGas { minimal, got } => {
198 format!("Insufficient gas. Min={}, Given={}", minimal, got)
199 }
200 InsufficientBalance { balance, cost } => format!(
201 "Insufficient balance for transaction. Balance={}, Cost={}",
202 balance, cost
203 ),
204 GasLimitExceeded { limit, got } => {
205 format!("Gas limit exceeded. Limit={}, Given={}", limit, got)
206 }
207 InvalidGasLimit(ref err) => format!("Invalid gas limit. {}", err),
208 InvalidSignature(ref err) => {
209 format!("Transaction has invalid signature: {}.", err)
210 }
211 TooBig => "Transaction too big".into(),
212 InvalidRlp(ref err) => {
213 format!("Transaction has invalid RLP structure: {}.", err)
214 }
215 ZeroGasPrice => "Zero gas price is not allowed".into(),
216 FutureTransactionType {tx_type, current_height, enable_height} => format!("Transaction type {tx_type} is not enabled, current height {current_height}, enable height {enable_height}"),
217 InvalidReceiver => "Sending transaction to invalid address. The first four bits of address must be 0x0, 0x1, or 0x8.".into(),
218 TooLargeNonce => "Transaction nonce is too large.".into(),
219 CreateInitCodeSizeLimit => "Transaction initcode is too large.".into(),
220 EmptyAuthorizationList => "Empty authorization list".into(),
221 PriortyGreaterThanMaxFee => "Max priority fee greater than max fee".into(),
222 };
223
224 f.write_fmt(format_args!("Transaction error ({})", msg))
225 }
226}
227
228impl error::Error for TransactionError {
229 fn description(&self) -> &str { "Transaction error" }
230}
231
232#[derive(
233 Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default,
234)]
235pub enum Action {
236 #[default]
238 Create,
239 Call(Address),
242}
243
244impl Decodable for Action {
245 fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
246 if rlp.is_empty() {
247 Ok(Action::Create)
248 } else {
249 Ok(Action::Call(rlp.as_val()?))
250 }
251 }
252}
253
254impl Encodable for Action {
255 fn rlp_append(&self, stream: &mut RlpStream) {
256 match *self {
257 Action::Create => stream.append_internal(&""),
258 Action::Call(ref address) => stream.append_internal(address),
259 };
260 }
261}
262
263#[derive(
264 Debug,
265 Clone,
266 PartialEq,
267 Eq,
268 Serialize,
269 Deserialize,
270 RlpEncodable,
271 RlpDecodable,
272)]
273#[serde(rename_all = "camelCase")]
274pub struct AccessListItem {
275 pub address: Address,
276 pub storage_keys: Vec<H256>,
277}
278
279pub type AccessList = Vec<AccessListItem>;
280
281#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
282pub enum Transaction {
283 Native(TypedNativeTransaction),
284 Ethereum(EthereumTransaction),
285}
286
287impl Default for Transaction {
288 fn default() -> Self {
289 Transaction::Native(TypedNativeTransaction::Cip155(Default::default()))
290 }
291}
292
293impl From<NativeTransaction> for Transaction {
294 fn from(tx: NativeTransaction) -> Self {
295 Self::Native(TypedNativeTransaction::Cip155(tx))
296 }
297}
298
299impl From<Eip155Transaction> for Transaction {
300 fn from(tx: Eip155Transaction) -> Self {
301 Self::Ethereum(EthereumTransaction::Eip155(tx))
302 }
303}
304
305macro_rules! access_common_ref {
306 ($field:ident, $ty:ty) => {
307 pub fn $field(&self) -> &$ty {
308 match self {
309 Transaction::Native(tx) => tx.$field(),
310 Transaction::Ethereum(tx) => tx.$field(),
311 }
312 }
313 };
314}
315
316macro_rules! access_common {
317 ($field:ident, $ty:ident) => {
318 pub fn $field(&self) -> $ty {
319 match self {
320 Transaction::Native(tx) => tx.$field(),
321 Transaction::Ethereum(tx) => tx.$field(),
322 }
323 }
324 };
325}
326
327impl Transaction {
328 access_common_ref!(gas, U256);
329
330 access_common_ref!(gas_price, U256);
331
332 access_common_ref!(max_priority_gas_price, U256);
333
334 access_common_ref!(data, Bytes);
335
336 access_common_ref!(nonce, U256);
337
338 access_common!(action, Action);
339
340 access_common_ref!(value, U256);
341
342 pub fn chain_id(&self) -> Option<u32> {
343 match self {
344 Transaction::Native(tx) => Some(*tx.chain_id()),
345 Transaction::Ethereum(tx) => tx.chain_id(),
346 }
347 }
348
349 pub fn storage_limit(&self) -> Option<u64> {
350 match self {
351 Transaction::Native(tx) => Some(*tx.storage_limit()),
352 Transaction::Ethereum(_tx) => None,
353 }
354 }
355
356 pub fn nonce_mut(&mut self) -> &mut U256 {
357 match self {
358 Transaction::Native(tx) => tx.nonce_mut(),
359 Transaction::Ethereum(tx) => tx.nonce_mut(),
360 }
361 }
362
363 pub fn data_mut(&mut self) -> &mut Vec<u8> {
364 match self {
365 Transaction::Native(tx) => tx.data_mut(),
366 Transaction::Ethereum(tx) => tx.data_mut(),
367 }
368 }
369
370 pub fn type_id(&self) -> u8 {
371 match self {
372 Transaction::Native(TypedNativeTransaction::Cip155(_))
373 | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => 0,
374
375 Transaction::Native(TypedNativeTransaction::Cip2930(_))
376 | Transaction::Ethereum(EthereumTransaction::Eip2930(_)) => 1,
377
378 Transaction::Native(TypedNativeTransaction::Cip1559(_))
379 | Transaction::Ethereum(EthereumTransaction::Eip1559(_)) => 2,
380
381 Transaction::Ethereum(EthereumTransaction::Eip7702(_)) => 4,
382 }
383 }
384
385 pub fn is_legacy(&self) -> bool {
386 matches!(
387 self,
388 Transaction::Native(TypedNativeTransaction::Cip155(_))
389 | Transaction::Ethereum(EthereumTransaction::Eip155(_))
390 )
391 }
392
393 pub fn is_2718(&self) -> bool { !self.is_legacy() }
394
395 pub fn after_1559(&self) -> bool {
396 use EthereumTransaction::*;
397 use TypedNativeTransaction::*;
398 matches!(
399 self,
400 Transaction::Native(Cip1559(_))
401 | Transaction::Ethereum(Eip1559(_) | Eip7702(_))
402 )
403 }
404
405 pub fn after_7702(&self) -> bool {
406 matches!(self, Transaction::Ethereum(EthereumTransaction::Eip7702(_)))
407 }
408
409 pub fn access_list(&self) -> Option<&AccessList> {
410 match self {
411 Transaction::Native(tx) => tx.access_list(),
412 Transaction::Ethereum(tx) => tx.access_list(),
413 }
414 }
415
416 pub fn authorization_list(&self) -> Option<&AuthorizationList> {
417 match self {
418 Transaction::Native(_tx) => None,
419 Transaction::Ethereum(tx) => tx.authorization_list(),
420 }
421 }
422
423 pub fn authorization_len(&self) -> usize {
424 self.authorization_list().map_or(0, Vec::len)
425 }
426}
427
428impl Transaction {
429 pub fn priority_gas_price(&self, base_price: &U256) -> U256 {
430 std::cmp::min(
431 *self.max_priority_gas_price(),
432 self.gas_price() - base_price,
433 )
434 }
435
436 pub fn effective_gas_price(&self, base_price: &U256) -> U256 {
437 base_price + self.priority_gas_price(base_price)
438 }
439
440 pub fn hash_for_compute_signature(&self) -> H256 {
444 let mut s = RlpStream::new();
445 let mut type_prefix = vec![];
446 match self {
447 Transaction::Native(TypedNativeTransaction::Cip155(tx)) => {
448 s.append(tx);
449 }
450 Transaction::Native(TypedNativeTransaction::Cip1559(tx)) => {
451 s.append(tx);
452 type_prefix.extend_from_slice(TYPED_NATIVE_TX_PREFIX);
453 type_prefix.push(CIP1559_TYPE);
454 }
455 Transaction::Native(TypedNativeTransaction::Cip2930(tx)) => {
456 s.append(tx);
457 type_prefix.extend_from_slice(TYPED_NATIVE_TX_PREFIX);
458 type_prefix.push(CIP2930_TYPE);
459 }
460 Transaction::Ethereum(EthereumTransaction::Eip155(tx)) => {
461 s.append(tx);
462 }
463 Transaction::Ethereum(EthereumTransaction::Eip1559(tx)) => {
464 s.append(tx);
465 type_prefix.push(EIP1559_TYPE);
466 }
467 Transaction::Ethereum(EthereumTransaction::Eip2930(tx)) => {
468 s.append(tx);
469 type_prefix.push(EIP2930_TYPE);
470 }
471 Transaction::Ethereum(EthereumTransaction::Eip7702(tx)) => {
472 s.append(tx);
473 type_prefix.push(EIP7702_TYPE);
474 }
475 };
476 let encoded = s.as_raw();
477 let mut out = vec![0; type_prefix.len() + encoded.len()];
478 out[0..type_prefix.len()].copy_from_slice(&type_prefix);
479 out[type_prefix.len()..].copy_from_slice(encoded);
480 keccak(&out)
481 }
482
483 pub fn space(&self) -> Space {
484 match self {
485 Transaction::Native(_) => Space::Native,
486 Transaction::Ethereum(_) => Space::Ethereum,
487 }
488 }
489
490 pub fn sign(self, secret: &Secret) -> SignedTransaction {
491 let sig =
492 crate::keylib::sign(secret, &self.hash_for_compute_signature())
493 .expect(
494 "data is valid and context has signing capabilities; qed",
495 );
496 let tx_with_sig = self.with_signature(sig);
497 let public = tx_with_sig
498 .recover_public()
499 .expect("secret is valid so it's recoverable");
500 SignedTransaction::new(public, tx_with_sig)
501 }
502
503 pub fn with_signature(self, sig: Signature) -> TransactionWithSignature {
505 TransactionWithSignature {
506 transaction: TransactionWithSignatureSerializePart {
507 unsigned: self,
508 r: U256::from_big_endian(sig.r()),
509 s: U256::from_big_endian(sig.s()),
510 v: sig.v(),
511 },
512 hash: H256::zero(),
513 rlp_size: None,
514 }
515 .compute_hash()
516 }
517}
518
519impl MallocSizeOf for Transaction {
520 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
521 self.data().size_of(ops)
522 }
523}
524
525#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
527pub struct TransactionWithSignatureSerializePart {
528 pub unsigned: Transaction,
530 pub v: u8,
533 pub r: U256,
535 pub s: U256,
537}
538
539impl Encodable for TransactionWithSignatureSerializePart {
540 fn rlp_append(&self, s: &mut RlpStream) {
541 match self.unsigned {
542 Transaction::Native(TypedNativeTransaction::Cip155(ref tx)) => {
543 s.begin_list(4);
544 s.append(tx);
545 s.append(&self.v);
546 s.append(&self.r);
547 s.append(&self.s);
548 }
549 Transaction::Ethereum(EthereumTransaction::Eip155(ref tx)) => {
550 let Eip155Transaction {
551 nonce,
552 gas_price,
553 gas,
554 action,
555 value,
556 data,
557 chain_id,
558 } = tx;
559 let legacy_v = eip155_signature::add_chain_replay_protection(
560 self.v,
561 chain_id.map(|x| x as u64),
562 );
563 s.begin_list(9);
564 s.append(nonce);
565 s.append(gas_price);
566 s.append(gas);
567 s.append(action);
568 s.append(value);
569 s.append(data);
570 s.append(&legacy_v);
571 s.append(&self.r);
572 s.append(&self.s);
573 }
574 Transaction::Ethereum(EthereumTransaction::Eip2930(ref tx)) => {
575 s.append_raw(&[EIP2930_TYPE], 0);
576 s.begin_list(11);
577 s.append(&tx.chain_id);
578 s.append(&tx.nonce);
579 s.append(&tx.gas_price);
580 s.append(&tx.gas);
581 s.append(&tx.action);
582 s.append(&tx.value);
583 s.append(&tx.data);
584 s.append_list(&tx.access_list);
585 s.append(&self.v);
586 s.append(&self.r);
587 s.append(&self.s);
588 }
589 Transaction::Ethereum(EthereumTransaction::Eip1559(ref tx)) => {
590 s.append_raw(&[EIP1559_TYPE], 0);
591 s.begin_list(12);
592 s.append(&tx.chain_id);
593 s.append(&tx.nonce);
594 s.append(&tx.max_priority_fee_per_gas);
595 s.append(&tx.max_fee_per_gas);
596 s.append(&tx.gas);
597 s.append(&tx.action);
598 s.append(&tx.value);
599 s.append(&tx.data);
600 s.append_list(&tx.access_list);
601 s.append(&self.v);
602 s.append(&self.r);
603 s.append(&self.s);
604 }
605 Transaction::Ethereum(EthereumTransaction::Eip7702(ref tx)) => {
606 s.append_raw(&[EIP7702_TYPE], 0);
607 s.begin_list(13);
608 s.append(&tx.chain_id);
609 s.append(&tx.nonce);
610 s.append(&tx.max_priority_fee_per_gas);
611 s.append(&tx.max_fee_per_gas);
612 s.append(&tx.gas);
613 s.append(&tx.destination);
614 s.append(&tx.value);
615 s.append(&tx.data);
616 s.append_list(&tx.access_list);
617 s.append_list(&tx.authorization_list);
618 s.append(&self.v);
619 s.append(&self.r);
620 s.append(&self.s);
621 }
622 Transaction::Native(TypedNativeTransaction::Cip2930(ref tx)) => {
623 s.append_raw(TYPED_NATIVE_TX_PREFIX, 0);
624 s.append_raw(&[CIP2930_TYPE], 0);
625 s.begin_list(4);
626 s.append(tx);
627 s.append(&self.v);
628 s.append(&self.r);
629 s.append(&self.s);
630 }
631 Transaction::Native(TypedNativeTransaction::Cip1559(ref tx)) => {
632 s.append_raw(TYPED_NATIVE_TX_PREFIX, 0);
633 s.append_raw(&[CIP1559_TYPE], 0);
634 s.begin_list(4);
635 s.append(tx);
636 s.append(&self.v);
637 s.append(&self.r);
638 s.append(&self.s);
639 } }
649 }
650}
651
652impl Decodable for TransactionWithSignatureSerializePart {
654 fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
655 if rlp.as_raw().is_empty() {
656 return Err(DecoderError::RlpInvalidLength);
657 }
658 if rlp.is_list() {
659 match rlp.item_count()? {
660 4 => {
661 let unsigned: NativeTransaction = rlp.val_at(0)?;
662 let v: u8 = rlp.val_at(1)?;
663 let r: U256 = rlp.val_at(2)?;
664 let s: U256 = rlp.val_at(3)?;
665 Ok(TransactionWithSignatureSerializePart {
666 unsigned: Transaction::Native(
667 TypedNativeTransaction::Cip155(unsigned),
668 ),
669 v,
670 r,
671 s,
672 })
673 }
674 9 => {
675 let nonce: U256 = rlp.val_at(0)?;
676 let gas_price: U256 = rlp.val_at(1)?;
677 let gas: U256 = rlp.val_at(2)?;
678 let action: Action = rlp.val_at(3)?;
679 let value: U256 = rlp.val_at(4)?;
680 let data: Vec<u8> = rlp.val_at(5)?;
681 let legacy_v: u64 = rlp.val_at(6)?;
682 let r: U256 = rlp.val_at(7)?;
683 let s: U256 = rlp.val_at(8)?;
684
685 let v = eip155_signature::extract_standard_v(legacy_v);
686 let chain_id =
687 match eip155_signature::extract_chain_id_from_legacy_v(
688 legacy_v,
689 ) {
690 Some(chain_id) if chain_id > (u32::MAX as u64) => {
691 return Err(DecoderError::Custom(
692 "Does not support chain_id >= 2^32",
693 ));
694 }
695 chain_id => chain_id.map(|x| x as u32),
696 };
697
698 Ok(TransactionWithSignatureSerializePart {
699 unsigned: Transaction::Ethereum(
700 EthereumTransaction::Eip155(Eip155Transaction {
701 nonce,
702 gas_price,
703 gas,
704 action,
705 value,
706 chain_id,
707 data,
708 }),
709 ),
710 v,
711 r,
712 s,
713 })
714 }
715 _ => Err(DecoderError::RlpInvalidLength),
716 }
717 } else {
718 match rlp.as_raw()[0] {
719 TYPED_NATIVE_TX_PREFIX_BYTE => {
720 if rlp.as_raw().len() <= 4
721 || rlp.as_raw()[0..3] != *TYPED_NATIVE_TX_PREFIX
722 {
723 return Err(DecoderError::RlpInvalidLength);
724 }
725 match rlp.as_raw()[3] {
726 CIP2930_TYPE => {
727 let rlp = Rlp::new(&rlp.as_raw()[4..]);
728 if rlp.item_count()? != 4 {
729 return Err(DecoderError::RlpIncorrectListLen);
730 }
731
732 let tx = rlp.val_at(0)?;
733 let v = rlp.val_at(1)?;
734 let r = rlp.val_at(2)?;
735 let s = rlp.val_at(3)?;
736 Ok(TransactionWithSignatureSerializePart {
737 unsigned: Transaction::Native(
738 TypedNativeTransaction::Cip2930(tx),
739 ),
740 v,
741 r,
742 s,
743 })
744 }
745 CIP1559_TYPE => {
746 let rlp = Rlp::new(&rlp.as_raw()[4..]);
747 if rlp.item_count()? != 4 {
748 return Err(DecoderError::RlpIncorrectListLen);
749 }
750
751 let tx = rlp.val_at(0)?;
752 let v = rlp.val_at(1)?;
753 let r = rlp.val_at(2)?;
754 let s = rlp.val_at(3)?;
755 Ok(TransactionWithSignatureSerializePart {
756 unsigned: Transaction::Native(
757 TypedNativeTransaction::Cip1559(tx),
758 ),
759 v,
760 r,
761 s,
762 })
763 }
764 _ => Err(DecoderError::RlpInvalidLength),
765 }
766 }
767 EIP2930_TYPE => {
768 let rlp = Rlp::new(&rlp.as_raw()[1..]);
769 if rlp.item_count()? != 11 {
770 return Err(DecoderError::RlpIncorrectListLen);
771 }
772
773 let tx = Eip2930Transaction {
774 chain_id: rlp.val_at(0)?,
775 nonce: rlp.val_at(1)?,
776 gas_price: rlp.val_at(2)?,
777 gas: rlp.val_at(3)?,
778 action: rlp.val_at(4)?,
779 value: rlp.val_at(5)?,
780 data: rlp.val_at(6)?,
781 access_list: rlp.list_at(7)?,
782 };
783 let v = rlp.val_at(8)?;
784 let r = rlp.val_at(9)?;
785 let s = rlp.val_at(10)?;
786 Ok(TransactionWithSignatureSerializePart {
787 unsigned: Transaction::Ethereum(
788 EthereumTransaction::Eip2930(tx),
789 ),
790 v,
791 r,
792 s,
793 })
794 }
795 EIP1559_TYPE => {
796 let rlp = Rlp::new(&rlp.as_raw()[1..]);
797 if rlp.item_count()? != 12 {
798 return Err(DecoderError::RlpIncorrectListLen);
799 }
800
801 let tx = Eip1559Transaction {
802 chain_id: rlp.val_at(0)?,
803 nonce: rlp.val_at(1)?,
804 max_priority_fee_per_gas: rlp.val_at(2)?,
805 max_fee_per_gas: rlp.val_at(3)?,
806 gas: rlp.val_at(4)?,
807 action: rlp.val_at(5)?,
808 value: rlp.val_at(6)?,
809 data: rlp.val_at(7)?,
810 access_list: rlp.list_at(8)?,
811 };
812 let v = rlp.val_at(9)?;
813 let r = rlp.val_at(10)?;
814 let s = rlp.val_at(11)?;
815 Ok(TransactionWithSignatureSerializePart {
816 unsigned: Transaction::Ethereum(
817 EthereumTransaction::Eip1559(tx),
818 ),
819 v,
820 r,
821 s,
822 })
823 }
824 EIP7702_TYPE => {
825 let rlp = Rlp::new(&rlp.as_raw()[1..]);
826 if rlp.item_count()? != 13 {
827 return Err(DecoderError::RlpIncorrectListLen);
828 }
829
830 let tx = Eip7702Transaction {
831 chain_id: rlp.val_at(0)?,
832 nonce: rlp.val_at(1)?,
833 max_priority_fee_per_gas: rlp.val_at(2)?,
834 max_fee_per_gas: rlp.val_at(3)?,
835 gas: rlp.val_at(4)?,
836 destination: rlp.val_at(5)?,
837 value: rlp.val_at(6)?,
838 data: rlp.val_at(7)?,
839 access_list: rlp.list_at(8)?,
840 authorization_list: rlp.list_at(9)?,
841 };
842 let v = rlp.val_at(10)?;
843 let r = rlp.val_at(11)?;
844 let s = rlp.val_at(12)?;
845 Ok(TransactionWithSignatureSerializePart {
846 unsigned: Transaction::Ethereum(
847 EthereumTransaction::Eip7702(tx),
848 ),
849 v,
850 r,
851 s,
852 })
853 }
854 _ => Err(DecoderError::RlpInvalidLength),
855 }
856 }
857 }
858}
859
860impl Deref for TransactionWithSignatureSerializePart {
861 type Target = Transaction;
862
863 fn deref(&self) -> &Self::Target { &self.unsigned }
864}
865
866impl DerefMut for TransactionWithSignatureSerializePart {
867 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.unsigned }
868}
869
870#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
872pub struct TransactionWithSignature {
873 pub transaction: TransactionWithSignatureSerializePart,
875 #[serde(skip)]
877 pub hash: H256,
878 #[serde(skip)]
880 pub rlp_size: Option<usize>,
881}
882
883impl Deref for TransactionWithSignature {
884 type Target = TransactionWithSignatureSerializePart;
885
886 fn deref(&self) -> &Self::Target { &self.transaction }
887}
888
889impl DerefMut for TransactionWithSignature {
890 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.transaction }
891}
892
893impl Decodable for TransactionWithSignature {
894 fn decode(tx_rlp: &Rlp) -> Result<Self, DecoderError> {
895 let rlp_size = Some(tx_rlp.as_raw().len());
896 let hash;
899 let transaction;
900 if tx_rlp.is_list() {
901 hash = keccak(tx_rlp.as_raw());
902 transaction = tx_rlp.as_val()?;
904 } else {
905 let b: Vec<u8> = tx_rlp.as_val()?;
907 hash = keccak(&b);
908 transaction = rlp::decode(&b)?;
909 };
910 Ok(TransactionWithSignature {
911 transaction,
912 hash,
913 rlp_size,
914 })
915 }
916}
917
918impl Encodable for TransactionWithSignature {
919 fn rlp_append(&self, s: &mut RlpStream) {
920 match &self.transaction.unsigned {
921 Transaction::Native(TypedNativeTransaction::Cip155(_))
922 | Transaction::Ethereum(EthereumTransaction::Eip155(_)) => {
923 s.append_internal(&self.transaction);
924 }
925 _ => {
926 s.append_internal(&rlp::encode(&self.transaction));
928 }
929 }
930 }
931}
932
933impl TransactionWithSignature {
934 pub fn new_unsigned(tx: Transaction) -> Self {
935 TransactionWithSignature {
936 transaction: TransactionWithSignatureSerializePart {
937 unsigned: tx,
938 s: 0.into(),
939 r: 0.into(),
940 v: 0,
941 },
942 hash: Default::default(),
943 rlp_size: None,
944 }
945 }
946
947 pub fn canonical_hash(&self) -> H256 {
950 keccak(&*self.transaction.rlp_bytes())
951 }
952
953 fn compute_hash(mut self) -> TransactionWithSignature {
954 self.hash = self.canonical_hash();
955 self
956 }
957
958 pub fn is_unsigned(&self) -> bool { self.r.is_zero() && self.s.is_zero() }
960
961 pub fn signature(&self) -> Signature {
963 let r: H256 = BigEndianHash::from_uint(&self.r);
964 let s: H256 = BigEndianHash::from_uint(&self.s);
965 Signature::from_rsv(&r, &s, self.v)
966 }
967
968 pub fn check_low_s(&self) -> Result<(), keylib::Error> {
970 if !self.signature().is_low_s() {
971 Err(keylib::Error::InvalidSignature)
972 } else {
973 Ok(())
974 }
975 }
976
977 pub fn check_y_parity(&self) -> Result<(), keylib::Error> {
978 if self.is_2718() && self.v > 1 {
979 Err(keylib::Error::InvalidYParity)
982 } else {
983 Ok(())
984 }
985 }
986
987 pub fn hash(&self) -> H256 { self.hash }
988
989 pub fn is_canonical_rlp(&self) -> bool {
992 self.hash == self.canonical_hash()
993 }
994
995 pub fn recover_public(&self) -> Result<Public, keylib::Error> {
997 recover(
998 &self.signature(),
999 &self.unsigned.hash_for_compute_signature(),
1000 )
1001 }
1002
1003 pub fn rlp_size(&self) -> usize {
1004 self.rlp_size.unwrap_or_else(|| self.rlp_bytes().len())
1005 }
1006
1007 pub fn from_raw(raw: &[u8]) -> Result<Self, DecoderError> {
1008 Ok(TransactionWithSignature {
1009 transaction: Rlp::new(raw).as_val()?,
1010 hash: keccak(raw),
1011 rlp_size: Some(raw.len()),
1012 })
1013 }
1014}
1015
1016impl MallocSizeOf for TransactionWithSignature {
1017 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
1018 self.unsigned.size_of(ops)
1019 }
1020}
1021
1022#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1024pub struct SignedTransaction {
1025 pub transaction: TransactionWithSignature,
1026 pub sender: Address,
1027 pub public: Option<Public>,
1028}
1029
1030impl Encodable for SignedTransaction {
1032 fn rlp_append(&self, s: &mut RlpStream) {
1033 s.begin_list(3);
1034 s.append(&self.transaction);
1035 s.append(&self.sender);
1036 s.append(&self.public);
1037 }
1038}
1039
1040impl Decodable for SignedTransaction {
1041 fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
1042 Ok(SignedTransaction {
1043 transaction: rlp.val_at(0)?,
1044 sender: rlp.val_at(1)?,
1045 public: rlp.val_at(2)?,
1046 })
1047 }
1048}
1049
1050impl Deref for SignedTransaction {
1051 type Target = TransactionWithSignature;
1052
1053 fn deref(&self) -> &Self::Target { &self.transaction }
1054}
1055
1056impl DerefMut for SignedTransaction {
1057 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.transaction }
1058}
1059
1060impl From<SignedTransaction> for TransactionWithSignature {
1061 fn from(tx: SignedTransaction) -> Self { tx.transaction }
1062}
1063
1064impl SignedTransaction {
1065 pub fn new(public: Public, transaction: TransactionWithSignature) -> Self {
1067 if transaction.is_unsigned() {
1068 SignedTransaction {
1069 transaction,
1070 sender: UNSIGNED_SENDER,
1071 public: None,
1072 }
1073 } else {
1074 let sender = public_to_address(
1075 &public,
1076 transaction.space() == Space::Native,
1077 );
1078 SignedTransaction {
1079 transaction,
1080 sender,
1081 public: Some(public),
1082 }
1083 }
1084 }
1085
1086 pub fn new_unsigned(transaction: TransactionWithSignature) -> Self {
1087 SignedTransaction {
1088 transaction,
1089 sender: UNSIGNED_SENDER,
1090 public: None,
1091 }
1092 }
1093
1094 pub fn set_public(&mut self, public: Public) {
1095 let type_nibble = self.unsigned.space() == Space::Native;
1096 self.sender = public_to_address(&public, type_nibble);
1097 self.public = Some(public);
1098 }
1099
1100 pub fn sender(&self) -> AddressWithSpace {
1102 self.sender.with_space(self.space())
1103 }
1104
1105 pub fn nonce(&self) -> &U256 { self.transaction.nonce() }
1106
1107 pub fn is_unsigned(&self) -> bool { self.transaction.is_unsigned() }
1109
1110 pub fn hash(&self) -> H256 { self.transaction.hash() }
1111
1112 pub fn gas(&self) -> &U256 { self.transaction.gas() }
1113
1114 pub fn gas_price(&self) -> &U256 { self.transaction.gas_price() }
1115
1116 pub fn gas_limit(&self) -> &U256 { self.transaction.gas() }
1117
1118 pub fn storage_limit(&self) -> Option<u64> {
1119 self.transaction.storage_limit()
1120 }
1121
1122 pub fn rlp_size(&self) -> usize { self.transaction.rlp_size() }
1123
1124 pub fn public(&self) -> &Option<Public> { &self.public }
1125
1126 pub fn verify_public(&self, skip: bool) -> Result<bool, keylib::Error> {
1127 if self.public.is_none() {
1128 return Ok(false);
1129 }
1130
1131 if !skip {
1132 let public = self.public.unwrap();
1133 Ok(verify_public(
1134 &public,
1135 &self.signature(),
1136 &self.unsigned.hash_for_compute_signature(),
1137 )?)
1138 } else {
1139 Ok(true)
1140 }
1141 }
1142
1143 pub fn cal_created_address(&self) -> Option<AddressWithSpace> {
1146 if let Action::Create = self.action() {
1147 let from = self.sender();
1148 let nonce = self.nonce();
1149 let space = self.space();
1150 let create_type = match space {
1151 Space::Native => {
1152 CreateContractAddressType::FromSenderNonceAndCodeHash
1153 }
1154 Space::Ethereum => CreateContractAddressType::FromSenderNonce,
1155 };
1156 let code = self.data().as_slice();
1157 let (created_address, _) = cal_contract_address_with_space(
1158 create_type,
1159 &from,
1160 nonce,
1161 code,
1162 );
1163 Some(created_address)
1164 } else {
1165 None
1166 }
1167 }
1168}
1169
1170impl MallocSizeOf for SignedTransaction {
1171 fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
1172 self.transaction.size_of(ops)
1173 }
1174}
1175
1176#[cfg(test)]
1177mod canonical_rlp_tests {
1178 use super::*;
1179 use EthereumTransaction::*;
1180 use TypedNativeTransaction::*;
1181
1182 fn part(unsigned: Transaction) -> TransactionWithSignatureSerializePart {
1183 TransactionWithSignatureSerializePart {
1184 unsigned,
1185 v: 1,
1186 r: U256::from(0x1234u64),
1187 s: U256::from(0x5678u64),
1188 }
1189 }
1190
1191 fn is_list_form(p: &TransactionWithSignatureSerializePart) -> bool {
1192 matches!(
1193 p.unsigned,
1194 Transaction::Native(Cip155(_)) | Transaction::Ethereum(Eip155(_))
1195 )
1196 }
1197
1198 fn wire(p: &TransactionWithSignatureSerializePart) -> Vec<u8> {
1201 let s = rlp::encode(p).to_vec();
1202 if is_list_form(p) {
1203 s
1204 } else {
1205 rlp::encode(&s).to_vec()
1206 }
1207 }
1208
1209 fn malleate(p: &TransactionWithSignatureSerializePart) -> Vec<u8> {
1212 let mut s = rlp::encode(p).to_vec();
1213 if is_list_form(p) {
1214 assert!(
1217 (0xc0..=0xf7).contains(&s[0]),
1218 "malleate: long-form list header not supported"
1219 );
1220 s[0] += 1;
1221 s.push(0xb8);
1222 s
1223 } else {
1224 s.push(0xb8);
1225 rlp::encode(&s).to_vec()
1226 }
1227 }
1228
1229 fn assert_predicate(p: TransactionWithSignatureSerializePart) {
1230 let canon: TransactionWithSignature = rlp::decode(&wire(&p)).unwrap();
1231 assert_eq!(canon.transaction, p);
1232 assert!(canon.is_canonical_rlp());
1233
1234 let mall: TransactionWithSignature =
1235 rlp::decode(&malleate(&p)).unwrap();
1236 assert_eq!(mall.transaction, p);
1237 assert!(!mall.is_canonical_rlp());
1238
1239 let raw = rlp::encode(&p).to_vec();
1240 assert!(TransactionWithSignature::from_raw(&raw)
1241 .unwrap()
1242 .is_canonical_rlp());
1243 }
1244
1245 #[test]
1247 fn is_canonical_rlp_all_variants() {
1248 let variants = [
1249 Transaction::Ethereum(Eip155(Eip155Transaction {
1250 chain_id: Some(1),
1251 ..Default::default()
1252 })),
1253 Transaction::Ethereum(Eip155(Eip155Transaction {
1255 chain_id: None,
1256 ..Default::default()
1257 })),
1258 Transaction::Native(Cip155(NativeTransaction {
1259 chain_id: 1,
1260 ..Default::default()
1261 })),
1262 Transaction::Ethereum(Eip2930(Eip2930Transaction {
1263 chain_id: 1,
1264 ..Default::default()
1265 })),
1266 Transaction::Ethereum(Eip1559(Eip1559Transaction {
1267 chain_id: 1,
1268 ..Default::default()
1269 })),
1270 Transaction::Ethereum(Eip7702(Eip7702Transaction {
1271 chain_id: 1,
1272 ..Default::default()
1273 })),
1274 Transaction::Native(Cip2930(Cip2930Transaction {
1275 chain_id: 1,
1276 ..Default::default()
1277 })),
1278 Transaction::Native(Cip1559(Cip1559Transaction {
1279 chain_id: 1,
1280 ..Default::default()
1281 })),
1282 ];
1283 for unsigned in variants {
1284 assert_predicate(part(unsigned));
1285 }
1286 }
1287}