Skip to content

Commit

Permalink
move pooled element to pooled.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
Rjected committed Aug 16, 2023
1 parent 13a4a8b commit 9b256dc
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 173 deletions.
175 changes: 2 additions & 173 deletions crates/primitives/src/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub use eip1559::TxEip1559;
pub use eip2930::TxEip2930;
pub use eip4844::{BlobTransaction, BlobTransactionSidecar, TxEip4844};
pub use legacy::TxLegacy;
pub use pooled::PooledTransactionsElement;

mod access_list;
mod eip1559;
Expand All @@ -32,6 +33,7 @@ mod eip4844;
mod error;
mod legacy;
mod meta;
mod pooled;
mod signature;
mod tx_type;
pub(crate) mod util;
Expand Down Expand Up @@ -1320,179 +1322,6 @@ impl IntoRecoveredTransaction for TransactionSignedEcRecovered {
}
}

/// A response to `GetPooledTransactions`. This can include either a blob transaction, or a
/// non-4844 signed transaction.
// TODO: redo arbitrary for this encoding - the previous encoding was incorrect
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PooledTransactionsElement {
/// A blob transaction, which includes the transaction, blob data, commitments, and proofs.
BlobTransaction(BlobTransaction),
/// A non-4844 signed transaction.
Transaction(TransactionSigned),
}

impl PooledTransactionsElement {
/// Decodes the "raw" format of transaction (e.g. `eth_sendRawTransaction`).
///
/// The raw transaction is either a legacy transaction or EIP-2718 typed transaction
/// For legacy transactions, the format is encoded as: `rlp(tx)`
/// For EIP-2718 typed transaction, the format is encoded as the type of the transaction
/// followed by the rlp of the transaction: `type` + `rlp(tx)`
///
/// For encoded EIP-4844 transactions, the blob sidecar _must_ be included.
pub fn decode_enveloped(tx: Bytes) -> Result<Self, DecodeError> {
let mut data = tx.as_ref();

if data.is_empty() {
return Err(DecodeError::InputTooShort)
}

// Check if the tx is a list - tx types are less than EMPTY_LIST_CODE (0xc0)
if data[0] >= EMPTY_LIST_CODE {
// decode as legacy transaction
Ok(Self::Transaction(TransactionSigned::decode_rlp_legacy_transaction(&mut data)?))
} else {
// decode the type byte, only decode BlobTransaction if it is a 4844 transaction
let tx_type = *data.first().ok_or(DecodeError::InputTooShort)?;

if tx_type == EIP4844_TX_TYPE_ID {
// Recall that the blob transaction response `TranactionPayload` is encoded like
// this: `rlp([tx_payload_body, blobs, commitments, proofs])`
//
// Note that `tx_payload_body` is a list:
// `[chain_id, nonce, max_priority_fee_per_gas, ..., y_parity, r, s]`
//
// This makes the full encoding:
// `tx_type (0x03) || rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
//
// First, we advance the buffer past the type byte
data.advance(1);

// Now, we decode the inner blob transaction:
// `rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
let blob_tx = BlobTransaction::decode_inner(&mut data)?;
Ok(PooledTransactionsElement::BlobTransaction(blob_tx))
} else {
// DO NOT advance the buffer for the type, since we want the enveloped decoding to
// decode it again and advance the buffer on its own.
let typed_tx = TransactionSigned::decode_enveloped_typed_transaction(&mut data)?;
Ok(PooledTransactionsElement::Transaction(typed_tx))
}
}
}

/// Returns the inner [TransactionSigned].
pub fn into_transaction(self) -> TransactionSigned {
match self {
Self::Transaction(tx) => tx,
Self::BlobTransaction(blob_tx) => blob_tx.transaction,
}
}
}

impl Encodable for PooledTransactionsElement {
/// Encodes an enveloped post EIP-4844 [PooledTransactionsElement].
fn encode(&self, out: &mut dyn bytes::BufMut) {
match self {
Self::Transaction(tx) => tx.encode(out),
Self::BlobTransaction(blob_tx) => {
// The inner encoding is used with `with_header` set to true, making the final
// encoding:
// `rlp(tx_type || rlp([transaction_payload_body, blobs, commitments, proofs]))`
blob_tx.encode_with_type_inner(out, true);
}
}
}

fn length(&self) -> usize {
match self {
Self::Transaction(tx) => tx.length(),
Self::BlobTransaction(blob_tx) => {
// the encoding uses a header, so we set `with_header` to true
blob_tx.payload_len_with_type(true)
}
}
}
}

