1use crate::broadcast::decode_list_with_memory_budget;
4use alloc::vec::Vec;
5use alloy_consensus::transaction::{PooledTransaction, TxHashRef};
6use alloy_eips::eip7594::{BlobCellMask, Cell};
7use alloy_primitives::{B128, B256};
8use alloy_rlp::{Decodable, RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper};
9use derive_more::{Constructor, Deref, IntoIterator};
10use reth_codecs_derive::add_arbitrary_tests;
11use reth_primitives_traits::InMemorySize;
12
13#[derive(
15 Clone,
16 Debug,
17 PartialEq,
18 Eq,
19 RlpEncodableWrapper,
20 RlpDecodableWrapper,
21 Default,
22 Deref,
23 IntoIterator,
24)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
27#[add_arbitrary_tests(rlp)]
28pub struct GetPooledTransactions(
29 pub Vec<B256>,
31);
32
33impl<T> From<Vec<T>> for GetPooledTransactions
34where
35 T: Into<B256>,
36{
37 fn from(hashes: Vec<T>) -> Self {
38 Self(hashes.into_iter().map(|h| h.into()).collect())
39 }
40}
41
42impl InMemorySize for GetPooledTransactions {
43 fn size(&self) -> usize {
44 self.0.len() * core::mem::size_of::<B256>()
45 }
46}
47
48#[derive(
57 Clone,
58 Debug,
59 PartialEq,
60 Eq,
61 RlpEncodableWrapper,
62 RlpDecodableWrapper,
63 IntoIterator,
64 Deref,
65 Constructor,
66)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68pub struct PooledTransactions<T = PooledTransaction>(
69 pub Vec<T>,
71);
72
73impl<T: Decodable + InMemorySize> PooledTransactions<T> {
74 pub fn decode_with_memory_budget(
78 buf: &mut &[u8],
79 memory_budget: usize,
80 ) -> alloy_rlp::Result<Self> {
81 decode_list_with_memory_budget(buf, memory_budget).map(Self)
82 }
83}
84
85impl<T: TxHashRef> PooledTransactions<T> {
86 pub fn hashes(&self) -> impl Iterator<Item = B256> + '_ {
88 self.iter().map(|tx| *tx.tx_hash())
89 }
90}
91
92impl<T, U> TryFrom<Vec<U>> for PooledTransactions<T>
93where
94 T: TryFrom<U>,
95{
96 type Error = T::Error;
97
98 fn try_from(txs: Vec<U>) -> Result<Self, Self::Error> {
99 txs.into_iter().map(T::try_from).collect()
100 }
101}
102
103impl<T> FromIterator<T> for PooledTransactions<T> {
104 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
105 Self(iter.into_iter().collect())
106 }
107}
108
109impl<T> Default for PooledTransactions<T> {
110 fn default() -> Self {
111 Self(Default::default())
112 }
113}
114
115#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct GetCells {
123 pub hashes: Vec<B256>,
125 pub cell_mask: B128,
128}
129
130impl GetCells {
131 pub fn cell_mask(&self) -> BlobCellMask {
133 BlobCellMask::from_bits(u128::from_le_bytes(self.cell_mask.into()))
134 }
135}
136
137impl InMemorySize for GetCells {
138 fn size(&self) -> usize {
139 self.hashes.len() * core::mem::size_of::<B256>() + core::mem::size_of::<B128>()
140 }
141}
142
143#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
146pub struct Cells {
147 pub hashes: Vec<B256>,
149 pub cells: Vec<Vec<Cell>>,
151 pub cell_mask: B128,
153}
154
155#[cfg(test)]
156mod tests {
157 use crate::{message::RequestPair, GetPooledTransactions, PooledTransactions};
158 use alloy_consensus::{transaction::PooledTransaction, TxEip1559, TxLegacy};
159 use alloy_primitives::{hex, Signature, TxKind, U256};
160 use alloy_rlp::{Decodable, Encodable};
161 use reth_chainspec::MIN_TRANSACTION_GAS;
162 use reth_ethereum_primitives::{Transaction, TransactionSigned};
163 use std::str::FromStr;
164
165 #[test]
166 fn encode_get_pooled_transactions() {
168 let expected = hex!(
169 "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
170 );
171 let mut data = vec![];
172 let request = RequestPair {
173 request_id: 1111,
174 message: GetPooledTransactions(vec![
175 hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
176 hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
177 ]),
178 };
179 request.encode(&mut data);
180 assert_eq!(data, expected);
181 }
182
183 #[test]
184 fn decode_get_pooled_transactions() {
186 let data = hex!(
187 "f847820457f842a000000000000000000000000000000000000000000000000000000000deadc0dea000000000000000000000000000000000000000000000000000000000feedbeef"
188 );
189 let request = RequestPair::<GetPooledTransactions>::decode(&mut &data[..]).unwrap();
190 assert_eq!(
191 request,
192 RequestPair {
193 request_id: 1111,
194 message: GetPooledTransactions(vec![
195 hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
196 hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
197 ])
198 }
199 );
200 }
201
202 #[test]
203 fn encode_pooled_transactions() {
205 let expected = hex!(
206 "f8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"
207 );
208 let mut data = vec![];
209 let txs = vec![
210 TransactionSigned::new_unhashed(
211 Transaction::Legacy(TxLegacy {
212 chain_id: Some(1),
213 nonce: 0x8u64,
214 gas_price: 0x4a817c808,
215 gas_limit: 0x2e248,
216 to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
217 value: U256::from(0x200u64),
218 input: Default::default(),
219 }),
220 Signature::new(
221 U256::from_str(
222 "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
223 )
224 .unwrap(),
225 U256::from_str(
226 "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
227 )
228 .unwrap(),
229 false,
230 ),
231 ),
232 TransactionSigned::new_unhashed(
233 Transaction::Legacy(TxLegacy {
234 chain_id: Some(1),
235 nonce: 0x09u64,
236 gas_price: 0x4a817c809,
237 gas_limit: 0x33450,
238 to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
239 value: U256::from(0x2d9u64),
240 input: Default::default(),
241 }),
242 Signature::new(
243 U256::from_str(
244 "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
245 )
246 .unwrap(),
247 U256::from_str(
248 "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
249 )
250 .unwrap(),
251 false,
252 ),
253 ),
254 ];
255 let message: Vec<PooledTransaction> = txs
256 .into_iter()
257 .map(|tx| {
258 PooledTransaction::try_from(tx)
259 .expect("Failed to convert TransactionSigned to PooledTransaction")
260 })
261 .collect();
262 let request = RequestPair {
263 request_id: 1111,
264 message: PooledTransactions(message), };
267 request.encode(&mut data);
268 assert_eq!(data, expected);
269 }
270
271 #[test]
272 fn decode_pooled_transactions() {
274 let data = hex!(
275 "f8d7820457f8d2f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb"
276 );
277 let txs = vec![
278 TransactionSigned::new_unhashed(
279 Transaction::Legacy(TxLegacy {
280 chain_id: Some(1),
281 nonce: 0x8u64,
282 gas_price: 0x4a817c808,
283 gas_limit: 0x2e248,
284 to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
285 value: U256::from(0x200u64),
286 input: Default::default(),
287 }),
288 Signature::new(
289 U256::from_str(
290 "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
291 )
292 .unwrap(),
293 U256::from_str(
294 "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
295 )
296 .unwrap(),
297 false,
298 ),
299 ),
300 TransactionSigned::new_unhashed(
301 Transaction::Legacy(TxLegacy {
302 chain_id: Some(1),
303 nonce: 0x09u64,
304 gas_price: 0x4a817c809,
305 gas_limit: 0x33450,
306 to: TxKind::Call(hex!("3535353535353535353535353535353535353535").into()),
307 value: U256::from(0x2d9u64),
308 input: Default::default(),
309 }),
310 Signature::new(
311 U256::from_str(
312 "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
313 )
314 .unwrap(),
315 U256::from_str(
316 "0x52f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb",
317 )
318 .unwrap(),
319 false,
320 ),
321 ),
322 ];
323 let message: Vec<PooledTransaction> = txs
324 .into_iter()
325 .map(|tx| {
326 PooledTransaction::try_from(tx)
327 .expect("Failed to convert TransactionSigned to PooledTransaction")
328 })
329 .collect();
330 let expected = RequestPair { request_id: 1111, message: PooledTransactions(message) };
331
332 let request = RequestPair::<PooledTransactions>::decode(&mut &data[..]).unwrap();
333 assert_eq!(request, expected);
334 }
335
336 #[test]
337 fn decode_pooled_transactions_network() {
338 let data = hex!(
339 "f9022980f90225f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631daf86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18"
340 );
341 let decoded_transactions =
342 RequestPair::<PooledTransactions>::decode(&mut &data[..]).unwrap();
343 let txs = vec![
344 TransactionSigned::new_unhashed(
345 Transaction::Legacy(TxLegacy {
346 chain_id: Some(4),
347 nonce: 15u64,
348 gas_price: 2200000000,
349 gas_limit: 34811,
350 to: TxKind::Call(hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into()),
351 value: U256::from(1234u64),
352 input: Default::default(),
353 }),
354 Signature::new(
355 U256::from_str(
356 "0x35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981",
357 )
358 .unwrap(),
359 U256::from_str(
360 "0x612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860",
361 )
362 .unwrap(),
363 true,
364 ),
365 ),
366 TransactionSigned::new_unhashed(
367 Transaction::Eip1559(TxEip1559 {
368 chain_id: 4,
369 nonce: 26u64,
370 max_priority_fee_per_gas: 1500000000,
371 max_fee_per_gas: 1500000013,
372 gas_limit: MIN_TRANSACTION_GAS,
373 to: TxKind::Call(hex!("61815774383099e24810ab832a5b2a5425c154d5").into()),
374 value: U256::from(3000000000000000000u64),
375 input: Default::default(),
376 access_list: Default::default(),
377 }),
378 Signature::new(
379 U256::from_str(
380 "0x59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd",
381 )
382 .unwrap(),
383 U256::from_str(
384 "0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469",
385 )
386 .unwrap(),
387 true,
388 ),
389 ),
390 TransactionSigned::new_unhashed(
391 Transaction::Legacy(TxLegacy {
392 chain_id: Some(4),
393 nonce: 3u64,
394 gas_price: 2000000000,
395 gas_limit: 10000000,
396 to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
397 value: U256::from(1000000000000000u64),
398 input: Default::default(),
399 }),
400 Signature::new(
401 U256::from_str(
402 "0xce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071",
403 )
404 .unwrap(),
405 U256::from_str(
406 "0x3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88",
407 )
408 .unwrap(),
409 false,
410 ),
411 ),
412 TransactionSigned::new_unhashed(
413 Transaction::Legacy(TxLegacy {
414 chain_id: Some(4),
415 nonce: 1u64,
416 gas_price: 1000000000,
417 gas_limit: 100000,
418 to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
419 value: U256::from(693361000000000u64),
420 input: Default::default(),
421 }),
422 Signature::new(
423 U256::from_str(
424 "0xe24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a",
425 )
426 .unwrap(),
427 U256::from_str(
428 "0x5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da",
429 )
430 .unwrap(),
431 false,
432 ),
433 ),
434 TransactionSigned::new_unhashed(
435 Transaction::Legacy(TxLegacy {
436 chain_id: Some(4),
437 nonce: 2u64,
438 gas_price: 1000000000,
439 gas_limit: 100000,
440 to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
441 value: U256::from(1000000000000000u64),
442 input: Default::default(),
443 }),
444 Signature::new(
445 U256::from_str(
446 "0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae",
447 )
448 .unwrap(),
449 U256::from_str(
450 "0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18",
451 )
452 .unwrap(),
453 false,
454 ),
455 ),
456 ];
457 let message: Vec<PooledTransaction> = txs
458 .into_iter()
459 .map(|tx| {
460 PooledTransaction::try_from(tx)
461 .expect("Failed to convert TransactionSigned to PooledTransaction")
462 })
463 .collect();
464 let expected_transactions =
465 RequestPair { request_id: 0, message: PooledTransactions(message) };
466
467 for (decoded, expected) in
469 decoded_transactions.message.0.iter().zip(expected_transactions.message.0.iter())
470 {
471 assert_eq!(decoded, expected);
472 }
473
474 assert_eq!(decoded_transactions, expected_transactions);
475 }
476
477 #[test]
478 fn encode_pooled_transactions_network() {
479 let expected = hex!(
480 "f9022980f90225f8650f84832156008287fb94cf7f9e66af820a19257a2108375b180b0ec491678204d2802ca035b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981a0612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860b87502f872041a8459682f008459682f0d8252089461815774383099e24810ab832a5b2a5425c154d58829a2241af62c000080c001a059e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafda0016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469f86b0384773594008398968094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba0ce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071a03ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88f86b01843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac3960468702769bb01b2a00802ba0e24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0aa05406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631daf86b02843b9aca00830186a094d3e8763675e4c425df46cc3b5c0f6cbdac39604687038d7ea4c68000802ba00eb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5aea03a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18"
481 );
482 let txs = vec![
483 TransactionSigned::new_unhashed(
484 Transaction::Legacy(TxLegacy {
485 chain_id: Some(4),
486 nonce: 15u64,
487 gas_price: 2200000000,
488 gas_limit: 34811,
489 to: TxKind::Call(hex!("cf7f9e66af820a19257a2108375b180b0ec49167").into()),
490 value: U256::from(1234u64),
491 input: Default::default(),
492 }),
493 Signature::new(
494 U256::from_str(
495 "0x35b7bfeb9ad9ece2cbafaaf8e202e706b4cfaeb233f46198f00b44d4a566a981",
496 )
497 .unwrap(),
498 U256::from_str(
499 "0x612638fb29427ca33b9a3be2a0a561beecfe0269655be160d35e72d366a6a860",
500 )
501 .unwrap(),
502 true,
503 ),
504 ),
505 TransactionSigned::new_unhashed(
506 Transaction::Eip1559(TxEip1559 {
507 chain_id: 4,
508 nonce: 26u64,
509 max_priority_fee_per_gas: 1500000000,
510 max_fee_per_gas: 1500000013,
511 gas_limit: MIN_TRANSACTION_GAS,
512 to: TxKind::Call(hex!("61815774383099e24810ab832a5b2a5425c154d5").into()),
513 value: U256::from(3000000000000000000u64),
514 input: Default::default(),
515 access_list: Default::default(),
516 }),
517 Signature::new(
518 U256::from_str(
519 "0x59e6b67f48fb32e7e570dfb11e042b5ad2e55e3ce3ce9cd989c7e06e07feeafd",
520 )
521 .unwrap(),
522 U256::from_str(
523 "0x016b83f4f980694ed2eee4d10667242b1f40dc406901b34125b008d334d47469",
524 )
525 .unwrap(),
526 true,
527 ),
528 ),
529 TransactionSigned::new_unhashed(
530 Transaction::Legacy(TxLegacy {
531 chain_id: Some(4),
532 nonce: 3u64,
533 gas_price: 2000000000,
534 gas_limit: 10000000,
535 to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
536 value: U256::from(1000000000000000u64),
537 input: Default::default(),
538 }),
539 Signature::new(
540 U256::from_str(
541 "0xce6834447c0a4193c40382e6c57ae33b241379c5418caac9cdc18d786fd12071",
542 )
543 .unwrap(),
544 U256::from_str(
545 "0x3ca3ae86580e94550d7c071e3a02eadb5a77830947c9225165cf9100901bee88",
546 )
547 .unwrap(),
548 false,
549 ),
550 ),
551 TransactionSigned::new_unhashed(
552 Transaction::Legacy(TxLegacy {
553 chain_id: Some(4),
554 nonce: 1u64,
555 gas_price: 1000000000,
556 gas_limit: 100000,
557 to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
558 value: U256::from(693361000000000u64),
559 input: Default::default(),
560 }),
561 Signature::new(
562 U256::from_str(
563 "0xe24d8bd32ad906d6f8b8d7741e08d1959df021698b19ee232feba15361587d0a",
564 )
565 .unwrap(),
566 U256::from_str(
567 "0x5406ad177223213df262cb66ccbb2f46bfdccfdfbbb5ffdda9e2c02d977631da",
568 )
569 .unwrap(),
570 false,
571 ),
572 ),
573 TransactionSigned::new_unhashed(
574 Transaction::Legacy(TxLegacy {
575 chain_id: Some(4),
576 nonce: 2u64,
577 gas_price: 1000000000,
578 gas_limit: 100000,
579 to: TxKind::Call(hex!("d3e8763675e4c425df46cc3b5c0f6cbdac396046").into()),
580 value: U256::from(1000000000000000u64),
581 input: Default::default(),
582 }),
583 Signature::new(
584 U256::from_str(
585 "0xeb96ca19e8a77102767a41fc85a36afd5c61ccb09911cec5d3e86e193d9c5ae",
586 )
587 .unwrap(),
588 U256::from_str(
589 "0x3a456401896b1b6055311536bf00a718568c744d8c1f9df59879e8350220ca18",
590 )
591 .unwrap(),
592 false,
593 ),
594 ),
595 ];
596 let message: Vec<PooledTransaction> = txs
597 .into_iter()
598 .map(|tx| {
599 PooledTransaction::try_from(tx)
600 .expect("Failed to convert TransactionSigned to PooledTransaction")
601 })
602 .collect();
603 let transactions = RequestPair { request_id: 0, message: PooledTransactions(message) };
604
605 let mut encoded = vec![];
606 transactions.encode(&mut encoded);
607 assert_eq!(encoded.len(), transactions.length());
608 let encoded_str = hex::encode(encoded);
609 let expected_str = hex::encode(expected);
610 assert_eq!(encoded_str.len(), expected_str.len());
611 assert_eq!(encoded_str, expected_str);
612 }
613}