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