From a052ff8bd40da2505b2e9ff93294035f87694c43 Mon Sep 17 00:00:00 2001 From: Noah Citron Date: Tue, 6 Sep 2022 12:08:05 -0400 Subject: [PATCH] fix: legacy transaction rlp decoding (#1672) * fix legacy tx rlp decoding * add test for type 0 tx in 2718 envelopes * update changelog * refactor * fmt --- CHANGELOG.md | 1 + ethers-core/src/types/transaction/response.rs | 79 ++++++++++++++++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c544f355..ec588d09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Unreleased +- Fix RLP decoding of legacy `Transaction` - Fix RLP encoding of `TransactionReceipt` [#1661](https://github.com/gakonst/ethers-rs/pull/1661) - Add `Unit8` helper type [#1639](https://github.com/gakonst/ethers-rs/pull/1639) - Add `evm.deployedBytecode.immutableReferences` output selector [#1523](https://github.com/gakonst/ethers-rs/pull/1523) diff --git a/ethers-core/src/types/transaction/response.rs b/ethers-core/src/types/transaction/response.rs index b127bbef..c1fb2e6e 100644 --- a/ethers-core/src/types/transaction/response.rs +++ b/ethers-core/src/types/transaction/response.rs @@ -344,9 +344,14 @@ impl Decodable for Transaction { true => Ok(Some(rlp.data()?.into())), false => Ok(None), }?; - let rest = rlp::Rlp::new( - rlp.as_raw().get(1..).ok_or(DecoderError::Custom("no transaction payload"))?, - ); + + let rest = if txn.transaction_type.is_some() { + rlp::Rlp::new( + rlp.as_raw().get(1..).ok_or(DecoderError::Custom("no transaction payload"))?, + ) + } else { + rlp.to_owned() + }; match txn.transaction_type { Some(x) if x == U64::from(1) => { @@ -758,6 +763,74 @@ mod tests { assert_eq!(decoded_transaction.hash(), tx.hash()); } + #[test] + fn decode_rlp_legacy() { + let tx = Transaction { + block_hash: None, + block_number: None, + from: Address::from_str("c26ad91f4e7a0cad84c4b9315f420ca9217e315d").unwrap(), + gas: U256::from_str_radix("0x10e2b", 16).unwrap(), + gas_price: Some(U256::from_str_radix("0x12ec276caf", 16).unwrap()), + hash: H256::from_str("929ff27a5c7833953df23103c4eb55ebdfb698678139d751c51932163877fada").unwrap(), + input: Bytes::from( + hex::decode("a9059cbb000000000000000000000000fdae129ecc2c27d166a3131098bc05d143fa258e0000000000000000000000000000000000000000000000000000000002faf080").unwrap() + ), + nonce: U256::zero(), + to: Some(Address::from_str("dac17f958d2ee523a2206206994597c13d831ec7").unwrap()), + transaction_index: None, + value: U256::zero(), + transaction_type: Some(U64::zero()), + v: U64::from(0x25), + r: U256::from_str_radix("c81e70f9e49e0d3b854720143e86d172fecc9e76ef8a8666f2fdc017017c5141", 16).unwrap(), + s: U256::from_str_radix("1dd3410180f6a6ca3e25ad3058789cd0df3321ed76b5b4dbe0a2bb2dc28ae274", 16).unwrap(), + chain_id: Some(U256::from(1)), + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + other: Default::default() + }; + + let rlp_bytes = hex::decode("f8aa808512ec276caf83010e2b94dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000fdae129ecc2c27d166a3131098bc05d143fa258e0000000000000000000000000000000000000000000000000000000002faf08025a0c81e70f9e49e0d3b854720143e86d172fecc9e76ef8a8666f2fdc017017c5141a01dd3410180f6a6ca3e25ad3058789cd0df3321ed76b5b4dbe0a2bb2dc28ae274").unwrap(); + + let decoded_transaction = Transaction::decode(&rlp::Rlp::new(&rlp_bytes)).unwrap(); + + assert_eq!(decoded_transaction.hash(), tx.hash()); + } + + #[test] + fn decode_rlp_legacy_in_envelope() { + let tx = Transaction { + block_hash: None, + block_number: None, + from: Address::from_str("c26ad91f4e7a0cad84c4b9315f420ca9217e315d").unwrap(), + gas: U256::from_str_radix("0x10e2b", 16).unwrap(), + gas_price: Some(U256::from_str_radix("0x12ec276caf", 16).unwrap()), + hash: H256::from_str("929ff27a5c7833953df23103c4eb55ebdfb698678139d751c51932163877fada").unwrap(), + input: Bytes::from( + hex::decode("a9059cbb000000000000000000000000fdae129ecc2c27d166a3131098bc05d143fa258e0000000000000000000000000000000000000000000000000000000002faf080").unwrap() + ), + nonce: U256::zero(), + to: Some(Address::from_str("dac17f958d2ee523a2206206994597c13d831ec7").unwrap()), + transaction_index: None, + value: U256::zero(), + transaction_type: Some(U64::zero()), + v: U64::from(0x25), + r: U256::from_str_radix("c81e70f9e49e0d3b854720143e86d172fecc9e76ef8a8666f2fdc017017c5141", 16).unwrap(), + s: U256::from_str_radix("1dd3410180f6a6ca3e25ad3058789cd0df3321ed76b5b4dbe0a2bb2dc28ae274", 16).unwrap(), + chain_id: Some(U256::from(1)), + access_list: None, + max_fee_per_gas: None, + max_priority_fee_per_gas: None, + other: Default::default() + }; + + let rlp_bytes = hex::decode("00f8aa808512ec276caf83010e2b94dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000fdae129ecc2c27d166a3131098bc05d143fa258e0000000000000000000000000000000000000000000000000000000002faf08025a0c81e70f9e49e0d3b854720143e86d172fecc9e76ef8a8666f2fdc017017c5141a01dd3410180f6a6ca3e25ad3058789cd0df3321ed76b5b4dbe0a2bb2dc28ae274").unwrap(); + + let decoded_transaction = Transaction::decode(&rlp::Rlp::new(&rlp_bytes)).unwrap(); + + assert_eq!(decoded_transaction.hash(), tx.hash()); + } + #[test] fn recover_from() { let tx = Transaction {