reth_eth_wire_types/
broadcast.rs

1//! Types for broadcasting new data.
2
3use crate::{EthMessage, EthVersion, NetworkPrimitives};
4use alloy_primitives::{Bytes, TxHash, B256, U128};
5use alloy_rlp::{
6    Decodable, Encodable, RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper,
7};
8use derive_more::{Constructor, Deref, DerefMut, From, IntoIterator};
9use reth_codecs_derive::{add_arbitrary_tests, generate_tests};
10use reth_primitives::TransactionSigned;
11use reth_primitives_traits::SignedTransaction;
12use std::{
13    collections::{HashMap, HashSet},
14    mem,
15    sync::Arc,
16};
17
18#[cfg(feature = "arbitrary")]
19use proptest::{collection::vec, prelude::*};
20#[cfg(feature = "arbitrary")]
21use proptest_arbitrary_interop::arb;
22
23/// This informs peers of new blocks that have appeared on the network.
24#[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Default)]
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 NewBlockHashes(
29    /// New block hashes and the block number for each blockhash.
30    /// Clients should request blocks using a [`GetBlockBodies`](crate::GetBlockBodies) message.
31    pub Vec<BlockHashNumber>,
32);
33
34// === impl NewBlockHashes ===
35
36impl NewBlockHashes {
37    /// Returns the latest block in the list of blocks.
38    pub fn latest(&self) -> Option<&BlockHashNumber> {
39        self.0.iter().fold(None, |latest, block| {
40            if let Some(latest) = latest {
41                return if latest.number > block.number { Some(latest) } else { Some(block) }
42            }
43            Some(block)
44        })
45    }
46}
47
48/// A block hash _and_ a block number.
49#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
51#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
52#[add_arbitrary_tests(rlp)]
53pub struct BlockHashNumber {
54    /// The block hash
55    pub hash: B256,
56    /// The block number
57    pub number: u64,
58}
59
60impl From<Vec<BlockHashNumber>> for NewBlockHashes {
61    fn from(v: Vec<BlockHashNumber>) -> Self {
62        Self(v)
63    }
64}
65
66impl From<NewBlockHashes> for Vec<BlockHashNumber> {
67    fn from(v: NewBlockHashes) -> Self {
68        v.0
69    }
70}
71
72/// A new block with the current total difficulty, which includes the difficulty of the returned
73/// block.
74#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
76#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
77pub struct NewBlock<B = reth_primitives::Block> {
78    /// A new block.
79    pub block: B,
80    /// The current total difficulty.
81    pub td: U128,
82}
83
84generate_tests!(#[rlp, 25] NewBlock<reth_primitives::Block>, EthNewBlockTests);
85
86/// This informs peers of transactions that have appeared on the network and are not yet included
87/// in a block.
88#[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Default)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
91#[add_arbitrary_tests(rlp, 10)]
92pub struct Transactions<T = TransactionSigned>(
93    /// New transactions for the peer to include in its mempool.
94    pub Vec<T>,
95);
96
97impl<T: SignedTransaction> Transactions<T> {
98    /// Returns `true` if the list of transactions contains any blob transactions.
99    pub fn has_eip4844(&self) -> bool {
100        self.0.iter().any(|tx| tx.is_eip4844())
101    }
102}
103
104impl<T> From<Vec<T>> for Transactions<T> {
105    fn from(txs: Vec<T>) -> Self {
106        Self(txs)
107    }
108}
109
110impl<T> From<Transactions<T>> for Vec<T> {
111    fn from(txs: Transactions<T>) -> Self {
112        txs.0
113    }
114}
115
116/// Same as [`Transactions`] but this is intended as egress message send from local to _many_ peers.
117///
118/// The list of transactions is constructed on per-peers basis, but the underlying transaction
119/// objects are shared.
120#[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper)]
121#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
122#[add_arbitrary_tests(rlp, 20)]
123pub struct SharedTransactions<T = TransactionSigned>(
124    /// New transactions for the peer to include in its mempool.
125    pub Vec<Arc<T>>,
126);
127
128/// A wrapper type for all different new pooled transaction types
129#[derive(Clone, Debug, PartialEq, Eq)]
130pub enum NewPooledTransactionHashes {
131    /// A list of transaction hashes valid for [66-68)
132    Eth66(NewPooledTransactionHashes66),
133    /// A list of transaction hashes valid from [68..]
134    ///
135    /// Note: it is assumed that the payload is valid (all vectors have the same length)
136    Eth68(NewPooledTransactionHashes68),
137}
138
139// === impl NewPooledTransactionHashes ===
140
141impl NewPooledTransactionHashes {
142    /// Returns the message [`EthVersion`].
143    pub const fn version(&self) -> EthVersion {
144        match self {
145            Self::Eth66(_) => EthVersion::Eth66,
146            Self::Eth68(_) => EthVersion::Eth68,
147        }
148    }
149
150    /// Returns `true` if the payload is valid for the given version
151    pub const fn is_valid_for_version(&self, version: EthVersion) -> bool {
152        match self {
153            Self::Eth66(_) => {
154                matches!(version, EthVersion::Eth67 | EthVersion::Eth66)
155            }
156            Self::Eth68(_) => {
157                matches!(version, EthVersion::Eth68)
158            }
159        }
160    }
161
162    /// Returns an iterator over all transaction hashes.
163    pub fn iter_hashes(&self) -> impl Iterator<Item = &B256> + '_ {
164        match self {
165            Self::Eth66(msg) => msg.0.iter(),
166            Self::Eth68(msg) => msg.hashes.iter(),
167        }
168    }
169
170    /// Returns an immutable reference to transaction hashes.
171    pub const fn hashes(&self) -> &Vec<B256> {
172        match self {
173            Self::Eth66(msg) => &msg.0,
174            Self::Eth68(msg) => &msg.hashes,
175        }
176    }
177
178    /// Returns a mutable reference to transaction hashes.
179    pub fn hashes_mut(&mut self) -> &mut Vec<B256> {
180        match self {
181            Self::Eth66(msg) => &mut msg.0,
182            Self::Eth68(msg) => &mut msg.hashes,
183        }
184    }
185
186    /// Consumes the type and returns all hashes
187    pub fn into_hashes(self) -> Vec<B256> {
188        match self {
189            Self::Eth66(msg) => msg.0,
190            Self::Eth68(msg) => msg.hashes,
191        }
192    }
193
194    /// Returns an iterator over all transaction hashes.
195    pub fn into_iter_hashes(self) -> impl Iterator<Item = B256> {
196        match self {
197            Self::Eth66(msg) => msg.0.into_iter(),
198            Self::Eth68(msg) => msg.hashes.into_iter(),
199        }
200    }
201
202    /// Shortens the number of hashes in the message, keeping the first `len` hashes and dropping
203    /// the rest. If `len` is greater than the number of hashes, this has no effect.
204    pub fn truncate(&mut self, len: usize) {
205        match self {
206            Self::Eth66(msg) => msg.0.truncate(len),
207            Self::Eth68(msg) => {
208                msg.types.truncate(len);
209                msg.sizes.truncate(len);
210                msg.hashes.truncate(len);
211            }
212        }
213    }
214
215    /// Returns true if the message is empty
216    pub fn is_empty(&self) -> bool {
217        match self {
218            Self::Eth66(msg) => msg.0.is_empty(),
219            Self::Eth68(msg) => msg.hashes.is_empty(),
220        }
221    }
222
223    /// Returns the number of hashes in the message
224    pub fn len(&self) -> usize {
225        match self {
226            Self::Eth66(msg) => msg.0.len(),
227            Self::Eth68(msg) => msg.hashes.len(),
228        }
229    }
230
231    /// Returns an immutable reference to the inner type if this an eth68 announcement.
232    pub const fn as_eth68(&self) -> Option<&NewPooledTransactionHashes68> {
233        match self {
234            Self::Eth66(_) => None,
235            Self::Eth68(msg) => Some(msg),
236        }
237    }
238
239    /// Returns a mutable reference to the inner type if this an eth68 announcement.
240    pub fn as_eth68_mut(&mut self) -> Option<&mut NewPooledTransactionHashes68> {
241        match self {
242            Self::Eth66(_) => None,
243            Self::Eth68(msg) => Some(msg),
244        }
245    }
246
247    /// Returns a mutable reference to the inner type if this an eth66 announcement.
248    pub fn as_eth66_mut(&mut self) -> Option<&mut NewPooledTransactionHashes66> {
249        match self {
250            Self::Eth66(msg) => Some(msg),
251            Self::Eth68(_) => None,
252        }
253    }
254
255    /// Returns the inner type if this an eth68 announcement.
256    pub fn take_eth68(&mut self) -> Option<NewPooledTransactionHashes68> {
257        match self {
258            Self::Eth66(_) => None,
259            Self::Eth68(msg) => Some(mem::take(msg)),
260        }
261    }
262
263    /// Returns the inner type if this an eth66 announcement.
264    pub fn take_eth66(&mut self) -> Option<NewPooledTransactionHashes66> {
265        match self {
266            Self::Eth66(msg) => Some(mem::take(msg)),
267            Self::Eth68(_) => None,
268        }
269    }
270}
271
272impl<N: NetworkPrimitives> From<NewPooledTransactionHashes> for EthMessage<N> {
273    fn from(value: NewPooledTransactionHashes) -> Self {
274        match value {
275            NewPooledTransactionHashes::Eth66(msg) => Self::NewPooledTransactionHashes66(msg),
276            NewPooledTransactionHashes::Eth68(msg) => Self::NewPooledTransactionHashes68(msg),
277        }
278    }
279}
280
281impl From<NewPooledTransactionHashes66> for NewPooledTransactionHashes {
282    fn from(hashes: NewPooledTransactionHashes66) -> Self {
283        Self::Eth66(hashes)
284    }
285}
286
287impl From<NewPooledTransactionHashes68> for NewPooledTransactionHashes {
288    fn from(hashes: NewPooledTransactionHashes68) -> Self {
289        Self::Eth68(hashes)
290    }
291}
292
293/// This informs peers of transaction hashes for transactions that have appeared on the network,
294/// but have not been included in a block.
295#[derive(Clone, Debug, PartialEq, Eq, RlpEncodableWrapper, RlpDecodableWrapper, Default)]
296#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
297#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
298#[add_arbitrary_tests(rlp)]
299pub struct NewPooledTransactionHashes66(
300    /// Transaction hashes for new transactions that have appeared on the network.
301    /// Clients should request the transactions with the given hashes using a
302    /// [`GetPooledTransactions`](crate::GetPooledTransactions) message.
303    pub Vec<B256>,
304);
305
306impl From<Vec<B256>> for NewPooledTransactionHashes66 {
307    fn from(v: Vec<B256>) -> Self {
308        Self(v)
309    }
310}
311
312/// Same as [`NewPooledTransactionHashes66`] but extends that beside the transaction hashes,
313/// the node sends the transaction types and their sizes (as defined in EIP-2718) as well.
314#[derive(Clone, Debug, PartialEq, Eq, Default)]
315#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
316pub struct NewPooledTransactionHashes68 {
317    /// Transaction types for new transactions that have appeared on the network.
318    ///
319    /// ## Note on RLP encoding and decoding
320    ///
321    /// In the [eth/68 spec](https://eips.ethereum.org/EIPS/eip-5793#specification) this is defined
322    /// the following way:
323    ///  * `[type_0: B_1, type_1: B_1, ...]`
324    ///
325    /// This would make it seem like the [`Encodable`] and
326    /// [`Decodable`] implementations should directly use a `Vec<u8>` for
327    /// encoding and decoding, because it looks like this field should be encoded as a _list_ of
328    /// bytes.
329    ///
330    /// However, [this is implemented in geth as a `[]byte`
331    /// type](https://github.com/ethereum/go-ethereum/blob/82d934b1dd80cdd8190803ea9f73ed2c345e2576/eth/protocols/eth/protocol.go#L308-L313),
332    /// which [ends up being encoded as a RLP
333    /// string](https://github.com/ethereum/go-ethereum/blob/82d934b1dd80cdd8190803ea9f73ed2c345e2576/rlp/encode_test.go#L171-L176),
334    /// **not** a RLP list.
335    ///
336    /// Because of this, we do not directly use the `Vec<u8>` when encoding and decoding, and
337    /// instead use the [`Encodable`] and [`Decodable`]
338    /// implementations for `&[u8]` instead, which encodes into a RLP string, and expects an RLP
339    /// string when decoding.
340    pub types: Vec<u8>,
341    /// Transaction sizes for new transactions that have appeared on the network.
342    pub sizes: Vec<usize>,
343    /// Transaction hashes for new transactions that have appeared on the network.
344    pub hashes: Vec<B256>,
345}
346
347#[cfg(feature = "arbitrary")]
348impl Arbitrary for NewPooledTransactionHashes68 {
349    type Parameters = ();
350    fn arbitrary_with(_args: ()) -> Self::Strategy {
351        // Generate a single random length for all vectors
352        let vec_length = any::<usize>().prop_map(|x| x % 100 + 1); // Lengths between 1 and 100
353
354        vec_length
355            .prop_flat_map(|len| {
356                // Use the generated length to create vectors of TxType, usize, and B256
357                let types_vec =
358                    vec(arb::<reth_primitives::TxType>().prop_map(|ty| ty as u8), len..=len);
359
360                // Map the usize values to the range 0..131072(0x20000)
361                let sizes_vec = vec(proptest::num::usize::ANY.prop_map(|x| x % 131072), len..=len);
362                let hashes_vec = vec(any::<B256>(), len..=len);
363
364                (types_vec, sizes_vec, hashes_vec)
365            })
366            .prop_map(|(types, sizes, hashes)| Self { types, sizes, hashes })
367            .boxed()
368    }
369
370    type Strategy = BoxedStrategy<Self>;
371}
372
373impl NewPooledTransactionHashes68 {
374    /// Returns an iterator over tx hashes zipped with corresponding metadata.
375    pub fn metadata_iter(&self) -> impl Iterator<Item = (&B256, (u8, usize))> {
376        self.hashes.iter().zip(self.types.iter().copied().zip(self.sizes.iter().copied()))
377    }
378}
379
380impl Encodable for NewPooledTransactionHashes68 {
381    fn encode(&self, out: &mut dyn bytes::BufMut) {
382        #[derive(RlpEncodable)]
383        struct EncodableNewPooledTransactionHashes68<'a> {
384            types: &'a [u8],
385            sizes: &'a Vec<usize>,
386            hashes: &'a Vec<B256>,
387        }
388
389        let encodable = EncodableNewPooledTransactionHashes68 {
390            types: &self.types[..],
391            sizes: &self.sizes,
392            hashes: &self.hashes,
393        };
394
395        encodable.encode(out);
396    }
397    fn length(&self) -> usize {
398        #[derive(RlpEncodable)]
399        struct EncodableNewPooledTransactionHashes68<'a> {
400            types: &'a [u8],
401            sizes: &'a Vec<usize>,
402            hashes: &'a Vec<B256>,
403        }
404
405        let encodable = EncodableNewPooledTransactionHashes68 {
406            types: &self.types[..],
407            sizes: &self.sizes,
408            hashes: &self.hashes,
409        };
410
411        encodable.length()
412    }
413}
414
415impl Decodable for NewPooledTransactionHashes68 {
416    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
417        #[derive(RlpDecodable)]
418        struct EncodableNewPooledTransactionHashes68 {
419            types: Bytes,
420            sizes: Vec<usize>,
421            hashes: Vec<B256>,
422        }
423
424        let encodable = EncodableNewPooledTransactionHashes68::decode(buf)?;
425        let msg = Self {
426            types: encodable.types.into(),
427            sizes: encodable.sizes,
428            hashes: encodable.hashes,
429        };
430
431        if msg.hashes.len() != msg.types.len() {
432            return Err(alloy_rlp::Error::ListLengthMismatch {
433                expected: msg.hashes.len(),
434                got: msg.types.len(),
435            })
436        }
437        if msg.hashes.len() != msg.sizes.len() {
438            return Err(alloy_rlp::Error::ListLengthMismatch {
439                expected: msg.hashes.len(),
440                got: msg.sizes.len(),
441            })
442        }
443
444        Ok(msg)
445    }
446}
447
448/// Validation pass that checks for unique transaction hashes.
449pub trait DedupPayload {
450    /// Value type in [`PartiallyValidData`] map.
451    type Value;
452
453    /// The payload contains no entries.
454    fn is_empty(&self) -> bool;
455
456    /// Returns the number of entries.
457    fn len(&self) -> usize;
458
459    /// Consumes self, returning an iterator over hashes in payload.
460    fn dedup(self) -> PartiallyValidData<Self::Value>;
461}
462
463/// Value in [`PartiallyValidData`] map obtained from an announcement.
464pub type Eth68TxMetadata = Option<(u8, usize)>;
465
466impl DedupPayload for NewPooledTransactionHashes {
467    type Value = Eth68TxMetadata;
468
469    fn is_empty(&self) -> bool {
470        self.is_empty()
471    }
472
473    fn len(&self) -> usize {
474        self.len()
475    }
476
477    fn dedup(self) -> PartiallyValidData<Self::Value> {
478        match self {
479            Self::Eth66(msg) => msg.dedup(),
480            Self::Eth68(msg) => msg.dedup(),
481        }
482    }
483}
484
485impl DedupPayload for NewPooledTransactionHashes68 {
486    type Value = Eth68TxMetadata;
487
488    fn is_empty(&self) -> bool {
489        self.hashes.is_empty()
490    }
491
492    fn len(&self) -> usize {
493        self.hashes.len()
494    }
495
496    fn dedup(self) -> PartiallyValidData<Self::Value> {
497        let Self { hashes, mut sizes, mut types } = self;
498
499        let mut deduped_data = HashMap::with_capacity(hashes.len());
500
501        for hash in hashes.into_iter().rev() {
502            if let (Some(ty), Some(size)) = (types.pop(), sizes.pop()) {
503                deduped_data.insert(hash, Some((ty, size)));
504            }
505        }
506
507        PartiallyValidData::from_raw_data_eth68(deduped_data)
508    }
509}
510
511impl DedupPayload for NewPooledTransactionHashes66 {
512    type Value = Eth68TxMetadata;
513
514    fn is_empty(&self) -> bool {
515        self.0.is_empty()
516    }
517
518    fn len(&self) -> usize {
519        self.0.len()
520    }
521
522    fn dedup(self) -> PartiallyValidData<Self::Value> {
523        let Self(hashes) = self;
524
525        let mut deduped_data = HashMap::with_capacity(hashes.len());
526
527        let noop_value: Eth68TxMetadata = None;
528
529        for hash in hashes.into_iter().rev() {
530            deduped_data.insert(hash, noop_value);
531        }
532
533        PartiallyValidData::from_raw_data_eth66(deduped_data)
534    }
535}
536
537/// Interface for handling mempool message data. Used in various filters in pipelines in
538/// `TransactionsManager` and in queries to `TransactionPool`.
539pub trait HandleMempoolData {
540    /// The announcement contains no entries.
541    fn is_empty(&self) -> bool;
542
543    /// Returns the number of entries.
544    fn len(&self) -> usize;
545
546    /// Retain only entries for which the hash in the entry satisfies a given predicate.
547    fn retain_by_hash(&mut self, f: impl FnMut(&TxHash) -> bool);
548}
549
550/// Extension of [`HandleMempoolData`] interface, for mempool messages that are versioned.
551pub trait HandleVersionedMempoolData {
552    /// Returns the announcement version, either [`Eth66`](EthVersion::Eth66) or
553    /// [`Eth68`](EthVersion::Eth68).
554    fn msg_version(&self) -> EthVersion;
555}
556
557impl<T: SignedTransaction> HandleMempoolData for Vec<T> {
558    fn is_empty(&self) -> bool {
559        self.is_empty()
560    }
561
562    fn len(&self) -> usize {
563        self.len()
564    }
565
566    fn retain_by_hash(&mut self, mut f: impl FnMut(&TxHash) -> bool) {
567        self.retain(|tx| f(tx.tx_hash()))
568    }
569}
570
571macro_rules! handle_mempool_data_map_impl {
572    ($data_ty:ty, $(<$generic:ident>)?) => {
573        impl$(<$generic>)? HandleMempoolData for $data_ty {
574            fn is_empty(&self) -> bool {
575                self.data.is_empty()
576            }
577
578            fn len(&self) -> usize {
579                self.data.len()
580            }
581
582            fn retain_by_hash(&mut self, mut f: impl FnMut(&TxHash) -> bool) {
583                self.data.retain(|hash, _| f(hash));
584            }
585        }
586    };
587}
588
589/// Data that has passed an initial validation pass that is not specific to any mempool message
590/// type.
591#[derive(Debug, Deref, DerefMut, IntoIterator)]
592pub struct PartiallyValidData<V> {
593    #[deref]
594    #[deref_mut]
595    #[into_iterator]
596    data: HashMap<TxHash, V>,
597    version: Option<EthVersion>,
598}
599
600handle_mempool_data_map_impl!(PartiallyValidData<V>, <V>);
601
602impl<V> PartiallyValidData<V> {
603    /// Wraps raw data.
604    pub const fn from_raw_data(data: HashMap<TxHash, V>, version: Option<EthVersion>) -> Self {
605        Self { data, version }
606    }
607
608    /// Wraps raw data with version [`EthVersion::Eth68`].
609    pub const fn from_raw_data_eth68(data: HashMap<TxHash, V>) -> Self {
610        Self::from_raw_data(data, Some(EthVersion::Eth68))
611    }
612
613    /// Wraps raw data with version [`EthVersion::Eth66`].
614    pub const fn from_raw_data_eth66(data: HashMap<TxHash, V>) -> Self {
615        Self::from_raw_data(data, Some(EthVersion::Eth66))
616    }
617
618    /// Returns a new [`PartiallyValidData`] with empty data from an [`Eth68`](EthVersion::Eth68)
619    /// announcement.
620    pub fn empty_eth68() -> Self {
621        Self::from_raw_data_eth68(HashMap::default())
622    }
623
624    /// Returns a new [`PartiallyValidData`] with empty data from an [`Eth66`](EthVersion::Eth66)
625    /// announcement.
626    pub fn empty_eth66() -> Self {
627        Self::from_raw_data_eth66(HashMap::default())
628    }
629
630    /// Returns the version of the message this data was received in if different versions of the
631    /// message exists, either [`Eth66`](EthVersion::Eth66) or [`Eth68`](EthVersion::Eth68).
632    pub const fn msg_version(&self) -> Option<EthVersion> {
633        self.version
634    }
635
636    /// Destructs returning the validated data.
637    pub fn into_data(self) -> HashMap<TxHash, V> {
638        self.data
639    }
640}
641
642/// Partially validated data from an announcement or a
643/// [`PooledTransactions`](crate::PooledTransactions) response.
644#[derive(Debug, Deref, DerefMut, IntoIterator, From)]
645pub struct ValidAnnouncementData {
646    #[deref]
647    #[deref_mut]
648    #[into_iterator]
649    data: HashMap<TxHash, Eth68TxMetadata>,
650    version: EthVersion,
651}
652
653handle_mempool_data_map_impl!(ValidAnnouncementData,);
654
655impl ValidAnnouncementData {
656    /// Destructs returning only the valid hashes and the announcement message version. Caution! If
657    /// this is [`Eth68`](EthVersion::Eth68) announcement data, this drops the metadata.
658    pub fn into_request_hashes(self) -> (RequestTxHashes, EthVersion) {
659        let hashes = self.data.into_keys().collect::<HashSet<_>>();
660
661        (RequestTxHashes::new(hashes), self.version)
662    }
663
664    /// Conversion from [`PartiallyValidData`] from an announcement. Note! [`PartiallyValidData`]
665    /// from an announcement, should have some [`EthVersion`]. Panics if [`PartiallyValidData`] has
666    /// version set to `None`.
667    pub fn from_partially_valid_data(data: PartiallyValidData<Eth68TxMetadata>) -> Self {
668        let PartiallyValidData { data, version } = data;
669
670        let version = version.expect("should have eth version for conversion");
671
672        Self { data, version }
673    }
674
675    /// Destructs returning the validated data.
676    pub fn into_data(self) -> HashMap<TxHash, Eth68TxMetadata> {
677        self.data
678    }
679}
680
681impl HandleVersionedMempoolData for ValidAnnouncementData {
682    fn msg_version(&self) -> EthVersion {
683        self.version
684    }
685}
686
687/// Hashes to request from a peer.
688#[derive(Debug, Default, Deref, DerefMut, IntoIterator, Constructor)]
689pub struct RequestTxHashes {
690    #[deref]
691    #[deref_mut]
692    #[into_iterator(owned, ref)]
693    hashes: HashSet<TxHash>,
694}
695
696impl RequestTxHashes {
697    /// Returns a new [`RequestTxHashes`] with given capacity for hashes. Caution! Make sure to
698    /// call [`HashSet::shrink_to_fit`] on [`RequestTxHashes`] when full, especially where it will
699    /// be stored in its entirety like in the future waiting for a
700    /// [`GetPooledTransactions`](crate::GetPooledTransactions) request to resolve.
701    pub fn with_capacity(capacity: usize) -> Self {
702        Self::new(HashSet::with_capacity(capacity))
703    }
704
705    /// Returns an new empty instance.
706    fn empty() -> Self {
707        Self::new(HashSet::default())
708    }
709
710    /// Retains the given number of elements, returning and iterator over the rest.
711    pub fn retain_count(&mut self, count: usize) -> Self {
712        let rest_capacity = self.hashes.len().saturating_sub(count);
713        if rest_capacity == 0 {
714            return Self::empty()
715        }
716        let mut rest = Self::with_capacity(rest_capacity);
717
718        let mut i = 0;
719        self.hashes.retain(|hash| {
720            if i >= count {
721                rest.insert(*hash);
722                return false
723            }
724            i += 1;
725
726            true
727        });
728
729        rest
730    }
731}
732
733impl FromIterator<(TxHash, Eth68TxMetadata)> for RequestTxHashes {
734    fn from_iter<I: IntoIterator<Item = (TxHash, Eth68TxMetadata)>>(iter: I) -> Self {
735        Self::new(iter.into_iter().map(|(hash, _)| hash).collect())
736    }
737}
738
739#[cfg(test)]
740mod tests {
741    use super::*;
742    use alloy_primitives::{b256, hex};
743    use std::str::FromStr;
744
745    /// Takes as input a struct / encoded hex message pair, ensuring that we encode to the exact hex
746    /// message, and decode to the exact struct.
747    fn test_encoding_vector<T: Encodable + Decodable + PartialEq + std::fmt::Debug>(
748        input: (T, &[u8]),
749    ) {
750        let (expected_decoded, expected_encoded) = input;
751        let mut encoded = Vec::new();
752        expected_decoded.encode(&mut encoded);
753
754        assert_eq!(hex::encode(&encoded), hex::encode(expected_encoded));
755
756        let decoded = T::decode(&mut encoded.as_ref()).unwrap();
757        assert_eq!(expected_decoded, decoded);
758    }
759
760    #[test]
761    fn can_return_latest_block() {
762        let mut blocks = NewBlockHashes(vec![BlockHashNumber { hash: B256::random(), number: 0 }]);
763        let latest = blocks.latest().unwrap();
764        assert_eq!(latest.number, 0);
765
766        blocks.0.push(BlockHashNumber { hash: B256::random(), number: 100 });
767        blocks.0.push(BlockHashNumber { hash: B256::random(), number: 2 });
768        let latest = blocks.latest().unwrap();
769        assert_eq!(latest.number, 100);
770    }
771
772    #[test]
773    fn eth_68_tx_hash_roundtrip() {
774        let vectors = vec![
775            (
776            NewPooledTransactionHashes68 { types: vec![], sizes: vec![], hashes: vec![] },
777            &hex!("c380c0c0")[..],
778            ),
779            (
780            NewPooledTransactionHashes68 {
781                types: vec![0x00],
782                sizes: vec![0x00],
783                hashes: vec![B256::from_str(
784                    "0x0000000000000000000000000000000000000000000000000000000000000000",
785                )
786                .unwrap()],
787            },
788            &hex!("e500c180e1a00000000000000000000000000000000000000000000000000000000000000000")[..],
789            ),
790            (
791            NewPooledTransactionHashes68 {
792                types: vec![0x00, 0x00],
793                sizes: vec![0x00, 0x00],
794                hashes: vec![
795                    B256::from_str(
796                        "0x0000000000000000000000000000000000000000000000000000000000000000",
797                    )
798                    .unwrap(),
799                    B256::from_str(
800                        "0x0000000000000000000000000000000000000000000000000000000000000000",
801                    )
802                    .unwrap(),
803                ],
804            },
805            &hex!("f84a820000c28080f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000")[..],
806            ),
807            (
808            NewPooledTransactionHashes68 {
809                types: vec![0x02],
810                sizes: vec![0xb6],
811                hashes: vec![B256::from_str(
812                    "0xfecbed04c7b88d8e7221a0a3f5dc33f220212347fc167459ea5cc9c3eb4c1124",
813                )
814                .unwrap()],
815            },
816            &hex!("e602c281b6e1a0fecbed04c7b88d8e7221a0a3f5dc33f220212347fc167459ea5cc9c3eb4c1124")[..],
817            ),
818            (
819            NewPooledTransactionHashes68 {
820                types: vec![0xff, 0xff],
821                sizes: vec![0xffffffff, 0xffffffff],
822                hashes: vec![
823                    B256::from_str(
824                        "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
825                    )
826                    .unwrap(),
827                    B256::from_str(
828                        "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
829                    )
830                    .unwrap(),
831                ],
832            },
833            &hex!("f85282ffffca84ffffffff84fffffffff842a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")[..],
834            ),
835            (
836            NewPooledTransactionHashes68 {
837                types: vec![0xff, 0xff],
838                sizes: vec![0xffffffff, 0xffffffff],
839                hashes: vec![
840                    B256::from_str(
841                        "0xbeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe",
842                    )
843                    .unwrap(),
844                    B256::from_str(
845                        "0xbeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe",
846                    )
847                    .unwrap(),
848                ],
849            },
850            &hex!("f85282ffffca84ffffffff84fffffffff842a0beefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafea0beefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafebeefcafe")[..],
851            ),
852            (
853            NewPooledTransactionHashes68 {
854                types: vec![0x10, 0x10],
855                sizes: vec![0xdeadc0de, 0xdeadc0de],
856                hashes: vec![
857                    B256::from_str(
858                        "0x3b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2",
859                    )
860                    .unwrap(),
861                    B256::from_str(
862                        "0x3b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2",
863                    )
864                    .unwrap(),
865                ],
866            },
867            &hex!("f852821010ca84deadc0de84deadc0def842a03b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2a03b9aca00f0671c9a2a1b817a0a78d3fe0c0f776cccb2a8c3c1b412a4f4e4d4e2")[..],
868            ),
869            (
870            NewPooledTransactionHashes68 {
871                types: vec![0x6f, 0x6f],
872                sizes: vec![0x7fffffff, 0x7fffffff],
873                hashes: vec![
874                    B256::from_str(
875                        "0x0000000000000000000000000000000000000000000000000000000000000002",
876                    )
877                    .unwrap(),
878                    B256::from_str(
879                        "0x0000000000000000000000000000000000000000000000000000000000000002",
880                    )
881                    .unwrap(),
882                ],
883            },
884            &hex!("f852826f6fca847fffffff847ffffffff842a00000000000000000000000000000000000000000000000000000000000000002a00000000000000000000000000000000000000000000000000000000000000002")[..],
885            ),
886        ];
887
888        for vector in vectors {
889            test_encoding_vector(vector);
890        }
891    }
892
893    #[test]
894    fn request_hashes_retain_count_keep_subset() {
895        let mut hashes = RequestTxHashes::new(
896            [
897                b256!("0000000000000000000000000000000000000000000000000000000000000001"),
898                b256!("0000000000000000000000000000000000000000000000000000000000000002"),
899                b256!("0000000000000000000000000000000000000000000000000000000000000003"),
900                b256!("0000000000000000000000000000000000000000000000000000000000000004"),
901                b256!("0000000000000000000000000000000000000000000000000000000000000005"),
902            ]
903            .into_iter()
904            .collect::<HashSet<_>>(),
905        );
906
907        let rest = hashes.retain_count(3);
908
909        assert_eq!(3, hashes.len());
910        assert_eq!(2, rest.len());
911    }
912
913    #[test]
914    fn request_hashes_retain_count_keep_all() {
915        let mut hashes = RequestTxHashes::new(
916            [
917                b256!("0000000000000000000000000000000000000000000000000000000000000001"),
918                b256!("0000000000000000000000000000000000000000000000000000000000000002"),
919                b256!("0000000000000000000000000000000000000000000000000000000000000003"),
920                b256!("0000000000000000000000000000000000000000000000000000000000000004"),
921                b256!("0000000000000000000000000000000000000000000000000000000000000005"),
922            ]
923            .into_iter()
924            .collect::<HashSet<_>>(),
925        );
926
927        let _ = hashes.retain_count(6);
928
929        assert_eq!(5, hashes.len());
930    }
931
932    #[test]
933    fn split_request_hashes_keep_none() {
934        let mut hashes = RequestTxHashes::new(
935            [
936                b256!("0000000000000000000000000000000000000000000000000000000000000001"),
937                b256!("0000000000000000000000000000000000000000000000000000000000000002"),
938                b256!("0000000000000000000000000000000000000000000000000000000000000003"),
939                b256!("0000000000000000000000000000000000000000000000000000000000000004"),
940                b256!("0000000000000000000000000000000000000000000000000000000000000005"),
941            ]
942            .into_iter()
943            .collect::<HashSet<_>>(),
944        );
945
946        let rest = hashes.retain_count(0);
947
948        assert_eq!(0, hashes.len());
949        assert_eq!(5, rest.len());
950    }
951}