1use crate::{EthMessage, EthVersion, NetworkPrimitives};
4use alloc::{sync::Arc, vec::Vec};
5use alloy_primitives::{
6 map::{HashMap, HashSet},
7 Bytes, TxHash, B128, B256, U128,
8};
9use alloy_rlp::{
10 Decodable, Encodable, Header, RlpDecodable, RlpDecodableWrapper, RlpEncodable,
11 RlpEncodableWrapper,
12};
13use core::{fmt::Debug, mem};
14use derive_more::{Constructor, Deref, DerefMut, From, IntoIterator};
15use reth_codecs_derive::{add_arbitrary_tests, generate_tests};
16use reth_ethereum_primitives::TransactionSigned;
17use reth_primitives_traits::{Block, InMemorySize, SignedTransaction};
18
19#[derive(
21 Clone,
22 Debug,
23 PartialEq,
24 Eq,
25 RlpEncodableWrapper,
26 RlpDecodableWrapper,
27 Default,
28 Deref,
29 DerefMut,
30 IntoIterator,
31)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
34#[add_arbitrary_tests(rlp)]
35pub struct NewBlockHashes(
36 pub Vec<BlockHashNumber>,
39);
40
41impl NewBlockHashes {
44 pub fn latest(&self) -> Option<&BlockHashNumber> {
46 self.iter().max_by_key(|b| b.number)
47 }
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
54#[add_arbitrary_tests(rlp)]
55pub struct BlockHashNumber {
56 pub hash: B256,
58 pub number: u64,
60}
61
62impl From<Vec<BlockHashNumber>> for NewBlockHashes {
63 fn from(v: Vec<BlockHashNumber>) -> Self {
64 Self(v)
65 }
66}
67
68impl From<NewBlockHashes> for Vec<BlockHashNumber> {
69 fn from(v: NewBlockHashes) -> Self {
70 v.0
71 }
72}
73
74pub trait NewBlockPayload:
76 Encodable + Decodable + Clone + Eq + Debug + Send + Sync + Unpin + 'static
77{
78 type Block: Block;
80
81 fn block(&self) -> &Self::Block;
83}
84
85#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
89#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
90pub struct NewBlock<B = reth_ethereum_primitives::Block> {
91 pub block: B,
93 pub td: U128,
95}
96
97impl<B: Block + 'static> NewBlockPayload for NewBlock<B> {
98 type Block = B;
99
100 fn block(&self) -> &Self::Block {
101 &self.block
102 }
103}
104
105generate_tests!(#[rlp, 25] NewBlock<reth_ethereum_primitives::Block>, EthNewBlockTests);
106
107#[derive(
110 Clone,
111 Debug,
112 PartialEq,
113 Eq,
114 RlpEncodableWrapper,
115 RlpDecodableWrapper,
116 Default,
117 Deref,
118 IntoIterator,
119)]
120#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
121#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
122#[add_arbitrary_tests(rlp, 10)]
123pub struct Transactions<T = TransactionSigned>(
124 pub Vec<T>,
126);
127
128impl<T: SignedTransaction> Transactions<T> {
129 pub fn has_eip4844(&self) -> bool {
131 self.iter().any(|tx| tx.is_eip4844())
132 }
133}
134
135impl<T> From<Vec<T>> for Transactions<T> {
136 fn from(txs: Vec<T>) -> Self {
137 Self(txs)
138 }
139}
140
141impl<T> From<Transactions<T>> for Vec<T> {
142 fn from(txs: Transactions<T>) -> Self {
143 txs.0
144 }
145}
146
147impl<T: Decodable + InMemorySize> Transactions<T> {
148 pub fn decode_with_memory_budget(
152 buf: &mut &[u8],
153 memory_budget: usize,
154 ) -> alloy_rlp::Result<Self> {
155 decode_list_with_memory_budget(buf, memory_budget).map(Self)
156 }
157}
158
159pub fn decode_list_with_memory_budget<T: Decodable + InMemorySize>(
162 buf: &mut &[u8],
163 memory_budget: usize,
164) -> alloy_rlp::Result<Vec<T>> {
165 let header = Header::decode(buf)?;
166 if !header.list {
167 return Err(alloy_rlp::Error::UnexpectedString);
168 }
169 if buf.len() < header.payload_length {
170 return Err(alloy_rlp::Error::InputTooShort);
171 }
172
173 let (payload, rest) = buf.split_at(header.payload_length);
174 let mut payload = payload;
175
176 let mut txs = Vec::new();
177 let mut total_size = 0usize;
178
179 while !payload.is_empty() {
180 let item = T::decode(&mut payload)?;
181 total_size = total_size.saturating_add(item.size());
182
183 if total_size > memory_budget {
184 break;
185 }
186
187 txs.push(item);
188 }
189
190 *buf = rest;
191 Ok(txs)
192}
193
194#[derive(
199 Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Deref, IntoIterator,
200)]
201#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
202#[add_arbitrary_tests(rlp, 20)]
203pub struct SharedTransactions<T = TransactionSigned>(
204 pub Vec<Arc<T>>,
206);
207
208#[derive(Clone, Debug, PartialEq, Eq)]
210#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
211pub enum NewPooledTransactionHashes {
212 Eth66(NewPooledTransactionHashes66),
214 Eth68(NewPooledTransactionHashes68),
218}
219
220impl NewPooledTransactionHashes {
223 pub const fn version(&self) -> EthVersion {
225 match self {
226 Self::Eth66(_) => EthVersion::Eth66,
227 Self::Eth68(_) => EthVersion::Eth68,
228 }
229 }
230
231 pub const fn is_valid_for_version(&self, version: EthVersion) -> bool {
233 match self {
234 Self::Eth66(_) => {
235 matches!(version, EthVersion::Eth67 | EthVersion::Eth66)
236 }
237 Self::Eth68(_) => {
238 matches!(
239 version,
240 EthVersion::Eth68 |
241 EthVersion::Eth69 |
242 EthVersion::Eth70 |
243 EthVersion::Eth71 |
244 EthVersion::Eth72
245 )
246 }
247 }
248 }
249
250 pub fn iter_hashes(&self) -> impl Iterator<Item = &B256> + '_ {
252 match self {
253 Self::Eth66(msg) => msg.iter(),
254 Self::Eth68(msg) => msg.hashes.iter(),
255 }
256 }
257
258 pub const fn hashes(&self) -> &Vec<B256> {
260 match self {
261 Self::Eth66(msg) => &msg.0,
262 Self::Eth68(msg) => &msg.hashes,
263 }
264 }
265
266 pub const fn hashes_mut(&mut self) -> &mut Vec<B256> {
268 match self {
269 Self::Eth66(msg) => &mut msg.0,
270 Self::Eth68(msg) => &mut msg.hashes,
271 }
272 }
273
274 pub fn into_hashes(self) -> Vec<B256> {
276 match self {
277 Self::Eth66(msg) => msg.0,
278 Self::Eth68(msg) => msg.hashes,
279 }
280 }
281
282 pub fn into_iter_hashes(self) -> impl Iterator<Item = B256> {
284 match self {
285 Self::Eth66(msg) => msg.into_iter(),
286 Self::Eth68(msg) => msg.hashes.into_iter(),
287 }
288 }
289
290 pub fn truncate(&mut self, len: usize) {
293 match self {
294 Self::Eth66(msg) => msg.truncate(len),
295 Self::Eth68(msg) => {
296 msg.types.truncate(len);
297 msg.sizes.truncate(len);
298 msg.hashes.truncate(len);
299 }
300 }
301 }
302
303 pub const fn is_empty(&self) -> bool {
305 match self {
306 Self::Eth66(msg) => msg.0.is_empty(),
307 Self::Eth68(msg) => msg.hashes.is_empty(),
308 }
309 }
310
311 pub const fn len(&self) -> usize {
313 match self {
314 Self::Eth66(msg) => msg.0.len(),
315 Self::Eth68(msg) => msg.hashes.len(),
316 }
317 }
318
319 pub const fn as_eth68(&self) -> Option<&NewPooledTransactionHashes68> {
321 match self {
322 Self::Eth66(_) => None,
323 Self::Eth68(msg) => Some(msg),
324 }
325 }
326
327 pub const fn as_eth68_mut(&mut self) -> Option<&mut NewPooledTransactionHashes68> {
329 match self {
330 Self::Eth66(_) => None,
331 Self::Eth68(msg) => Some(msg),
332 }
333 }
334
335 pub const fn as_eth66_mut(&mut self) -> Option<&mut NewPooledTransactionHashes66> {
337 match self {
338 Self::Eth66(msg) => Some(msg),
339 Self::Eth68(_) => None,
340 }
341 }
342
343 pub fn take_eth68(&mut self) -> Option<NewPooledTransactionHashes68> {
345 match self {
346 Self::Eth66(_) => None,
347 Self::Eth68(msg) => Some(mem::take(msg)),
348 }
349 }
350
351 pub fn take_eth66(&mut self) -> Option<NewPooledTransactionHashes66> {
353 match self {
354 Self::Eth66(msg) => Some(mem::take(msg)),
355 Self::Eth68(_) => None,
356 }
357 }
358}
359
360impl<N: NetworkPrimitives> From<NewPooledTransactionHashes> for EthMessage<N> {
361 fn from(value: NewPooledTransactionHashes) -> Self {
362 match value {
363 NewPooledTransactionHashes::Eth66(msg) => Self::NewPooledTransactionHashes66(msg),
364 NewPooledTransactionHashes::Eth68(msg) => Self::NewPooledTransactionHashes68(msg),
365 }
366 }
367}
368
369impl From<NewPooledTransactionHashes66> for NewPooledTransactionHashes {
370 fn from(hashes: NewPooledTransactionHashes66) -> Self {
371 Self::Eth66(hashes)
372 }
373}
374
375impl From<NewPooledTransactionHashes68> for NewPooledTransactionHashes {
376 fn from(hashes: NewPooledTransactionHashes68) -> Self {
377 Self::Eth68(hashes)
378 }
379}
380
381#[derive(
384 Clone,
385 Debug,
386 PartialEq,
387 Eq,
388 RlpEncodableWrapper,
389 RlpDecodableWrapper,
390 Default,
391 Deref,
392 DerefMut,
393 IntoIterator,
394)]
395#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
396#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
397#[add_arbitrary_tests(rlp)]
398pub struct NewPooledTransactionHashes66(
399 pub Vec<B256>,
403);
404
405impl From<Vec<B256>> for NewPooledTransactionHashes66 {
406 fn from(v: Vec<B256>) -> Self {
407 Self(v)
408 }
409}
410
411#[derive(Clone, Debug, PartialEq, Eq, Default)]
414#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
415pub struct NewPooledTransactionHashes68 {
416 pub types: Vec<u8>,
440 pub sizes: Vec<usize>,
442 pub hashes: Vec<B256>,
444}
445
446#[cfg(feature = "arbitrary")]
447impl proptest::prelude::Arbitrary for NewPooledTransactionHashes68 {
448 type Parameters = ();
449 fn arbitrary_with(_args: ()) -> Self::Strategy {
450 use proptest::{collection::vec, prelude::*};
451 let vec_length = any::<usize>().prop_map(|x| x % 100 + 1); vec_length
455 .prop_flat_map(|len| {
456 let types_vec = vec(
458 proptest_arbitrary_interop::arb::<reth_ethereum_primitives::TxType>()
459 .prop_map(|ty| ty as u8),
460 len..=len,
461 );
462
463 let sizes_vec = vec(proptest::num::usize::ANY.prop_map(|x| x % 131072), len..=len);
465 let hashes_vec = vec(any::<B256>(), len..=len);
466
467 (types_vec, sizes_vec, hashes_vec)
468 })
469 .prop_map(|(types, sizes, hashes)| Self { types, sizes, hashes })
470 .boxed()
471 }
472
473 type Strategy = proptest::prelude::BoxedStrategy<Self>;
474}
475
476impl NewPooledTransactionHashes68 {
477 pub fn metadata_iter(&self) -> impl Iterator<Item = (&B256, (u8, usize))> {
479 self.hashes.iter().zip(self.types.iter().copied().zip(self.sizes.iter().copied()))
480 }
481
482 pub fn push<T: SignedTransaction>(&mut self, tx: &T) {
484 self.hashes.push(*tx.tx_hash());
485 self.sizes.push(tx.encode_2718_len());
486 self.types.push(tx.ty());
487 }
488
489 pub fn extend<'a, T: SignedTransaction>(&mut self, txs: impl IntoIterator<Item = &'a T>) {
491 for tx in txs {
492 self.push(tx);
493 }
494 }
495
496 pub fn shrink_to_fit(&mut self) {
498 self.hashes.shrink_to_fit();
499 self.sizes.shrink_to_fit();
500 self.types.shrink_to_fit()
501 }
502
503 pub fn with_transaction<T: SignedTransaction>(mut self, tx: &T) -> Self {
505 self.push(tx);
506 self
507 }
508
509 pub fn with_transactions<'a, T: SignedTransaction>(
511 mut self,
512 txs: impl IntoIterator<Item = &'a T>,
513 ) -> Self {
514 self.extend(txs);
515 self
516 }
517}
518
519impl Encodable for NewPooledTransactionHashes68 {
520 fn encode(&self, out: &mut dyn bytes::BufMut) {
521 #[derive(RlpEncodable)]
522 struct EncodableNewPooledTransactionHashes68<'a> {
523 types: &'a [u8],
524 sizes: &'a Vec<usize>,
525 hashes: &'a Vec<B256>,
526 }
527
528 let encodable = EncodableNewPooledTransactionHashes68 {
529 types: &self.types[..],
530 sizes: &self.sizes,
531 hashes: &self.hashes,
532 };
533
534 encodable.encode(out);
535 }
536 fn length(&self) -> usize {
537 #[derive(RlpEncodable)]
538 struct EncodableNewPooledTransactionHashes68<'a> {
539 types: &'a [u8],
540 sizes: &'a Vec<usize>,
541 hashes: &'a Vec<B256>,
542 }
543
544 let encodable = EncodableNewPooledTransactionHashes68 {
545 types: &self.types[..],
546 sizes: &self.sizes,
547 hashes: &self.hashes,
548 };
549
550 encodable.length()
551 }
552}
553
554impl Decodable for NewPooledTransactionHashes68 {
555 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
556 #[derive(RlpDecodable)]
557 struct EncodableNewPooledTransactionHashes68 {
558 types: Bytes,
559 sizes: Vec<usize>,
560 hashes: Vec<B256>,
561 }
562
563 let encodable = EncodableNewPooledTransactionHashes68::decode(buf)?;
564 let msg = Self {
565 types: encodable.types.into(),
566 sizes: encodable.sizes,
567 hashes: encodable.hashes,
568 };
569
570 if msg.hashes.len() != msg.types.len() {
571 return Err(alloy_rlp::Error::ListLengthMismatch {
572 expected: msg.hashes.len(),
573 got: msg.types.len(),
574 })
575 }
576 if msg.hashes.len() != msg.sizes.len() {
577 return Err(alloy_rlp::Error::ListLengthMismatch {
578 expected: msg.hashes.len(),
579 got: msg.sizes.len(),
580 })
581 }
582
583 Ok(msg)
584 }
585}
586
587#[derive(Clone, Debug, PartialEq, Eq, Default)]
589#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
590pub struct NewPooledTransactionHashes72 {
591 pub types: Vec<u8>,
600 pub sizes: Vec<usize>,
602 pub hashes: Vec<B256>,
604 pub cell_mask: Option<B128>,
606}
607
608#[cfg(feature = "arbitrary")]
609impl proptest::prelude::Arbitrary for NewPooledTransactionHashes72 {
610 type Parameters = ();
611 fn arbitrary_with(_args: ()) -> Self::Strategy {
612 use proptest::{collection::vec, prelude::*};
613 let vec_length = any::<usize>().prop_map(|x| x % 100 + 1); vec_length
617 .prop_flat_map(|len| {
618 let types_vec = vec(
620 proptest_arbitrary_interop::arb::<reth_ethereum_primitives::TxType>()
621 .prop_map(|ty| ty as u8),
622 len..=len,
623 );
624
625 let sizes_vec = vec(proptest::num::usize::ANY.prop_map(|x| x % 131072), len..=len);
627 let hashes_vec = vec(any::<B256>(), len..=len);
628 let cell_mask = any::<Option<B128>>();
629
630 (types_vec, sizes_vec, hashes_vec, cell_mask)
631 })
632 .prop_map(|(types, sizes, hashes, cell_mask)| Self { types, sizes, hashes, cell_mask })
633 .boxed()
634 }
635
636 type Strategy = proptest::prelude::BoxedStrategy<Self>;
637}
638
639impl NewPooledTransactionHashes72 {
640 pub fn metadata_iter(&self) -> impl Iterator<Item = (&B256, (u8, usize))> {
642 self.hashes.iter().zip(self.types.iter().copied().zip(self.sizes.iter().copied()))
643 }
644
645 pub fn push<T: SignedTransaction>(&mut self, tx: &T) {
647 self.hashes.push(*tx.tx_hash());
648 self.sizes.push(tx.encode_2718_len());
649 self.types.push(tx.ty());
650 }
651
652 pub fn extend<'a, T: SignedTransaction>(&mut self, txs: impl IntoIterator<Item = &'a T>) {
654 for tx in txs {
655 self.push(tx);
656 }
657 }
658
659 pub fn shrink_to_fit(&mut self) {
661 self.hashes.shrink_to_fit();
662 self.sizes.shrink_to_fit();
663 self.types.shrink_to_fit()
664 }
665
666 pub fn with_transaction<T: SignedTransaction>(mut self, tx: &T) -> Self {
668 self.push(tx);
669 self
670 }
671
672 pub fn with_transactions<'a, T: SignedTransaction>(
674 mut self,
675 txs: impl IntoIterator<Item = &'a T>,
676 ) -> Self {
677 self.extend(txs);
678 self
679 }
680
681 fn payload_length(&self) -> usize {
682 self.types.as_slice().length() +
683 self.sizes.length() +
684 self.hashes.length() +
685 self.cell_mask.as_ref().map_or(1, Encodable::length)
686 }
687}
688
689impl Encodable for NewPooledTransactionHashes72 {
690 fn encode(&self, out: &mut dyn bytes::BufMut) {
691 Header { list: true, payload_length: self.payload_length() }.encode(out);
692 self.types.as_slice().encode(out);
693 self.sizes.encode(out);
694 self.hashes.encode(out);
695 if let Some(cell_mask) = &self.cell_mask {
696 cell_mask.encode(out);
697 } else {
698 out.put_u8(alloy_rlp::EMPTY_STRING_CODE);
699 }
700 }
701
702 fn length(&self) -> usize {
703 Header { list: true, payload_length: self.payload_length() }.length_with_payload()
704 }
705}
706
707impl Decodable for NewPooledTransactionHashes72 {
708 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
709 let Header { list, payload_length } = Header::decode(buf)?;
710 if !list {
711 return Err(alloy_rlp::Error::UnexpectedString)
712 }
713 if buf.len() < payload_length {
714 return Err(alloy_rlp::Error::InputTooShort)
715 }
716
717 let (mut payload, rest) = buf.split_at(payload_length);
718 let types = Bytes::decode(&mut payload)?;
719 let sizes = Vec::<usize>::decode(&mut payload)?;
720 let hashes = Vec::<B256>::decode(&mut payload)?;
721 let Some(first_byte) = payload.first().copied() else {
722 return Err(alloy_rlp::Error::InputTooShort)
723 };
724 let cell_mask = if first_byte == alloy_rlp::EMPTY_STRING_CODE {
725 payload = &payload[1..];
726 None
727 } else {
728 Some(B128::decode(&mut payload)?)
729 };
730
731 if !payload.is_empty() {
732 return Err(alloy_rlp::Error::ListLengthMismatch {
733 expected: payload_length,
734 got: payload_length - payload.len(),
735 })
736 }
737
738 let msg = Self { types: types.into(), sizes, hashes, cell_mask };
739
740 if msg.hashes.len() != msg.types.len() {
741 return Err(alloy_rlp::Error::ListLengthMismatch {
742 expected: msg.hashes.len(),
743 got: msg.types.len(),
744 })
745 }
746 if msg.hashes.len() != msg.sizes.len() {
747 return Err(alloy_rlp::Error::ListLengthMismatch {
748 expected: msg.hashes.len(),
749 got: msg.sizes.len(),
750 })
751 }
752
753 *buf = rest;
754
755 Ok(msg)
756 }
757}
758
759pub trait DedupPayload {
761 type Value;
763
764 fn is_empty(&self) -> bool;
766
767 fn len(&self) -> usize;
769
770 fn dedup(self) -> PartiallyValidData<Self::Value>;
772}
773
774pub type Eth68TxMetadata = Option<(u8, usize)>;
776
777impl DedupPayload for NewPooledTransactionHashes {
778 type Value = Eth68TxMetadata;
779
780 fn is_empty(&self) -> bool {
781 self.is_empty()
782 }
783
784 fn len(&self) -> usize {
785 self.len()
786 }
787
788 fn dedup(self) -> PartiallyValidData<Self::Value> {
789 match self {
790 Self::Eth66(msg) => msg.dedup(),
791 Self::Eth68(msg) => msg.dedup(),
792 }
793 }
794}
795
796impl DedupPayload for NewPooledTransactionHashes68 {
797 type Value = Eth68TxMetadata;
798
799 fn is_empty(&self) -> bool {
800 self.hashes.is_empty()
801 }
802
803 fn len(&self) -> usize {
804 self.hashes.len()
805 }
806
807 fn dedup(self) -> PartiallyValidData<Self::Value> {
808 let Self { hashes, mut sizes, mut types } = self;
809
810 let mut deduped_data = HashMap::with_capacity_and_hasher(hashes.len(), Default::default());
811
812 for hash in hashes.into_iter().rev() {
813 if let (Some(ty), Some(size)) = (types.pop(), sizes.pop()) {
814 deduped_data.insert(hash, Some((ty, size)));
815 }
816 }
817
818 PartiallyValidData::from_raw_data_eth68(deduped_data)
819 }
820}
821
822impl DedupPayload for NewPooledTransactionHashes66 {
823 type Value = Eth68TxMetadata;
824
825 fn is_empty(&self) -> bool {
826 self.0.is_empty()
827 }
828
829 fn len(&self) -> usize {
830 self.0.len()
831 }
832
833 fn dedup(self) -> PartiallyValidData<Self::Value> {
834 let Self(hashes) = self;
835
836 let mut deduped_data = HashMap::with_capacity_and_hasher(hashes.len(), Default::default());
837
838 let noop_value: Eth68TxMetadata = None;
839
840 for hash in hashes.into_iter().rev() {
841 deduped_data.insert(hash, noop_value);
842 }
843
844 PartiallyValidData::from_raw_data_eth66(deduped_data)
845 }
846}
847
848pub trait HandleMempoolData {
851 fn is_empty(&self) -> bool;
853
854 fn len(&self) -> usize;
856
857 fn retain_by_hash(&mut self, f: impl FnMut(&TxHash) -> bool);
859}
860
861pub trait HandleVersionedMempoolData {
863 fn msg_version(&self) -> EthVersion;
866}
867
868impl<T: SignedTransaction> HandleMempoolData for Vec<T> {
869 fn is_empty(&self) -> bool {
870 self.is_empty()
871 }
872
873 fn len(&self) -> usize {
874 self.len()
875 }
876
877 fn retain_by_hash(&mut self, mut f: impl FnMut(&TxHash) -> bool) {
878 self.retain(|tx| f(tx.tx_hash()))
879 }
880}
881
882macro_rules! handle_mempool_data_map_impl {
883 ($data_ty:ty, $(<$generic:ident>)?) => {
884 impl$(<$generic>)? HandleMempoolData for $data_ty {
885 fn is_empty(&self) -> bool {
886 self.data.is_empty()
887 }
888
889 fn len(&self) -> usize {
890 self.data.len()
891 }
892
893 fn retain_by_hash(&mut self, mut f: impl FnMut(&TxHash) -> bool) {
894 self.data.retain(|hash, _| f(hash));
895 }
896 }
897 };
898}
899
900#[derive(Debug, Deref, DerefMut, IntoIterator)]
903pub struct PartiallyValidData<V> {
904 #[deref]
905 #[deref_mut]
906 #[into_iterator]
907 data: HashMap<TxHash, V>,
908 version: Option<EthVersion>,
909}
910
911handle_mempool_data_map_impl!(PartiallyValidData<V>, <V>);
912
913impl<V> PartiallyValidData<V> {
914 pub const fn from_raw_data(data: HashMap<TxHash, V>, version: Option<EthVersion>) -> Self {
916 Self { data, version }
917 }
918
919 pub const fn from_raw_data_eth68(data: HashMap<TxHash, V>) -> Self {
921 Self::from_raw_data(data, Some(EthVersion::Eth68))
922 }
923
924 pub const fn from_raw_data_eth66(data: HashMap<TxHash, V>) -> Self {
926 Self::from_raw_data(data, Some(EthVersion::Eth66))
927 }
928
929 pub fn empty_eth68() -> Self {
932 Self::from_raw_data_eth68(HashMap::default())
933 }
934
935 pub fn empty_eth66() -> Self {
938 Self::from_raw_data_eth66(HashMap::default())
939 }
940
941 pub const fn msg_version(&self) -> Option<EthVersion> {
944 self.version
945 }
946
947 pub fn into_data(self) -> HashMap<TxHash, V> {
949 self.data
950 }
951}
952
953#[derive(Debug, Deref, DerefMut, IntoIterator, From)]
956pub struct ValidAnnouncementData {
957 #[deref]
958 #[deref_mut]
959 #[into_iterator]
960 data: HashMap<TxHash, Eth68TxMetadata>,
961 version: EthVersion,
962}
963
964handle_mempool_data_map_impl!(ValidAnnouncementData,);
965
966impl ValidAnnouncementData {
967 pub fn into_request_hashes(self) -> (RequestTxHashes, EthVersion) {
970 let hashes = self.data.into_keys().collect::<HashSet<_>>();
971
972 (RequestTxHashes::new(hashes), self.version)
973 }
974
975 pub fn from_partially_valid_data(data: PartiallyValidData<Eth68TxMetadata>) -> Self {
979 let PartiallyValidData { data, version } = data;
980
981 let version = version.expect("should have eth version for conversion");
982
983 Self { data, version }
984 }
985
986 pub fn into_data(self) -> HashMap<TxHash, Eth68TxMetadata> {
988 self.data
989 }
990}
991
992impl HandleVersionedMempoolData for ValidAnnouncementData {
993 fn msg_version(&self) -> EthVersion {
994 self.version
995 }
996}
997
998#[derive(Debug, Default, Deref, DerefMut, IntoIterator, Constructor)]
1000pub struct RequestTxHashes {
1001 #[deref]
1002 #[deref_mut]
1003 #[into_iterator(owned, ref)]
1004 hashes: HashSet<TxHash>,
1005}
1006
1007impl RequestTxHashes {
1008 pub fn with_capacity(capacity: usize) -> Self {
1013 Self::new(HashSet::with_capacity_and_hasher(capacity, Default::default()))
1014 }
1015
1016 fn empty() -> Self {
1018 Self::new(HashSet::default())
1019 }
1020
1021 pub fn retain_count(&mut self, count: usize) -> Self {
1023 let rest_capacity = self.hashes.len().saturating_sub(count);
1024 if rest_capacity == 0 {
1025 return Self::empty()
1026 }
1027 let mut rest = Self::with_capacity(rest_capacity);
1028
1029 let mut i = 0;
1030 self.hashes.retain(|hash| {
1031 if i >= count {
1032 rest.insert(*hash);
1033 return false
1034 }
1035 i += 1;
1036
1037 true
1038 });
1039
1040 rest
1041 }
1042}
1043
1044impl FromIterator<(TxHash, Eth68TxMetadata)> for RequestTxHashes {
1045 fn from_iter<I: IntoIterator<Item = (TxHash, Eth68TxMetadata)>>(iter: I) -> Self {
1046 Self::new(iter.into_iter().map(|(hash, _)| hash).collect())
1047 }
1048}
1049
1050#[derive(Clone, Debug, PartialEq, Eq, Default, RlpEncodable, RlpDecodable)]
1053#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1054#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
1055pub struct BlockRangeUpdate {
1056 pub earliest: u64,
1058 pub latest: u64,
1060 pub latest_hash: B256,
1062}
1063
1064impl InMemorySize for NewPooledTransactionHashes {
1065 fn size(&self) -> usize {
1066 match self {
1067 Self::Eth66(msg) => msg.0.len() * core::mem::size_of::<B256>(),
1068 Self::Eth68(msg) => {
1069 msg.types.len() * core::mem::size_of::<u8>() +
1070 msg.sizes.len() * core::mem::size_of::<usize>() +
1071 msg.hashes.len() * core::mem::size_of::<B256>()
1072 }
1073 }
1074 }
1075}
1076
1077#[cfg(test)]
1078mod tests {
1079 use super::*;
1080 use alloy_consensus::{transaction::TxHashRef, Typed2718};
1081 use alloy_eips::eip2718::Encodable2718;
1082 use alloy_primitives::{b256, hex, Signature, U256};
1083 use reth_ethereum_primitives::{Transaction, TransactionSigned};
1084 use std::str::FromStr;
1085
1086 fn test_encoding_vector<T: Encodable + Decodable + PartialEq + core::fmt::Debug>(
1089 input: (T, &[u8]),
1090 ) {
1091 let (expected_decoded, expected_encoded) = input;
1092 let mut encoded = Vec::new();
1093 expected_decoded.encode(&mut encoded);
1094
1095 assert_eq!(hex::encode(&encoded), hex::encode(expected_encoded));
1096
1097 let decoded = T::decode(&mut encoded.as_ref()).unwrap();
1098 assert_eq!(expected_decoded, decoded);
1099 }
1100
1101 #[test]
1102 fn can_return_latest_block() {
1103 let mut blocks = NewBlockHashes(vec![BlockHashNumber { hash: B256::random(), number: 0 }]);
1104 let latest = blocks.latest().unwrap();
1105 assert_eq!(latest.number, 0);
1106
1107 blocks.push(BlockHashNumber { hash: B256::random(), number: 100 });
1108 blocks.push(BlockHashNumber { hash: B256::random(), number: 2 });
1109 let latest = blocks.latest().unwrap();
1110 assert_eq!(latest.number, 100);
1111 }
1112
1113 #[test]
1114 fn eth_68_tx_hash_roundtrip() {
1115 let vectors = vec![
1116 (
1117 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] },
1118 &hex!("c380c0c0")[..],
1119 ),
1120 (
1121 NewPooledTransactionHashes68 {
1122 types: vec![0x00],
1123 sizes: vec![0x00],
1124 hashes: vec![
1125 B256::from_str(
1126 "0x0000000000000000000000000000000000000000000000000000000000000000",
1127 )
1128 .unwrap(),
1129 ],
1130 },
1131 &hex!(
1132 "e500c180e1a00000000000000000000000000000000000000000000000000000000000000000"
1133 )[..],
1134 ),
1135 (
1136 NewPooledTransactionHashes68 {
1137 types: vec![0x00, 0x00],
1138 sizes: vec![0x00, 0x00],
1139 hashes: vec![
1140 B256::from_str(
1141 "0x0000000000000000000000000000000000000000000000000000000000000000",
1142 )
1143 .unwrap(),
1144 B256::from_str(
1145 "0x0000000000000000000000000000000000000000000000000000000000000000",
1146 )
1147 .unwrap(),
1148 ],
1149 },
1150 &hex!(
1151 "f84a820000c28080f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000"
1152 )[..],
1153 ),
1154 (
1155 NewPooledTransactionHashes68 {
1156 types: vec![0x02],
1157 sizes: vec![0xb6],
1158 hashes: vec![
1159 B256::from_str(
1160 "0xfecbed04c7b88d8e7221a0a3f5dc33f220212347fc167459ea5cc9c3eb4c1124",
1161 )
1162 .unwrap(),
1163 ],
1164 },
1165 &hex!(
1166 "e602c281b6e1a0fecbed04c7b88d8e7221a0a3f5dc33f220212347fc167459ea5cc9c3eb4c1124"
1167 )[..],
1168 ),
1169 (
1170 NewPooledTransactionHashes68 {
1171 types: vec![0xff, 0xff],
1172 sizes: vec![0xffffffff, 0xffffffff],
1173 hashes: vec![
1174 B256::from_str(
1175 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1176 )
1177 .unwrap(),
1178 B256::from_str(
1179 "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
1180 )
1181 .unwrap(),
1182 ],
1183 },
1184 &hex!(
1185 "f85282ffffca84ffffffff84fffffffff842a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
1186 )[..],
1187 ),
1188 (
1189 NewPooledTransactionHashes68 {
1190 types: vec![0xff, 0xff],
1191 sizes: vec![0xffffffff, 0xffffffff],
1192 hashes: vec![
1193 B256::from_str(
1194 "0xbeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe",
1195 )
1196 .unwrap(),
1197 B256::from_str(
1198 "0xbeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe",
1199 )
1200 .unwrap(),
1201 ],
1202 },
1203 &hex!(
1204 "f85282ffffca84ffffffff84fffffffff842a0beefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafea0beefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe"
1205 )[..],
1206 ),
1207 (
1208 NewPooledTransactionHashes68 {
1209 types: vec![0x10, 0x10],
1210 sizes: vec![0xdeadc0de, 0xdeadc0de],
1211 hashes: vec![
1212 B256::from_str(
1213 "0x3b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2",
1214 )
1215 .unwrap(),
1216 B256::from_str(
1217 "0x3b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2",
1218 )
1219 .unwrap(),
1220 ],
1221 },
1222 &hex!(
1223 "f852821010ca84deadc0de84deadc0def842a03b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2a03b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2"
1224 )[..],
1225 ),
1226 (
1227 NewPooledTransactionHashes68 {
1228 types: vec![0x6f, 0x6f],
1229 sizes: vec![0x7fffffff, 0x7fffffff],
1230 hashes: vec![
1231 B256::from_str(
1232 "0x0000000000000000000000000000000000000000000000000000000000000002",
1233 )
1234 .unwrap(),
1235 B256::from_str(
1236 "0x0000000000000000000000000000000000000000000000000000000000000002",
1237 )
1238 .unwrap(),
1239 ],
1240 },
1241 &hex!(
1242 "f852826f6fca847fffffff847ffffffff842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000002"
1243 )[..],
1244 ),
1245 ];
1246
1247 for vector in vectors {
1248 test_encoding_vector(vector);
1249 }
1250 }
1251
1252 #[test]
1253 fn eth_72_tx_hash_roundtrip() {
1254 let vectors = vec![
1255 (
1256 NewPooledTransactionHashes72 {
1257 types: vec![],
1258 sizes: vec![],
1259 hashes: vec![],
1260 cell_mask: None,
1261 },
1262 &hex!("c480c0c080")[..],
1263 ),
1264 (
1265 NewPooledTransactionHashes72 {
1266 types: vec![],
1267 sizes: vec![],
1268 hashes: vec![],
1269 cell_mask: Some(B128::repeat_byte(0x11)),
1270 },
1271 &hex!("d480c0c09011111111111111111111111111111111")[..],
1272 ),
1273 ];
1274
1275 for vector in vectors {
1276 test_encoding_vector(vector);
1277 }
1278 }
1279
1280 #[test]
1281 fn eth_72_rejects_missing_cell_mask() {
1282 let encoded_eth68_payload = hex!("c380c0c0");
1283
1284 let result = NewPooledTransactionHashes72::decode(&mut encoded_eth68_payload.as_ref());
1285
1286 assert!(matches!(result, Err(alloy_rlp::Error::InputTooShort)));
1287 }
1288
1289 #[test]
1290 fn request_hashes_retain_count_keep_subset() {
1291 let mut hashes = RequestTxHashes::new(
1292 [
1293 b256!("0x0000000000000000000000000000000000000000000000000000000000000001"),
1294 b256!("0x0000000000000000000000000000000000000000000000000000000000000002"),
1295 b256!("0x0000000000000000000000000000000000000000000000000000000000000003"),
1296 b256!("0x0000000000000000000000000000000000000000000000000000000000000004"),
1297 b256!("0x0000000000000000000000000000000000000000000000000000000000000005"),
1298 ]
1299 .into_iter()
1300 .collect::<HashSet<_>>(),
1301 );
1302
1303 let rest = hashes.retain_count(3);
1304
1305 assert_eq!(3, hashes.len());
1306 assert_eq!(2, rest.len());
1307 }
1308
1309 #[test]
1310 fn request_hashes_retain_count_keep_all() {
1311 let mut hashes = RequestTxHashes::new(
1312 [
1313 b256!("0x0000000000000000000000000000000000000000000000000000000000000001"),
1314 b256!("0x0000000000000000000000000000000000000000000000000000000000000002"),
1315 b256!("0x0000000000000000000000000000000000000000000000000000000000000003"),
1316 b256!("0x0000000000000000000000000000000000000000000000000000000000000004"),
1317 b256!("0x0000000000000000000000000000000000000000000000000000000000000005"),
1318 ]
1319 .into_iter()
1320 .collect::<HashSet<_>>(),
1321 );
1322
1323 let _ = hashes.retain_count(6);
1324
1325 assert_eq!(5, hashes.len());
1326 }
1327
1328 #[test]
1329 fn split_request_hashes_keep_none() {
1330 let mut hashes = RequestTxHashes::new(
1331 [
1332 b256!("0x0000000000000000000000000000000000000000000000000000000000000001"),
1333 b256!("0x0000000000000000000000000000000000000000000000000000000000000002"),
1334 b256!("0x0000000000000000000000000000000000000000000000000000000000000003"),
1335 b256!("0x0000000000000000000000000000000000000000000000000000000000000004"),
1336 b256!("0x0000000000000000000000000000000000000000000000000000000000000005"),
1337 ]
1338 .into_iter()
1339 .collect::<HashSet<_>>(),
1340 );
1341
1342 let rest = hashes.retain_count(0);
1343
1344 assert_eq!(0, hashes.len());
1345 assert_eq!(5, rest.len());
1346 }
1347
1348 fn signed_transaction() -> impl SignedTransaction {
1349 TransactionSigned::new_unhashed(
1350 Transaction::Legacy(Default::default()),
1351 Signature::new(
1352 U256::from_str(
1353 "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12",
1354 )
1355 .unwrap(),
1356 U256::from_str(
1357 "0x64b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10",
1358 )
1359 .unwrap(),
1360 false,
1361 ),
1362 )
1363 }
1364
1365 #[test]
1366 fn test_pooled_tx_hashes_68_push() {
1367 let tx = signed_transaction();
1368 let mut tx_hashes =
1369 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] };
1370 tx_hashes.push(&tx);
1371 assert_eq!(tx_hashes.types.len(), 1);
1372 assert_eq!(tx_hashes.sizes.len(), 1);
1373 assert_eq!(tx_hashes.hashes.len(), 1);
1374 assert_eq!(tx_hashes.types[0], tx.ty());
1375 assert_eq!(tx_hashes.sizes[0], tx.encode_2718_len());
1376 assert_eq!(tx_hashes.hashes[0], *tx.tx_hash());
1377 }
1378
1379 #[test]
1380 fn test_pooled_tx_hashes_68_extend() {
1381 let tx = signed_transaction();
1382 let txs = vec![tx.clone(), tx.clone()];
1383 let mut tx_hashes =
1384 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] };
1385 tx_hashes.extend(&txs);
1386 assert_eq!(tx_hashes.types.len(), 2);
1387 assert_eq!(tx_hashes.sizes.len(), 2);
1388 assert_eq!(tx_hashes.hashes.len(), 2);
1389 assert_eq!(tx_hashes.types[0], tx.ty());
1390 assert_eq!(tx_hashes.sizes[0], tx.encode_2718_len());
1391 assert_eq!(tx_hashes.hashes[0], *tx.tx_hash());
1392 assert_eq!(tx_hashes.types[1], tx.ty());
1393 assert_eq!(tx_hashes.sizes[1], tx.encode_2718_len());
1394 assert_eq!(tx_hashes.hashes[1], *tx.tx_hash());
1395 }
1396
1397 #[test]
1398 fn test_pooled_tx_hashes_68_with_transaction() {
1399 let tx = signed_transaction();
1400 let tx_hashes =
1401 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] }
1402 .with_transaction(&tx);
1403 assert_eq!(tx_hashes.types.len(), 1);
1404 assert_eq!(tx_hashes.sizes.len(), 1);
1405 assert_eq!(tx_hashes.hashes.len(), 1);
1406 assert_eq!(tx_hashes.types[0], tx.ty());
1407 assert_eq!(tx_hashes.sizes[0], tx.encode_2718_len());
1408 assert_eq!(tx_hashes.hashes[0], *tx.tx_hash());
1409 }
1410
1411 #[test]
1412 fn test_pooled_tx_hashes_68_with_transactions() {
1413 let tx = signed_transaction();
1414 let txs = vec![tx.clone(), tx.clone()];
1415 let tx_hashes =
1416 NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] }
1417 .with_transactions(&txs);
1418 assert_eq!(tx_hashes.types.len(), 2);
1419 assert_eq!(tx_hashes.sizes.len(), 2);
1420 assert_eq!(tx_hashes.hashes.len(), 2);
1421 assert_eq!(tx_hashes.types[0], tx.ty());
1422 assert_eq!(tx_hashes.sizes[0], tx.encode_2718_len());
1423 assert_eq!(tx_hashes.hashes[0], *tx.tx_hash());
1424 assert_eq!(tx_hashes.types[1], tx.ty());
1425 assert_eq!(tx_hashes.sizes[1], tx.encode_2718_len());
1426 assert_eq!(tx_hashes.hashes[1], *tx.tx_hash());
1427 }
1428}