impl Decodable for PooledTransactionsElement {
/// Decodes an enveloped post EIP-4844 [PooledTransactionsElement].
///
/// CAUTION: this expects that `buf` is `[id, rlp(tx)]`
fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
// From the EIP-4844 spec:
// Blob transactions have two network representations. During transaction gossip responses
// (`PooledTransactions`), the EIP-2718 `TransactionPayload` of the blob transaction is
// wrapped to become:
//
// `rlp([tx_payload_body, blobs, commitments, proofs])`
//
// This means the full wire encoding is:
// `rlp(tx_type || rlp([transaction_payload_body, blobs, commitments, proofs]))`
//
// First, we check whether or not the transaction is a legacy transaction.
if buf.is_empty() {
return Err(DecodeError::InputTooShort)
}

// keep this around for buffer advancement post-legacy decoding
let mut original_encoding = *buf;

// If the header is a list header, it is a legacy transaction. Otherwise, it is a typed
// transaction
let header = Header::decode(buf)?;

// Check if the tx is a list
if header.list {
// decode as legacy transaction
let legacy_tx =
TransactionSigned::decode_rlp_legacy_transaction(&mut original_encoding)?;

// advance the buffer based on how far `decode_rlp_legacy_transaction` advanced the
// buffer
*buf = original_encoding;

Ok(PooledTransactionsElement::Transaction(legacy_tx))
} else {
// decode the type byte, only decode BlobTransaction if it is a 4844 transaction
let tx_type = *buf.first().ok_or(DecodeError::InputTooShort)?;

if tx_type == EIP4844_TX_TYPE_ID {
// Recall that the blob transaction response `TranactionPayload` is encoded like
// this: `rlp([tx_payload_body, blobs, commitments, proofs])`
//
// Note that `tx_payload_body` is a list:
// `[chain_id, nonce, max_priority_fee_per_gas, ..., y_parity, r, s]`
//
// This makes the full encoding:
// `tx_type (0x03) || rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
//
// First, we advance the buffer past the type byte
buf.advance(1);

// Now, we decode the inner blob transaction:
// `rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
let blob_tx = BlobTransaction::decode_inner(buf)?;
Ok(PooledTransactionsElement::BlobTransaction(blob_tx))
} else {
// DO NOT advance the buffer for the type, since we want the enveloped decoding to
// decode it again and advance the buffer on its own.
let typed_tx = TransactionSigned::decode_enveloped_typed_transaction(buf)?;
Ok(PooledTransactionsElement::Transaction(typed_tx))
}
}
}
}

impl From<TransactionSigned> for PooledTransactionsElement {
/// Converts from a [TransactionSigned] to a [PooledTransactionsElement].
///
/// NOTE: This will always return a [PooledTransactionsElement::Transaction] variant.
fn from(tx: TransactionSigned) -> Self {
Self::Transaction(tx)
}
}

