reth_eth_wire_types/
capability.rs

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