alloy_consensus/transaction/
pooled.rs1use super::EthereumTxEnvelope;
5use crate::{
6 error::ValueError, Signed, TxEip4844, TxEip4844Variant, TxEip4844WithSidecar, TxEnvelope,
7};
8
9pub type PooledTransaction = EthereumTxEnvelope<TxEip4844WithSidecar>;
17
18impl PooledTransaction {
19 pub fn into_envelope(self) -> TxEnvelope {
21 match self {
22 Self::Legacy(tx) => tx.into(),
23 Self::Eip2930(tx) => tx.into(),
24 Self::Eip1559(tx) => tx.into(),
25 Self::Eip7702(tx) => tx.into(),
26 Self::Eip4844(tx) => tx.into(),
27 }
28 }
29}
30
31impl TryFrom<Signed<TxEip4844Variant>> for PooledTransaction {
32 type Error = ValueError<Signed<TxEip4844Variant>>;
33
34 fn try_from(value: Signed<TxEip4844Variant>) -> Result<Self, Self::Error> {
35 let (value, signature, hash) = value.into_parts();
36 match value {
37 tx @ TxEip4844Variant::TxEip4844(_) => Err(ValueError::new_static(
38 Signed::new_unchecked(tx, signature, hash),
39 "pooled transaction requires 4844 sidecar",
40 )),
41 TxEip4844Variant::TxEip4844WithSidecar(tx) => {
42 Ok(Signed::new_unchecked(tx, signature, hash).into())
43 }
44 }
45 }
46}
47
48impl TryFrom<TxEnvelope> for PooledTransaction {
49 type Error = ValueError<TxEnvelope>;
50
51 fn try_from(value: TxEnvelope) -> Result<Self, Self::Error> {
52 value.try_into_pooled()
53 }
54}
55
56impl TryFrom<EthereumTxEnvelope<TxEip4844>> for PooledTransaction {
57 type Error = ValueError<EthereumTxEnvelope<TxEip4844>>;
58
59 fn try_from(value: EthereumTxEnvelope<TxEip4844>) -> Result<Self, Self::Error> {
60 value.try_into_pooled()
61 }
62}
63
64impl From<PooledTransaction> for TxEnvelope {
65 fn from(tx: PooledTransaction) -> Self {
66 tx.into_envelope()
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use crate::Transaction;
73
74 use super::*;
75 use alloy_eips::{Decodable2718, Encodable2718};
76 use alloy_primitives::{address, hex, Bytes};
77 use alloy_rlp::Decodable;
78 use std::path::PathBuf;
79
80 #[test]
81 fn invalid_legacy_pooled_decoding_input_too_short() {
82 let input_too_short = [
83 &hex!("d90b0280808bc5cd028083c5cdfd9e407c56565656")[..],
85 &hex!("c10b02808083c5cd028883c5cdfd9e407c56565656"),
91 &hex!("c10b0280808bc5cd028083c5cdfd9e407c56565656"),
92 &hex!("d40b02808083c5cdeb8783c5acfd9e407c5656565656"),
95 &hex!("d30102808083c5cd02887dc5cdfd9e64fd9e407c56"),
96 ];
97
98 for hex_data in &input_too_short {
99 let input_rlp = &mut &hex_data[..];
100 let res = PooledTransaction::decode(input_rlp);
101
102 assert!(
103 res.is_err(),
104 "expected err after decoding rlp input: {:x?}",
105 Bytes::copy_from_slice(hex_data)
106 );
107
108 let input_rlp = &mut &hex_data[..];
110 let res = PooledTransaction::decode_2718(input_rlp);
111
112 assert!(
113 res.is_err(),
114 "expected err after decoding enveloped rlp input: {:x?}",
115 Bytes::copy_from_slice(hex_data)
116 );
117 }
118 }
119
120 #[test]
122 fn decode_eip1559_enveloped() {
123 let data = hex!("02f903d382426882ba09832dc6c0848674742682ed9694714b6a4ea9b94a8a7d9fd362ed72630688c8898c80b90364492d24749189822d8512430d3f3ff7a2ede675ac08265c08e2c56ff6fdaa66dae1cdbe4a5d1d7809f3e99272d067364e597542ac0c369d69e22a6399c3e9bee5da4b07e3f3fdc34c32c3d88aa2268785f3e3f8086df0934b10ef92cfffc2e7f3d90f5e83302e31382e302d64657600000000000000000000000000000000000000000000569e75fc77c1a856f6daaf9e69d8a9566ca34aa47f9133711ce065a571af0cfd000000000000000000000000e1e210594771824dad216568b91c9cb4ceed361c00000000000000000000000000000000000000000000000000000000000546e00000000000000000000000000000000000000000000000000000000000e4e1c00000000000000000000000000000000000000000000000000000000065d6750c00000000000000000000000000000000000000000000000000000000000f288000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002cf600000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000000f1628e56fa6d8c50e5b984a58c0df14de31c7b857ce7ba499945b99252976a93d06dcda6776fc42167fbe71cb59f978f5ef5b12577a90b132d14d9c6efa528076f0161d7bf03643cfc5490ec5084f4a041db7f06c50bd97efa08907ba79ddcac8b890f24d12d8db31abbaaf18985d54f400449ee0559a4452afe53de5853ce090000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000064ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000c080a01428023fc54a27544abc421d5d017b9a7c5936ad501cbdecd0d9d12d04c1a033a0753104bbf1c87634d6ff3f0ffa0982710612306003eb022363b57994bdef445a"
124);
125
126 let res = PooledTransaction::decode_2718(&mut &data[..]).unwrap();
127 assert_eq!(res.to(), Some(address!("714b6a4ea9b94a8a7d9fd362ed72630688c8898c")));
128 }
129
130 #[test]
131 fn legacy_valid_pooled_decoding() {
132 let data = &hex!("d30b02808083c5cdeb8783c5acfd9e407c565656")[..];
143
144 let input_rlp = &mut &data[..];
145 let res = PooledTransaction::decode(input_rlp);
146 assert!(res.is_ok());
147 assert!(input_rlp.is_empty());
148
149 let res = PooledTransaction::decode_2718(&mut &data[..]);
151 assert!(res.is_ok());
152 }
153
154 #[test]
155 fn decode_encode_raw_4844_rlp() {
156 let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("testdata/4844rlp");
157 let dir = std::fs::read_dir(path).expect("Unable to read folder");
158 for entry in dir {
159 let entry = entry.unwrap();
160 let content = std::fs::read_to_string(entry.path()).unwrap();
161 let raw = hex::decode(content.trim()).unwrap();
162 let tx = PooledTransaction::decode_2718(&mut raw.as_ref())
163 .map_err(|err| {
164 panic!("Failed to decode transaction: {:?} {:?}", err, entry.path());
165 })
166 .unwrap();
167 assert!(tx.is_eip4844());
169 let encoded = tx.encoded_2718();
170 assert_eq!(encoded.as_slice(), &raw[..], "{:?}", entry.path());
171 }
172 }
173}