reth_eth_wire_types/
transactions.rs

1//! Implements the `GetPooledTransactions` and `PooledTransactions` message types.
2
3use alloc::vec::Vec;
4use alloy_consensus::transaction::PooledTransaction;
5use alloy_eips::eip2718::Encodable2718;
6use alloy_primitives::B256;
7use alloy_rlp::{RlpDecodableWrapper, RlpEncodableWrapper};
8use derive_more::{Constructor, Deref, IntoIterator};
9use reth_codecs_derive::add_arbitrary_tests;
10
11/// A list of transaction hashes that the peer would like transaction bodies for.
12#[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Default)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
15#[add_arbitrary_tests(rlp)]
16pub struct GetPooledTransactions(
17    /// The transaction hashes to request transaction bodies for.
18    pub Vec<B256>,
19);
20
21impl<T> From<Vec<T>> for GetPooledTransactions
22where
23    T: Into<B256>,
24{
25    fn from(hashes: Vec<T>) -> Self {
26        Self(hashes.into_iter().map(|h| h.into()).collect())
27    }
28}
29
30/// The response to [`GetPooledTransactions`], containing the transaction bodies associated with
31/// the requested hashes.
32///
33/// This response may not contain all bodies requested, but the bodies should be in the same order
34/// as the request's hashes. Hashes may be skipped, and the client should ensure that each body
35/// corresponds to a requested hash. Hashes may need to be re-requested if the bodies are not
36/// included in the response.
37// #[derive_arbitrary(rlp, 10)]
38#[derive(
39    Clone,
40    Debug,
41    PartialEq,
42    Eq,
43    RlpEncodableWrapper,
44    RlpDecodableWrapper,
45    IntoIterator,
46    Deref,
47    Constructor,
48)]
49#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
50pub struct PooledTransactions<T = PooledTransaction>(
51    /// The transaction bodies, each of which should correspond to a requested hash.
52    pub Vec<T>,
53);
54
55impl<T: Encodable2718> PooledTransactions<T> {
56    /// Returns an iterator over the transaction hashes in this response.
57    pub fn hashes(&self) -> impl Iterator<Item = B256> + '_ {
58        self.0.iter().map(|tx| tx.trie_hash())
59    }
60}
61
62impl<T, U> TryFrom<Vec<U>> for PooledTransactions<T>
63where
64    T: TryFrom<U>,
65{
66    type Error = T::Error;
67
68    fn try_from(txs: Vec<U>) -> Result<Self, Self::Error> {
69        txs.into_iter().map(T::try_from).collect()
70    }
71}
72
73impl<T> FromIterator<T> for PooledTransactions<T> {
74    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
75        Self(iter.into_iter().collect())
76    }
77}
78
79impl<T> Default for PooledTransactions<T> {
80    fn default() -> Self {
81        Self(Default::default())
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use crate::{message::RequestPair, GetPooledTransactions, PooledTransactions};
88    use alloy_consensus::{transaction::PooledTransaction, TxEip1559, TxLegacy};
89    use alloy_primitives::{hex, Signature, TxKind, U256};
90    use alloy_rlp::{Decodable, Encodable};
91    use reth_chainspec::MIN_TRANSACTION_GAS;
92    use reth_ethereum_primitives::{Transaction, TransactionSigned};
93    use std::str::FromStr;
94
95    #[test]
96    // Test vector from: https://eips.ethereum.org/EIPS/eip-2481
97    fn encode_get_pooled_transactions() {
98        let expected = hex!(
99            "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
100        );
101        let mut data = vec![];
102        let request = RequestPair {
103            request_id: 1111,
104            message: GetPooledTransactions(vec![
105                hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
106                hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
107            ]),
108        };
109        request.encode(&mut data);
110        assert_eq!(data, expected);
111    }
112
113    #[test]
114    // Test vector from: https://eips.ethereum.org/EIPS/eip-2481
115    fn decode_get_pooled_transactions() {
116        let data = hex!(
117            "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
118        );
119        let request = RequestPair::<GetPooledTransactions>::decode(&mut &data[..]).unwrap();
120        assert_eq!(
121            request,
122            RequestPair {
123                request_id: 1111,
124                message: GetPooledTransactions(vec![
125                    hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
126                    hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
127                ])
128            }
129        );
130    }
131
132    #[test]
133    // Test vector from: https://eips.ethereum.org/EIPS/eip-2481
134    fn encode_pooled_transactions() {
135        let expected = hex!(
136            "f8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"
137        );
138        let mut data = vec![];
139        let txs = vec![
140            TransactionSigned::new_unhashed(
141                Transaction::Legacy(TxLegacy {
142                    chain_id: Some(1),
143                    nonce: 0x8u64,
144                    gas_price: 0x4a817c808,
145                    gas_limit: 0x2e248,
146                    to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
147                    value: U256::from(0x200u64),
148                    input: Default::default(),
149                }),
150                Signature::new(
151                    U256::from_str(
152                        "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
153                    )
154                    .unwrap(),
155                    U256::from_str(
156                        "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
157                    )
158                    .unwrap(),
159                    false,
160                ),
161            ),
162            TransactionSigned::new_unhashed(
163                Transaction::Legacy(TxLegacy {
164                    chain_id: Some(1),
165                    nonce: 0x09u64,
166                    gas_price: 0x4a817c809,
167                    gas_limit: 0x33450,
168                    to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
169                    value: U256::from(0x2d9u64),
170                    input: Default::default(),
171                }),
172                Signature::new(
173                    U256::from_str(
174                        "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
175                    )
176                    .unwrap(),
177                    U256::from_str(
178                        "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
179                    )
180                    .unwrap(),
181                    false,
182                ),
183            ),
184        ];
185        let message: Vec<PooledTransaction> = txs
186            .into_iter()
187            .map(|tx| {
188                PooledTransaction::try_from(tx)
189                    .expect("Failed to convert TransactionSigned to PooledTransaction")
190            })
191            .collect();
192        let request = RequestPair {
193            request_id: 1111,
194            message: PooledTransactions(message), /* Assuming PooledTransactions wraps a
195                                                   * Vec<PooledTransaction> */
196        };
197        request.encode(&mut data);
198        assert_eq!(data, expected);
199    }
200
201    #[test]
202    // Test vector from: https://eips.ethereum.org/EIPS/eip-2481
203    fn decode_pooled_transactions() {
204        let data = hex!(
205            "f8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"
206        );
207        let txs = vec![
208            TransactionSigned::new_unhashed(
209                Transaction::Legacy(TxLegacy {
210                    chain_id: Some(1),
211                    nonce: 0x8u64,
212                    gas_price: 0x4a817c808,
213                    gas_limit: 0x2e248,
214                    to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
215                    value: U256::from(0x200u64),
216                    input: Default::default(),
217                }),
218                Signature::new(
219                    U256::from_str(
220                        "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
221                    )
222                    .unwrap(),
223                    U256::from_str(
224                        "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
225                    )
226                    .unwrap(),
227                    false,
228                ),
229            ),
230            TransactionSigned::new_unhashed(
231                Transaction::Legacy(TxLegacy {
232                    chain_id: Some(1),
233                    nonce: 0x09u64,
234                    gas_price: 0x4a817c809,
235                    gas_limit: 0x33450,
236                    to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
237                    value: U256::from(0x2d9u64),
238                    input: Default::default(),
239                }),
240                Signature::new(
241                    U256::from_str(
242                        "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
243                    )
244                    .unwrap(),
245                    U256::from_str(
246                        "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
247                    )
248                    .unwrap(),
249                    false,
250                ),
251            ),
252        ];
253        let message: Vec<PooledTransaction> = txs
254            .into_iter()
255            .map(|tx| {
256                PooledTransaction::try_from(tx)
257                    .expect("Failed to convert TransactionSigned to PooledTransaction")
258            })
259            .collect();
260        let expected = RequestPair { request_id: 1111, message: PooledTransactions(message) };
261
262        let request = RequestPair::<PooledTransactions>::decode(&mut &data[..]).unwrap();
263        assert_eq!(request, expected);
264    }
265
266    #[test]
267    fn decode_pooled_transactions_network() {
268        let data = hex!(
269            "f9022980f90225f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631daf86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18"
270        );
271        let decoded_transactions =
272            RequestPair::<PooledTransactions>::decode(&mut &data[..]).unwrap();
273        let txs = vec![
274            TransactionSigned::new_unhashed(
275                Transaction::Legacy(TxLegacy {
276                    chain_id: Some(4),
277                    nonce: 15u64,
278                    gas_price: 2200000000,
279                    gas_limit: 34811,
280                    to: TxKind::Call(hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into()),
281                    value: U256::from(1234u64),
282                    input: Default::default(),
283                }),
284                Signature::new(
285                    U256::from_str(
286                        "0x35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981",
287                    )
288                    .unwrap(),
289                    U256::from_str(
290                        "0x612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860",
291                    )
292                    .unwrap(),
293                    true,
294                ),
295            ),
296            TransactionSigned::new_unhashed(
297                Transaction::Eip1559(TxEip1559 {
298                    chain_id: 4,
299                    nonce: 26u64,
300                    max_priority_fee_per_gas: 1500000000,
301                    max_fee_per_gas: 1500000013,
302                    gas_limit: MIN_TRANSACTION_GAS,
303                    to: TxKind::Call(hex!("61815774383099e24810ab832a5b2a5425c154d5").into()),
304                    value: U256::from(3000000000000000000u64),
305                    input: Default::default(),
306                    access_list: Default::default(),
307                }),
308                Signature::new(
309                    U256::from_str(
310                        "0x59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd",
311                    )
312                    .unwrap(),
313                    U256::from_str(
314                        "0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469",
315                    )
316                    .unwrap(),
317                    true,
318                ),
319            ),
320            TransactionSigned::new_unhashed(
321                Transaction::Legacy(TxLegacy {
322                    chain_id: Some(4),
323                    nonce: 3u64,
324                    gas_price: 2000000000,
325                    gas_limit: 10000000,
326                    to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
327                    value: U256::from(1000000000000000u64),
328                    input: Default::default(),
329                }),
330                Signature::new(
331                    U256::from_str(
332                        "0xce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071",
333                    )
334                    .unwrap(),
335                    U256::from_str(
336                        "0x3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88",
337                    )
338                    .unwrap(),
339                    false,
340                ),
341            ),
342            TransactionSigned::new_unhashed(
343                Transaction::Legacy(TxLegacy {
344                    chain_id: Some(4),
345                    nonce: 1u64,
346                    gas_price: 1000000000,
347                    gas_limit: 100000,
348                    to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
349                    value: U256::from(693361000000000u64),
350                    input: Default::default(),
351                }),
352                Signature::new(
353                    U256::from_str(
354                        "0xe24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a",
355                    )
356                    .unwrap(),
357                    U256::from_str(
358                        "0x5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da",
359                    )
360                    .unwrap(),
361                    false,
362                ),
363            ),
364            TransactionSigned::new_unhashed(
365                Transaction::Legacy(TxLegacy {
366                    chain_id: Some(4),
367                    nonce: 2u64,
368                    gas_price: 1000000000,
369                    gas_limit: 100000,
370                    to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
371                    value: U256::from(1000000000000000u64),
372                    input: Default::default(),
373                }),
374                Signature::new(
375                    U256::from_str(
376                        "0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae",
377                    )
378                    .unwrap(),
379                    U256::from_str(
380                        "0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18",
381                    )
382                    .unwrap(),
383                    false,
384                ),
385            ),
386        ];
387        let message: Vec<PooledTransaction> = txs
388            .into_iter()
389            .map(|tx| {
390                PooledTransaction::try_from(tx)
391                    .expect("Failed to convert TransactionSigned to PooledTransaction")
392            })
393            .collect();
394        let expected_transactions =
395            RequestPair { request_id: 0, message: PooledTransactions(message) };
396
397        // checking tx by tx for easier debugging if there are any regressions
398        for (decoded, expected) in
399            decoded_transactions.message.0.iter().zip(expected_transactions.message.0.iter())
400        {
401            assert_eq!(decoded, expected);
402        }
403
404        assert_eq!(decoded_transactions, expected_transactions);
405    }
406
407    #[test]
408    fn encode_pooled_transactions_network() {
409        let expected = hex!(
410            "f9022980f90225f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631daf86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18"
411        );
412        let txs = vec![
413            TransactionSigned::new_unhashed(
414                Transaction::Legacy(TxLegacy {
415                    chain_id: Some(4),
416                    nonce: 15u64,
417                    gas_price: 2200000000,
418                    gas_limit: 34811,
419                    to: TxKind::Call(hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into()),
420                    value: U256::from(1234u64),
421                    input: Default::default(),
422                }),
423                Signature::new(
424                    U256::from_str(
425                        "0x35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981",
426                    )
427                    .unwrap(),
428                    U256::from_str(
429                        "0x612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860",
430                    )
431                    .unwrap(),
432                    true,
433                ),
434            ),
435            TransactionSigned::new_unhashed(
436                Transaction::Eip1559(TxEip1559 {
437                    chain_id: 4,
438                    nonce: 26u64,
439                    max_priority_fee_per_gas: 1500000000,
440                    max_fee_per_gas: 1500000013,
441                    gas_limit: MIN_TRANSACTION_GAS,
442                    to: TxKind::Call(hex!("61815774383099e24810ab832a5b2a5425c154d5").into()),
443                    value: U256::from(3000000000000000000u64),
444                    input: Default::default(),
445                    access_list: Default::default(),
446                }),
447                Signature::new(
448                    U256::from_str(
449                        "0x59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd",
450                    )
451                    .unwrap(),
452                    U256::from_str(
453                        "0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469",
454                    )
455                    .unwrap(),
456                    true,
457                ),
458            ),
459            TransactionSigned::new_unhashed(
460                Transaction::Legacy(TxLegacy {
461                    chain_id: Some(4),
462                    nonce: 3u64,
463                    gas_price: 2000000000,
464                    gas_limit: 10000000,
465                    to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
466                    value: U256::from(1000000000000000u64),
467                    input: Default::default(),
468                }),
469                Signature::new(
470                    U256::from_str(
471                        "0xce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071",
472                    )
473                    .unwrap(),
474                    U256::from_str(
475                        "0x3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88",
476                    )
477                    .unwrap(),
478                    false,
479                ),
480            ),
481            TransactionSigned::new_unhashed(
482                Transaction::Legacy(TxLegacy {
483                    chain_id: Some(4),
484                    nonce: 1u64,
485                    gas_price: 1000000000,
486                    gas_limit: 100000,
487                    to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
488                    value: U256::from(693361000000000u64),
489                    input: Default::default(),
490                }),
491                Signature::new(
492                    U256::from_str(
493                        "0xe24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a",
494                    )
495                    .unwrap(),
496                    U256::from_str(
497                        "0x5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da",
498                    )
499                    .unwrap(),
500                    false,
501                ),
502            ),
503            TransactionSigned::new_unhashed(
504                Transaction::Legacy(TxLegacy {
505                    chain_id: Some(4),
506                    nonce: 2u64,
507                    gas_price: 1000000000,
508                    gas_limit: 100000,
509                    to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
510                    value: U256::from(1000000000000000u64),
511                    input: Default::default(),
512                }),
513                Signature::new(
514                    U256::from_str(
515                        "0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae",
516                    )
517                    .unwrap(),
518                    U256::from_str(
519                        "0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18",
520                    )
521                    .unwrap(),
522                    false,
523                ),
524            ),
525        ];
526        let message: Vec<PooledTransaction> = txs
527            .into_iter()
528            .map(|tx| {
529                PooledTransaction::try_from(tx)
530                    .expect("Failed to convert TransactionSigned to PooledTransaction")
531            })
532            .collect();
533        let transactions = RequestPair { request_id: 0, message: PooledTransactions(message) };
534
535        let mut encoded = vec![];
536        transactions.encode(&mut encoded);
537        assert_eq!(encoded.len(), transactions.length());
538        let encoded_str = hex::encode(encoded);
539        let expected_str = hex::encode(expected);
540        assert_eq!(encoded_str.len(), expected_str.len());
541        assert_eq!(encoded_str, expected_str);
542    }
543}