Skip to main content

reth_eth_wire_types/
capability.rs

1//! All capability related types
2
3use crate::{EthMessageID, EthVersion};
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 [`EthVersion::Eth66`] capability.
89    pub const fn eth_66() -> Self {
90        Self::eth(EthVersion::Eth66)
91    }
92
93    /// Returns the [`EthVersion::Eth67`] capability.
94    pub const fn eth_67() -> Self {
95        Self::eth(EthVersion::Eth67)
96    }
97
98    /// Returns the [`EthVersion::Eth68`] capability.
99    pub const fn eth_68() -> Self {
100        Self::eth(EthVersion::Eth68)
101    }
102
103    /// Returns the [`EthVersion::Eth69`] capability.
104    pub const fn eth_69() -> Self {
105        Self::eth(EthVersion::Eth69)
106    }
107
108    /// Returns the [`EthVersion::Eth70`] capability.
109    pub const fn eth_70() -> Self {
110        Self::eth(EthVersion::Eth70)
111    }
112
113    /// Returns the [`EthVersion::Eth71`] capability.
114    pub const fn eth_71() -> Self {
115        Self::eth(EthVersion::Eth71)
116    }
117
118    /// Whether this is eth v66 protocol.
119    #[inline]
120    pub fn is_eth_v66(&self) -> bool {
121        self.name == "eth" && self.version == 66
122    }
123
124    /// Whether this is eth v67.
125    #[inline]
126    pub fn is_eth_v67(&self) -> bool {
127        self.name == "eth" && self.version == 67
128    }
129
130    /// Whether this is eth v68.
131    #[inline]
132    pub fn is_eth_v68(&self) -> bool {
133        self.name == "eth" && self.version == 68
134    }
135
136    /// Whether this is eth v69.
137    #[inline]
138    pub fn is_eth_v69(&self) -> bool {
139        self.name == "eth" && self.version == 69
140    }
141
142    /// Whether this is eth v70.
143    #[inline]
144    pub fn is_eth_v70(&self) -> bool {
145        self.name == "eth" && self.version == 70
146    }
147
148    /// Whether this is eth v71.
149    #[inline]
150    pub fn is_eth_v71(&self) -> bool {
151        self.name == "eth" && self.version == 71
152    }
153
154    /// Whether this is any eth version.
155    #[inline]
156    pub fn is_eth(&self) -> bool {
157        self.is_eth_v66() ||
158            self.is_eth_v67() ||
159            self.is_eth_v68() ||
160            self.is_eth_v69() ||
161            self.is_eth_v70() ||
162            self.is_eth_v71()
163    }
164}
165
166impl fmt::Display for Capability {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        write!(f, "{}/{}", self.name, self.version)
169    }
170}
171
172impl From<EthVersion> for Capability {
173    #[inline]
174    fn from(value: EthVersion) -> Self {
175        Self::eth(value)
176    }
177}
178
179#[cfg(any(test, feature = "arbitrary"))]
180impl<'a> arbitrary::Arbitrary<'a> for Capability {
181    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
182        let version = u.int_in_range(66..=71)?; // Valid eth protocol versions are 66-71
183                                                // Only generate valid eth protocol name for now since it's the only supported protocol
184        Ok(Self::new_static("eth", version))
185    }
186}
187
188/// Represents all capabilities of a node.
189#[derive(Debug, Clone, Eq, PartialEq)]
190pub struct Capabilities {
191    /// All Capabilities and their versions
192    inner: Vec<Capability>,
193    eth_66: bool,
194    eth_67: bool,
195    eth_68: bool,
196    eth_69: bool,
197    eth_70: bool,
198    eth_71: bool,
199}
200
201impl Capabilities {
202    /// Create a new instance from the given vec.
203    pub fn new(value: Vec<Capability>) -> Self {
204        Self {
205            eth_66: value.iter().any(Capability::is_eth_v66),
206            eth_67: value.iter().any(Capability::is_eth_v67),
207            eth_68: value.iter().any(Capability::is_eth_v68),
208            eth_69: value.iter().any(Capability::is_eth_v69),
209            eth_70: value.iter().any(Capability::is_eth_v70),
210            eth_71: value.iter().any(Capability::is_eth_v71),
211            inner: value,
212        }
213    }
214
215    /// Returns true if this peer advertises an eth protocol version that is `>= version`.
216    ///
217    /// This is **not** an exact-match check: a peer advertising only `eth/71` will return
218    /// `true` for any of `Eth66..=Eth71`, because eth versions are additive — a newer version
219    /// implies support for the messages of all earlier versions.
220    ///
221    /// Use this to gate requests on a minimum protocol version (e.g. BAL requires `eth/71`),
222    /// not to check whether a peer advertises a specific version verbatim. For exact-version
223    /// checks use the `supports_eth_vXX` helpers (e.g. [`Self::supports_eth_v71`]).
224    pub const fn supports_eth_at_least(&self, version: &EthVersion) -> bool {
225        match version {
226            EthVersion::Eth66 => {
227                self.eth_66 ||
228                    self.eth_67 ||
229                    self.eth_68 ||
230                    self.eth_69 ||
231                    self.eth_70 ||
232                    self.eth_71
233            }
234            EthVersion::Eth67 => {
235                self.eth_67 || self.eth_68 || self.eth_69 || self.eth_70 || self.eth_71
236            }
237            EthVersion::Eth68 => self.eth_68 || self.eth_69 || self.eth_70 || self.eth_71,
238            EthVersion::Eth69 => self.eth_69 || self.eth_70 || self.eth_71,
239            EthVersion::Eth70 => self.eth_70 || self.eth_71,
240            EthVersion::Eth71 => self.eth_71,
241        }
242    }
243
244    /// Returns all capabilities.
245    #[inline]
246    pub fn capabilities(&self) -> &[Capability] {
247        &self.inner
248    }
249
250    /// Consumes the type and returns the all capabilities.
251    #[inline]
252    pub fn into_inner(self) -> Vec<Capability> {
253        self.inner
254    }
255
256    /// Whether the peer supports `eth` sub-protocol.
257    #[inline]
258    pub const fn supports_eth(&self) -> bool {
259        self.eth_71 || self.eth_70 || self.eth_69 || self.eth_68 || self.eth_67 || self.eth_66
260    }
261
262    /// Whether this peer supports eth v66 protocol.
263    #[inline]
264    pub const fn supports_eth_v66(&self) -> bool {
265        self.eth_66
266    }
267
268    /// Whether this peer supports eth v67 protocol.
269    #[inline]
270    pub const fn supports_eth_v67(&self) -> bool {
271        self.eth_67
272    }
273
274    /// Whether this peer supports eth v68 protocol.
275    #[inline]
276    pub const fn supports_eth_v68(&self) -> bool {
277        self.eth_68
278    }
279
280    /// Whether this peer supports eth v69 protocol.
281    #[inline]
282    pub const fn supports_eth_v69(&self) -> bool {
283        self.eth_69
284    }
285
286    /// Whether this peer supports eth v70 protocol.
287    #[inline]
288    pub const fn supports_eth_v70(&self) -> bool {
289        self.eth_70
290    }
291
292    /// Whether this peer supports eth v71 protocol.
293    #[inline]
294    pub const fn supports_eth_v71(&self) -> bool {
295        self.eth_71
296    }
297}
298
299impl From<Vec<Capability>> for Capabilities {
300    fn from(value: Vec<Capability>) -> Self {
301        Self::new(value)
302    }
303}
304
305impl Encodable for Capabilities {
306    fn encode(&self, out: &mut dyn BufMut) {
307        self.inner.encode(out)
308    }
309}
310
311impl Decodable for Capabilities {
312    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
313        let inner = Vec::<Capability>::decode(buf)?;
314
315        Ok(Self {
316            eth_66: inner.iter().any(Capability::is_eth_v66),
317            eth_67: inner.iter().any(Capability::is_eth_v67),
318            eth_68: inner.iter().any(Capability::is_eth_v68),
319            eth_69: inner.iter().any(Capability::is_eth_v69),
320            eth_70: inner.iter().any(Capability::is_eth_v70),
321            eth_71: inner.iter().any(Capability::is_eth_v71),
322            inner,
323        })
324    }
325}