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
37impl Encodable for RawCapabilityMessage {
38    /// Encodes the `RawCapabilityMessage` into an RLP byte stream.
39    fn encode(&self, out: &mut dyn BufMut) {
40        self.id.encode(out);
41        out.put_slice(&self.payload);
42    }
43
44    /// Returns the total length of the encoded message.
45    fn length(&self) -> usize {
46        self.id.length() + self.payload.len()
47    }
48}
49
50impl Decodable for RawCapabilityMessage {
51    /// Decodes a `RawCapabilityMessage` from an RLP byte stream.
52    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
53        let id = usize::decode(buf)?;
54        let payload = Bytes::copy_from_slice(buf);
55        *buf = &buf[buf.len()..];
56
57        Ok(Self { id, payload })
58    }
59}
60
61/// A message indicating a supported capability and capability version.
62#[add_arbitrary_tests(rlp)]
63#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default, Hash)]
64#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
65pub struct Capability {
66    /// The name of the subprotocol
67    pub name: Cow<'static, str>,
68    /// The version of the subprotocol
69    pub version: usize,
70}
71
72impl Capability {
73    /// Create a new `Capability` with the given name and version.
74    pub const fn new(name: String, version: usize) -> Self {
75        Self { name: Cow::Owned(name), version }
76    }
77
78    /// Create a new `Capability` with the given static name and version.
79    pub const fn new_static(name: &'static str, version: usize) -> Self {
80        Self { name: Cow::Borrowed(name), version }
81    }
82
83    /// Returns the corresponding eth capability for the given version.
84    pub const fn eth(version: EthVersion) -> Self {
85        Self::new_static("eth", version as usize)
86    }
87
88    /// Returns the corresponding snap capability for the given version.
89    pub const fn snap(version: SnapVersion) -> Self {
90        Self::new_static("snap", version as usize)
91    }
92
93    /// Returns the [`EthVersion::Eth66`] capability.
94    pub const fn eth_66() -> Self {
95        Self::eth(EthVersion::Eth66)
96    }
97
98    /// Returns the [`EthVersion::Eth67`] capability.
99    pub const fn eth_67() -> Self {
100        Self::eth(EthVersion::Eth67)
101    }
102
103    /// Returns the [`EthVersion::Eth68`] capability.
104    pub const fn eth_68() -> Self {
105        Self::eth(EthVersion::Eth68)
106    }
107
108    /// Returns the [`EthVersion::Eth69`] capability.
109    pub const fn eth_69() -> Self {
110        Self::eth(EthVersion::Eth69)
111    }
112
113    /// Returns the [`EthVersion::Eth70`] capability.
114    pub const fn eth_70() -> Self {
115        Self::eth(EthVersion::Eth70)
116    }
117
118    /// Returns the [`EthVersion::Eth71`] capability.
119    pub const fn eth_71() -> Self {
120        Self::eth(EthVersion::Eth71)
121    }
122
123    /// Returns the [`EthVersion::Eth72`] capability.
124    pub const fn eth_72() -> Self {
125        Self::eth(EthVersion::Eth72)
126    }
127
128    /// Returns the `snap/1` capability.
129    pub const fn snap_1() -> Self {
130        Self::snap(SnapVersion::V1)
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 since it's the only supported protocol
211        Ok(Self::new_static("eth", version))
212    }
213}
214
215/// Represents all capabilities of a node.
216#[derive(Debug, Clone, Eq, PartialEq)]
217pub struct Capabilities {
218    /// All Capabilities and their versions
219    inner: Vec<Capability>,
220    eth_66: bool,
221    eth_67: bool,
222    eth_68: bool,
223    eth_69: bool,
224    eth_70: bool,
225    eth_71: bool,
226    eth_72: bool,
227}
228
229impl Capabilities {
230    /// Create a new instance from the given vec.
231    pub fn new(value: Vec<Capability>) -> Self {
232        Self {
233            eth_66: value.iter().any(Capability::is_eth_v66),
234            eth_67: value.iter().any(Capability::is_eth_v67),
235            eth_68: value.iter().any(Capability::is_eth_v68),
236            eth_69: value.iter().any(Capability::is_eth_v69),
237            eth_70: value.iter().any(Capability::is_eth_v70),
238            eth_71: value.iter().any(Capability::is_eth_v71),
239            eth_72: value.iter().any(Capability::is_eth_v72),
240            inner: value,
241        }
242    }
243
244    /// Returns true if this peer advertises an eth protocol version that is `>= version`.
245    ///
246    /// This is **not** an exact-match check: a peer advertising only `eth/71` will return
247    /// `true` for any of `Eth66..=Eth71`, because eth versions are additive — a newer version
248    /// implies support for the messages of all earlier versions.
249    ///
250    /// Use this to gate requests on a minimum protocol version (e.g. BAL requires `eth/71`),
251    /// not to check whether a peer advertises a specific version verbatim. For exact-version
252    /// checks use the `supports_eth_vXX` helpers (e.g. [`Self::supports_eth_v71`]).
253    pub const fn supports_eth_at_least(&self, version: &EthVersion) -> bool {
254        match version {
255            EthVersion::Eth66 => {
256                self.eth_66 ||
257                    self.eth_67 ||
258                    self.eth_68 ||
259                    self.eth_69 ||
260                    self.eth_70 ||
261                    self.eth_71 ||
262                    self.eth_72
263            }
264            EthVersion::Eth67 => {
265                self.eth_67 ||
266                    self.eth_68 ||
267                    self.eth_69 ||
268                    self.eth_70 ||
269                    self.eth_71 ||
270                    self.eth_72
271            }
272            EthVersion::Eth68 => {
273                self.eth_68 || self.eth_69 || self.eth_70 || self.eth_71 || self.eth_72
274            }
275            EthVersion::Eth69 => self.eth_69 || self.eth_70 || self.eth_71 || self.eth_72,
276            EthVersion::Eth70 => self.eth_70 || self.eth_71 || self.eth_72,
277            EthVersion::Eth71 => self.eth_71 || self.eth_72,
278            EthVersion::Eth72 => self.eth_72,
279        }
280    }
281
282    /// Returns all capabilities.
283    #[inline]
284    pub fn capabilities(&self) -> &[Capability] {
285        &self.inner
286    }
287
288    /// Consumes the type and returns the all capabilities.
289    #[inline]
290    pub fn into_inner(self) -> Vec<Capability> {
291        self.inner
292    }
293
294    /// Whether the peer supports `eth` sub-protocol.
295    #[inline]
296    pub const fn supports_eth(&self) -> bool {
297        self.eth_71 || self.eth_70 || self.eth_69 || self.eth_68 || self.eth_67 || self.eth_66
298    }
299
300    /// Whether this peer supports eth v66 protocol.
301    #[inline]
302    pub const fn supports_eth_v66(&self) -> bool {
303        self.eth_66
304    }
305
306    /// Whether this peer supports eth v67 protocol.
307    #[inline]
308    pub const fn supports_eth_v67(&self) -> bool {
309        self.eth_67
310    }
311
312    /// Whether this peer supports eth v68 protocol.
313    #[inline]
314    pub const fn supports_eth_v68(&self) -> bool {
315        self.eth_68
316    }
317
318    /// Whether this peer supports eth v69 protocol.
319    #[inline]
320    pub const fn supports_eth_v69(&self) -> bool {
321        self.eth_69
322    }
323
324    /// Whether this peer supports eth v70 protocol.
325    #[inline]
326    pub const fn supports_eth_v70(&self) -> bool {
327        self.eth_70
328    }
329
330    /// Whether this peer supports eth v71 protocol.
331    #[inline]
332    pub const fn supports_eth_v71(&self) -> bool {
333        self.eth_71
334    }
335}
336
337impl From<Vec<Capability>> for Capabilities {
338    fn from(value: Vec<Capability>) -> Self {
339        Self::new(value)
340    }
341}
342
343impl Encodable for Capabilities {
344    fn encode(&self, out: &mut dyn BufMut) {
345        self.inner.encode(out)
346    }
347}
348
349impl Decodable for Capabilities {
350    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
351        let inner = Vec::<Capability>::decode(buf)?;
352
353        Ok(Self {
354            eth_66: inner.iter().any(Capability::is_eth_v66),
355            eth_67: inner.iter().any(Capability::is_eth_v67),
356            eth_68: inner.iter().any(Capability::is_eth_v68),
357            eth_69: inner.iter().any(Capability::is_eth_v69),
358            eth_70: inner.iter().any(Capability::is_eth_v70),
359            eth_71: inner.iter().any(Capability::is_eth_v71),
360            eth_72: inner.iter().any(Capability::is_eth_v72),
361            inner,
362        })
363    }
364}