1use crate::alloc::string::ToString;
4use alloc::string::String;
5use alloy_rlp::{Decodable, Encodable, Error as RlpError};
6use bytes::BufMut;
7use core::{fmt, str::FromStr};
8use derive_more::Display;
9use reth_codecs_derive::add_arbitrary_tests;
10
11#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
13#[error("Unknown eth protocol version: {0}")]
14pub struct ParseVersionError(String);
15
16#[repr(u8)]
18#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Display)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
21pub enum EthVersion {
22 Eth66 = 66,
24 Eth67 = 67,
26 Eth68 = 68,
28 Eth69 = 69,
30 Eth70 = 70,
32 Eth71 = 71,
34}
35
36impl EthVersion {
37 pub const LATEST: Self = Self::Eth69;
39
40 pub const ALL_VERSIONS: &'static [Self] = &[Self::Eth69, Self::Eth68, Self::Eth67, Self::Eth66];
42
43 pub const fn is_eth66(&self) -> bool {
45 matches!(self, Self::Eth66)
46 }
47
48 pub const fn is_eth67(&self) -> bool {
50 matches!(self, Self::Eth67)
51 }
52
53 pub const fn is_eth68(&self) -> bool {
55 matches!(self, Self::Eth68)
56 }
57
58 pub const fn is_eth69(&self) -> bool {
60 matches!(self, Self::Eth69)
61 }
62
63 pub const fn is_eth70(&self) -> bool {
65 matches!(self, Self::Eth70)
66 }
67
68 pub const fn is_eth71(&self) -> bool {
70 matches!(self, Self::Eth71)
71 }
72
73 pub const fn is_eth69_or_newer(&self) -> bool {
75 matches!(self, Self::Eth69 | Self::Eth70 | Self::Eth71)
76 }
77}
78
79impl Encodable for EthVersion {
81 fn encode(&self, out: &mut dyn BufMut) {
82 (*self as u8).encode(out)
83 }
84
85 fn length(&self) -> usize {
86 (*self as u8).length()
87 }
88}
89
90impl Decodable for EthVersion {
93 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
94 let version = u8::decode(buf)?;
95 Self::try_from(version).map_err(|_| RlpError::Custom("invalid eth version"))
96 }
97}
98
99impl TryFrom<&str> for EthVersion {
109 type Error = ParseVersionError;
110
111 #[inline]
112 fn try_from(s: &str) -> Result<Self, Self::Error> {
113 match s {
114 "66" => Ok(Self::Eth66),
115 "67" => Ok(Self::Eth67),
116 "68" => Ok(Self::Eth68),
117 "69" => Ok(Self::Eth69),
118 "70" => Ok(Self::Eth70),
119 "71" => Ok(Self::Eth71),
120 _ => Err(ParseVersionError(s.to_string())),
121 }
122 }
123}
124
125impl TryFrom<u8> for EthVersion {
135 type Error = ParseVersionError;
136
137 #[inline]
138 fn try_from(u: u8) -> Result<Self, Self::Error> {
139 match u {
140 66 => Ok(Self::Eth66),
141 67 => Ok(Self::Eth67),
142 68 => Ok(Self::Eth68),
143 69 => Ok(Self::Eth69),
144 70 => Ok(Self::Eth70),
145 71 => Ok(Self::Eth71),
146 _ => Err(ParseVersionError(u.to_string())),
147 }
148 }
149}
150
151impl FromStr for EthVersion {
152 type Err = ParseVersionError;
153
154 #[inline]
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 Self::try_from(s)
157 }
158}
159
160impl From<EthVersion> for u8 {
161 #[inline]
162 fn from(v: EthVersion) -> Self {
163 v as Self
164 }
165}
166
167impl From<EthVersion> for &'static str {
168 #[inline]
169 fn from(v: EthVersion) -> &'static str {
170 match v {
171 EthVersion::Eth66 => "66",
172 EthVersion::Eth67 => "67",
173 EthVersion::Eth68 => "68",
174 EthVersion::Eth69 => "69",
175 EthVersion::Eth70 => "70",
176 EthVersion::Eth71 => "71",
177 }
178 }
179}
180
181#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
183#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
184#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
185#[add_arbitrary_tests(rlp)]
186pub enum ProtocolVersion {
187 V4 = 4,
189 #[default]
191 V5 = 5,
192}
193
194impl fmt::Display for ProtocolVersion {
195 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196 write!(f, "v{}", *self as u8)
197 }
198}
199
200impl Encodable for ProtocolVersion {
201 fn encode(&self, out: &mut dyn BufMut) {
202 (*self as u8).encode(out)
203 }
204 fn length(&self) -> usize {
205 (*self as u8).length()
207 }
208}
209
210impl Decodable for ProtocolVersion {
211 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
212 let version = u8::decode(buf)?;
213 match version {
214 4 => Ok(Self::V4),
215 5 => Ok(Self::V5),
216 _ => Err(RlpError::Custom("unknown p2p protocol version")),
217 }
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::EthVersion;
224 use alloy_rlp::{Decodable, Encodable, Error as RlpError};
225 use bytes::BytesMut;
226
227 #[test]
228 fn test_eth_version_try_from_str() {
229 assert_eq!(EthVersion::Eth66, EthVersion::try_from("66").unwrap());
230 assert_eq!(EthVersion::Eth67, EthVersion::try_from("67").unwrap());
231 assert_eq!(EthVersion::Eth68, EthVersion::try_from("68").unwrap());
232 assert_eq!(EthVersion::Eth69, EthVersion::try_from("69").unwrap());
233 assert_eq!(EthVersion::Eth70, EthVersion::try_from("70").unwrap());
234 assert_eq!(EthVersion::Eth71, EthVersion::try_from("71").unwrap());
235 }
236
237 #[test]
238 fn test_eth_version_from_str() {
239 assert_eq!(EthVersion::Eth66, "66".parse().unwrap());
240 assert_eq!(EthVersion::Eth67, "67".parse().unwrap());
241 assert_eq!(EthVersion::Eth68, "68".parse().unwrap());
242 assert_eq!(EthVersion::Eth69, "69".parse().unwrap());
243 assert_eq!(EthVersion::Eth70, "70".parse().unwrap());
244 assert_eq!(EthVersion::Eth71, "71".parse().unwrap());
245 }
246
247 #[test]
248 fn test_eth_version_rlp_encode() {
249 let versions = [
250 EthVersion::Eth66,
251 EthVersion::Eth67,
252 EthVersion::Eth68,
253 EthVersion::Eth69,
254 EthVersion::Eth70,
255 EthVersion::Eth71,
256 ];
257
258 for version in versions {
259 let mut encoded = BytesMut::new();
260 version.encode(&mut encoded);
261
262 assert_eq!(encoded.len(), 1);
263 assert_eq!(encoded[0], version as u8);
264 }
265 }
266 #[test]
267 fn test_eth_version_rlp_decode() {
268 let test_cases = [
269 (66_u8, Ok(EthVersion::Eth66)),
270 (67_u8, Ok(EthVersion::Eth67)),
271 (68_u8, Ok(EthVersion::Eth68)),
272 (69_u8, Ok(EthVersion::Eth69)),
273 (70_u8, Ok(EthVersion::Eth70)),
274 (71_u8, Ok(EthVersion::Eth71)),
275 (65_u8, Err(RlpError::Custom("invalid eth version"))),
276 ];
277
278 for (input, expected) in test_cases {
279 let mut encoded = BytesMut::new();
280 input.encode(&mut encoded);
281
282 let mut slice = encoded.as_ref();
283 let result = EthVersion::decode(&mut slice);
284 assert_eq!(result, expected);
285 }
286 }
287}