mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore: rm pooledtx element type (#13286)
This commit is contained in:
@ -45,9 +45,9 @@ pub use static_file::StaticFileSegment;
|
||||
|
||||
pub use transaction::{
|
||||
util::secp256k1::{public_key_to_address, recover_signer_unchecked, sign_message},
|
||||
BlobTransaction, InvalidTransactionError, PooledTransactionsElement,
|
||||
PooledTransactionsElementEcRecovered, RecoveredTx, Transaction, TransactionMeta,
|
||||
TransactionSigned, TransactionSignedEcRecovered, TxType,
|
||||
InvalidTransactionError, PooledTransactionsElement, PooledTransactionsElementEcRecovered,
|
||||
RecoveredTx, Transaction, TransactionMeta, TransactionSigned, TransactionSignedEcRecovered,
|
||||
TxType,
|
||||
};
|
||||
|
||||
pub use alloy_consensus::ReceiptWithBloom;
|
||||
|
||||
@ -2,20 +2,24 @@
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::{
|
||||
transaction::RlpEcdsaTx, SignableTransaction, Signed, Transaction as _, TxEip1559, TxEip2930,
|
||||
TxEip4844, TxEip4844Variant, TxEip7702, TxLegacy, Typed2718, TypedTransaction,
|
||||
transaction::{PooledTransaction, RlpEcdsaTx},
|
||||
SignableTransaction, Signed, Transaction as _, TxEip1559, TxEip2930, TxEip4844,
|
||||
TxEip4844Variant, TxEip4844WithSidecar, TxEip7702, TxLegacy, Typed2718, TypedTransaction,
|
||||
};
|
||||
use alloy_eips::{
|
||||
eip2718::{Decodable2718, Eip2718Error, Eip2718Result, Encodable2718},
|
||||
eip2930::AccessList,
|
||||
eip4844::BlobTransactionSidecar,
|
||||
eip7702::SignedAuthorization,
|
||||
};
|
||||
use alloy_primitives::{
|
||||
keccak256, Address, Bytes, ChainId, PrimitiveSignature as Signature, TxHash, TxKind, B256, U256,
|
||||
};
|
||||
use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header};
|
||||
pub use compat::FillTxEnv;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use derive_more::{AsRef, Deref};
|
||||
pub use meta::TransactionMeta;
|
||||
use once_cell as _;
|
||||
#[cfg(not(feature = "std"))]
|
||||
use once_cell::sync::{Lazy as LazyLock, OnceCell as OnceLock};
|
||||
@ -23,24 +27,20 @@ use once_cell::sync::{Lazy as LazyLock, OnceCell as OnceLock};
|
||||
use op_alloy_consensus::DepositTransaction;
|
||||
#[cfg(feature = "optimism")]
|
||||
use op_alloy_consensus::TxDeposit;
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
use reth_primitives_traits::{InMemorySize, SignedTransaction};
|
||||
use revm_primitives::{AuthorizationList, TxEnv};
|
||||
use serde::{Deserialize, Serialize};
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::{LazyLock, OnceLock};
|
||||
|
||||
pub use compat::FillTxEnv;
|
||||
pub use meta::TransactionMeta;
|
||||
pub use pooled::{PooledTransactionsElement, PooledTransactionsElementEcRecovered};
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
pub use reth_primitives_traits::{
|
||||
transaction::error::{
|
||||
InvalidTransactionError, TransactionConversionError, TryFromRecoveredTransactionError,
|
||||
},
|
||||
WithEncoded,
|
||||
};
|
||||
pub use sidecar::BlobTransaction;
|
||||
use reth_primitives_traits::{InMemorySize, SignedTransaction};
|
||||
use revm_primitives::{AuthorizationList, TxEnv};
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use signature::{recover_signer, recover_signer_unchecked};
|
||||
#[cfg(feature = "std")]
|
||||
use std::sync::{LazyLock, OnceLock};
|
||||
pub use tx_type::TxType;
|
||||
|
||||
/// Handling transaction signature operations, including signature recovery,
|
||||
@ -52,7 +52,6 @@ pub(crate) mod access_list;
|
||||
mod compat;
|
||||
mod meta;
|
||||
mod pooled;
|
||||
mod sidecar;
|
||||
mod tx_type;
|
||||
|
||||
#[cfg(any(test, feature = "reth-codec"))]
|
||||
@ -857,7 +856,7 @@ impl TransactionSigned {
|
||||
/// Tries to convert a [`TransactionSigned`] into a [`PooledTransactionsElement`].
|
||||
///
|
||||
/// This function used as a helper to convert from a decoded p2p broadcast message to
|
||||
/// [`PooledTransactionsElement`]. Since [`BlobTransaction`] is disallowed to be broadcasted on
|
||||
/// [`PooledTransactionsElement`]. Since EIP4844 variants are disallowed to be broadcasted on
|
||||
/// p2p, return an err if `tx` is [`Transaction::Eip4844`].
|
||||
pub fn try_into_pooled(self) -> Result<PooledTransactionsElement, Self> {
|
||||
let hash = self.hash();
|
||||
@ -882,6 +881,32 @@ impl TransactionSigned {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts from an EIP-4844 [`RecoveredTx`] to a
|
||||
/// [`PooledTransactionsElementEcRecovered`] with the given sidecar.
|
||||
///
|
||||
/// Returns an `Err` containing the original `TransactionSigned` if the transaction is not
|
||||
/// EIP-4844.
|
||||
pub fn try_into_pooled_eip4844(
|
||||
self,
|
||||
sidecar: BlobTransactionSidecar,
|
||||
) -> Result<PooledTransactionsElement, Self> {
|
||||
let hash = self.hash();
|
||||
Ok(match self {
|
||||
// If the transaction is an EIP-4844 transaction...
|
||||
Self { transaction: Transaction::Eip4844(tx), signature, .. } => {
|
||||
// Construct a pooled eip488 tx with the provided sidecar.
|
||||
PooledTransactionsElement::Eip4844(Signed::new_unchecked(
|
||||
TxEip4844WithSidecar { tx, sidecar },
|
||||
signature,
|
||||
hash,
|
||||
))
|
||||
}
|
||||
// If the transaction is not EIP-4844, return an error with the original
|
||||
// transaction.
|
||||
_ => return Err(self),
|
||||
})
|
||||
}
|
||||
|
||||
/// Transaction hash. Used to identify transaction.
|
||||
pub fn hash(&self) -> TxHash {
|
||||
*self.tx_hash()
|
||||
@ -1165,6 +1190,26 @@ impl From<RecoveredTx> for TransactionSigned {
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<TransactionSigned> for PooledTransaction {
|
||||
type Error = TransactionConversionError;
|
||||
|
||||
fn try_from(tx: TransactionSigned) -> Result<Self, Self::Error> {
|
||||
tx.try_into_pooled().map_err(|_| TransactionConversionError::UnsupportedForP2P)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PooledTransaction> for TransactionSigned {
|
||||
fn from(tx: PooledTransaction) -> Self {
|
||||
match tx {
|
||||
PooledTransaction::Legacy(signed) => signed.into(),
|
||||
PooledTransaction::Eip2930(signed) => signed.into(),
|
||||
PooledTransaction::Eip1559(signed) => signed.into(),
|
||||
PooledTransaction::Eip4844(signed) => signed.into(),
|
||||
PooledTransaction::Eip7702(signed) => signed.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for TransactionSigned {
|
||||
/// This encodes the transaction _with_ the signature, and an rlp header.
|
||||
///
|
||||
@ -1406,6 +1451,13 @@ impl From<Signed<Transaction>> for TransactionSigned {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Signed<TxEip4844WithSidecar>> for TransactionSigned {
|
||||
fn from(value: Signed<TxEip4844WithSidecar>) -> Self {
|
||||
let (tx, sig, hash) = value.into_parts();
|
||||
Self::new(tx.tx.into(), sig, hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TransactionSigned> for Signed<Transaction> {
|
||||
fn from(value: TransactionSigned) -> Self {
|
||||
let (tx, sig, hash) = value.into_parts();
|
||||
|
||||
@ -1,670 +1,12 @@
|
||||
//! Defines the types for blob transactions, legacy, and other EIP-2718 transactions included in a
|
||||
//! response to `GetPooledTransactions`.
|
||||
|
||||
use super::{
|
||||
recover_signer_unchecked, signature::recover_signer, TransactionConversionError, TxEip7702,
|
||||
};
|
||||
use crate::{BlobTransaction, RecoveredTx, Transaction, TransactionSigned};
|
||||
use alloc::vec::Vec;
|
||||
use alloy_consensus::{
|
||||
constants::EIP4844_TX_TYPE_ID,
|
||||
transaction::{RlpEcdsaTx, TxEip1559, TxEip2930, TxEip4844, TxLegacy},
|
||||
SignableTransaction, Signed, TxEip4844WithSidecar, Typed2718,
|
||||
};
|
||||
use alloy_eips::{
|
||||
eip2718::{Decodable2718, Eip2718Result, Encodable2718},
|
||||
eip2930::AccessList,
|
||||
eip4844::BlobTransactionSidecar,
|
||||
eip7702::SignedAuthorization,
|
||||
};
|
||||
use alloy_primitives::{
|
||||
Address, Bytes, ChainId, PrimitiveSignature as Signature, TxHash, TxKind, B256, U256,
|
||||
};
|
||||
use alloy_rlp::{Decodable, Encodable, Error as RlpError, Header};
|
||||
use bytes::Buf;
|
||||
use core::hash::{Hash, Hasher};
|
||||
use reth_primitives_traits::{InMemorySize, SignedTransaction};
|
||||
use revm_primitives::keccak256;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::RecoveredTx;
|
||||
use alloy_eips::eip4844::BlobTransactionSidecar;
|
||||
use reth_primitives_traits::transaction::error::TransactionConversionError;
|
||||
|
||||
/// A response to `GetPooledTransactions`. This can include either a blob transaction, or a
|
||||
/// non-4844 signed transaction.
|
||||
#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PooledTransactionsElement {
|
||||
/// An untagged [`TxLegacy`].
|
||||
Legacy(Signed<TxLegacy>),
|
||||
/// A [`TxEip2930`] tagged with type 1.
|
||||
Eip2930(Signed<TxEip2930>),
|
||||
/// A [`TxEip1559`] tagged with type 2.
|
||||
Eip1559(Signed<TxEip1559>),
|
||||
/// A [`TxEip7702`] tagged with type 4.
|
||||
Eip7702(Signed<TxEip7702>),
|
||||
/// A blob transaction, which includes the transaction, blob data, commitments, and proofs.
|
||||
BlobTransaction(BlobTransaction),
|
||||
}
|
||||
|
||||
impl PooledTransactionsElement {
|
||||
/// Converts from an EIP-4844 [`RecoveredTx`] to a
|
||||
/// [`PooledTransactionsElementEcRecovered`] with the given sidecar.
|
||||
///
|
||||
/// Returns an `Err` containing the original `TransactionSigned` if the transaction is not
|
||||
/// EIP-4844.
|
||||
pub fn try_from_blob_transaction(
|
||||
tx: TransactionSigned,
|
||||
sidecar: BlobTransactionSidecar,
|
||||
) -> Result<Self, TransactionSigned> {
|
||||
let hash = tx.hash();
|
||||
Ok(match tx {
|
||||
// If the transaction is an EIP-4844 transaction...
|
||||
TransactionSigned { transaction: Transaction::Eip4844(tx), signature, .. } => {
|
||||
// Construct a `PooledTransactionsElement::BlobTransaction` with provided sidecar.
|
||||
Self::BlobTransaction(BlobTransaction(Signed::new_unchecked(
|
||||
TxEip4844WithSidecar { tx, sidecar },
|
||||
signature,
|
||||
hash,
|
||||
)))
|
||||
}
|
||||
// If the transaction is not EIP-4844, return an error with the original
|
||||
// transaction.
|
||||
_ => return Err(tx),
|
||||
})
|
||||
}
|
||||
|
||||
/// Heavy operation that return signature hash over rlp encoded transaction.
|
||||
/// It is only for signature signing or signer recovery.
|
||||
pub fn signature_hash(&self) -> B256 {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.signature_hash(),
|
||||
Self::Eip2930(tx) => tx.signature_hash(),
|
||||
Self::Eip1559(tx) => tx.signature_hash(),
|
||||
Self::Eip7702(tx) => tx.signature_hash(),
|
||||
Self::BlobTransaction(tx) => tx.signature_hash(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference to transaction hash. Used to identify transaction.
|
||||
pub const fn hash(&self) -> &TxHash {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.hash(),
|
||||
Self::Eip2930(tx) => tx.hash(),
|
||||
Self::Eip1559(tx) => tx.hash(),
|
||||
Self::Eip7702(tx) => tx.hash(),
|
||||
Self::BlobTransaction(tx) => tx.0.hash(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the signature of the transaction.
|
||||
pub const fn signature(&self) -> &Signature {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.signature(),
|
||||
Self::Eip2930(tx) => tx.signature(),
|
||||
Self::Eip1559(tx) => tx.signature(),
|
||||
Self::Eip7702(tx) => tx.signature(),
|
||||
Self::BlobTransaction(tx) => tx.0.signature(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Recover signer from signature and hash.
|
||||
///
|
||||
/// Returns `None` if the transaction's signature is invalid, see also [`Self::recover_signer`].
|
||||
pub fn recover_signer(&self) -> Option<Address> {
|
||||
recover_signer(self.signature(), self.signature_hash())
|
||||
}
|
||||
|
||||
/// Tries to recover signer and return [`PooledTransactionsElementEcRecovered`].
|
||||
///
|
||||
/// Returns `Err(Self)` if the transaction's signature is invalid, see also
|
||||
/// [`Self::recover_signer`].
|
||||
pub fn try_into_ecrecovered(self) -> Result<PooledTransactionsElementEcRecovered, Self> {
|
||||
match self.recover_signer() {
|
||||
None => Err(self),
|
||||
Some(signer) => Ok(RecoveredTx { signed_transaction: self, signer }),
|
||||
}
|
||||
}
|
||||
|
||||
/// This encodes the transaction _without_ the signature, and is only suitable for creating a
|
||||
/// hash intended for signing.
|
||||
pub fn encode_for_signing(&self, out: &mut dyn bytes::BufMut) {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().encode_for_signing(out),
|
||||
Self::Eip2930(tx) => tx.tx().encode_for_signing(out),
|
||||
Self::Eip1559(tx) => tx.tx().encode_for_signing(out),
|
||||
Self::BlobTransaction(tx) => tx.tx().encode_for_signing(out),
|
||||
Self::Eip7702(tx) => tx.tx().encode_for_signing(out),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create [`RecoveredTx`] by converting this transaction into
|
||||
/// [`TransactionSigned`] and [`Address`] of the signer.
|
||||
pub fn into_ecrecovered_transaction(self, signer: Address) -> RecoveredTx {
|
||||
RecoveredTx::from_signed_transaction(self.into_transaction(), signer)
|
||||
}
|
||||
|
||||
/// Returns the inner [`TransactionSigned`].
|
||||
pub fn into_transaction(self) -> TransactionSigned {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.into(),
|
||||
Self::Eip2930(tx) => tx.into(),
|
||||
Self::Eip1559(tx) => tx.into(),
|
||||
Self::Eip7702(tx) => tx.into(),
|
||||
Self::BlobTransaction(blob_tx) => blob_tx.into_parts().0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if the transaction is an EIP-4844 transaction.
|
||||
#[inline]
|
||||
pub const fn is_eip4844(&self) -> bool {
|
||||
matches!(self, Self::BlobTransaction(_))
|
||||
}
|
||||
|
||||
/// Returns the [`TxLegacy`] variant if the transaction is a legacy transaction.
|
||||
pub const fn as_legacy(&self) -> Option<&TxLegacy> {
|
||||
match self {
|
||||
Self::Legacy(tx) => Some(tx.tx()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`TxEip2930`] variant if the transaction is an EIP-2930 transaction.
|
||||
pub const fn as_eip2930(&self) -> Option<&TxEip2930> {
|
||||
match self {
|
||||
Self::Eip2930(tx) => Some(tx.tx()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`TxEip1559`] variant if the transaction is an EIP-1559 transaction.
|
||||
pub const fn as_eip1559(&self) -> Option<&TxEip1559> {
|
||||
match self {
|
||||
Self::Eip1559(tx) => Some(tx.tx()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`TxEip4844`] variant if the transaction is an EIP-4844 transaction.
|
||||
pub const fn as_eip4844(&self) -> Option<&TxEip4844> {
|
||||
match self {
|
||||
Self::BlobTransaction(tx) => Some(tx.0.tx().tx()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [`TxEip7702`] variant if the transaction is an EIP-7702 transaction.
|
||||
pub const fn as_eip7702(&self) -> Option<&TxEip7702> {
|
||||
match self {
|
||||
Self::Eip7702(tx) => Some(tx.tx()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the blob gas used for all blobs of the EIP-4844 transaction if it is an EIP-4844
|
||||
/// transaction.
|
||||
///
|
||||
/// This is the number of blobs times the
|
||||
/// [`DATA_GAS_PER_BLOB`](alloy_eips::eip4844::DATA_GAS_PER_BLOB) a single blob consumes.
|
||||
pub fn blob_gas_used(&self) -> Option<u64> {
|
||||
self.as_eip4844().map(TxEip4844::blob_gas)
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for PooledTransactionsElement {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.trie_hash().hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for PooledTransactionsElement {
|
||||
/// This encodes the transaction _with_ the signature, and an rlp header.
|
||||
///
|
||||
/// For legacy transactions, it encodes the transaction data:
|
||||
/// `rlp(tx-data)`
|
||||
///
|
||||
/// For EIP-2718 typed transactions, it encodes the transaction type followed by the rlp of the
|
||||
/// transaction:
|
||||
/// `rlp(tx-type || rlp(tx-data))`
|
||||
fn encode(&self, out: &mut dyn bytes::BufMut) {
|
||||
self.network_encode(out);
|
||||
}
|
||||
|
||||
fn length(&self) -> usize {
|
||||
let mut payload_length = self.encode_2718_len();
|
||||
if !Encodable2718::is_legacy(self) {
|
||||
payload_length += Header { list: false, payload_length }.length();
|
||||
}
|
||||
|
||||
payload_length
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for PooledTransactionsElement {
|
||||
/// Decodes an enveloped post EIP-4844 [`PooledTransactionsElement`].
|
||||
///
|
||||
/// CAUTION: this expects that `buf` is `rlp(tx_type || rlp(tx-data))`
|
||||
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
|
||||
// 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(RlpError::InputTooShort)
|
||||
}
|
||||
|
||||
// keep the original buf around for 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 tx = Self::fallback_decode(&mut original_encoding)?;
|
||||
|
||||
// advance the buffer by however long the legacy transaction decoding advanced the
|
||||
// buffer
|
||||
*buf = original_encoding;
|
||||
|
||||
Ok(tx)
|
||||
} else {
|
||||
// decode the type byte, only decode BlobTransaction if it is a 4844 transaction
|
||||
let tx_type = *buf.first().ok_or(RlpError::InputTooShort)?;
|
||||
let remaining_len = buf.len();
|
||||
|
||||
// Advance the buffer past the type byte
|
||||
buf.advance(1);
|
||||
|
||||
let tx = Self::typed_decode(tx_type, buf).map_err(RlpError::from)?;
|
||||
|
||||
// check that the bytes consumed match the payload length
|
||||
let bytes_consumed = remaining_len - buf.len();
|
||||
if bytes_consumed != header.payload_length {
|
||||
return Err(RlpError::UnexpectedLength)
|
||||
}
|
||||
|
||||
Ok(tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable2718 for PooledTransactionsElement {
|
||||
fn type_flag(&self) -> Option<u8> {
|
||||
match self {
|
||||
Self::Legacy(_) => None,
|
||||
Self::Eip2930(_) => Some(0x01),
|
||||
Self::Eip1559(_) => Some(0x02),
|
||||
Self::BlobTransaction(_) => Some(0x03),
|
||||
Self::Eip7702(_) => Some(0x04),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_2718_len(&self) -> usize {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.eip2718_encoded_length(),
|
||||
Self::Eip2930(tx) => tx.eip2718_encoded_length(),
|
||||
Self::Eip1559(tx) => tx.eip2718_encoded_length(),
|
||||
Self::Eip7702(tx) => tx.eip2718_encoded_length(),
|
||||
Self::BlobTransaction(tx) => tx.eip2718_encoded_length(),
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_2718(&self, out: &mut dyn alloy_rlp::BufMut) {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.eip2718_encode(out),
|
||||
Self::Eip2930(tx) => tx.eip2718_encode(out),
|
||||
Self::Eip1559(tx) => tx.eip2718_encode(out),
|
||||
Self::Eip7702(tx) => tx.eip2718_encode(out),
|
||||
Self::BlobTransaction(tx) => tx.eip2718_encode(out),
|
||||
}
|
||||
}
|
||||
|
||||
fn trie_hash(&self) -> B256 {
|
||||
*self.hash()
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable2718 for PooledTransactionsElement {
|
||||
fn typed_decode(ty: u8, buf: &mut &[u8]) -> Eip2718Result<Self> {
|
||||
match ty {
|
||||
EIP4844_TX_TYPE_ID => {
|
||||
// Recall that the blob transaction response `TransactionPayload` 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])`
|
||||
|
||||
// Now, we decode the inner blob transaction:
|
||||
// `rlp([[chain_id, nonce, ...], blobs, commitments, proofs])`
|
||||
let blob_tx = BlobTransaction::decode_inner(buf)?;
|
||||
Ok(Self::BlobTransaction(blob_tx))
|
||||
}
|
||||
tx_type => {
|
||||
let typed_tx = TransactionSigned::typed_decode(tx_type, buf)?;
|
||||
let hash = typed_tx.hash();
|
||||
match typed_tx.transaction {
|
||||
Transaction::Legacy(_) => Err(RlpError::Custom(
|
||||
"legacy transactions should not be a result of typed decoding",
|
||||
).into()),
|
||||
// because we checked the tx type, we can be sure that the transaction is not a
|
||||
// blob transaction
|
||||
Transaction::Eip4844(_) => Err(RlpError::Custom(
|
||||
"EIP-4844 transactions can only be decoded with transaction type 0x03",
|
||||
).into()),
|
||||
Transaction::Eip2930(tx) => Ok(Self::Eip2930 (
|
||||
Signed::new_unchecked(tx, typed_tx.signature, hash)
|
||||
)),
|
||||
Transaction::Eip1559(tx) => Ok(Self::Eip1559( Signed::new_unchecked(tx, typed_tx.signature, hash))),
|
||||
Transaction::Eip7702(tx) => Ok(Self::Eip7702( Signed::new_unchecked(tx, typed_tx.signature, hash))),
|
||||
#[cfg(feature = "optimism")]
|
||||
Transaction::Deposit(_) => Err(RlpError::Custom("Optimism deposit transaction cannot be decoded to PooledTransactionsElement").into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fallback_decode(buf: &mut &[u8]) -> Eip2718Result<Self> {
|
||||
Ok(Self::Legacy(TxLegacy::rlp_decode_signed(buf)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl Typed2718 for PooledTransactionsElement {
|
||||
fn ty(&self) -> u8 {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().ty(),
|
||||
Self::Eip2930(tx) => tx.tx().ty(),
|
||||
Self::Eip1559(tx) => tx.tx().ty(),
|
||||
Self::BlobTransaction(tx) => tx.tx().ty(),
|
||||
Self::Eip7702(tx) => tx.tx().ty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl alloy_consensus::Transaction for PooledTransactionsElement {
|
||||
fn chain_id(&self) -> Option<ChainId> {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().chain_id(),
|
||||
Self::Eip2930(tx) => tx.tx().chain_id(),
|
||||
Self::Eip1559(tx) => tx.tx().chain_id(),
|
||||
Self::Eip7702(tx) => tx.tx().chain_id(),
|
||||
Self::BlobTransaction(tx) => tx.tx().chain_id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn nonce(&self) -> u64 {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().nonce(),
|
||||
Self::Eip2930(tx) => tx.tx().nonce(),
|
||||
Self::Eip1559(tx) => tx.tx().nonce(),
|
||||
Self::Eip7702(tx) => tx.tx().nonce(),
|
||||
Self::BlobTransaction(tx) => tx.tx().nonce(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gas_limit(&self) -> u64 {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().gas_limit(),
|
||||
Self::Eip2930(tx) => tx.tx().gas_limit(),
|
||||
Self::Eip1559(tx) => tx.tx().gas_limit(),
|
||||
Self::Eip7702(tx) => tx.tx().gas_limit(),
|
||||
Self::BlobTransaction(tx) => tx.tx().gas_limit(),
|
||||
}
|
||||
}
|
||||
|
||||
fn gas_price(&self) -> Option<u128> {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().gas_price(),
|
||||
Self::Eip2930(tx) => tx.tx().gas_price(),
|
||||
Self::Eip1559(tx) => tx.tx().gas_price(),
|
||||
Self::Eip7702(tx) => tx.tx().gas_price(),
|
||||
Self::BlobTransaction(tx) => tx.tx().gas_price(),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_fee_per_gas(&self) -> u128 {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().max_fee_per_gas(),
|
||||
Self::Eip2930(tx) => tx.tx().max_fee_per_gas(),
|
||||
Self::Eip1559(tx) => tx.tx().max_fee_per_gas(),
|
||||
Self::Eip7702(tx) => tx.tx().max_fee_per_gas(),
|
||||
Self::BlobTransaction(tx) => tx.tx().max_fee_per_gas(),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_priority_fee_per_gas(&self) -> Option<u128> {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().max_priority_fee_per_gas(),
|
||||
Self::Eip2930(tx) => tx.tx().max_priority_fee_per_gas(),
|
||||
Self::Eip1559(tx) => tx.tx().max_priority_fee_per_gas(),
|
||||
Self::Eip7702(tx) => tx.tx().max_priority_fee_per_gas(),
|
||||
Self::BlobTransaction(tx) => tx.tx().max_priority_fee_per_gas(),
|
||||
}
|
||||
}
|
||||
|
||||
fn max_fee_per_blob_gas(&self) -> Option<u128> {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().max_fee_per_blob_gas(),
|
||||
Self::Eip2930(tx) => tx.tx().max_fee_per_blob_gas(),
|
||||
Self::Eip1559(tx) => tx.tx().max_fee_per_blob_gas(),
|
||||
Self::Eip7702(tx) => tx.tx().max_fee_per_blob_gas(),
|
||||
Self::BlobTransaction(tx) => tx.tx().max_fee_per_blob_gas(),
|
||||
}
|
||||
}
|
||||
|
||||
fn priority_fee_or_price(&self) -> u128 {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().priority_fee_or_price(),
|
||||
Self::Eip2930(tx) => tx.tx().priority_fee_or_price(),
|
||||
Self::Eip1559(tx) => tx.tx().priority_fee_or_price(),
|
||||
Self::Eip7702(tx) => tx.tx().priority_fee_or_price(),
|
||||
Self::BlobTransaction(tx) => tx.tx().priority_fee_or_price(),
|
||||
}
|
||||
}
|
||||
|
||||
fn effective_gas_price(&self, base_fee: Option<u64>) -> u128 {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().effective_gas_price(base_fee),
|
||||
Self::Eip2930(tx) => tx.tx().effective_gas_price(base_fee),
|
||||
Self::Eip1559(tx) => tx.tx().effective_gas_price(base_fee),
|
||||
Self::Eip7702(tx) => tx.tx().effective_gas_price(base_fee),
|
||||
Self::BlobTransaction(tx) => tx.tx().effective_gas_price(base_fee),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_dynamic_fee(&self) -> bool {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().is_dynamic_fee(),
|
||||
Self::Eip2930(tx) => tx.tx().is_dynamic_fee(),
|
||||
Self::Eip1559(tx) => tx.tx().is_dynamic_fee(),
|
||||
Self::Eip7702(tx) => tx.tx().is_dynamic_fee(),
|
||||
Self::BlobTransaction(tx) => tx.tx().is_dynamic_fee(),
|
||||
}
|
||||
}
|
||||
|
||||
fn kind(&self) -> TxKind {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().kind(),
|
||||
Self::Eip2930(tx) => tx.tx().kind(),
|
||||
Self::Eip1559(tx) => tx.tx().kind(),
|
||||
Self::Eip7702(tx) => tx.tx().kind(),
|
||||
Self::BlobTransaction(tx) => tx.tx().kind(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_create(&self) -> bool {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().is_create(),
|
||||
Self::Eip2930(tx) => tx.tx().is_create(),
|
||||
Self::Eip1559(tx) => tx.tx().is_create(),
|
||||
Self::Eip7702(tx) => tx.tx().is_create(),
|
||||
Self::BlobTransaction(tx) => tx.tx().is_create(),
|
||||
}
|
||||
}
|
||||
|
||||
fn value(&self) -> U256 {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().value(),
|
||||
Self::Eip2930(tx) => tx.tx().value(),
|
||||
Self::Eip1559(tx) => tx.tx().value(),
|
||||
Self::Eip7702(tx) => tx.tx().value(),
|
||||
Self::BlobTransaction(tx) => tx.tx().value(),
|
||||
}
|
||||
}
|
||||
|
||||
fn input(&self) -> &Bytes {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().input(),
|
||||
Self::Eip2930(tx) => tx.tx().input(),
|
||||
Self::Eip1559(tx) => tx.tx().input(),
|
||||
Self::Eip7702(tx) => tx.tx().input(),
|
||||
Self::BlobTransaction(tx) => tx.tx().input(),
|
||||
}
|
||||
}
|
||||
|
||||
fn access_list(&self) -> Option<&AccessList> {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().access_list(),
|
||||
Self::Eip2930(tx) => tx.tx().access_list(),
|
||||
Self::Eip1559(tx) => tx.tx().access_list(),
|
||||
Self::Eip7702(tx) => tx.tx().access_list(),
|
||||
Self::BlobTransaction(tx) => tx.tx().access_list(),
|
||||
}
|
||||
}
|
||||
|
||||
fn blob_versioned_hashes(&self) -> Option<&[B256]> {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().blob_versioned_hashes(),
|
||||
Self::Eip2930(tx) => tx.tx().blob_versioned_hashes(),
|
||||
Self::Eip1559(tx) => tx.tx().blob_versioned_hashes(),
|
||||
Self::Eip7702(tx) => tx.tx().blob_versioned_hashes(),
|
||||
Self::BlobTransaction(tx) => tx.tx().blob_versioned_hashes(),
|
||||
}
|
||||
}
|
||||
|
||||
fn authorization_list(&self) -> Option<&[SignedAuthorization]> {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.tx().authorization_list(),
|
||||
Self::Eip2930(tx) => tx.tx().authorization_list(),
|
||||
Self::Eip1559(tx) => tx.tx().authorization_list(),
|
||||
Self::Eip7702(tx) => tx.tx().authorization_list(),
|
||||
Self::BlobTransaction(tx) => tx.tx().authorization_list(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SignedTransaction for PooledTransactionsElement {
|
||||
fn tx_hash(&self) -> &TxHash {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.hash(),
|
||||
Self::Eip2930(tx) => tx.hash(),
|
||||
Self::Eip1559(tx) => tx.hash(),
|
||||
Self::Eip7702(tx) => tx.hash(),
|
||||
Self::BlobTransaction(tx) => tx.hash(),
|
||||
}
|
||||
}
|
||||
|
||||
fn signature(&self) -> &Signature {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.signature(),
|
||||
Self::Eip2930(tx) => tx.signature(),
|
||||
Self::Eip1559(tx) => tx.signature(),
|
||||
Self::Eip7702(tx) => tx.signature(),
|
||||
Self::BlobTransaction(tx) => tx.signature(),
|
||||
}
|
||||
}
|
||||
|
||||
fn recover_signer(&self) -> Option<Address> {
|
||||
let signature_hash = self.signature_hash();
|
||||
recover_signer(self.signature(), signature_hash)
|
||||
}
|
||||
|
||||
fn recover_signer_unchecked_with_buf(&self, buf: &mut Vec<u8>) -> Option<Address> {
|
||||
self.encode_for_signing(buf);
|
||||
let signature_hash = keccak256(buf);
|
||||
recover_signer_unchecked(self.signature(), signature_hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl InMemorySize for PooledTransactionsElement {
|
||||
fn size(&self) -> usize {
|
||||
match self {
|
||||
Self::Legacy(tx) => tx.size(),
|
||||
Self::Eip2930(tx) => tx.size(),
|
||||
Self::Eip1559(tx) => tx.size(),
|
||||
Self::Eip7702(tx) => tx.size(),
|
||||
Self::BlobTransaction(tx) => tx.size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PooledTransactionsElementEcRecovered> for PooledTransactionsElement {
|
||||
fn from(recovered: PooledTransactionsElementEcRecovered) -> Self {
|
||||
recovered.into_signed()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<TransactionSigned> for PooledTransactionsElement {
|
||||
type Error = TransactionConversionError;
|
||||
|
||||
fn try_from(tx: TransactionSigned) -> Result<Self, Self::Error> {
|
||||
tx.try_into_pooled().map_err(|_| TransactionConversionError::UnsupportedForP2P)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PooledTransactionsElement> for TransactionSigned {
|
||||
fn from(element: PooledTransactionsElement) -> Self {
|
||||
match element {
|
||||
PooledTransactionsElement::Legacy(tx) => tx.into(),
|
||||
PooledTransactionsElement::Eip2930(tx) => tx.into(),
|
||||
PooledTransactionsElement::Eip1559(tx) => tx.into(),
|
||||
PooledTransactionsElement::Eip7702(tx) => tx.into(),
|
||||
PooledTransactionsElement::BlobTransaction(blob_tx) => blob_tx.into_parts().0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
impl<'a> arbitrary::Arbitrary<'a> for PooledTransactionsElement {
|
||||
/// Generates an arbitrary `PooledTransactionsElement`.
|
||||
///
|
||||
/// This function generates an arbitrary `PooledTransactionsElement` by creating a transaction
|
||||
/// and, if applicable, generating a sidecar for blob transactions.
|
||||
///
|
||||
/// It handles the generation of sidecars and constructs the resulting
|
||||
/// `PooledTransactionsElement`.
|
||||
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
|
||||
// Attempt to create a `TransactionSigned` with arbitrary data.
|
||||
let tx_signed = TransactionSigned::arbitrary(u)?;
|
||||
// Attempt to create a `PooledTransactionsElement` with arbitrary data, handling the Result.
|
||||
match tx_signed.try_into_pooled() {
|
||||
Ok(tx) => Ok(tx),
|
||||
Err(tx) => {
|
||||
let (tx, sig, hash) = tx.into_parts();
|
||||
match tx {
|
||||
Transaction::Eip4844(tx) => {
|
||||
let sidecar = BlobTransactionSidecar::arbitrary(u)?;
|
||||
Ok(Self::BlobTransaction(BlobTransaction(Signed::new_unchecked(
|
||||
TxEip4844WithSidecar { tx, sidecar },
|
||||
sig,
|
||||
hash,
|
||||
))))
|
||||
}
|
||||
_ => Err(arbitrary::Error::IncorrectFormat),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Tmp alias for the transaction type.
|
||||
pub type PooledTransactionsElement = alloy_consensus::transaction::PooledTransaction;
|
||||
|
||||
/// A signed pooled transaction with recovered signer.
|
||||
pub type PooledTransactionsElementEcRecovered<T = PooledTransactionsElement> = RecoveredTx<T>;
|
||||
@ -673,7 +15,7 @@ impl PooledTransactionsElementEcRecovered {
|
||||
/// Transform back to [`RecoveredTx`]
|
||||
pub fn into_ecrecovered_transaction(self) -> RecoveredTx {
|
||||
let (tx, signer) = self.to_components();
|
||||
tx.into_ecrecovered_transaction(signer)
|
||||
RecoveredTx::from_signed_transaction(tx.into(), signer)
|
||||
}
|
||||
|
||||
/// Converts from an EIP-4844 [`RecoveredTx`] to a
|
||||
@ -685,9 +27,9 @@ impl PooledTransactionsElementEcRecovered {
|
||||
sidecar: BlobTransactionSidecar,
|
||||
) -> Result<Self, RecoveredTx> {
|
||||
let RecoveredTx { signer, signed_transaction } = tx;
|
||||
let transaction =
|
||||
PooledTransactionsElement::try_from_blob_transaction(signed_transaction, sidecar)
|
||||
.map_err(|tx| RecoveredTx { signer, signed_transaction: tx })?;
|
||||
let transaction = signed_transaction
|
||||
.try_into_pooled_eip4844(sidecar)
|
||||
.map_err(|tx| RecoveredTx { signer, signed_transaction: tx })?;
|
||||
Ok(Self::from_signed_transaction(transaction, signer))
|
||||
}
|
||||
}
|
||||
@ -709,8 +51,10 @@ impl TryFrom<RecoveredTx> for PooledTransactionsElementEcRecovered {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloy_consensus::Transaction as _;
|
||||
use alloy_consensus::{transaction::RlpEcdsaTx, Transaction as _, TxLegacy};
|
||||
use alloy_eips::eip2718::Decodable2718;
|
||||
use alloy_primitives::{address, hex};
|
||||
use alloy_rlp::Decodable;
|
||||
use assert_matches::assert_matches;
|
||||
use bytes::Bytes;
|
||||
|
||||
@ -761,10 +105,7 @@ mod tests {
|
||||
);
|
||||
|
||||
let res = PooledTransactionsElement::decode_2718(&mut &data[..]).unwrap();
|
||||
assert_eq!(
|
||||
res.into_transaction().to(),
|
||||
Some(address!("714b6a4ea9b94a8a7d9fd362ed72630688c8898c"))
|
||||
);
|
||||
assert_eq!(res.to(), Some(address!("714b6a4ea9b94a8a7d9fd362ed72630688c8898c")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@ -1,247 +0,0 @@
|
||||
#![cfg_attr(docsrs, doc(cfg(feature = "c-kzg")))]
|
||||
|
||||
use crate::{Transaction, TransactionSigned};
|
||||
use alloy_consensus::{transaction::RlpEcdsaTx, Signed, TxEip4844WithSidecar};
|
||||
use alloy_eips::eip4844::BlobTransactionSidecar;
|
||||
use derive_more::Deref;
|
||||
use reth_primitives_traits::InMemorySize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A response to `GetPooledTransactions` that includes blob data, their commitments, and their
|
||||
/// corresponding proofs.
|
||||
///
|
||||
/// This is defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#networking) as an element
|
||||
/// of a `PooledTransactions` response.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Deref)]
|
||||
pub struct BlobTransaction(pub Signed<TxEip4844WithSidecar>);
|
||||
|
||||
impl BlobTransaction {
|
||||
/// Constructs a new [`BlobTransaction`] from a [`TransactionSigned`] and a
|
||||
/// [`BlobTransactionSidecar`].
|
||||
///
|
||||
/// Returns an error if the signed transaction is not [`Transaction::Eip4844`]
|
||||
pub fn try_from_signed(
|
||||
tx: TransactionSigned,
|
||||
sidecar: BlobTransactionSidecar,
|
||||
) -> Result<Self, (TransactionSigned, BlobTransactionSidecar)> {
|
||||
let hash = tx.hash();
|
||||
let TransactionSigned { transaction, signature, .. } = tx;
|
||||
match transaction {
|
||||
Transaction::Eip4844(transaction) => Ok(Self(Signed::new_unchecked(
|
||||
TxEip4844WithSidecar { tx: transaction, sidecar },
|
||||
signature,
|
||||
hash,
|
||||
))),
|
||||
transaction => {
|
||||
let tx = TransactionSigned::new(transaction, signature, hash);
|
||||
Err((tx, sidecar))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Splits the [`BlobTransaction`] into its [`TransactionSigned`] and [`BlobTransactionSidecar`]
|
||||
/// components.
|
||||
pub fn into_parts(self) -> (TransactionSigned, BlobTransactionSidecar) {
|
||||
let (transaction, signature, hash) = self.0.into_parts();
|
||||
let (transaction, sidecar) = transaction.into_parts();
|
||||
let transaction = TransactionSigned::new(transaction.into(), signature, hash);
|
||||
(transaction, sidecar)
|
||||
}
|
||||
|
||||
/// Decodes a [`BlobTransaction`] from RLP. This expects the encoding to be:
|
||||
/// `rlp([transaction_payload_body, blobs, commitments, proofs])`
|
||||
///
|
||||
/// where `transaction_payload_body` is a list:
|
||||
/// `[chain_id, nonce, max_priority_fee_per_gas, ..., y_parity, r, s]`
|
||||
///
|
||||
/// Note: this should be used only when implementing other RLP decoding methods, and does not
|
||||
/// represent the full RLP decoding of the `PooledTransactionsElement` type.
|
||||
pub(crate) fn decode_inner(data: &mut &[u8]) -> alloy_rlp::Result<Self> {
|
||||
let (transaction, signature, hash) =
|
||||
TxEip4844WithSidecar::rlp_decode_signed(data)?.into_parts();
|
||||
Ok(Self(Signed::new_unchecked(transaction, signature, hash)))
|
||||
}
|
||||
}
|
||||
|
||||
impl InMemorySize for BlobTransaction {
|
||||
fn size(&self) -> usize {
|
||||
// TODO(mattsse): replace with next alloy bump
|
||||
self.0.hash().size() +
|
||||
self.0.signature().size() +
|
||||
self.0.tx().tx().size() +
|
||||
self.0.tx().sidecar.size()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "c-kzg"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{kzg::Blob, PooledTransactionsElement};
|
||||
use alloc::vec::Vec;
|
||||
use alloy_eips::{
|
||||
eip2718::{Decodable2718, Encodable2718},
|
||||
eip4844::Bytes48,
|
||||
};
|
||||
use alloy_primitives::hex;
|
||||
use std::{fs, path::PathBuf, str::FromStr};
|
||||
|
||||
#[test]
|
||||
fn test_blob_transaction_sidecar_generation() {
|
||||
// Read the contents of the JSON file into a string.
|
||||
let json_content = fs::read_to_string(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/transaction/blob_data/blob1.json"),
|
||||
)
|
||||
.expect("Failed to read the blob data file");
|
||||
|
||||
// Parse the JSON contents into a serde_json::Value
|
||||
let json_value: serde_json::Value =
|
||||
serde_json::from_str(&json_content).expect("Failed to deserialize JSON");
|
||||
|
||||
// Extract blob data from JSON and convert it to Blob
|
||||
let blobs: Vec<Blob> = vec![Blob::from_hex(
|
||||
json_value.get("data").unwrap().as_str().expect("Data is not a valid string"),
|
||||
)
|
||||
.unwrap()];
|
||||
|
||||
// Generate a BlobTransactionSidecar from the blobs
|
||||
let sidecar = BlobTransactionSidecar::try_from_blobs(blobs).unwrap();
|
||||
|
||||
// Assert commitment equality
|
||||
assert_eq!(
|
||||
sidecar.commitments,
|
||||
vec![
|
||||
Bytes48::from_str(json_value.get("commitment").unwrap().as_str().unwrap()).unwrap()
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blob_transaction_sidecar_size() {
|
||||
// Vector to store blob data from each file
|
||||
let mut blobs: Vec<Blob> = Vec::new();
|
||||
|
||||
// Iterate over each file in the folder
|
||||
for entry in fs::read_dir(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/transaction/blob_data/"),
|
||||
)
|
||||
.expect("Failed to read blob_data folder")
|
||||
{
|
||||
let entry = entry.expect("Failed to read directory entry");
|
||||
let file_path = entry.path();
|
||||
|
||||
// Ensure the entry is a file and not a directory
|
||||
if !file_path.is_file() || file_path.extension().unwrap_or_default() != "json" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Read the contents of the JSON file into a string.
|
||||
let json_content =
|
||||
fs::read_to_string(file_path).expect("Failed to read the blob data file");
|
||||
|
||||
// Parse the JSON contents into a serde_json::Value
|
||||
let json_value: serde_json::Value =
|
||||
serde_json::from_str(&json_content).expect("Failed to deserialize JSON");
|
||||
|
||||
// Extract blob data from JSON and convert it to Blob
|
||||
if let Some(data) = json_value.get("data") {
|
||||
if let Some(data_str) = data.as_str() {
|
||||
if let Ok(blob) = Blob::from_hex(data_str) {
|
||||
blobs.push(blob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a BlobTransactionSidecar from the blobs
|
||||
let sidecar = BlobTransactionSidecar::try_from_blobs(blobs).unwrap();
|
||||
|
||||
// Assert sidecar size
|
||||
assert_eq!(sidecar.size(), 524672);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blob_transaction_sidecar_rlp_encode() {
|
||||
// Read the contents of the JSON file into a string.
|
||||
let json_content = fs::read_to_string(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/transaction/blob_data/blob1.json"),
|
||||
)
|
||||
.expect("Failed to read the blob data file");
|
||||
|
||||
// Parse the JSON contents into a serde_json::Value
|
||||
let json_value: serde_json::Value =
|
||||
serde_json::from_str(&json_content).expect("Failed to deserialize JSON");
|
||||
|
||||
// Extract blob data from JSON and convert it to Blob
|
||||
let blobs: Vec<Blob> = vec![Blob::from_hex(
|
||||
json_value.get("data").unwrap().as_str().expect("Data is not a valid string"),
|
||||
)
|
||||
.unwrap()];
|
||||
|
||||
// Generate a BlobTransactionSidecar from the blobs
|
||||
let sidecar = BlobTransactionSidecar::try_from_blobs(blobs).unwrap();
|
||||
|
||||
// Create a vector to store the encoded RLP
|
||||
let mut encoded_rlp = Vec::new();
|
||||
|
||||
// Encode the inner data of the BlobTransactionSidecar into RLP
|
||||
sidecar.rlp_encode_fields(&mut encoded_rlp);
|
||||
|
||||
// Assert the equality between the expected RLP from the JSON and the encoded RLP
|
||||
assert_eq!(json_value.get("rlp").unwrap().as_str().unwrap(), hex::encode(&encoded_rlp));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blob_transaction_sidecar_rlp_decode() {
|
||||
// Read the contents of the JSON file into a string.
|
||||
let json_content = fs::read_to_string(
|
||||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/transaction/blob_data/blob1.json"),
|
||||
)
|
||||
.expect("Failed to read the blob data file");
|
||||
|
||||
// Parse the JSON contents into a serde_json::Value
|
||||
let json_value: serde_json::Value =
|
||||
serde_json::from_str(&json_content).expect("Failed to deserialize JSON");
|
||||
|
||||
// Extract blob data from JSON and convert it to Blob
|
||||
let blobs: Vec<Blob> = vec![Blob::from_hex(
|
||||
json_value.get("data").unwrap().as_str().expect("Data is not a valid string"),
|
||||
)
|
||||
.unwrap()];
|
||||
|
||||
// Generate a BlobTransactionSidecar from the blobs
|
||||
let sidecar = BlobTransactionSidecar::try_from_blobs(blobs).unwrap();
|
||||
|
||||
// Create a vector to store the encoded RLP
|
||||
let mut encoded_rlp = Vec::new();
|
||||
|
||||
// Encode the inner data of the BlobTransactionSidecar into RLP
|
||||
sidecar.rlp_encode_fields(&mut encoded_rlp);
|
||||
|
||||
// Decode the RLP-encoded data back into a BlobTransactionSidecar
|
||||
let decoded_sidecar =
|
||||
BlobTransactionSidecar::rlp_decode_fields(&mut encoded_rlp.as_slice()).unwrap();
|
||||
|
||||
// Assert the equality between the original BlobTransactionSidecar and the decoded one
|
||||
assert_eq!(sidecar, decoded_sidecar);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode_encode_raw_4844_rlp() {
|
||||
let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("testdata/4844rlp");
|
||||
let dir = fs::read_dir(path).expect("Unable to read folder");
|
||||
for entry in dir {
|
||||
let entry = entry.unwrap();
|
||||
let content = fs::read_to_string(entry.path()).unwrap();
|
||||
let raw = hex::decode(content.trim()).unwrap();
|
||||
let tx = PooledTransactionsElement::decode_2718(&mut raw.as_ref())
|
||||
.map_err(|err| {
|
||||
panic!("Failed to decode transaction: {:?} {:?}", err, entry.path());
|
||||
})
|
||||
.unwrap();
|
||||
// We want to test only EIP-4844 transactions
|
||||
assert!(tx.is_eip4844());
|
||||
let encoded = tx.encoded_2718();
|
||||
assert_eq!(encoded.as_slice(), &raw[..], "{:?}", entry.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user