darkfi/net/message.rs
1/* This file is part of DarkFi (https://dark.fi)
2 *
3 * Copyright (C) 2020-2026 Dyne.org foundation
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19use std::net::Ipv6Addr;
20
21use darkfi_serial::{
22 async_trait, serialize_async, AsyncDecodable, AsyncEncodable, SerialDecodable, SerialEncodable,
23};
24use url::{Host, Url};
25
26use crate::{net::metering::MeteringConfiguration, util::time::NanoTimestamp};
27
28/// Generic message template.
29pub trait Message: 'static + Send + Sync + AsyncDecodable + AsyncEncodable {
30 const NAME: &'static str;
31 /// Message bytes vector length limit.
32 /// Set to 0 for no limit.
33 const MAX_BYTES: u64;
34 /// Message metering score value.
35 /// Set to 0 for no impact in metering.
36 const METERING_SCORE: u64;
37 /// Message metering configuration for rate limit.
38 /// Use `MeteringConfiguration::default()` for no limit.
39 const METERING_CONFIGURATION: MeteringConfiguration;
40}
41
42/// Generic serialized message template.
43pub struct SerializedMessage {
44 pub command: String,
45 pub payload: Vec<u8>,
46}
47
48impl SerializedMessage {
49 pub async fn new<M: Message>(message: &M) -> Self {
50 Self { command: M::NAME.to_string(), payload: serialize_async(message).await }
51 }
52}
53
54#[macro_export]
55macro_rules! impl_p2p_message {
56 ($st:ty, $nm:expr, $mb:expr, $ms:expr, $mc:expr) => {
57 impl Message for $st {
58 const NAME: &'static str = $nm;
59 const MAX_BYTES: u64 = $mb;
60 const METERING_SCORE: u64 = $ms;
61 const METERING_CONFIGURATION: MeteringConfiguration = $mc;
62 }
63 };
64}
65
66/// Maximum command (message name) length in bytes.
67pub const MAX_COMMAND_LENGTH: u8 = 255;
68
69/// For each message configs a threshold was calculated by taking the
70/// maximum number of messages in a 10 seconds window and multiply it
71/// by 2 not to be strict.
72pub const PING_PONG_METERING_CONFIGURATION: MeteringConfiguration = MeteringConfiguration {
73 threshold: 4,
74 sleep_step: 1000,
75 expiry_time: NanoTimestamp::from_secs(10),
76};
77
78/// Ping-Pong messages fields size:
79/// * nonce = 2
80pub const PING_PONG_MAX_BYTES: u64 = 2;
81
82/// Outbound keepalive message.
83#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
84pub struct PingMessage {
85 pub nonce: u16,
86}
87impl_p2p_message!(PingMessage, "ping", PING_PONG_MAX_BYTES, 1, PING_PONG_METERING_CONFIGURATION);
88
89/// Inbound keepalive message.
90#[derive(Debug, Copy, Clone, SerialEncodable, SerialDecodable)]
91pub struct PongMessage {
92 pub nonce: u16,
93}
94impl_p2p_message!(PongMessage, "pong", PING_PONG_MAX_BYTES, 1, PING_PONG_METERING_CONFIGURATION);
95
96/// Requests address of outbound connection.
97#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
98pub struct GetAddrsMessage {
99 /// Maximum number of addresses with preferred
100 /// transports to receive. Response vector will
101 /// also contain addresses without the preferred
102 /// transports, so its size will be 2 * max.
103 pub max: u32,
104 /// Preferred addresses transports.
105 pub transports: Vec<String>,
106}
107pub const GET_ADDRS_METERING_CONFIGURATION: MeteringConfiguration = MeteringConfiguration {
108 threshold: 6,
109 sleep_step: 1000,
110 expiry_time: NanoTimestamp::from_secs(10),
111};
112
113/// GetAddrs message fields size:
114/// * max = 4
115/// * transports = 1 (vec_len) + 4 + 4 + 4 + 4 + 4 + 8 + 8 + 8 + 8 = 53
116///
117/// Transports is list of all transports to be shared specified in protocol_address.
118pub const GET_ADDRS_MAX_BYTES: u64 = 57;
119
120impl_p2p_message!(
121 GetAddrsMessage,
122 "getaddr",
123 GET_ADDRS_MAX_BYTES,
124 1,
125 GET_ADDRS_METERING_CONFIGURATION
126);
127
128/// Sends address information to inbound connection.
129#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
130pub struct AddrsMessage {
131 pub addrs: Vec<(Url, u64)>,
132}
133pub const ADDRS_METERING_CONFIGURATION: MeteringConfiguration = MeteringConfiguration {
134 threshold: 6,
135 sleep_step: 1000,
136 expiry_time: NanoTimestamp::from_secs(10),
137};
138
139/// Addrs message fields size:
140/// * addrs = 1 (vec_len) + (u8::MAX * 2) * 128
141///
142/// Url type is estimated to be max 128 bytes here and for other message below.
143pub const ADDRS_MAX_BYTES: u64 = 65281;
144
145impl_p2p_message!(AddrsMessage, "addr", ADDRS_MAX_BYTES, 1, ADDRS_METERING_CONFIGURATION);
146
147/// Requests version information of outbound connection.
148#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
149pub struct VersionMessage {
150 /// Only used for debugging. Compromises privacy when set.
151 pub node_id: String,
152 /// App identifier
153 pub app_name: String,
154 /// Identifies protocol version being used by the node.
155 pub version: semver::Version,
156 /// UNIX timestamp of when the VersionMessage was created.
157 pub timestamp: u64,
158 /// Network address of the node receiving this message (before
159 /// resolving).
160 pub connect_recv_addr: Url,
161 /// Network address of the node receiving this message (after
162 /// resolving). Optional because only used by outbound connections.
163 pub resolve_recv_addr: Option<Url>,
164 /// External address of the sender node, if it exists (empty
165 /// otherwise).
166 pub ext_send_addr: Vec<Url>,
167 /// List of features consisting of a tuple of (services, version)
168 /// to be enabled for this connection.
169 pub features: Vec<(String, u32)>,
170}
171pub const VERSION_METERING_CONFIGURATION: MeteringConfiguration = MeteringConfiguration {
172 threshold: 4,
173 sleep_step: 1000,
174 expiry_time: NanoTimestamp::from_secs(10),
175};
176
177/// Version message fields size:
178/// * node_id = 8 (this will be empty most of the time)
179/// * version = 128 (look at VerackMessage for the reasoning)
180/// * timestamp = 8
181/// * connect_recv_addr = 128
182/// * resolve_recv_addr = 1 (enum_len) + 128(url) = 129
183/// * ext_send_addr = 1 (vec_len) + 128 * 10 = 1281 (10 is a reasonable cap for number of external addresses)
184/// * features = 1 (vec_len) + (32 (service_name) + 4 (service_version)) * 10 = 361 (10 features is an estimate)
185pub const VERSION_MAX_BYTES: u64 = 2043;
186
187impl_p2p_message!(VersionMessage, "version", VERSION_MAX_BYTES, 1, VERSION_METERING_CONFIGURATION);
188
189impl VersionMessage {
190 pub(in crate::net) fn get_ipv6_addr(&self) -> Option<Ipv6Addr> {
191 let host = self.connect_recv_addr.host()?;
192 // Check the reported address is Ipv6
193 match host {
194 Host::Ipv6(addr) => Some(addr),
195 _ => None,
196 }
197 }
198}
199
200/// Sends version information to inbound connection.
201/// Response to `VersionMessage`.
202#[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
203pub struct VerackMessage {
204 /// App version
205 pub app_version: semver::Version,
206 /// App identifier
207 pub app_name: String,
208}
209pub const VERACK_METERING_CONFIGURATION: MeteringConfiguration = MeteringConfiguration {
210 threshold: 4,
211 sleep_step: 1000,
212 expiry_time: NanoTimestamp::from_secs(10),
213};
214
215/// Verack message fields size:
216/// * app_version = 24 (major = 8, minor = 8, patch = 8) + 52 (prerelease = 1(str_len) + 51(str)) + 52 (build = 1(str_len) + 51(str))
217///
218/// Prerelease and build strings are variable length but shouldn't be larger than 102 bytes.
219pub const VERACK_MAX_BYTES: u64 = 128;
220
221impl_p2p_message!(VerackMessage, "verack", VERACK_MAX_BYTES, 1, VERACK_METERING_CONFIGURATION);