#[cfg(test)]
mod tests {
use crate::{
Expand Down
178 changes: 178 additions & 0 deletions crates/primitives/src/transaction/pooled.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//! Includes the
use crate::{BlobTransaction, Bytes, TransactionSigned, EIP4844_TX_TYPE_ID};
use bytes::Buf;
use reth_rlp::{Decodable, DecodeError, Encodable, Header, EMPTY_LIST_CODE};
use serde::{Deserialize, Serialize};

/// A response to `GetPooledTransactions`. This can include either a blob transaction, or a
/// non-4844 signed transaction.
// TODO: redo arbitrary for this encoding - the previous encoding was incorrect
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum PooledTransactionsElement {
/// A blob transaction, which includes the transaction, blob data, commitments, and proofs.
BlobTransaction(BlobTransaction),
/// A non-4844 signed transaction.
Transaction(TransactionSigned),
}

impl PooledTransactionsElement {
/// Decodes the "raw" format of transaction (e.g. `eth_sendRawTransaction`).
///
/// The raw transaction is either a legacy transaction or EIP-2718 typed transaction
/// For legacy transactions, the format is encoded as: `rlp(tx)`
/// For EIP-2718 typed transaction, the format is encoded as the type of the transaction
/// followed by the rlp of the transaction: `type` + `rlp(tx)`
///
/// For encoded EIP-4844 transactions, the blob sidecar _must_ be included.
pub fn decode_enveloped(tx: Bytes) -> Result<Self, DecodeError> {
let mut data = tx.as_ref();

if data.is_empty() {
return Err(DecodeError::InputTooShort)
}

// Check if the tx is a list - tx types are less than EMPTY_LIST_CODE (0xc0)
if data[0] >= EMPTY_LIST_CODE {
// decode as legacy transaction
Ok(Self::Transaction(TransactionSigned::decode_rlp_legacy_transaction(&mut data)?))
} else {
// decode the type byte, only decode BlobTransaction if it is a 4844 transaction
let tx_type = *data.first().ok_or(DecodeError::InputTooShort)?;

if tx_type == EIP4844_TX_TYPE_ID {
// Recall that the blob transaction response `TranactionPayload` is encoded like
// this: `rlp([tx_payload_body, blobs, commitments, proofs])`
//
// Note that `tx_payload_body` is a list:
// `[chain_id, nonce, max_priority_fee_per_gas, ..., y_parity, r, s]`
//
// This makes the full encoding:
// `tx_type (0x03) || rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
//
// First, we advance the buffer past the type byte
data.advance(1);

// Now, we decode the inner blob transaction:
// `rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
let blob_tx = BlobTransaction::decode_inner(&mut data)?;
Ok(PooledTransactionsElement::BlobTransaction(blob_tx))
} else {
// DO NOT advance the buffer for the type, since we want the enveloped decoding to
// decode it again and advance the buffer on its own.
let typed_tx = TransactionSigned::decode_enveloped_typed_transaction(&mut data)?;
Ok(PooledTransactionsElement::Transaction(typed_tx))
}
}
}

/// Returns the inner [TransactionSigned].
pub fn into_transaction(self) -> TransactionSigned {
match self {
Self::Transaction(tx) => tx,
Self::BlobTransaction(blob_tx) => blob_tx.transaction,
}
}
}

impl Encodable for PooledTransactionsElement {
/// Encodes an enveloped post EIP-4844 [PooledTransactionsElement].
fn encode(&self, out: &mut dyn bytes::BufMut) {
match self {
Self::Transaction(tx) => tx.encode(out),
Self::BlobTransaction(blob_tx) => {
// The inner encoding is used with `with_header` set to true, making the final
// encoding:
// `rlp(tx_type || rlp([transaction_payload_body, blobs, commitments, proofs]))`
blob_tx.encode_with_type_inner(out, true);
}
}
}

fn length(&self) -> usize {
match self {
Self::Transaction(tx) => tx.length(),
Self::BlobTransaction(blob_tx) => {
// the encoding uses a header, so we set `with_header` to true
blob_tx.payload_len_with_type(true)
}
}
}
}

impl Decodable for PooledTransactionsElement {
/// Decodes an enveloped post EIP-4844 [PooledTransactionsElement].
///
/// CAUTION: this expects that `buf` is `[id, rlp(tx)]`
fn decode(buf: &mut &[u8]) -> Result<Self, DecodeError> {
// From the EIP-4844 spec:
// Blob transactions have two network representations. During transaction gossip responses
// (`PooledTransactions`), the EIP-2718 `TransactionPayload` of the blob transaction is
// wrapped to become:
//
// `rlp([tx_payload_body, blobs, commitments, proofs])`
//
// This means the full wire encoding is:
// `rlp(tx_type || rlp([transaction_payload_body, blobs, commitments, proofs]))`
//
// First, we check whether or not the transaction is a legacy transaction.
if buf.is_empty() {
return Err(DecodeError::InputTooShort)
}

// keep this around for buffer advancement post-legacy decoding
let mut original_encoding = *buf;

// If the header is a list header, it is a legacy transaction. Otherwise, it is a typed
// transaction
let header = Header::decode(buf)?;

// Check if the tx is a list
if header.list {
// decode as legacy transaction
let legacy_tx =
TransactionSigned::decode_rlp_legacy_transaction(&mut original_encoding)?;

// advance the buffer based on how far `decode_rlp_legacy_transaction` advanced the
// buffer
*buf = original_encoding;

Ok(PooledTransactionsElement::Transaction(legacy_tx))
} else {
// decode the type byte, only decode BlobTransaction if it is a 4844 transaction
let tx_type = *buf.first().ok_or(DecodeError::InputTooShort)?;

if tx_type == EIP4844_TX_TYPE_ID {
// Recall that the blob transaction response `TranactionPayload` is encoded like
// this: `rlp([tx_payload_body, blobs, commitments, proofs])`
//
// Note that `tx_payload_body` is a list:
// `[chain_id, nonce, max_priority_fee_per_gas, ..., y_parity, r, s]`
//
// This makes the full encoding:
// `tx_type (0x03) || rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
//
// First, we advance the buffer past the type byte
buf.advance(1);

// Now, we decode the inner blob transaction:
// `rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
let blob_tx = BlobTransaction::decode_inner(buf)?;
Ok(PooledTransactionsElement::BlobTransaction(blob_tx))
} else {
// DO NOT advance the buffer for the type, since we want the enveloped decoding to
// decode it again and advance the buffer on its own.
let typed_tx = TransactionSigned::decode_enveloped_typed_transaction(buf)?;
Ok(PooledTransactionsElement::Transaction(typed_tx))
}
}
}
}

impl From<TransactionSigned> for PooledTransactionsElement {
/// Converts from a [TransactionSigned] to a [PooledTransactionsElement].
///
/// NOTE: This will always return a [PooledTransactionsElement::Transaction] variant.
fn from(tx: TransactionSigned) -> Self {
Self::Transaction(tx)
}
}

0 comments on commit 9b256dc

Please sign in to comment.