reth_eth_wire_types/
capability.rs

1//! All capability related types
2
3use crate::EthVersion;
4use alloc::{borrow::Cow, string::String, vec::Vec};
5use alloy_rlp::{Decodable, Encodable, RlpDecodable, RlpEncodable};
6use bytes::BufMut;
7use core::fmt;
8use reth_codecs_derive::add_arbitrary_tests;
9
10/// A message indicating a supported capability and capability version.
11#[add_arbitrary_tests(rlp)]
12#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable, RlpDecodable, Default, Hash)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Capability {
15    /// The name of the subprotocol
16    pub name: Cow<'static, str>,
17    /// The version of the subprotocol
18    pub version: usize,
19}
20
21impl Capability {
22    /// Create a new `Capability` with the given name and version.
23    pub const fn new(name: String, version: usize) -> Self {
24        Self { name: Cow::Owned(name), version }
25    }
26
27    /// Create a new `Capability` with the given static name and version.
28    pub const fn new_static(name: &'static str, version: usize) -> Self {
29        Self { name: Cow::Borrowed(name), version }
30    }
31
32    /// Returns the corresponding eth capability for the given version.
33    pub const fn eth(version: EthVersion) -> Self {
34        Self::new_static("eth", version as usize)
35    }
36
37    /// Returns the [`EthVersion::Eth66`] capability.
38    pub const fn eth_66() -> Self {
39        Self::eth(EthVersion::Eth66)
40    }
41
42    /// Returns the [`EthVersion::Eth67`] capability.
43    pub const fn eth_67() -> Self {
44        Self::eth(EthVersion::Eth67)
45    }
46
47    /// Returns the [`EthVersion::Eth68`] capability.
48    pub const fn eth_68() -> Self {
49        Self::eth(EthVersion::Eth68)
50    }
51
52    /// Whether this is eth v66 protocol.
53    #[inline]
54    pub fn is_eth_v66(&self) -> bool {
55        self.name == "eth" && self.version == 66
56    }
57
58    /// Whether this is eth v67.
59    #[inline]
60    pub fn is_eth_v67(&self) -> bool {
61        self.name == "eth" && self.version == 67
62    }
63
64    /// Whether this is eth v68.
65    #[inline]
66    pub fn is_eth_v68(&self) -> bool {
67        self.name == "eth" && self.version == 68
68    }
69
70    /// Whether this is any eth version.
71    #[inline]
72    pub fn is_eth(&self) -> bool {
73        self.is_eth_v66() || self.is_eth_v67() || self.is_eth_v68()
74    }
75}
76
77impl fmt::Display for Capability {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "{}/{}", self.name, self.version)
80    }
81}
82
83impl From<EthVersion> for Capability {
84    #[inline]
85    fn from(value: EthVersion) -> Self {
86        Self::eth(value)
87    }
88}
89
90#[cfg(any(test, feature = "arbitrary"))]
91impl<'a> arbitrary::Arbitrary<'a> for Capability {
92    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
93        let version = u.int_in_range(66..=69)?; // Valid eth protocol versions are 66-69
94                                                // Only generate valid eth protocol name for now since it's the only supported protocol
95        Ok(Self::new_static("eth", version))
96    }
97}
98
99/// Represents all capabilities of a node.
100#[derive(Debug, Clone, Eq, PartialEq)]
101pub struct Capabilities {
102    /// All Capabilities and their versions
103    inner: Vec<Capability>,
104    eth_66: bool,
105    eth_67: bool,
106    eth_68: bool,
107}
108
109impl Capabilities {
110    /// Returns all capabilities.
111    #[inline]
112    pub fn capabilities(&self) -> &[Capability] {
113        &self.inner
114    }
115
116    /// Consumes the type and returns the all capabilities.
117    #[inline]
118    pub fn into_inner(self) -> Vec<Capability> {
119        self.inner
120    }
121
122    /// Whether the peer supports `eth` sub-protocol.
123    #[inline]
124    pub const fn supports_eth(&self) -> bool {
125        self.eth_68 || self.eth_67 || self.eth_66
126    }
127
128    /// Whether this peer supports eth v66 protocol.
129    #[inline]
130    pub const fn supports_eth_v66(&self) -> bool {
131        self.eth_66
132    }
133
134    /// Whether this peer supports eth v67 protocol.
135    #[inline]
136    pub const fn supports_eth_v67(&self) -> bool {
137        self.eth_67
138    }
139
140    /// Whether this peer supports eth v68 protocol.
141    #[inline]
142    pub const fn supports_eth_v68(&self) -> bool {
143        self.eth_68
144    }
145}
146
147impl From<Vec<Capability>> for Capabilities {
148    fn from(value: Vec<Capability>) -> Self {
149        Self {
150            eth_66: value.iter().any(Capability::is_eth_v66),
151            eth_67: value.iter().any(Capability::is_eth_v67),
152            eth_68: value.iter().any(Capability::is_eth_v68),
153            inner: value,
154        }
155    }
156}
157
158impl Encodable for Capabilities {
159    fn encode(&self, out: &mut dyn BufMut) {
160        self.inner.encode(out)
161    }
162}
163
164impl Decodable for Capabilities {
165    fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
166        let inner = Vec::<Capability>::decode(buf)?;
167
168        Ok(Self {
169            eth_66: inner.iter().any(Capability::is_eth_v66),
170            eth_67: inner.iter().any(Capability::is_eth_v67),
171            eth_68: inner.iter().any(Capability::is_eth_v68),
172            inner,
173        })
174    }
175}