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);