reth_codecs/alloy/transaction/
eip4844.rs

1//! Compact implementation for [`AlloyTxEip4844`]
2
3use crate::{Compact, CompactPlaceholder};
4use alloc::vec::Vec;
5use alloy_consensus::TxEip4844 as AlloyTxEip4844;
6use alloy_eips::eip2930::AccessList;
7use alloy_primitives::{Address, Bytes, ChainId, B256, U256};
8use reth_codecs_derive::add_arbitrary_tests;
9
10/// [EIP-4844 Blob Transaction](https://eips.ethereum.org/EIPS/eip-4844#blob-transaction)
11///
12/// This is a helper type to use derive on it instead of manually managing `bitfield`.
13///
14/// By deriving `Compact` here, any future changes or enhancements to the `Compact` derive
15/// will automatically apply to this type.
16///
17/// Notice: Make sure this struct is 1:1 with [`alloy_consensus::TxEip4844`]
18#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Compact)]
19#[reth_codecs(crate = "crate")]
20#[cfg_attr(any(test, feature = "test-utils"), derive(serde::Serialize, serde::Deserialize))]
21#[cfg_attr(feature = "test-utils", allow(unreachable_pub), visibility::make(pub))]
22#[add_arbitrary_tests(crate, compact)]
23pub(crate) struct TxEip4844 {
24    chain_id: ChainId,
25    nonce: u64,
26    gas_limit: u64,
27    max_fee_per_gas: u128,
28    max_priority_fee_per_gas: u128,
29    /// TODO(debt): this should be removed if we break the DB.
30    /// Makes sure that the Compact bitflag struct has one bit after the above field:
31    /// <https://github.com/paradigmxyz/reth/pull/8291#issuecomment-2117545016>
32    #[cfg_attr(
33        feature = "test-utils",
34        serde(
35            serialize_with = "serialize_placeholder",
36            deserialize_with = "deserialize_placeholder"
37        )
38    )]
39    placeholder: Option<CompactPlaceholder>,
40    to: Address,
41    value: U256,
42    access_list: AccessList,
43    blob_versioned_hashes: Vec<B256>,
44    max_fee_per_blob_gas: u128,
45    input: Bytes,
46}
47
48impl Compact for AlloyTxEip4844 {
49    fn to_compact<B>(&self, buf: &mut B) -> usize
50    where
51        B: bytes::BufMut + AsMut<[u8]>,
52    {
53        let tx = TxEip4844 {
54            chain_id: self.chain_id,
55            nonce: self.nonce,
56            gas_limit: self.gas_limit,
57            max_fee_per_gas: self.max_fee_per_gas,
58            max_priority_fee_per_gas: self.max_priority_fee_per_gas,
59            placeholder: Some(()),
60            to: self.to,
61            value: self.value,
62            access_list: self.access_list.clone(),
63            blob_versioned_hashes: self.blob_versioned_hashes.clone(),
64            max_fee_per_blob_gas: self.max_fee_per_blob_gas,
65            input: self.input.clone(),
66        };
67        tx.to_compact(buf)
68    }
69
70    fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) {
71        let (tx, _) = TxEip4844::from_compact(buf, len);
72        let alloy_tx = Self {
73            chain_id: tx.chain_id,
74            nonce: tx.nonce,
75            gas_limit: tx.gas_limit,
76            max_fee_per_gas: tx.max_fee_per_gas,
77            max_priority_fee_per_gas: tx.max_priority_fee_per_gas,
78            to: tx.to,
79            value: tx.value,
80            access_list: tx.access_list,
81            blob_versioned_hashes: tx.blob_versioned_hashes,
82            max_fee_per_blob_gas: tx.max_fee_per_blob_gas,
83            input: tx.input,
84        };
85        (alloy_tx, buf)
86    }
87}
88
89#[cfg(any(test, feature = "test-utils"))]
90impl<'a> arbitrary::Arbitrary<'a> for TxEip4844 {
91    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
92        Ok(Self {
93            chain_id: ChainId::arbitrary(u)?,
94            nonce: u64::arbitrary(u)?,
95            gas_limit: u64::arbitrary(u)?,
96            max_fee_per_gas: u128::arbitrary(u)?,
97            max_priority_fee_per_gas: u128::arbitrary(u)?,
98            // Should always be Some for TxEip4844
99            placeholder: Some(()),
100            to: Address::arbitrary(u)?,
101            value: U256::arbitrary(u)?,
102            access_list: AccessList::arbitrary(u)?,
103            blob_versioned_hashes: Vec::<B256>::arbitrary(u)?,
104            max_fee_per_blob_gas: u128::arbitrary(u)?,
105            input: Bytes::arbitrary(u)?,
106        })
107    }
108}
109
110#[cfg(feature = "test-utils")]
111fn serialize_placeholder<S>(value: &Option<()>, serializer: S) -> Result<S::Ok, S::Error>
112where
113    S: serde::Serializer,
114{
115    // Required otherwise `serde_json` will serialize it as null and would be `None` when decoding
116    // it again.
117    match value {
118        Some(()) => serializer.serialize_str("placeholder"), // Custom serialization
119        None => serializer.serialize_none(),
120    }
121}
122
123#[cfg(feature = "test-utils")]
124fn deserialize_placeholder<'de, D>(deserializer: D) -> Result<Option<()>, D::Error>
125where
126    D: serde::Deserializer<'de>,
127{
128    use serde::de::Deserialize;
129    let s: Option<String> = Option::deserialize(deserializer)?;
130    match s.as_deref() {
131        Some("placeholder") => Ok(Some(())),
132        None => Ok(None),
133        _ => Err(serde::de::Error::custom("unexpected value")),
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use alloy_primitives::{address, bytes};
141
142    #[test]
143    fn backwards_compatible_txkind_test() {
144        // TxEip4844 encoded with TxKind on to field
145        // holesky tx hash: <0xa3b1668225bf0fbfdd6c19aa6fd071fa4ff5d09a607c67ccd458b97735f745ac>
146        let tx = bytes!("224348a100426844cb2dc6c0b2d05e003b9aca0079c9109b764609df928d16fc4a91e9081f7e87db09310001019101fb28118ceccaabca22a47e35b9c3f12eb2dcb25e5c543d5b75e6cd841f0a05328d26ef16e8450000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000007b399987d24fc5951f3e94a4cb16e87414bf22290000000000000000000000001670090000000000000000000000000000010001302e31382e302d64657600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000009e640a6aadf4f664cf467b795c31332f44acbe6c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006614c2d1000000000000000000000000000000000000000000000000000000000014012c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000093100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000093100000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f06fd78f4dcdf089263524731620941747b9b93fd8f631557e25b23845a78b685bd82f9d36bce2f4cc812b6e5191df52479d349089461ffe76e9f2fa2848a0fe1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410819f04aba17677807c61ae72afdddf7737f26931ecfa8af05b7c669808b36a2587e32c90bb0ed2100266dd7797c80121a109a2b0fe941ca5a580e438988cac81c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
147        let (tx, _) = TxEip4844::from_compact(&tx, tx.len());
148        assert_eq!(tx.to, address!("0x79C9109b764609df928d16fC4a91e9081F7e87DB"));
149        assert_eq!(tx.placeholder, Some(()));
150        assert_eq!(tx.input, bytes!("ef16e8450000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000052000000000000000000000000000000000000000000000000000000000000004c000000000000000000000000000000000000000000000000000000000000000200000000000000000000000007b399987d24fc5951f3e94a4cb16e87414bf22290000000000000000000000001670090000000000000000000000000000010001302e31382e302d64657600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000420000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000009e640a6aadf4f664cf467b795c31332f44acbe6c000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000002c00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006614c2d1000000000000000000000000000000000000000000000000000000000014012c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000093100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000093100000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f06fd78f4dcdf089263524731620941747b9b93fd8f631557e25b23845a78b685bd82f9d36bce2f4cc812b6e5191df52479d349089461ffe76e9f2fa2848a0fe1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410819f04aba17677807c61ae72afdddf7737f26931ecfa8af05b7c669808b36a2587e32c90bb0ed2100266dd7797c80121a109a2b0fe941ca5a580e438988cac81c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"));
151    }
152}