Skip to main content

reth_eth_wire_types/
capability.rs

1//! All capability related types
2
3use crate::{EthMessageID, EthVersion, SnapVersion};
4use alloc::{borrow::Cow, string::String, vec::Vec};
5use alloy_primitives::bytes::Bytes;
6use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable};
7use bytes::BufMut;
8use core::fmt;
9use reth_codecs_derive::add_arbitrary_tests;
10
11/// A Capability message consisting of the message-id and the payload.
12#[derive(Debug, Clone, Eq, PartialEq)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct RawCapabilityMessage {
15    /// Identifier of the message.
16    pub id: usize,
17    /// Actual __encoded__ payload
18    pub payload: Bytes,
19}
20
21impl RawCapabilityMessage {
22    /// Creates a new capability message with the given id and payload.
23    pub const fn new(id: usize, payload: Bytes) -> Self {
24        Self { id, payload }
25    }
26
27    /// Creates a raw message for the eth sub-protocol.
28    ///
29    /// Caller must ensure that the rlp encoded `payload` matches the given `id`.
30    ///
31    /// See also  [`EthMessage`](crate::EthMessage)
32    pub const fn eth(id: EthMessageID, payload: Bytes) -> Self {
33        Self::new(id.to_u8() as usize, payload)
34    }
35
36    /// Encodes this message (`id` followed by its payload) to bytes.
37    pub fn encoded(&self) -> Bytes {
38        alloy_rlp::encode(self).into()
39    }
40}
41
42impl Encodable for RawCapabilityMessage {
43    /// Encodes the `RawCapabilityMessage` into an RLP byte stream.
44    fn encode(&self, out: &mut dyn BufMut) {
45        self.id.encode(out);
46        out.put_slice(&self.payload);
47    }
48
49    /// Returns the total length of the encoded message.
50    fn length(&self) -> usize {
51        self.id.length() + self.payload.len()
52    }
53}
54
55impl Decodable for RawCapabilityMessage {
56    /// Decodes a `RawCapabilityMessage` from an RLP byte stream.
57    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
58        let id = usize::decode(buf)?;
59        let payload = Bytes::copy_from_slice(buf);
60        *buf = &buf[buf.len()..];
61
62        Ok(Self { id, payload })
63    }
64}
65
66/// A message indicating a supported capability and capability version.
67#[add_arbitrary_tests(rlp)]
68#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default, Hash)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
70pub struct Capability {
71    /// The name of the subprotocol
72    pub name: Cow<'static, str>,
73    /// The version of the subprotocol
74    pub version: usize,
75}
76
77impl Capability {
78    /// Create a new `Capability` with the given name and version.
79    pub const fn new(name: String, version: usize) -> Self {
80        Self { name: Cow::Owned(name), version }
81    }
82
83    /// Create a new `Capability` with the given static name and version.
84    pub const fn new_static(name: &'static str, version: usize) -> Self {
85        Self { name: Cow::Borrowed(name), version }
86    }
87
88    /// Returns the corresponding eth capability for the given version.
89    pub const fn eth(version: EthVersion) -> Self {
90        Self::new_static("eth", version as usize)
91    }
92
93    /// Returns the corresponding snap capability for the given version.
94    pub const fn snap(version: SnapVersion) -> Self {
95        Self::new_static("snap", version as usize)
96    }
97
98    /// Returns the [`EthVersion::Eth66`] capability.
99    pub const fn eth_66() -> Self {
100        Self::eth(EthVersion::Eth66)
101    }
102
103    /// Returns the [`EthVersion::Eth67`] capability.
104    pub const fn eth_67() -> Self {
105        Self::eth(EthVersion::Eth67)
106    }
107
108    /// Returns the [`EthVersion::Eth68`] capability.
109    pub const fn eth_68() -> Self {
110        Self::eth(EthVersion::Eth68)
111    }
112
113    /// Returns the [`EthVersion::Eth69`] capability.
114    pub const fn eth_69() -> Self {
115        Self::eth(EthVersion::Eth69)
116    }
117
118    /// Returns the [`EthVersion::Eth70`] capability.
119    pub const fn eth_70() -> Self {
120        Self::eth(EthVersion::Eth70)
121    }
122
123    /// Returns the [`EthVersion::Eth71`] capability.
124    pub const fn eth_71() -> Self {
125        Self::eth(EthVersion::Eth71)
126    }
127
128    /// Returns the [`EthVersion::Eth72`] capability.
129    pub const fn eth_72() -> Self {
130        Self::eth(EthVersion::Eth72)
131    }
132
133    /// Returns the `snap/2` capability.
134    pub const fn snap_2() -> Self {
135        Self::snap(SnapVersion::V2)
136    }
137
138    /// Whether this is eth v66 protocol.
139    #[inline]
140    pub fn is_eth_v66(&self) -> bool {
141        self.name == "eth" && self.version == 66
142    }
143
144    /// Whether this is eth v67.
145    #[inline]
146    pub fn is_eth_v67(&self) -> bool {
147        self.name == "eth" && self.version == 67
148    }
149
150    /// Whether this is eth v68.
151    #[inline]
152    pub fn is_eth_v68(&self) -> bool {
153        self.name == "eth" && self.version == 68
154    }
155
156    /// Whether this is eth v69.
157    #[inline]
158    pub fn is_eth_v69(&self) -> bool {
159        self.name == "eth" && self.version == 69
160    }
161
162    /// Whether this is eth v70.
163    #[inline]
164    pub fn is_eth_v70(&self) -> bool {
165        self.name == "eth" && self.version == 70
166    }
167
168    /// Whether this is eth v71.
169    #[inline]
170    pub fn is_eth_v71(&self) -> bool {
171        self.name == "eth" && self.version == 71
172    }
173
174    /// Whether this is eth v72.
175    #[inline]
176    pub fn is_eth_v72(&self) -> bool {
177        self.name == "eth" && self.version == 72
178    }
179
180    /// Whether this is any eth version.
181    #[inline]
182    pub fn is_eth(&self) -> bool {
183        self.is_eth_v66() ||
184            self.is_eth_v67() ||
185            self.is_eth_v68() ||
186            self.is_eth_v69() ||
187            self.is_eth_v70() ||
188            self.is_eth_v71() ||
189            self.is_eth_v72()
190    }
191}
192
193impl fmt::Display for Capability {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        write!(f, "{}/{}", self.name, self.version)
196    }
197}
198
199impl From<EthVersion> for Capability {
200    #[inline]
201    fn from(value: EthVersion) -> Self {
202        Self::eth(value)
203    }
204}
205
206#[cfg(any(test, feature = "arbitrary"))]
207impl<'a> arbitrary::Arbitrary<'a> for Capability {
208    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
209        let version = u.int_in_range(66..=71)?; // Valid eth protocol versions are 66-71
210                                                // Only generate valid eth protocol name for now
211                                                // since it's the only supported protocol
212        Ok(Self::new_static("eth", version))
213    }
214}
215
216/// Represents all capabilities of a node.
217#[derive(Debug, Clone, Eq, PartialEq)]
218pub struct Capabilities {
219    /// All Capabilities and their versions
220    inner: Vec<Capability>,
221    eth_66: bool,
222    eth_67: bool,
223    eth_68: bool,
224    eth_69: bool,
225    eth_70: bool,
226    eth_71: bool,
227    eth_72: bool,
228}
229
230impl Capabilities {
231    /// Create a new instance from the given vec.
232    pub fn new(value: Vec<Capability>) -> Self {
233        Self {
234            eth_66: value.iter().any(Capability::is_eth_v66),
235            eth_67: value.iter().any(Capability::is_eth_v67),
236            eth_68: value.iter().any(Capability::is_eth_v68),
237            eth_69: value.iter().any(Capability::is_eth_v69),
238            eth_70: value.iter().any(Capability::is_eth_v70),
239            eth_71: value.iter().any(Capability::is_eth_v71),
240            eth_72: value.iter().any(Capability::is_eth_v72),
241            inner: value,
242        }
243    }
244
245    /// Returns true if this peer advertises an eth protocol version that is `>= version`.
246    ///
247    /// This is **not** an exact-match check: a peer advertising only `eth/71` will return
248    /// `true` for any of `Eth66..=Eth71`, because eth versions are additive — a newer version
249    /// implies support for the messages of all earlier versions.
250    ///
251    /// Use this to gate requests on a minimum protocol version (e.g. BAL requires `eth/71`),
252    /// not to check whether a peer advertises a specific version verbatim. For exact-version
253    /// checks use the `supports_eth_vXX` helpers (e.g. [`Self::supports_eth_v71`]).
254    pub const fn supports_eth_at_least(&self, version: &EthVersion) -> bool {
255        match version {
256            EthVersion::Eth66 => {
257                self.eth_66 ||
258                    self.eth_67 ||
259                    self.eth_68 ||
260                    self.eth_69 ||
261                    self.eth_70 ||
262                    self.eth_71 ||
263                    self.eth_72
264            }
265            EthVersion::Eth67 => {
266                self.eth_67 ||
267                    self.eth_68 ||
268                    self.eth_69 ||
269                    self.eth_70 ||
270                    self.eth_71 ||
271                    self.eth_72
272            }
273            EthVersion::Eth68 => {
274                self.eth_68 || self.eth_69 || self.eth_70 || self.eth_71 || self.eth_72
275            }
276            EthVersion::Eth69 => self.eth_69 || self.eth_70 || self.eth_71 || self.eth_72,
277            EthVersion::Eth70 => self.eth_70 || self.eth_71 || self.eth_72,
278            EthVersion::Eth71 => self.eth_71 || self.eth_72,
279            EthVersion::Eth72 => self.eth_72,
280        }
281    }
282
283    /// Returns all capabilities.
284    #[inline]
285    pub fn capabilities(&self) -> &[Capability] {
286        &self.inner
287    }
288
289    /// Consumes the type and returns the all capabilities.
290    #[inline]
291    pub fn into_inner(self) -> Vec<Capability> {
292        self.inner
293    }
294
295    /// Whether the peer supports `eth` sub-protocol.
296    #[inline]
297    pub const fn supports_eth(&self) -> bool {
298        self.eth_72 ||
299            self.eth_71 ||
300            self.eth_70 ||
301            self.eth_69 ||
302            self.eth_68 ||
303            self.eth_67 ||
304            self.eth_66
305    }
306
307    /// Whether this peer supports eth v66 protocol.
308    #[inline]
309    pub const fn supports_eth_v66(&self) -> bool {
310        self.eth_66
311    }
312
313    /// Whether this peer supports eth v67 protocol.
314    #[inline]
315    pub const fn supports_eth_v67(&self) -> bool {
316        self.eth_67
317    }
318
319    /// Whether this peer supports eth v68 protocol.
320    #[inline]
321    pub const fn supports_eth_v68(&self) -> bool {
322        self.eth_68
323    }
324
325    /// Whether this peer supports eth v69 protocol.
326    #[inline]
327    pub const fn supports_eth_v69(&self) -> bool {
328        self.eth_69
329    }
330
331    /// Whether this peer supports eth v70 protocol.
332    #[inline]
333    pub const fn supports_eth_v70(&self) -> bool {
334        self.eth_70
335    }
336
337    /// Whether this peer supports eth v71 protocol.
338    #[inline]
339    pub const fn supports_eth_v71(&self) -> bool {
340        self.eth_71
341    }
342}
343
344impl From<Vec<Capability>> for Capabilities {
345    fn from(value: Vec<Capability>) -> Self {
346        Self::new(value)
347    }
348}
349
350impl Encodable for Capabilities {
351    fn encode(&self, out: &mut dyn BufMut) {
352        self.inner.encode(out)
353    }
354}
355
356impl Decodable for Capabilities {
357    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
358        let inner = Vec::<Capability>::decode(buf)?;
359
360        Ok(Self {
361            eth_66: inner.iter().any(Capability::is_eth_v66),
362            eth_67: inner.iter().any(Capability::is_eth_v67),
363            eth_68: inner.iter().any(Capability::is_eth_v68),
364            eth_69: inner.iter().any(Capability::is_eth_v69),
365            eth_70: inner.iter().any(Capability::is_eth_v70),
366            eth_71: inner.iter().any(Capability::is_eth_v71),
367            eth_72: inner.iter().any(Capability::is_eth_v72),
368            inner,
369        })
370    }
371}