1use crate::{Capability, EthVersion, ProtocolVersion};
2use alloy_rlp::{Decodable, Header, RlpEncodable};
3use reth_codecs::add_arbitrary_tests;
4use reth_network_peers::PeerId;
5use reth_primitives_traits::constants::RETH_CLIENT_VERSION;
6
7pub(crate) const DEFAULT_TCP_PORT: u16 = 30303;
11
12use crate::protocol::Protocol;
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16#[derive(Debug, Clone, PartialEq, Eq)]
26#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
27pub struct HelloMessageWithProtocols {
28 pub protocol_version: ProtocolVersion,
30 pub client_version: String,
33 pub protocols: Vec<Protocol>,
35 pub port: u16,
39 pub id: PeerId,
41}
42
43impl HelloMessageWithProtocols {
44 pub const fn builder(id: PeerId) -> HelloMessageBuilder {
55 HelloMessageBuilder::new(id)
56 }
57
58 #[inline]
60 pub fn message(&self) -> HelloMessage {
61 HelloMessage {
62 protocol_version: self.protocol_version,
63 client_version: self.client_version.clone(),
64 capabilities: self.protocols.iter().map(|p| p.cap.clone()).collect(),
65 port: self.port,
66 id: self.id,
67 }
68 }
69
70 pub fn into_message(self) -> HelloMessage {
72 HelloMessage {
73 protocol_version: self.protocol_version,
74 client_version: self.client_version,
75 capabilities: self.protocols.into_iter().map(|p| p.cap).collect(),
76 port: self.port,
77 id: self.id,
78 }
79 }
80
81 #[inline]
83 pub fn contains_protocol(&self, protocol: &Protocol) -> bool {
84 self.protocols.iter().any(|p| p.cap == protocol.cap)
85 }
86
87 #[inline]
91 pub fn try_add_protocol(&mut self, protocol: Protocol) -> Result<(), Protocol> {
92 if self.contains_protocol(&protocol) {
93 Err(protocol)
94 } else {
95 self.protocols.push(protocol);
96 Ok(())
97 }
98 }
99
100 pub fn with_snap(mut self, enable: bool) -> Self {
105 if enable {
106 let _ = self.try_add_protocol(Protocol::snap_2());
107 } else {
108 self.protocols.retain(|p| p.cap.name != "snap");
109 }
110 self
111 }
112}
113
114#[derive(Clone, Debug, PartialEq, Eq, RlpEncodable)]
121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
123#[add_arbitrary_tests(rlp)]
124pub struct HelloMessage {
125 pub protocol_version: ProtocolVersion,
127 pub client_version: String,
130 pub capabilities: Vec<Capability>,
132 pub port: u16,
134 pub id: PeerId,
136}
137
138impl Decodable for HelloMessage {
141 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
142 let mut payload = Header::decode_bytes(buf, true)?;
143 Ok(Self {
144 protocol_version: Decodable::decode(&mut payload)?,
145 client_version: Decodable::decode(&mut payload)?,
146 capabilities: Decodable::decode(&mut payload)?,
147 port: Decodable::decode(&mut payload)?,
148 id: Decodable::decode(&mut payload)?,
149 })
150 }
151}
152
153impl HelloMessage {
154 pub const fn builder(id: PeerId) -> HelloMessageBuilder {
165 HelloMessageBuilder::new(id)
166 }
167}
168
169#[derive(Debug)]
171pub struct HelloMessageBuilder {
172 pub protocol_version: Option<ProtocolVersion>,
174 pub client_version: Option<String>,
177 pub protocols: Option<Vec<Protocol>>,
179 pub port: Option<u16>,
181 pub id: PeerId,
183}
184
185impl HelloMessageBuilder {
188 pub const fn new(id: PeerId) -> Self {
190 Self { protocol_version: None, client_version: None, protocols: None, port: None, id }
191 }
192
193 pub const fn port(mut self, port: u16) -> Self {
195 self.port = Some(port);
196 self
197 }
198
199 pub fn protocol(mut self, protocols: impl Into<Protocol>) -> Self {
201 self.protocols.get_or_insert_with(Vec::new).push(protocols.into());
202 self
203 }
204
205 pub fn protocols(mut self, protocols: impl IntoIterator<Item = Protocol>) -> Self {
207 self.protocols.get_or_insert_with(Vec::new).extend(protocols);
208 self
209 }
210
211 pub fn client_version(mut self, client_version: impl Into<String>) -> Self {
213 self.client_version = Some(client_version.into());
214 self
215 }
216
217 pub const fn protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
219 self.protocol_version = Some(protocol_version);
220 self
221 }
222
223 pub fn build(self) -> HelloMessageWithProtocols {
230 let Self { protocol_version, client_version, protocols, port, id } = self;
231 HelloMessageWithProtocols {
232 protocol_version: protocol_version.unwrap_or_default(),
233 client_version: client_version.unwrap_or_else(|| RETH_CLIENT_VERSION.to_string()),
234 protocols: protocols.unwrap_or_else(|| {
235 EthVersion::ALL_VERSIONS.iter().copied().map(Into::into).collect()
236 }),
237 port: port.unwrap_or(DEFAULT_TCP_PORT),
238 id,
239 }
240 }
241}
242
243#[cfg(test)]
244mod tests {
245 use crate::{
246 p2pstream::P2PMessage, Capability, EthVersion, HelloMessage, HelloMessageWithProtocols,
247 ProtocolVersion,
248 };
249 use alloy_rlp::{Decodable, Encodable, Header, EMPTY_STRING_CODE};
250 use reth_network_peers::pk2id;
251 use secp256k1::SecretKey;
252
253 #[test]
254 fn test_hello_encoding_round_trip() {
255 let secret_key = SecretKey::new(&mut rand::rng());
256 let id = pk2id(&secret_key.public_key());
257 let hello = P2PMessage::Hello(HelloMessage {
258 protocol_version: ProtocolVersion::V5,
259 client_version: "reth/0.1.0".to_string(),
260 capabilities: vec![Capability::new_static("eth", EthVersion::Eth67 as usize)],
261 port: 30303,
262 id,
263 });
264
265 let mut hello_encoded = Vec::new();
266 hello.encode(&mut hello_encoded);
267
268 let hello_decoded = P2PMessage::decode(&mut &hello_encoded[..]).unwrap();
269
270 assert_eq!(hello, hello_decoded);
271 }
272
273 #[test]
274 fn hello_encoding_length() {
275 let secret_key = SecretKey::new(&mut rand::rng());
276 let id = pk2id(&secret_key.public_key());
277 let hello = P2PMessage::Hello(HelloMessage {
278 protocol_version: ProtocolVersion::V5,
279 client_version: "reth/0.1.0".to_string(),
280 capabilities: vec![Capability::new_static("eth", EthVersion::Eth67 as usize)],
281 port: 30303,
282 id,
283 });
284
285 let mut hello_encoded = Vec::new();
286 hello.encode(&mut hello_encoded);
287
288 assert_eq!(hello_encoded.len(), hello.length());
289 }
290 #[test]
293 fn test_default_protocols_still_include_eth69() {
294 let secret_key = SecretKey::new(&mut rand::rng());
296 let id = pk2id(&secret_key.public_key());
297 let hello = HelloMessageWithProtocols::builder(id).build();
298
299 let has_eth69 = hello
300 .protocols
301 .iter()
302 .any(|p| p.cap.name == "eth" && p.cap.version == EthVersion::Eth69 as usize);
303 assert!(has_eth69, "Default protocols should include Eth69");
304 }
305
306 #[test]
307 fn hello_message_id_prefix() {
308 let secret_key = SecretKey::new(&mut rand::rng());
310 let id = pk2id(&secret_key.public_key());
311 let hello = P2PMessage::Hello(HelloMessage {
312 protocol_version: ProtocolVersion::V5,
313 client_version: "reth/0.1.0".to_string(),
314 capabilities: vec![Capability::new_static("eth", EthVersion::Eth67 as usize)],
315 port: 30303,
316 id,
317 });
318
319 let mut hello_encoded = Vec::new();
320 hello.encode(&mut hello_encoded);
321
322 assert_eq!(hello_encoded[0], EMPTY_STRING_CODE);
324 }
325
326 #[test]
327 fn hello_decode_ignores_extra_fields() {
328 let secret_key = SecretKey::new(&mut rand::rng());
330 let hello = HelloMessage {
331 protocol_version: ProtocolVersion::V5,
332 client_version: "reth/0.1.0".to_string(),
333 capabilities: vec![Capability::new_static("eth", EthVersion::Eth68 as usize)],
334 port: 30303,
335 id: pk2id(&secret_key.public_key()),
336 };
337
338 let mut payload = Vec::new();
339 hello.protocol_version.encode(&mut payload);
340 hello.client_version.encode(&mut payload);
341 hello.capabilities.encode(&mut payload);
342 hello.port.encode(&mut payload);
343 hello.id.encode(&mut payload);
344 42u8.encode(&mut payload);
345 "future".encode(&mut payload);
346
347 let mut encoded = Vec::new();
348 Header { list: true, payload_length: payload.len() }.encode(&mut encoded);
349 encoded.extend_from_slice(&payload);
350 encoded.push(0xff);
351
352 let mut buf = encoded.as_slice();
353 assert_eq!(HelloMessage::decode(&mut buf).unwrap(), hello);
354 assert_eq!(buf, [0xff]);
355 }
356}