1use crate::{EthMessage, EthVersion, NetworkPrimitives};
4use alloc::{sync::Arc, vec::Vec};
5use alloy_consensus::transaction::TxHashRef;
6use alloy_eips::eip2718::Typed2718;
7use alloy_primitives::{bytes::BufMut, Bytes, TxHash, B128, B256, U128};
8use alloy_rlp::{
9 decode_append, Decodable, Encodable, Header, RlpDecodable, RlpDecodableWrapper, RlpEncodable,
10 RlpEncodableWrapper,
11};
12use core::{fmt::Debug, mem};
13use derive_more::{Deref, DerefMut, IntoIterator};
14use reth_codecs_derive::{add_arbitrary_tests, generate_tests};
15use reth_ethereum_primitives::TransactionSigned;
16use reth_primitives_traits::{sync::OnceLock, Block, InMemorySize, SignedTransaction};
17
18pub const SOFT_LIMIT_COUNT_HASHES_IN_NEW_POOLED_TRANSACTIONS_BROADCAST_MESSAGE: usize = 4096;
25
26#[derive(
28 Clone,
29 Debug,
30 PartialEq,
31 Eq,
32 RlpEncodableWrapper,
33 RlpDecodableWrapper,
34 Default,
35 Deref,
36 DerefMut,
37 IntoIterator,
38)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
41#[add_arbitrary_tests(rlp)]
42pub struct NewBlockHashes(
43 pub Vec<BlockHashNumber>,
46);
47
48impl NewBlockHashes {
51 pub fn latest(&self) -> Option<&BlockHashNumber> {
53 self.iter().max_by_key(|b| b.number)
54 }
55}
56
57#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
59#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
60#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
61#[add_arbitrary_tests(rlp)]
62pub struct BlockHashNumber {
63 pub hash: B256,
65 pub number: u64,
67}
68
69impl From<Vec<BlockHashNumber>> for NewBlockHashes {
70 fn from(v: Vec<BlockHashNumber>) -> Self {
71 Self(v)
72 }
73}
74
75impl From<NewBlockHashes> for Vec<BlockHashNumber> {
76 fn from(v: NewBlockHashes) -> Self {
77 v.0
78 }
79}
80
81pub trait NewBlockPayload:
83 Encodable + Decodable + Clone + Eq + Debug + Send + Sync + Unpin + 'static
84{
85 type Block: Block;
87
88 fn block(&self) -> &Self::Block;
90}
91
92#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
96#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
97pub struct NewBlock<B = reth_ethereum_primitives::Block> {
98 pub block: B,
100 pub td: U128,
102}
103
104impl<B: Block + 'static> NewBlockPayload for NewBlock<B> {
105 type Block = B;
106
107 fn block(&self) -> &Self::Block {
108 &self.block
109 }
110}
111
112generate_tests!(#[rlp, 25] NewBlock<reth_ethereum_primitives::Block>, EthNewBlockTests);
113
114#[derive(
117 Clone,
118 Debug,
119 PartialEq,
120 Eq,
121 RlpEncodableWrapper,
122 RlpDecodableWrapper,
123 Default,
124 Deref,
125 IntoIterator,
126)]
127#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
128#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
129#[add_arbitrary_tests(rlp, 10)]
130pub struct Transactions<T = TransactionSigned>(
131 pub Vec<T>,
133);
134
135impl<T: SignedTransaction> Transactions<T> {
136 pub fn has_eip4844(&self) -> bool {
138 self.iter().any(|tx| tx.is_eip4844())
139 }
140}
141
142impl<T> From<Vec<T>> for Transactions<T> {
143 fn from(txs: Vec<T>) -> Self {
144 Self(txs)
145 }
146}
147
148impl<T> From<Transactions<T>> for Vec<T> {
149 fn from(txs: Transactions<T>) -> Self {
150 txs.0
151 }
152}
153
154impl<T: Decodable + InMemorySize> Transactions<T> {
155 pub fn decode_with_memory_budget(
159 buf: &mut &[u8],
160 memory_budget: usize,
161 ) -> alloy_rlp::Result<Self> {
162 decode_list_with_memory_budget(buf, memory_budget).map(Self)
163 }
164}
165
166pub fn decode_list_with_memory_budget<T: Decodable + InMemorySize>(
169 buf: &mut &[u8],
170 memory_budget: usize,
171) -> alloy_rlp::Result<Vec<T>> {
172 let Header { list, payload_length } = Header::decode(buf)?;
173 if !list {
174 return Err(alloy_rlp::Error::UnexpectedString)
175 }
176 let (mut payload, rest) = buf.split_at(payload_length);
178
179 let mut txs = Vec::with_capacity(estimated_transaction_list_capacity(payload_length));
180 let mut total_size = 0usize;
181
182 while !payload.is_empty() {
183 let item = T::decode(&mut payload)?;
184 total_size = total_size.saturating_add(item.size());
185
186 if total_size > memory_budget {
187 break;
188 }
189
190 txs.push(item);
191 }
192
193 *buf = rest;
194 Ok(txs)
195}
196
197const MIN_TRANSACTION_RLP_SIZE_ESTIMATE: usize = 128;
200const MIN_PREALLOCATED_TRANSACTIONS: usize = 4;
201const MAX_PREALLOCATED_TRANSACTIONS: usize = 1024;
202
203const fn estimated_transaction_list_capacity(payload_length: usize) -> usize {
204 let estimate = payload_length / MIN_TRANSACTION_RLP_SIZE_ESTIMATE;
205 if estimate < MIN_PREALLOCATED_TRANSACTIONS {
206 0
207 } else if estimate > MAX_PREALLOCATED_TRANSACTIONS {
208 MAX_PREALLOCATED_TRANSACTIONS
209 } else {
210 estimate
211 }
212}
213
214#[derive(
219 Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Deref, IntoIterator,
220)]
221#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
222#[add_arbitrary_tests(rlp, 20)]
223pub struct SharedTransactions<T = TransactionSigned>(
224 pub Vec<Arc<T>>,
226);
227
228pub trait BroadcastPoolTransaction:
230 Encodable + TxHashRef + Typed2718 + Send + Sync + 'static
231{
232}
233
234impl<T> BroadcastPoolTransaction for T where
235 T: Encodable + TxHashRef + Typed2718 + Send + Sync + 'static
236{
237}
238
239pub struct LazyEncoded<T: ?Sized> {
244 value: Arc<T>,
245 encoded: Arc<OnceLock<Bytes>>,
246}
247
248impl<T: ?Sized> Clone for LazyEncoded<T> {
249 fn clone(&self) -> Self {
250 Self { value: Arc::clone(&self.value), encoded: Arc::clone(&self.encoded) }
251 }
252}
253
254impl LazyEncoded<dyn BroadcastPoolTransaction> {
255 pub fn new<T>(value: T) -> Self
257 where
258 T: BroadcastPoolTransaction,
259 {
260 let value: Arc<dyn BroadcastPoolTransaction> = Arc::new(value);
261 Self { value, encoded: Arc::new(OnceLock::new()) }
262 }
263}
264
265impl<T: Encodable + ?Sized> Encodable for LazyEncoded<T> {
266 fn encode(&self, out: &mut dyn BufMut) {
267 let encoded = self.encoded.get_or_init(|| self.encode_uncached());
268 out.put_slice(encoded);
269 }
270
271 fn length(&self) -> usize {
272 self.encoded.get_or_init(|| self.encode_uncached()).len()
273 }
274}
275
276impl<T: Encodable + ?Sized> LazyEncoded<T> {
277 fn encode_uncached(&self) -> Bytes {
278 alloy_rlp::encode(&self.value).into()
279 }
280}
281
282impl<T: TxHashRef + ?Sized> TxHashRef for LazyEncoded<T> {
283 fn tx_hash(&self) -> &TxHash {
284 self.value.tx_hash()
285 }
286}
287
288impl<T: Typed2718 + ?Sized> Typed2718 for LazyEncoded<T> {
289 fn ty(&self) -> u8 {
290 self.value.ty()
291 }
292}
293
294impl<T: ?Sized> Debug for LazyEncoded<T> {
295 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
296 f.debug_struct("LazyEncoded")
297 .field("is_cached", &self.encoded.get().is_some())
298 .finish_non_exhaustive()
299 }
300}
301
302pub type LazyEncodedTransaction = LazyEncoded<dyn BroadcastPoolTransaction>;
304
305#[derive(Clone, Debug, Deref, RlpEncodableWrapper)]
313pub struct BroadcastPoolTransactions(pub Vec<LazyEncodedTransaction>);
314
315impl BroadcastPoolTransactions {
316 pub fn iter_hashes(&self) -> impl Iterator<Item = &TxHash> + '_ {
318 self.0.iter().map(TxHashRef::tx_hash)
319 }
320}
321
322#[derive(Clone, Debug, PartialEq, Eq)]
324#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
325pub enum NewPooledTransactionHashes {
326 Eth66(NewPooledTransactionHashes66),
328 Eth68(NewPooledTransactionHashes68),
332 Eth72(NewPooledTransactionHashes72),
339}
340
341impl NewPooledTransactionHashes {
344 pub const fn version(&self) -> EthVersion {
346 match self {
347 Self::Eth66(_) => EthVersion::Eth66,
348 Self::Eth68(_) => EthVersion::Eth68,
349 Self::Eth72(_) => EthVersion::Eth72,
350 }
351 }
352
353 pub const fn is_valid_for_version(&self, version: EthVersion) -> bool {
355 match self {
356 Self::Eth66(_) => {
357 matches!(version, EthVersion::Eth67 | EthVersion::Eth66)
358 }
359 Self::Eth68(_) => {
360 matches!(
361 version,
362 EthVersion::Eth68 | EthVersion::Eth69 | EthVersion::Eth70 | EthVersion::Eth71
363 )
364 }
365 Self::Eth72(_) => version.is_eth72(),
366 }
367 }
368
369 pub fn iter_hashes(&self) -> impl Iterator<Item = &B256> + '_ {
371 match self {
372 Self::Eth66(msg) => msg.iter(),
373 Self::Eth68(msg) => msg.hashes.iter(),
374 Self::Eth72(msg) => msg.hashes.iter(),
375 }
376 }
377
378 pub const fn hashes(&self) -> &Vec<B256> {
380 match self {
381 Self::Eth66(msg) => &msg.0,
382 Self::Eth68(msg) => &msg.hashes,
383 Self::Eth72(msg) => &msg.hashes,
384 }
385 }
386
387 pub const fn hashes_mut(&mut self) -> &mut Vec<B256> {
389 match self {
390 Self::Eth66(msg) => &mut msg.0,
391 Self::Eth68(msg) => &mut msg.hashes,
392 Self::Eth72(msg) => &mut msg.hashes,
393 }
394 }
395
396 pub fn into_hashes(self) -> Vec<B256> {
398 match self {
399 Self::Eth66(msg) => msg.0,
400 Self::Eth68(msg) => msg.hashes,
401 Self::Eth72(msg) => msg.hashes,
402 }
403 }
404
405 pub fn into_iter_hashes(self) -> impl Iterator<Item = B256> {
407 match self {
408 Self::Eth66(msg) => msg.into_iter(),
409 Self::Eth68(msg) => msg.hashes.into_iter(),
410 Self::Eth72(msg) => msg.hashes.into_iter(),
411 }
412 }
413
414 pub fn truncate(&mut self, len: usize) {
417 match self {
418 Self::Eth66(msg) => msg.truncate(len),
419 Self::Eth68(msg) => {
420 msg.types.truncate(len);
421 msg.sizes.truncate(len);
422 msg.hashes.truncate(len);
423 }
424 Self::Eth72(msg) => {
425 msg.types.truncate(len);
426 msg.sizes.truncate(len);
427 msg.hashes.truncate(len);
428 }
429 }
430 }
431
432 pub const fn is_empty(&self) -> bool {
434 match self {
435 Self::Eth66(msg) => msg.0.is_empty(),
436 Self::Eth68(msg) => msg.hashes.is_empty(),
437 Self::Eth72(msg) => msg.hashes.is_empty(),
438 }
439 }
440
441 pub const fn len(&self) -> usize {
443 match self {
444 Self::Eth66(msg) => msg.0.len(),
445 Self::Eth68(msg) => msg.hashes.len(),
446 Self::Eth72(msg) => msg.hashes.len(),
447 }
448 }
449
450 pub const fn as_eth72(&self) -> Option<&NewPooledTransactionHashes72> {
452 match self {
453 Self::Eth66(_) | Self::Eth68(_) => None,
454 Self::Eth72(msg) => Some(msg),
455 }
456 }
457
458 pub const fn as_eth72_mut(&mut self) -> Option<&mut NewPooledTransactionHashes72> {
460 match self {
461 Self::Eth66(_) | Self::Eth68(_) => None,
462 Self::Eth72(msg) => Some(msg),
463 }
464 }
465
466 pub const fn as_eth68(&self) -> Option<&NewPooledTransactionHashes68> {
468 match self {
469 Self::Eth66(_) | Self::Eth72(_) => None,
470 Self::Eth68(msg) => Some(msg),
471 }
472 }
473
474 pub const fn as_eth68_mut(&mut self) -> Option<&mut NewPooledTransactionHashes68> {
476 match self {
477 Self::Eth66(_) | Self::Eth72(_) => None,
478 Self::Eth68(msg) => Some(msg),
479 }
480 }
481
482 pub const fn as_eth66_mut(&mut self) -> Option<&mut NewPooledTransactionHashes66> {
484 match self {
485 Self::Eth66(msg) => Some(msg),
486 Self::Eth68(_) | Self::Eth72(_) => None,
487 }
488 }
489
490 pub fn take_eth68(&mut self) -> Option<NewPooledTransactionHashes68> {
492 match self {
493 Self::Eth66(_) | Self::Eth72(_) => None,
494 Self::Eth68(msg) => Some(mem::take(msg)),
495 }
496 }
497
498 pub fn take_eth66(&mut self) -> Option<NewPooledTransactionHashes66> {
500 match self {
501 Self::Eth66(msg) => Some(mem::take(msg)),
502 Self::Eth68(_) | Self::Eth72(_) => None,
503 }
504 }
505}
506
507impl<N: NetworkPrimitives> From<NewPooledTransactionHashes> for EthMessage<N> {
508 fn from(value: NewPooledTransactionHashes) -> Self {
509 match value {
510 NewPooledTransactionHashes::Eth66(msg) => Self::NewPooledTransactionHashes66(msg),
511 NewPooledTransactionHashes::Eth68(msg) => Self::NewPooledTransactionHashes68(msg),
512 NewPooledTransactionHashes::Eth72(msg) => Self::NewPooledTransactionHashes72(msg),
513 }
514 }
515}
516
517impl From<NewPooledTransactionHashes66> for NewPooledTransactionHashes {
518 fn from(hashes: NewPooledTransactionHashes66) -> Self {
519 Self::Eth66(hashes)
520 }
521}
522
523impl From<NewPooledTransactionHashes68> for NewPooledTransactionHashes {
524 fn from(hashes: NewPooledTransactionHashes68) -> Self {
525 Self::Eth68(hashes)
526 }
527}
528
529impl From<NewPooledTransactionHashes72> for NewPooledTransactionHashes {
530 fn from(hashes: NewPooledTransactionHashes72) -> Self {
531 Self::Eth72(hashes)
532 }
533}
534
535#[derive(
538 Clone,
539 Debug,
540 PartialEq,
541 Eq,
542 RlpEncodableWrapper,
543 RlpDecodableWrapper,
544 Default,
545 Deref,
546 DerefMut,
547 IntoIterator,
548)]
549#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
550#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
551#[add_arbitrary_tests(rlp)]
552pub struct NewPooledTransactionHashes66(
553 pub Vec<B256>,
557);
558
559impl NewPooledTransactionHashes66 {
560 pub fn with_capacity(capacity: usize) -> Self {
562 Self(Vec::with_capacity(capacity))
563 }
564}
565
566impl From<Vec<B256>> for NewPooledTransactionHashes66 {
567 fn from(v: Vec<B256>) -> Self {
568 Self(v)
569 }
570}
571
572#[derive(Clone, Debug, PartialEq, Eq, Default)]
575#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
576pub struct NewPooledTransactionHashes68 {
577 pub types: Vec<u8>,
601 pub sizes: Vec<usize>,
603 pub hashes: Vec<B256>,
605}
606
607#[cfg(feature = "arbitrary")]
608impl proptest::prelude::Arbitrary for NewPooledTransactionHashes68 {
609 type Parameters = ();
610 fn arbitrary_with(_args: ()) -> Self::Strategy {
611 use proptest::{collection::vec, prelude::*};
612 let vec_length = any::<usize>().prop_map(|x| x % 100 + 1); vec_length
616 .prop_flat_map(|len| {
617 let types_vec = vec(
619 proptest_arbitrary_interop::arb::<reth_ethereum_primitives::TxType>()
620 .prop_map(|ty| ty as u8),
621 len..=len,
622 );
623
624 let sizes_vec = vec(proptest::num::usize::ANY.prop_map(|x| x % 131072), len..=len);
626 let hashes_vec = vec(any::<B256>(), len..=len);
627
628 (types_vec, sizes_vec, hashes_vec)
629 })
630 .prop_map(|(types, sizes, hashes)| Self { types, sizes, hashes })
631 .boxed()
632 }
633
634 type Strategy = proptest::prelude::BoxedStrategy<Self>;
635}
636
637impl NewPooledTransactionHashes68 {
638 pub const fn len(&self) -> usize {
640 self.hashes.len()
641 }
642
643 pub const fn is_empty(&self) -> bool {
645 self.hashes.is_empty()
646 }
647
648 pub fn with_capacity(capacity: usize) -> Self {
650 Self {
651 types: Vec::with_capacity(capacity),
652 sizes: Vec::with_capacity(capacity),
653 hashes: Vec::with_capacity(capacity),
654 }
655 }
656
657 pub fn metadata_iter(&self) -> impl Iterator<Item = (&B256, (u8, usize))> {
659 self.hashes.iter().zip(self.types.iter().copied().zip(self.sizes.iter().copied()))
660 }
661
662 pub fn push<T: SignedTransaction>(&mut self, tx: &T) {
664 self.hashes.push(*tx.tx_hash());
665 self.sizes.push(tx.encode_2718_len());
666 self.types.push(tx.ty());
667 }
668
669 pub fn extend<'a, T: SignedTransaction>(&mut self, txs: impl IntoIterator<Item = &'a T>) {
671 for tx in txs {
672 self.push(tx);
673 }
674 }
675
676 pub fn shrink_to_fit(&mut self) {
678 self.hashes.shrink_to_fit();
679 self.sizes.shrink_to_fit();
680 self.types.shrink_to_fit()
681 }
682
683 pub fn with_transaction<T: SignedTransaction>(mut self, tx: &T) -> Self {
685 self.push(tx);
686 self
687 }
688
689 pub fn with_transactions<'a, T: SignedTransaction>(
691 mut self,
692 txs: impl IntoIterator<Item = &'a T>,
693 ) -> Self {
694 self.extend(txs);
695 self
696 }
697}
698
699impl Encodable for NewPooledTransactionHashes68 {
700 fn encode(&self, out: &mut dyn bytes::BufMut) {
701 #[derive(RlpEncodable)]
702 struct EncodableNewPooledTransactionHashes68<'a> {
703 types: &'a [u8],
704 sizes: &'a Vec<usize>,
705 hashes: &'a Vec<B256>,
706 }
707
708 let encodable = EncodableNewPooledTransactionHashes68 {
709 types: &self.types[..],
710 sizes: &self.sizes,
711 hashes: &self.hashes,
712 };
713
714 encodable.encode(out);
715 }
716 fn length(&self) -> usize {
717 #[derive(RlpEncodable)]
718 struct EncodableNewPooledTransactionHashes68<'a> {
719 types: &'a [u8],
720 sizes: &'a Vec<usize>,
721 hashes: &'a Vec<B256>,
722 }
723
724 let encodable = EncodableNewPooledTransactionHashes68 {
725 types: &self.types[..],
726 sizes: &self.sizes,
727 hashes: &self.hashes,
728 };
729
730 encodable.length()
731 }
732}
733
734impl Decodable for NewPooledTransactionHashes68 {
735 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
736 let Header { list, payload_length } = Header::decode(buf)?;
737 if !list {
738 return Err(alloy_rlp::Error::UnexpectedString)
739 }
740 let (mut payload, rest) = buf.split_at(payload_length);
742 let (types, sizes, hashes) = decode_pooled_transaction_hashes_payload(&mut payload)?;
743
744 if !payload.is_empty() {
745 return Err(alloy_rlp::Error::ListLengthMismatch {
746 expected: payload_length,
747 got: payload_length - payload.len(),
748 })
749 }
750
751 ensure_pooled_transaction_hashes_lengths(hashes.len(), types.len(), sizes.len())?;
752
753 *buf = rest;
754 Ok(Self { types, sizes, hashes })
755 }
756}
757
758#[derive(Clone, Debug, PartialEq, Eq, Default)]
761#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
762pub struct NewPooledTransactionHashes72 {
763 pub types: Vec<u8>,
772 pub sizes: Vec<usize>,
774 pub hashes: Vec<B256>,
776 pub cell_mask: Option<B128>,
788}
789
790#[cfg(feature = "arbitrary")]
791impl proptest::prelude::Arbitrary for NewPooledTransactionHashes72 {
792 type Parameters = ();
793 fn arbitrary_with(_args: ()) -> Self::Strategy {
794 use proptest::{collection::vec, prelude::*};
795 let vec_length = any::<usize>().prop_map(|x| x % 100 + 1); vec_length
799 .prop_flat_map(|len| {
800 let types_vec = vec(
802 proptest_arbitrary_interop::arb::<reth_ethereum_primitives::TxType>()
803 .prop_map(|ty| ty as u8),
804 len..=len,
805 );
806
807 let sizes_vec = vec(proptest::num::usize::ANY.prop_map(|x| x % 131072), len..=len);
809 let hashes_vec = vec(any::<B256>(), len..=len);
810 let cell_mask =
813 any::<Option<B128>>().prop_map(|mask| mask.filter(|mask| !mask.is_zero()));
814
815 (types_vec, sizes_vec, hashes_vec, cell_mask)
816 })
817 .prop_map(|(types, sizes, hashes, cell_mask)| Self { types, sizes, hashes, cell_mask })
818 .boxed()
819 }
820
821 type Strategy = proptest::prelude::BoxedStrategy<Self>;
822}
823
824impl NewPooledTransactionHashes72 {
825 pub const fn len(&self) -> usize {
827 self.hashes.len()
828 }
829
830 pub const fn is_empty(&self) -> bool {
832 self.hashes.is_empty()
833 }
834
835 pub const ALL_CELLS_MASK: B128 = B128::repeat_byte(0xff);
840
841 pub fn with_capacity(capacity: usize) -> Self {
843 Self {
844 types: Vec::with_capacity(capacity),
845 sizes: Vec::with_capacity(capacity),
846 hashes: Vec::with_capacity(capacity),
847 cell_mask: None,
848 }
849 }
850
851 pub fn metadata_iter(&self) -> impl Iterator<Item = (&B256, (u8, usize))> {
853 self.hashes.iter().zip(self.types.iter().copied().zip(self.sizes.iter().copied()))
854 }
855
856 pub fn push<T: SignedTransaction>(&mut self, tx: &T) {
858 self.hashes.push(*tx.tx_hash());
859 self.sizes.push(tx.encode_2718_len());
860 self.types.push(tx.ty());
861 if tx.is_eip4844() {
862 self.cell_mask = Some(Self::ALL_CELLS_MASK);
863 }
864 }
865
866 pub fn extend<'a, T: SignedTransaction>(&mut self, txs: impl IntoIterator<Item = &'a T>) {
868 for tx in txs {
869 self.push(tx);
870 }
871 }
872
873 pub fn shrink_to_fit(&mut self) {
875 self.hashes.shrink_to_fit();
876 self.sizes.shrink_to_fit();
877 self.types.shrink_to_fit()
878 }
879
880 pub fn with_transaction<T: SignedTransaction>(mut self, tx: &T) -> Self {
882 self.push(tx);
883 self
884 }
885
886 pub fn with_transactions<'a, T: SignedTransaction>(
888 mut self,
889 txs: impl IntoIterator<Item = &'a T>,
890 ) -> Self {
891 self.extend(txs);
892 self
893 }
894
895 fn payload_length(&self) -> usize {
896 self.types.as_slice().length() +
897 self.sizes.length() +
898 self.hashes.length() +
899 self.cell_mask.unwrap_or_default().length()
900 }
901}
902
903impl Encodable for NewPooledTransactionHashes72 {
904 fn encode(&self, out: &mut dyn bytes::BufMut) {
905 Header { list: true, payload_length: self.payload_length() }.encode(out);
906 self.types.as_slice().encode(out);
907 self.sizes.encode(out);
908 self.hashes.encode(out);
909 self.cell_mask.unwrap_or_default().encode(out);
911 }
912
913 fn length(&self) -> usize {
914 Header { list: true, payload_length: self.payload_length() }.length_with_payload()
915 }
916}
917
918impl Decodable for NewPooledTransactionHashes72 {
919 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
920 let Header { list, payload_length } = Header::decode(buf)?;
921 if !list {
922 return Err(alloy_rlp::Error::UnexpectedString)
923 }
924 let (mut payload, rest) = buf.split_at(payload_length);
926 let (types, sizes, hashes) = decode_pooled_transaction_hashes_payload(&mut payload)?;
927 let Some(first_byte) = payload.first().copied() else {
928 return Err(alloy_rlp::Error::InputTooShort)
929 };
930 let cell_mask = if first_byte == alloy_rlp::EMPTY_STRING_CODE {
931 payload = &payload[1..];
933 None
934 } else {
935 Some(B128::decode(&mut payload)?).filter(|mask| !mask.is_zero())
938 };
939
940 if !payload.is_empty() {
941 return Err(alloy_rlp::Error::ListLengthMismatch {
942 expected: payload_length,
943 got: payload_length - payload.len(),
944 })
945 }
946
947 ensure_pooled_transaction_hashes_lengths(hashes.len(), types.len(), sizes.len())?;
948
949 *buf = rest;
950 Ok(Self { types, sizes, hashes, cell_mask })
951 }
952}
953
954const NEW_POOLED_TRANSACTION_HASHES_DECODE_CAP: usize =
959 2 * SOFT_LIMIT_COUNT_HASHES_IN_NEW_POOLED_TRANSACTIONS_BROADCAST_MESSAGE;
960
961#[inline]
962fn decode_pooled_transaction_hashes_payload(
963 payload: &mut &[u8],
964) -> alloy_rlp::Result<(Vec<u8>, Vec<usize>, Vec<B256>)> {
965 let types = Bytes::decode(payload)?;
966 let capacity = types.len().min(NEW_POOLED_TRANSACTION_HASHES_DECODE_CAP);
967
968 let mut sizes = Vec::with_capacity(capacity);
969 decode_append(payload, &mut sizes)?;
970
971 let mut hashes = Vec::with_capacity(capacity);
972 decode_append(payload, &mut hashes)?;
973
974 Ok((types.into(), sizes, hashes))
975}
976
977#[inline]
978const fn ensure_pooled_transaction_hashes_lengths(
979 hashes_len: usize,
980 types_len: usize,
981 sizes_len: usize,
982) -> alloy_rlp::Result<()> {
983 if hashes_len != types_len {
984 return Err(alloy_rlp::Error::ListLengthMismatch { expected: hashes_len, got: types_len })
985 }
986 if hashes_len != sizes_len {
987 return Err(alloy_rlp::Error::ListLengthMismatch { expected: hashes_len, got: sizes_len })
988 }
989
990 Ok(())
991}
992
993pub trait HandleMempoolData {
996 fn is_empty(&self) -> bool;
998
999 fn len(&self) -> usize;
1001
1002 fn retain_by_hash(&mut self, f: impl FnMut(&TxHash) -> bool);
1004}
1005
1006impl<T: SignedTransaction> HandleMempoolData for Vec<T> {
1007 fn is_empty(&self) -> bool {
1008 self.is_empty()
1009 }
1010
1011 fn len(&self) -> usize {
1012 self.len()
1013 }
1014
1015 fn retain_by_hash(&mut self, mut f: impl FnMut(&TxHash) -> bool) {
1016 self.retain(|tx| f(tx.tx_hash()))
1017 }
1018}
1019
1020#[derive(Clone, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)]
1023#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1024#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1025pub struct BlockRangeUpdate {
1026 pub earliest: u64,
1028 pub latest: u64,
1030 pub latest_hash: B256,
1032}
1033
1034impl InMemorySize for NewPooledTransactionHashes {
1035 fn size(&self) -> usize {
1036 match self {
1037 Self::Eth66(msg) => msg.0.len() * core::mem::size_of::<B256>(),
1038 Self::Eth68(msg) => {
1039 msg.types.len() * core::mem::size_of::<u8>() +
1040 msg.sizes.len() * core::mem::size_of::<usize>() +
1041 msg.hashes.len() * core::mem::size_of::<B256>()
1042 }
1043 Self::Eth72(msg) => {
1044 msg.types.len() * core::mem::size_of::<u8>() +
1045 msg.sizes.len() * core::mem::size_of::<usize>() +
1046 msg.hashes.len() * core::mem::size_of::<B256>() +
1047 core::mem::size_of::<B128>()
1048 }
1049 }
1050 }
1051}
1052
1053#[cfg(test)]
1054mod tests {
1055 use super::*;
1056 use alloy_consensus::{transaction::TxHashRef, Typed2718};
1057 use alloy_eips::eip2718::Encodable2718;
1058 use alloy_primitives::{b256, hex, Bytes, Signature, U256};
1059 use alloy_rlp::{RlpDecodable, RlpEncodable};
1060 use proptest::prelude::*;
1061 use reth_ethereum_primitives::{Transaction, TransactionSigned};
1062 use std::str::FromStr;
1063
1064 fn test_encoding_vector<T: Encodable + Decodable + PartialEq + core::fmt::Debug>(
1067 input: (T, &[u8]),
1068 ) {
1069 let (expected_decoded, expected_encoded) = input;
1070 let mut encoded = Vec::new();
1071 expected_decoded.encode(&mut encoded);
1072
1073 assert_eq!(hex::encode(&encoded), hex::encode(expected_encoded));
1074
1075 let decoded = T::decode(&mut encoded.as_ref()).unwrap();
1076 assert_eq!(expected_decoded, decoded);
1077 }
1078
1079 fn encoded<T: Encodable>(value: &T) -> Vec<u8> {
1080 let mut out = Vec::new();
1081 value.encode(&mut out);
1082 out
1083 }
1084
1085 #[derive(RlpEncodable, RlpDecodable)]
1086 struct EncodableNewPooledTransactionHashes68 {
1087 types: Bytes,
1088 sizes: Vec<usize>,
1089 hashes: Vec<B256>,
1090 }
1091
1092 type NewPooledTransactionHashes68Fields = (Vec<u8>, Vec<usize>, Vec<B256>);
1093
1094 fn decode_eth68_hashes_derived(
1095 buf: &mut &[u8],
1096 ) -> alloy_rlp::Result<NewPooledTransactionHashes68> {
1097 let encodable = EncodableNewPooledTransactionHashes68::decode(buf)?;
1098 let msg = NewPooledTransactionHashes68 {
1099 types: encodable.types.into(),
1100 sizes: encodable.sizes,
1101 hashes: encodable.hashes,
1102 };
1103
1104 ensure_pooled_transaction_hashes_lengths(
1105 msg.hashes.len(),
1106 msg.types.len(),
1107 msg.sizes.len(),
1108 )?;
1109
1110 Ok(msg)
1111 }
1112
1113 fn eth68_hash_fields_strategy() -> impl Strategy<Value = NewPooledTransactionHashes68Fields> {
1114 (0usize..128, 0usize..128, 0usize..128).prop_flat_map(
1115 |(types_len, sizes_len, hashes_len)| {
1116 (
1117 proptest::collection::vec(any::<u8>(), types_len),
1118 proptest::collection::vec(0usize..131_072, sizes_len),
1119 proptest::collection::vec(any::<B256>(), hashes_len),
1120 )
1121 },
1122 )
1123 }
1124
1125 proptest! {
1126 #[test]
1127 fn broadcast_pool_transactions_match_shared_transactions_encoding(
1128 txs in proptest::collection::vec(
1129 proptest_arbitrary_interop::arb::<TransactionSigned>(),
1130 0..32,
1131 )
1132 ) {
1133 let shared = SharedTransactions::<TransactionSigned>(
1134 txs.iter().cloned().map(Arc::new).collect(),
1135 );
1136 let broadcast = BroadcastPoolTransactions(
1137 txs.iter().cloned().map(LazyEncoded::new).collect(),
1138 );
1139
1140 prop_assert_eq!(broadcast.length(), shared.length());
1141
1142 let shared_encoded = encoded(&shared);
1143 let broadcast_encoded = encoded(&broadcast);
1144 prop_assert_eq!(&broadcast_encoded, &shared_encoded);
1145
1146 let broadcast_encoded_cached = encoded(&broadcast);
1147 prop_assert_eq!(&broadcast_encoded_cached, &shared_encoded);
1148
1149 let mut shared_bytes = shared_encoded.as_slice();
1150 let decoded_shared = SharedTransactions::<TransactionSigned>::decode(&mut shared_bytes)
1151 .expect("shared transactions decode");
1152 prop_assert!(shared_bytes.is_empty());
1153
1154 let mut broadcast_bytes = broadcast_encoded.as_slice();
1155 let decoded_broadcast =
1156 SharedTransactions::<TransactionSigned>::decode(&mut broadcast_bytes)
1157 .expect("broadcast pool transactions decode as shared transactions");
1158 prop_assert!(broadcast_bytes.is_empty());
1159
1160 prop_assert_eq!(decoded_broadcast, decoded_shared);
1161 }
1162
1163 #[test]
1164 fn eth_68_handrolled_decode_matches_derived_implementation(
1165 (types, sizes, hashes) in eth68_hash_fields_strategy()
1166 ) {
1167 let encodable = EncodableNewPooledTransactionHashes68 {
1168 types: Bytes::from(types),
1169 sizes,
1170 hashes,
1171 };
1172 let encoded = encoded(&encodable);
1173
1174 let mut derived_buf = encoded.as_slice();
1175 let derived = decode_eth68_hashes_derived(&mut derived_buf);
1176
1177 let mut handrolled_buf = encoded.as_slice();
1178 let handrolled = NewPooledTransactionHashes68::decode(&mut handrolled_buf);
1179
1180 let handrolled_is_ok = handrolled.is_ok();
1181 prop_assert_eq!(&handrolled, &derived);
1182 if handrolled_is_ok {
1183 prop_assert!(derived_buf.is_empty());
1184 prop_assert!(handrolled_buf.is_empty());
1185 }
1186 }
1187 }
1188
1189 #[test]
1190 fn can_return_latest_block() {
1191 let mut blocks = NewBlockHashes(vec![BlockHashNumber { hash: B256::random(), number: 0 }]);
1192 let latest = blocks.latest().unwrap();
1193 assert_eq!(latest.number, 0);
1194
1195 blocks.push(BlockHashNumber { hash: B256::random(), number: 100 });
1196 blocks.push(BlockHashNumber { hash: B256::random(), number: 2 });
1197 let latest = blocks.latest().unwrap();
1198 assert_eq!(latest.number, 100);
1199 }
1200
1201 #[test]
1202 fn eth_68_tx_hash_roundtrip() {
1203 let vectors = vec![
1204 (
1205 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] },
1206 &hex!("c380c0c0")[..],
1207 ),
1208 (
1209 NewPooledTransactionHashes68 {
1210 types: vec![0x00],
1211 sizes: vec![0x00],
1212 hashes: vec![
1213 B256::ZERO,
1214 ],
1215 },
1216 &hex!(
1217 "e500c180e1a00000000000000000000000000000000000000000000000000000000000000000"
1218 )[..],
1219 ),
1220 (
1221 NewPooledTransactionHashes68 {
1222 types: vec![0x00, 0x00],
1223 sizes: vec![0x00, 0x00],
1224 hashes: vec![
1225 B256::ZERO,
1226 B256::ZERO,
1227 ],
1228 },
1229 &hex!(
1230 "f84a820000c28080f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000"
1231 )[..],
1232 ),
1233 (
1234 NewPooledTransactionHashes68 {
1235 types: vec![0x02],
1236 sizes: vec![0xb6],
1237 hashes: vec![
1238 b256!("0xfecbed04c7b88d8e7221a0a3f5dc33f220212347fc167459ea5cc9c3eb4c1124"),
1239 ],
1240 },
1241 &hex!(
1242 "e602c281b6e1a0fecbed04c7b88d8e7221a0a3f5dc33f220212347fc167459ea5cc9c3eb4c1124"
1243 )[..],
1244 ),
1245 (
1246 NewPooledTransactionHashes68 {
1247 types: vec![0xff, 0xff],
1248 sizes: vec![0xffffffff, 0xffffffff],
1249 hashes: vec![
1250 B256::repeat_byte(0xff),
1251 B256::repeat_byte(0xff),
1252 ],
1253 },
1254 &hex!(
1255 "f85282ffffca84ffffffff84fffffffff842a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1256 )[..],
1257 ),
1258 (
1259 NewPooledTransactionHashes68 {
1260 types: vec![0xff, 0xff],
1261 sizes: vec![0xffffffff, 0xffffffff],
1262 hashes: vec![
1263 b256!("0xbeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe"),
1264 b256!("0xbeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe"),
1265 ],
1266 },
1267 &hex!(
1268 "f85282ffffca84ffffffff84fffffffff842a0beefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafea0beefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe"
1269 )[..],
1270 ),
1271 (
1272 NewPooledTransactionHashes68 {
1273 types: vec![0x10, 0x10],
1274 sizes: vec![0xdeadc0de, 0xdeadc0de],
1275 hashes: vec![
1276 b256!("0x3b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2"),
1277 b256!("0x3b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2"),
1278 ],
1279 },
1280 &hex!(
1281 "f852821010ca84deadc0de84deadc0def842a03b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2a03b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2"
1282 )[..],
1283 ),
1284 (
1285 NewPooledTransactionHashes68 {
1286 types: vec![0x6f, 0x6f],
1287 sizes: vec![0x7fffffff, 0x7fffffff],
1288 hashes: vec![
1289 B256::with_last_byte(2),
1290 B256::with_last_byte(2),
1291 ],
1292 },
1293 &hex!(
1294 "f852826f6fca847fffffff847ffffffff842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000002"
1295 )[..],
1296 ),
1297 ];
1298
1299 for vector in vectors {
1300 test_encoding_vector(vector);
1301 }
1302 }
1303
1304 #[test]
1305 fn eth_72_zero_cell_mask_decodes_as_none() {
1306 let zero = NewPooledTransactionHashes72 {
1309 types: vec![],
1310 sizes: vec![],
1311 hashes: vec![],
1312 cell_mask: Some(B128::ZERO),
1313 };
1314
1315 let mut encoded = Vec::new();
1316 zero.encode(&mut encoded);
1317
1318 let decoded = NewPooledTransactionHashes72::decode(&mut &encoded[..]).unwrap();
1319
1320 assert_eq!(decoded.cell_mask, None);
1321 assert_eq!(encoded, hex!("d480c0c09000000000000000000000000000000000"));
1322 }
1323
1324 #[test]
1325 fn eth_72_tx_hash_roundtrip() {
1326 let vectors = vec![
1327 (
1330 NewPooledTransactionHashes72 {
1331 types: vec![],
1332 sizes: vec![],
1333 hashes: vec![],
1334 cell_mask: None,
1335 },
1336 &hex!("d480c0c09000000000000000000000000000000000")[..],
1337 ),
1338 (
1339 NewPooledTransactionHashes72 {
1340 types: vec![],
1341 sizes: vec![],
1342 hashes: vec![],
1343 cell_mask: Some(B128::repeat_byte(0x11)),
1344 },
1345 &hex!("d480c0c09011111111111111111111111111111111")[..],
1346 ),
1347 ];
1348
1349 for vector in vectors {
1350 test_encoding_vector(vector);
1351 }
1352 }
1353
1354 #[test]
1355 fn eth_72_decodes_spec_nil_cell_mask() {
1356 let encoded = hex!("c480c0c080");
1359
1360 let decoded = NewPooledTransactionHashes72::decode(&mut encoded.as_ref()).unwrap();
1361
1362 assert_eq!(decoded.cell_mask, None);
1363 }
1364
1365 #[test]
1366 fn eth_72_rejects_missing_cell_mask() {
1367 let encoded_eth68_payload = hex!("c380c0c0");
1368
1369 let result = NewPooledTransactionHashes72::decode(&mut encoded_eth68_payload.as_ref());
1370
1371 assert!(matches!(result, Err(alloy_rlp::Error::InputTooShort)));
1372 }
1373
1374 fn signed_transaction() -> impl SignedTransaction {
1375 TransactionSigned::new_unhashed(
1376 Transaction::Legacy(Default::default()),
1377 Signature::new(
1378 U256::from_str(
1379 "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
1380 )
1381 .unwrap(),
1382 U256::from_str(
1383 "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
1384 )
1385 .unwrap(),
1386 false,
1387 ),
1388 )
1389 }
1390
1391 #[test]
1392 fn test_pooled_tx_hashes_68_push() {
1393 let tx = signed_transaction();
1394 let mut tx_hashes =
1395 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] };
1396 tx_hashes.push(&tx);
1397 assert_eq!(tx_hashes.types.len(), 1);
1398 assert_eq!(tx_hashes.sizes.len(), 1);
1399 assert_eq!(tx_hashes.hashes.len(), 1);
1400 assert_eq!(tx_hashes.types[0], tx.ty());
1401 assert_eq!(tx_hashes.sizes[0], tx.encode_2718_len());
1402 assert_eq!(tx_hashes.hashes[0], *tx.tx_hash());
1403 }
1404
1405 #[test]
1406 fn test_pooled_tx_hashes_68_extend() {
1407 let tx = signed_transaction();
1408 let txs = vec![tx.clone(), tx.clone()];
1409 let mut tx_hashes =
1410 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] };
1411 tx_hashes.extend(&txs);
1412 assert_eq!(tx_hashes.types.len(), 2);
1413 assert_eq!(tx_hashes.sizes.len(), 2);
1414 assert_eq!(tx_hashes.hashes.len(), 2);
1415 assert_eq!(tx_hashes.types[0], tx.ty());
1416 assert_eq!(tx_hashes.sizes[0], tx.encode_2718_len());
1417 assert_eq!(tx_hashes.hashes[0], *tx.tx_hash());
1418 assert_eq!(tx_hashes.types[1], tx.ty());
1419 assert_eq!(tx_hashes.sizes[1], tx.encode_2718_len());
1420 assert_eq!(tx_hashes.hashes[1], *tx.tx_hash());
1421 }
1422
1423 #[test]
1424 fn test_pooled_tx_hashes_68_with_transaction() {
1425 let tx = signed_transaction();
1426 let tx_hashes =
1427 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] }
1428 .with_transaction(&tx);
1429 assert_eq!(tx_hashes.types.len(), 1);
1430 assert_eq!(tx_hashes.sizes.len(), 1);
1431 assert_eq!(tx_hashes.hashes.len(), 1);
1432 assert_eq!(tx_hashes.types[0], tx.ty());
1433 assert_eq!(tx_hashes.sizes[0], tx.encode_2718_len());
1434 assert_eq!(tx_hashes.hashes[0], *tx.tx_hash());
1435 }
1436
1437 #[test]
1438 fn test_pooled_tx_hashes_68_with_transactions() {
1439 let tx = signed_transaction();
1440 let txs = vec![tx.clone(), tx.clone()];
1441 let tx_hashes =
1442 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] }
1443 .with_transactions(&txs);
1444 assert_eq!(tx_hashes.types.len(), 2);
1445 assert_eq!(tx_hashes.sizes.len(), 2);
1446 assert_eq!(tx_hashes.hashes.len(), 2);
1447 assert_eq!(tx_hashes.types[0], tx.ty());
1448 assert_eq!(tx_hashes.sizes[0], tx.encode_2718_len());
1449 assert_eq!(tx_hashes.hashes[0], *tx.tx_hash());
1450 assert_eq!(tx_hashes.types[1], tx.ty());
1451 assert_eq!(tx_hashes.sizes[1], tx.encode_2718_len());
1452 assert_eq!(tx_hashes.hashes[1], *tx.tx_hash());
1453 }
1454}