Skip to content

Commit

Permalink
fix(eips/consensus): correctly decode txs on TxEnvelope (#148)
Browse files Browse the repository at this point in the history
* fix(eips): correctly decode txs on TxEnvelope

* chore: add proper check
  • Loading branch information
Evalir authored Jan 23, 2024
1 parent e06af5e commit 269d902
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 7 deletions.
41 changes: 41 additions & 0 deletions crates/consensus/src/transaction/envelope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,47 @@ mod tests {
use alloy_network::{Transaction, TxKind};
use alloy_primitives::{Address, Bytes, Signature, B256, U256};

#[test]
#[cfg(feature = "k256")]
// Test vector from https://etherscan.io/tx/0xce4dc6d7a7549a98ee3b071b67e970879ff51b5b95d1c340bacd80fa1e1aab31
fn test_decode_live_1559_tx() {
use alloy_primitives::address;

let raw_tx = alloy_primitives::hex::decode("02f86f0102843b9aca0085029e7822d68298f094d9e1459a7a482635700cbc20bbaf52d495ab9c9680841b55ba3ac080a0c199674fcb29f353693dd779c017823b954b3c69dffa3cd6b2a6ff7888798039a028ca912de909e7e6cdef9cdcaf24c54dd8c1032946dfa1d85c206b32a9064fe8").unwrap();
let res = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap();

assert_eq!(res.tx_type(), TxType::Eip1559);

let tx = match res {
TxEnvelope::Eip1559(tx) => tx,
_ => unreachable!(),
};

assert_eq!(tx.tx().to, TxKind::Call(address!("D9e1459A7A482635700cBc20BBAF52D495Ab9C96")));
let from = tx.recover_signer().unwrap();
assert_eq!(from, address!("001e2b7dE757bA469a57bF6b23d982458a07eFcE"));
}

#[test]
#[cfg(feature = "k256")]
// Test vector from https://etherscan.io/tx/0x280cde7cdefe4b188750e76c888f13bd05ce9a4d7767730feefe8a0e50ca6fc4
fn test_decode_live_legacy_tx() {
use alloy_primitives::address;

let raw_tx = alloy_primitives::hex::decode("f9015482078b8505d21dba0083022ef1947a250d5630b4cf539739df2c5dacb4c659f2488d880c46549a521b13d8b8e47ff36ab50000000000000000000000000000000000000000000066ab5a608bd00a23f2fe000000000000000000000000000000000000000000000000000000000000008000000000000000000000000048c04ed5691981c42154c6167398f95e8f38a7ff00000000000000000000000000000000000000000000000000000000632ceac70000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc20000000000000000000000006c6ee5e31d828de241282b9606c8e98ea48526e225a0c9077369501641a92ef7399ff81c21639ed4fd8fc69cb793cfa1dbfab342e10aa0615facb2f1bcf3274a354cfe384a38d0cc008a11c2dd23a69111bc6930ba27a8").unwrap();
let res = TxEnvelope::decode(&mut raw_tx.as_slice()).unwrap();
assert_eq!(res.tx_type(), TxType::Legacy);

let tx = match res {
TxEnvelope::Legacy(tx) => tx,
_ => unreachable!(),
};

assert_eq!(tx.tx().to, TxKind::Call(address!("7a250d5630B4cF539739dF2C5dAcb4c659F2488D")));
let from = tx.recover_signer().unwrap();
assert_eq!(from, address!("a12e1462d0ceD572f396F58B6E2D03894cD7C8a4"));
}

fn test_encode_decode_roundtrip<T: Transaction>(tx: T)
where
Signed<T, T::Signature>: Into<TxEnvelope>,
Expand Down
22 changes: 15 additions & 7 deletions crates/eips/src/eip2718.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! [EIP-2718]: https://eips.ethereum.org/EIPS/eip-2718
use alloy_primitives::{keccak256, Sealed, B256};
use alloy_rlp::{BufMut, Header};
use alloy_rlp::{BufMut, Header, EMPTY_STRING_CODE};

// https://eips.ethereum.org/EIPS/eip-2718#transactiontype-only-goes-up-to-0x7f
const TX_TYPE_BYTE_MAX: u8 = 0x7f;
Expand Down Expand Up @@ -60,28 +60,36 @@ pub trait Decodable2718: Sized {
/// Decode an EIP-2718 transaction in the network format.
///
/// The network format is the RLP encoded string consisting of the
/// type-flag prepneded to an opaque inner encoding. The inner encoding is
/// type-flag prepended to an opaque inner encoding. The inner encoding is
/// RLP for all current Ethereum transaction types, but may not be in future
/// versions of the protocol.
fn network_decode(buf: &mut &[u8]) -> Result<Self, Eip2718Error> {
let h_decode = &mut *buf;
let h = Header::decode(h_decode)?;
// Keep the original buffer around by copying it.
let mut h_decode = *buf;
let h = Header::decode(&mut h_decode)?;

// If it's a list, we need to fallback to the legacy decoding.
if h.list {
return Self::fallback_decode(buf);
} else {
*buf = h_decode;
}

let pre_len = buf.len();
if pre_len == 0 || pre_len < h.payload_length {
let remaining_len = buf.len();

if remaining_len == 0 || remaining_len < h.payload_length {
return Err(alloy_rlp::Error::InputTooShort.into());
}

let ty = buf[0];
let buf = &mut &buf[1..];
let tx = Self::typed_decode(ty, buf)?;

if buf.len() != pre_len - h.payload_length {
let bytes_consumed = remaining_len - buf.len();
// because Header::decode works for single bytes (including the tx type), returning a
// string Header with payload_length of 1, we need to make sure this check is only
// performed for transactions with a string header
if bytes_consumed != h.payload_length && h_decode[0] > EMPTY_STRING_CODE {
return Err(alloy_rlp::Error::UnexpectedLength.into());
}

Expand Down

0 comments on commit 269d902

Please sign in to comment.