Skip to main content

rns_core/
constants.rs

1// =============================================================================
2// Reticulum protocol constants
3// Ported from Python RNS source
4// =============================================================================
5
6use rns_crypto::stamp_value::StampCost;
7
8// --- From Reticulum.py ---
9
10/// Maximum transmission unit in bytes
11pub const MTU: usize = 500;
12
13/// Truncated hash length in bits
14pub const TRUNCATED_HASHLENGTH: usize = 128;
15
16/// Minimum header size: 2 (flags + hops) + 1 (context) + 16 (dest hash)
17pub const HEADER_MINSIZE: usize = 2 + 1 + (TRUNCATED_HASHLENGTH / 8);
18
19/// Maximum header size: 2 (flags + hops) + 1 (context) + 32 (transport_id + dest hash)
20pub const HEADER_MAXSIZE: usize = 2 + 1 + (TRUNCATED_HASHLENGTH / 8) * 2;
21
22/// Minimum IFAC size
23pub const IFAC_MIN_SIZE: usize = 1;
24
25/// Maximum data unit: MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE
26pub const MDU: usize = MTU - HEADER_MAXSIZE - IFAC_MIN_SIZE;
27
28// --- From Identity.py ---
29
30/// Full key size in bits (256 X25519 + 256 Ed25519)
31pub const KEYSIZE: usize = 512;
32
33/// Ratchet key size in bits
34pub const RATCHETSIZE: usize = 256;
35
36/// Received ratchet expiry in seconds (30 days)
37pub const RATCHET_EXPIRY: u64 = 60 * 60 * 24 * 30;
38
39/// Token overhead in bytes (16 IV + 32 HMAC)
40pub const TOKEN_OVERHEAD: usize = 48;
41
42/// AES-128 block size in bytes
43pub const AES128_BLOCKSIZE: usize = 16;
44
45/// Full hash length in bits (SHA-256)
46pub const HASHLENGTH: usize = 256;
47
48/// Signature length in bits (Ed25519)
49pub const SIGLENGTH: usize = KEYSIZE;
50
51/// Name hash length in bits. Defined with the other identity sizes so the
52/// derived announce and destination layouts read in one block.
53pub const NAME_HASH_LENGTH: usize = 80;
54
55/// Derived key length in bytes. The retired 32-byte legacy derivation is not
56/// part of the current wire format and has no native constant.
57pub const DERIVED_KEY_LENGTH: usize = 64;
58
59/// Detached stamp length in bytes (14-byte stamps keep the search space viable)
60pub const STAMP_LENGTH: usize = 14;
61
62/// Extension payload header length in bytes (marker + flags)
63pub const EXT_HEADER_LENGTH: usize = 2;
64
65/// Announced link request cost length in bytes: the 1/256 fraction, then the
66/// whole part in the trailing anchor (two bytes since upstream 796ef2d4).
67pub const LR_COST_LENGTH: usize = 2;
68
69/// Announce extension payload length in bytes (header + stamp + link cost)
70pub const EXT_PAYLOAD_LENGTH: usize = EXT_HEADER_LENGTH + STAMP_LENGTH + LR_COST_LENGTH;
71
72/// Path request extension payload length in bytes (header + stamp)
73pub const PR_EXT_PAYLOAD_LENGTH: usize = EXT_HEADER_LENGTH + STAMP_LENGTH;
74
75/// Extension flags byte marking an unstamped path request relayed by a
76/// stamp-aware node (transitional sentinel, upstream 7c1b6486).
77pub const PR_FLAG_UNSTAMPED_RELAY: u8 = 0xFF;
78
79/// Marker identifying a stamp extension payload. Never a valid msgpack
80/// object boundary, so it cannot be confused with application data.
81pub const EXT_PAYLOAD_MARKER: u8 = 0xC1;
82
83/// Unstamped 1.6.0 path request length: destination hash, transport id, tag.
84pub const PATH_REQUEST_LENGTH: usize = 48;
85
86/// Stamped path request length: unstamped data plus the extension payload.
87pub const PATH_REQUEST_STAMPED_LENGTH: usize = PATH_REQUEST_LENGTH + PR_EXT_PAYLOAD_LENGTH;
88
89/// Legacy path request length without a transport id: destination hash and
90/// tag (`Transport.PR_LEGACY_LENGTH`, rgit `cda4d72b`).
91pub const PATH_REQUEST_LEGACY_LENGTH: usize = 32;
92
93/// Whether `len` is a path request length upstream accepts since rgit
94/// `cda4d72b`: legacy, unstamped or stamped.
95pub const fn is_valid_path_request_length(len: usize) -> bool {
96    len == PATH_REQUEST_LEGACY_LENGTH
97        || len == PATH_REQUEST_LENGTH
98        || len == PATH_REQUEST_STAMPED_LENGTH
99}
100
101/// Whether incoming announces are checked for stamps.
102pub const STAMPS_ENABLED: bool = true;
103
104/// Whether stamp requirements are enforced. Disabled during the upstream
105/// transition period; unstamped announces are still accepted.
106pub const STAMPS_ENFORCED: bool = false;
107
108/// Hard cap on stamp cost.
109pub const STAMP_COST_CAP: u8 = 100;
110
111/// Default required announce stamp cost (`Transport.DESTINATION_COST`,
112/// 23.5 since rgit a8cc6cb2; `cost_to_fp(23.5)`).
113pub const DESTINATION_STAMP_COST: StampCost = StampCost::from_fp(6016);
114
115/// Default target stamp value for emitted announces (upstream
116/// `Transport.DESTINATION_VALUE`, raised from 23 to 25 in d7749b0b).
117pub const DESTINATION_STAMP_VALUE: StampCost = StampCost::from_whole(25);
118
119/// Default required path request stamp cost (`Transport.PR_COST`, 19.8
120/// in a8cc6cb2 and 15.8 since f57fa265; `cost_to_fp(15.8)`).
121pub const PATH_REQUEST_STAMP_COST: StampCost = StampCost::from_fp(4045);
122
123/// Default target stamp value for emitted path requests (`Transport.PR_VALUE`,
124/// 19.8 in a8cc6cb2 and 15.8 since f57fa265).
125pub const PATH_REQUEST_STAMP_VALUE: StampCost = StampCost::from_fp(4045);
126
127/// Default link request stamp cost (`Transport.LR_COST`, 19.2 in a8cc6cb2
128/// and 12.5 since f57fa265; `cost_to_fp(12.5)`). A destination that sets no
129/// cost of its own announces and requires it, fraction included since
130/// 796ef2d4, and `ALWAYS_STAMP_LRS` stamps requests to destinations that
131/// announced no cost at it.
132pub const LINK_REQUEST_STAMP_COST: StampCost = StampCost::from_fp(3200);
133
134/// Link request stamp cost of the published blackhole list destination,
135/// `rnstransport.info.blackhole` (`blackhole_destination.set_link_cost(5.25)`
136/// since upstream 426673e9; `cost_to_fp(5.25)`).
137pub const BLACKHOLE_LINK_COST: StampCost = StampCost::from_fp(1344);
138
139/// Whether destination announces emitted by this instance are stamped.
140pub const STAMP_DESTINATIONS: bool = false;
141
142/// Whether path requests emitted by this instance are stamped.
143pub const STAMP_PRS: bool = false;
144
145/// Whether link requests emitted by this instance are stamped when the
146/// destination announces a cost. On again since upstream a8cc6cb2, which
147/// also dropped the `[stamps] stamp_lrs` option.
148pub const STAMP_LRS: bool = true;
149
150/// Whether link requests are stamped at `LINK_REQUEST_STAMP_COST` even when
151/// the destination announced no cost (`Transport.ALWAYS_STAMP_LRS`,
152/// `[stamps] always_stamp_lrs`, a8cc6cb2).
153pub const ALWAYS_STAMP_LRS: bool = false;
154
155/// Whether link request stamp requirements are enforced.
156pub const LR_STAMPS_ENFORCED: bool = false;
157
158/// Global hard cap on the announced link request cost.
159pub const LINK_COST_CAP: u8 = 40;
160
161/// Timeout for deferred link request stamp generation (seconds).
162pub const LINK_STAMP_TIMEOUT: u64 = 15;
163
164/// Timeout for path request stamp generation on the stamper worker
165/// (seconds, upstream `Transport.PR_STAMP_TIMEOUT`). Emission of the request
166/// is cancelled when no stamp is found in time. Upstream c0fe08c0 only
167/// corrected the spelling in this attribute's comment; the value and its
168/// use are unchanged from d7749b0b.
169pub const PATH_REQUEST_STAMP_TIMEOUT: u64 = 15;
170
171/// Lower bound applied to the configurable stamp generation timeouts
172/// (`lr_stamp_timeout`, `pr_stamp_timeout`), in seconds.
173pub const STAMP_TIMEOUT_MIN: f64 = 0.020;
174
175const _: () = assert!(STAMP_COST_CAP as u32 == rns_crypto::stamp_value::STAMP_COST_CAP);
176const _: () = assert!(LINK_REQUEST_STAMP_COST.whole() <= LINK_COST_CAP as u32);
177
178// --- From Packet.py ---
179
180/// Packet types
181pub const PACKET_TYPE_DATA: u8 = 0x00;
182pub const PACKET_TYPE_ANNOUNCE: u8 = 0x01;
183pub const PACKET_TYPE_LINKREQUEST: u8 = 0x02;
184pub const PACKET_TYPE_PROOF: u8 = 0x03;
185
186/// Header types
187pub const HEADER_1: u8 = 0x00;
188pub const HEADER_2: u8 = 0x01;
189
190/// Packet context types
191pub const CONTEXT_NONE: u8 = 0x00;
192pub const CONTEXT_RESOURCE: u8 = 0x01;
193pub const CONTEXT_RESOURCE_ADV: u8 = 0x02;
194pub const CONTEXT_RESOURCE_REQ: u8 = 0x03;
195pub const CONTEXT_RESOURCE_HMU: u8 = 0x04;
196pub const CONTEXT_RESOURCE_PRF: u8 = 0x05;
197pub const CONTEXT_RESOURCE_ICL: u8 = 0x06;
198pub const CONTEXT_RESOURCE_RCL: u8 = 0x07;
199pub const CONTEXT_CACHE_REQUEST: u8 = 0x08;
200pub const CONTEXT_REQUEST: u8 = 0x09;
201pub const CONTEXT_RESPONSE: u8 = 0x0A;
202pub const CONTEXT_PATH_RESPONSE: u8 = 0x0B;
203pub const CONTEXT_COMMAND: u8 = 0x0C;
204pub const CONTEXT_COMMAND_STATUS: u8 = 0x0D;
205pub const CONTEXT_CHANNEL: u8 = 0x0E;
206pub const CONTEXT_KEEPALIVE: u8 = 0xFA;
207pub const CONTEXT_LINKIDENTIFY: u8 = 0xFB;
208pub const CONTEXT_LINKCLOSE: u8 = 0xFC;
209pub const CONTEXT_LINKPROOF: u8 = 0xFD;
210pub const CONTEXT_LRRTT: u8 = 0xFE;
211pub const CONTEXT_LRPROOF: u8 = 0xFF;
212
213/// Context flag values
214pub const FLAG_SET: u8 = 0x01;
215pub const FLAG_UNSET: u8 = 0x00;
216
217/// Encrypted MDU: floor((MDU - TOKEN_OVERHEAD - KEYSIZE/16) / AES128_BLOCKSIZE) * AES128_BLOCKSIZE - 1
218pub const ENCRYPTED_MDU: usize = {
219    let numerator = MDU - TOKEN_OVERHEAD - KEYSIZE / 16;
220    (numerator / AES128_BLOCKSIZE) * AES128_BLOCKSIZE - 1
221};
222
223/// Plain MDU (same as MDU)
224pub const PLAIN_MDU: usize = MDU;
225
226/// Explicit proof length: HASHLENGTH/8 + SIGLENGTH/8 = 32 + 64 = 96
227pub const EXPL_LENGTH: usize = HASHLENGTH / 8 + SIGLENGTH / 8;
228
229/// Implicit proof length: SIGLENGTH/8 = 64
230pub const IMPL_LENGTH: usize = SIGLENGTH / 8;
231
232/// Receipt status constants
233pub const RECEIPT_FAILED: u8 = 0x00;
234pub const RECEIPT_SENT: u8 = 0x01;
235pub const RECEIPT_DELIVERED: u8 = 0x02;
236pub const RECEIPT_CULLED: u8 = 0xFF;
237
238// --- From Destination.py ---
239
240/// Destination types
241pub const DESTINATION_SINGLE: u8 = 0x00;
242pub const DESTINATION_GROUP: u8 = 0x01;
243pub const DESTINATION_PLAIN: u8 = 0x02;
244pub const DESTINATION_LINK: u8 = 0x03;
245
246/// Destination directions
247pub const DESTINATION_IN: u8 = 0x11;
248pub const DESTINATION_OUT: u8 = 0x12;
249
250// --- From Transport.py ---
251
252/// Transport types
253pub const TRANSPORT_BROADCAST: u8 = 0x00;
254pub const TRANSPORT_TRANSPORT: u8 = 0x01;
255pub const TRANSPORT_RELAY: u8 = 0x02;
256pub const TRANSPORT_TUNNEL: u8 = 0x03;
257
258/// Maximum hops
259pub const PATHFINDER_M: u8 = 128;
260
261/// Default transport hop limiter (upstream `Transport.HOP_LIMITER`).
262/// Inbound packets whose hop count has reached the node's hop limiter are
263/// dropped before the packet filter and counted per interface as
264/// `hop_limiter_hits`. The limiter is a runtime value configured with
265/// `[reticulum] hop_limiter`, clamped to `1..=PATHFINDER_M`; packets with a
266/// hop count at or above `PATHFINDER_M` already fail to decode.
267pub const HOP_LIMITER: u8 = PATHFINDER_M;
268
269// --- PATHFINDER algorithm ---
270
271/// Retransmit retries (total sends = PATHFINDER_R + 1)
272pub const PATHFINDER_R: u8 = 1;
273
274/// Grace period between retries (seconds)
275pub const PATHFINDER_G: f64 = 5.0;
276
277/// Random window for announce rebroadcast (seconds)
278pub const PATHFINDER_RW: f64 = 0.5;
279
280/// Path expiry = 7 days (seconds)
281pub const PATHFINDER_E: f64 = 604800.0;
282
283// --- Path expiry by interface mode ---
284
285/// Access Point path expiry = 1 day
286pub const AP_PATH_TIME: f64 = 86400.0;
287
288/// Roaming path expiry = 6 hours
289pub const ROAMING_PATH_TIME: f64 = 21600.0;
290
291// --- Announce bandwidth cap ---
292
293/// Default announce bandwidth cap (2% of interface bandwidth)
294pub const ANNOUNCE_CAP: f64 = 0.02;
295
296/// Maximum queued announces per interface
297pub const MAX_QUEUED_ANNOUNCES: usize = 4096;
298
299/// Queued announce lifetime (3 hours)
300pub const QUEUED_ANNOUNCE_LIFE: f64 = 3.0 * 60.0 * 60.0;
301
302/// Retention TTL for announce retransmission state (seconds)
303pub const ANNOUNCE_TABLE_TTL: f64 = 30.0;
304
305/// Maximum retained bytes for announce retransmission state
306pub const ANNOUNCE_TABLE_MAX_BYTES: usize = 4 * 1024 * 1024;
307
308// --- Table limits ---
309
310/// How many local rebroadcasts of an announce is allowed
311pub const LOCAL_REBROADCASTS_MAX: u8 = 2;
312
313/// Maximum number of random blobs per destination to keep in memory
314pub const MAX_RANDOM_BLOBS: usize = 64;
315
316/// Maximum number of announce timestamps to keep per destination
317pub const MAX_RATE_TIMESTAMPS: usize = 16;
318
319/// Maximum packet hashlist size before rotation
320pub const HASHLIST_MAXSIZE: usize = 250_000;
321
322/// Maximum announce signature cache entries (dedup verified signatures)
323pub const ANNOUNCE_SIG_CACHE_MAXSIZE: usize = 2_000;
324
325/// TTL for announce signature cache entries (seconds)
326pub const ANNOUNCE_SIG_CACHE_TTL: f64 = 600.0;
327
328// --- Timeouts ---
329
330/// Reverse table entry timeout (8 minutes)
331pub const REVERSE_TIMEOUT: f64 = 480.0;
332
333/// Destination table entry timeout (7 days)
334pub const DESTINATION_TIMEOUT: f64 = 604800.0;
335
336/// Path table entry retainment after the last verifiable liveness event.
337pub const PATH_TIMEOUT: f64 = DESTINATION_TIMEOUT;
338
339/// Linger time for never-used paths on transport instances (1.75 hours).
340pub const UNUSED_PATH_LINGER: f64 = 6300.0;
341
342/// Linger time for never-used paths on leaf instances (12 minutes).
343pub const UNUSED_PATH_LINGER_LEAF: f64 = 720.0;
344
345/// Maximum unused path pool target.
346pub const PATH_POOL_UNUSED_TARGET: usize = 2048;
347
348/// Interfaces with a known bitrate below this many bits per second are slow:
349/// never-used paths learnt on them linger `PATH_SLOW_LINGER_FACTOR` times
350/// longer and are culled last when the unused pool exceeds its target.
351pub const PATH_SLOW_BITRATE: u64 = 25_000;
352
353/// Linger time multiplier for never-used paths on slow interfaces.
354pub const PATH_SLOW_LINGER_FACTOR: f64 = 10.0;
355
356/// Default announce rate table retainment (1 hour).
357pub const RATE_TABLE_RETAIN: f64 = 3600.0;
358
359/// Tunnel table entry timeout (8 hours)
360pub const TUNNEL_TIMEOUT: f64 = 28800.0;
361
362/// Tunnel path retention timeout (8 hours)
363pub const TUNNEL_PATH_TIMEOUT: f64 = 28800.0;
364
365/// Link stale time = 2 * KEEPALIVE(360) = 720 seconds
366pub const LINK_STALE_TIME: f64 = 720.0;
367
368/// Link timeout = STALE_TIME * 1.25 = 900 seconds
369pub const LINK_TIMEOUT: f64 = 900.0;
370
371/// Link establishment timeout per hop (seconds)
372pub const LINK_ESTABLISHMENT_TIMEOUT_PER_HOP: f64 = 6.0;
373
374// --- Path request ---
375
376/// Default timeout for path requests (seconds)
377pub const PATH_REQUEST_TIMEOUT: f64 = 15.0;
378
379/// Timeout for suppressing or batching repeated path requests (seconds).
380pub const PATH_REQUEST_GATE_TIMEOUT: f64 = 45.0;
381
382/// Minimum usable interface bitrate for timeout calculations (bits/second).
383pub const MINIMUM_BITRATE: u64 = 5;
384
385/// Grace time before a path announcement is made (seconds)
386pub const PATH_REQUEST_GRACE: f64 = 0.4;
387
388/// Extra grace time for roaming-mode interfaces (seconds)
389pub const PATH_REQUEST_RG: f64 = 1.5;
390
391/// Minimum interval for automated path requests (seconds)
392pub const PATH_REQUEST_MI: f64 = 20.0;
393
394/// Seconds between path rediscovery requests sent from the queue
395/// (`Transport.discovery_pr_tx_throttle`).
396pub const DISCOVERY_PR_TX_THROTTLE: f64 = 0.5;
397
398/// Maximum number of queued path rediscovery requests
399/// (`Transport.max_queued_discovery_prs`).
400pub const MAX_QUEUED_DISCOVERY_PRS: usize = 32;
401
402/// Maximum amount of unique path request tags to remember
403pub const MAX_PR_TAGS: usize = 16_000;
404
405// --- Job intervals ---
406
407/// Announce check interval (seconds)
408pub const ANNOUNCES_CHECK_INTERVAL: f64 = 1.0;
409
410/// Table culling interval (seconds)
411pub const TABLES_CULL_INTERVAL: f64 = 5.0;
412
413/// Link check interval (seconds)
414pub const LINKS_CHECK_INTERVAL: f64 = 1.0;
415
416// --- Ingress Control (from Interface.py) ---
417
418/// Interface "new" period in seconds (2 hours).
419pub const IC_NEW_TIME: f64 = 7200.0;
420/// Announce frequency threshold for new interfaces (announces/sec).
421pub const IC_BURST_FREQ_NEW: f64 = 3.0;
422/// Announce frequency threshold for mature interfaces (announces/sec).
423pub const IC_BURST_FREQ: f64 = 10.0;
424/// Path request frequency threshold for new interfaces (requests/sec).
425pub const IC_PR_BURST_FREQ_NEW: f64 = 3.0;
426/// Path request frequency threshold for mature interfaces (requests/sec).
427pub const IC_PR_BURST_FREQ: f64 = 8.0;
428/// Minimum burst active duration in seconds.
429pub const IC_BURST_HOLD: f64 = 15.0;
430/// Penalty delay before releasing held announces (seconds).
431pub const IC_BURST_PENALTY: f64 = 15.0;
432/// Interval between individual held announce releases (seconds).
433pub const IC_HELD_RELEASE_INTERVAL: f64 = 5.0;
434/// Minimum path request samples before egress limiting.
435pub const EC_BURST_MIN_SAMPLES: usize = 2;
436/// Default egress path request limit (requests/sec).
437pub const EC_PR_FREQ: f64 = 5.0;
438/// Maximum held announces per interface.
439pub const IC_MAX_HELD_ANNOUNCES: usize = 256;
440
441// --- Interface modes (from Interface.py) ---
442
443pub const MODE_FULL: u8 = 0x01;
444pub const MODE_POINT_TO_POINT: u8 = 0x02;
445pub const MODE_ACCESS_POINT: u8 = 0x03;
446pub const MODE_ROAMING: u8 = 0x04;
447pub const MODE_BOUNDARY: u8 = 0x05;
448pub const MODE_GATEWAY: u8 = 0x06;
449pub const MODE_INTERNAL: u8 = 0x07;
450
451/// Interface modes that forward path requests for unknown destinations.
452/// Python: Interface.DISCOVER_PATHS_FOR = [MODE_ACCESS_POINT, MODE_GATEWAY, MODE_ROAMING, MODE_INTERNAL]
453pub const DISCOVER_PATHS_FOR: [u8; 4] =
454    [MODE_ACCESS_POINT, MODE_GATEWAY, MODE_ROAMING, MODE_INTERNAL];
455
456/// Interface modes searched for an unknown path request received on a boundary interface.
457pub const BOUNDARY_SEARCH_MODES: [u8; 2] = [MODE_BOUNDARY, MODE_GATEWAY];
458
459/// Discovery path request expiry (seconds) — requests older than this are culled.
460pub const DISCOVERY_PATH_REQUEST_TIMEOUT: f64 = 15.0;
461
462// --- Path states ---
463
464pub const STATE_UNKNOWN: u8 = 0x00;
465pub const STATE_UNRESPONSIVE: u8 = 0x01;
466pub const STATE_RESPONSIVE: u8 = 0x02;
467
468// --- From Link.py ---
469
470/// Link ephemeral public key size: 32 (X25519) + 32 (Ed25519)
471pub const LINK_ECPUBSIZE: usize = 64;
472
473/// Link key size in bytes
474pub const LINK_KEYSIZE: usize = 32;
475
476/// Link MTU signalling bytes size
477pub const LINK_MTU_SIZE: usize = 3;
478
479/// Maximum keepalive interval in seconds
480pub const LINK_KEEPALIVE_MAX: f64 = 360.0;
481
482/// Minimum keepalive interval in seconds
483pub const LINK_KEEPALIVE_MIN: f64 = 5.0;
484
485/// Maximum RTT used for keepalive scaling
486pub const LINK_KEEPALIVE_MAX_RTT: f64 = 1.75;
487
488/// RTT timeout factor for stale→close transition
489pub const LINK_KEEPALIVE_TIMEOUT_FACTOR: f64 = 4.0;
490
491/// Grace period for stale→close transition
492pub const LINK_STALE_GRACE: f64 = 5.0;
493
494/// Factor to compute stale_time from keepalive
495pub const LINK_STALE_FACTOR: f64 = 2.0;
496
497/// Traffic timeout factor
498pub const LINK_TRAFFIC_TIMEOUT_FACTOR: f64 = 6.0;
499
500/// Link MDU: floor((MTU - IFAC_MIN_SIZE - HEADER_MINSIZE - TOKEN_OVERHEAD) / AES128_BLOCKSIZE) * AES128_BLOCKSIZE - 1
501pub const LINK_MDU: usize = {
502    let numerator = MTU - IFAC_MIN_SIZE - HEADER_MINSIZE - TOKEN_OVERHEAD;
503    (numerator / AES128_BLOCKSIZE) * AES128_BLOCKSIZE - 1
504};
505
506/// Link MTU bytemask (21-bit MTU field)
507pub const LINK_MTU_BYTEMASK: u32 = 0x1FFFFF;
508
509/// Smallest link MTU that leaves room for one AES block of link payload:
510/// IFAC_MIN_SIZE + HEADER_MINSIZE + TOKEN_OVERHEAD + AES128_BLOCKSIZE = 84.
511/// Smaller signalled MTUs are raised to this value.
512pub const LINK_MTU_MIN: u32 =
513    (IFAC_MIN_SIZE + HEADER_MINSIZE + TOKEN_OVERHEAD + AES128_BLOCKSIZE) as u32;
514
515/// Largest MTU expressible in the 21-bit link signalling field.
516pub const LINK_MTU_MAX: u32 = LINK_MTU_BYTEMASK;
517
518/// Link mode bytemask (3-bit mode field in upper byte)
519pub const LINK_MODE_BYTEMASK: u8 = 0xE0;
520
521// --- From Channel.py ---
522
523/// Initial window size at channel setup
524pub const CHANNEL_WINDOW: u16 = 2;
525
526/// Absolute minimum window size
527pub const CHANNEL_WINDOW_MIN: u16 = 2;
528
529/// Minimum window limit for slow links
530pub const CHANNEL_WINDOW_MIN_LIMIT_SLOW: u16 = 2;
531
532/// Minimum window limit for medium-speed links
533pub const CHANNEL_WINDOW_MIN_LIMIT_MEDIUM: u16 = 5;
534
535/// Minimum window limit for fast links
536pub const CHANNEL_WINDOW_MIN_LIMIT_FAST: u16 = 16;
537
538/// Maximum window size for slow links
539pub const CHANNEL_WINDOW_MAX_SLOW: u16 = 5;
540
541/// Maximum window size for medium-speed links
542pub const CHANNEL_WINDOW_MAX_MEDIUM: u16 = 12;
543
544/// Maximum window size for fast links
545pub const CHANNEL_WINDOW_MAX_FAST: u16 = 48;
546
547/// Minimum flexibility between window_max and window_min
548pub const CHANNEL_WINDOW_FLEXIBILITY: u16 = 4;
549
550/// Maximum sequence number
551pub const CHANNEL_SEQ_MAX: u16 = 0xFFFF;
552
553/// Sequence number modulus
554pub const CHANNEL_SEQ_MODULUS: u32 = 0x10000;
555
556/// Maximum number of send tries per envelope
557pub const CHANNEL_MAX_TRIES: u8 = 5;
558
559/// RTT threshold for fast links
560pub const CHANNEL_RTT_FAST: f64 = 0.18;
561
562/// RTT threshold for medium links
563pub const CHANNEL_RTT_MEDIUM: f64 = 0.75;
564
565/// RTT threshold for slow links
566pub const CHANNEL_RTT_SLOW: f64 = 1.45;
567
568/// Number of consecutive fast rounds to upgrade window
569pub const CHANNEL_FAST_RATE_THRESHOLD: u16 = 10;
570
571/// Channel envelope overhead: msgtype(2) + seq(2) + len(2)
572pub const CHANNEL_ENVELOPE_OVERHEAD: usize = 6;
573
574// --- From Buffer.py ---
575
576/// System message type for stream data
577pub const STREAM_DATA_MSGTYPE: u16 = 0xFF00;
578
579/// Maximum stream ID (14 bits)
580pub const STREAM_ID_MAX: u16 = 0x3FFF;
581
582/// Stream data overhead: 2 (stream header) + 6 (channel envelope)
583pub const STREAM_DATA_OVERHEAD: usize = 2 + CHANNEL_ENVELOPE_OVERHEAD;
584
585// --- From Resource.py ---
586
587/// Initial window size at beginning of transfer
588pub const RESOURCE_WINDOW: usize = 4;
589
590/// Absolute minimum window size during transfer
591pub const RESOURCE_WINDOW_MIN: usize = 2;
592
593/// Maximum window size for slow links
594pub const RESOURCE_WINDOW_MAX_SLOW: usize = 10;
595
596/// Maximum window size for very slow links
597pub const RESOURCE_WINDOW_MAX_VERY_SLOW: usize = 4;
598
599/// Maximum window size for fast links
600pub const RESOURCE_WINDOW_MAX_FAST: usize = 75;
601
602/// Global maximum window (for calculations)
603pub const RESOURCE_WINDOW_MAX: usize = RESOURCE_WINDOW_MAX_FAST;
604
605/// Minimum flexibility between window_max and window_min
606pub const RESOURCE_WINDOW_FLEXIBILITY: usize = 4;
607
608/// Sustained fast-rate rounds before enabling fast window
609/// = WINDOW_MAX_SLOW - WINDOW - 2 = 10 - 4 - 2 = 4
610pub const RESOURCE_FAST_RATE_THRESHOLD: usize = RESOURCE_WINDOW_MAX_SLOW - RESOURCE_WINDOW - 2;
611
612/// Sustained very-slow-rate rounds before capping to very slow
613pub const RESOURCE_VERY_SLOW_RATE_THRESHOLD: usize = 2;
614
615/// Fast rate threshold: 50 Kbps in bytes/sec = 50000 / 8 = 6250.0
616pub const RESOURCE_RATE_FAST: f64 = (50 * 1000) as f64 / 8.0;
617
618/// Very slow rate threshold: 2 Kbps in bytes/sec = 2000 / 8 = 250.0
619pub const RESOURCE_RATE_VERY_SLOW: f64 = (2 * 1000) as f64 / 8.0;
620
621/// Number of bytes in a map hash
622pub const RESOURCE_MAPHASH_LEN: usize = 4;
623
624/// Resource SDU = Packet.MDU (NOT ENCRYPTED_MDU)
625pub const RESOURCE_SDU: usize = MDU;
626
627/// Random hash size prepended to resource data
628pub const RESOURCE_RANDOM_HASH_SIZE: usize = 4;
629
630/// Maximum efficient resource size (1 MB - 1)
631pub const RESOURCE_MAX_EFFICIENT_SIZE: usize = 1024 * 1024 - 1;
632
633/// Maximum metadata size (16 MB - 1)
634pub const RESOURCE_METADATA_MAX_SIZE: usize = 16 * 1024 * 1024 - 1;
635
636/// Maximum auto-compress size (64 MB)
637pub const RESOURCE_AUTO_COMPRESS_MAX_SIZE: usize = 64 * 1024 * 1024;
638
639/// Part timeout factor (before RTT measured)
640pub const RESOURCE_PART_TIMEOUT_FACTOR: f64 = 4.0;
641
642/// Part timeout factor (after first RTT measured)
643pub const RESOURCE_PART_TIMEOUT_FACTOR_AFTER_RTT: f64 = 2.0;
644
645/// Proof timeout factor (reduced when awaiting proof)
646pub const RESOURCE_PROOF_TIMEOUT_FACTOR: f64 = 3.0;
647
648/// Additional wait allowance, in SDU-sized transfer units, while waiting for HMU data.
649pub const RESOURCE_HMU_WAIT_FACTOR: f64 = 3.5;
650
651/// Maximum retries for part transfers
652pub const RESOURCE_MAX_RETRIES: usize = 16;
653
654/// Maximum retries for advertisement
655pub const RESOURCE_MAX_ADV_RETRIES: usize = 4;
656
657/// Sender grace time (seconds)
658pub const RESOURCE_SENDER_GRACE_TIME: f64 = 10.0;
659
660/// Processing grace for advertisement response (seconds)
661pub const RESOURCE_PROCESSING_GRACE: f64 = 1.0;
662
663/// Retry grace time (seconds)
664pub const RESOURCE_RETRY_GRACE_TIME: f64 = 0.25;
665
666/// Per-retry delay (seconds)
667pub const RESOURCE_PER_RETRY_DELAY: f64 = 0.5;
668
669/// Maximum watchdog sleep interval (seconds)
670pub const RESOURCE_WATCHDOG_MAX_SLEEP: f64 = 1.0;
671
672/// Response max grace time (seconds)
673pub const RESOURCE_RESPONSE_MAX_GRACE_TIME: f64 = 10.0;
674
675/// Advertisement overhead in bytes (fixed msgpack overhead)
676pub const RESOURCE_ADVERTISEMENT_OVERHEAD: usize = 134;
677
678/// Maximum hashmap entries per advertisement segment
679/// = floor((LINK_MDU - ADVERTISEMENT_OVERHEAD) / MAPHASH_LEN)
680pub const RESOURCE_HASHMAP_MAX_LEN: usize =
681    (LINK_MDU - RESOURCE_ADVERTISEMENT_OVERHEAD) / RESOURCE_MAPHASH_LEN;
682
683/// Collision guard size = 2 * WINDOW_MAX + HASHMAP_MAX_LEN
684pub const RESOURCE_COLLISION_GUARD_SIZE: usize = 2 * RESOURCE_WINDOW_MAX + RESOURCE_HASHMAP_MAX_LEN;
685
686/// Hashmap not exhausted flag
687pub const RESOURCE_HASHMAP_IS_NOT_EXHAUSTED: u8 = 0x00;
688
689/// Hashmap exhausted flag
690pub const RESOURCE_HASHMAP_IS_EXHAUSTED: u8 = 0xFF;
691
692/// Hashmap empty flag: the advertisement carried no hashmap because it would
693/// not fit the link MDU, so the receiver asks for the first segment
694pub const RESOURCE_HASHMAP_IS_EMPTY: u8 = 0xFE;
695
696#[cfg(test)]
697mod tests {
698    use super::*;
699
700    #[test]
701    fn test_derived_constants() {
702        // MTU = 500
703        assert_eq!(MTU, 500);
704
705        // HEADER_MINSIZE = 2 + 1 + 16 = 19
706        assert_eq!(HEADER_MINSIZE, 19);
707
708        // HEADER_MAXSIZE = 2 + 1 + 32 = 35
709        assert_eq!(HEADER_MAXSIZE, 35);
710
711        // MDU = 500 - 35 - 1 = 464
712        assert_eq!(MDU, 464);
713
714        // ENCRYPTED_MDU = floor((464 - 48 - 32) / 16) * 16 - 1 = floor(384/16)*16 - 1 = 24*16 - 1 = 383
715        assert_eq!(ENCRYPTED_MDU, 383);
716
717        // PLAIN_MDU = MDU = 464
718        assert_eq!(PLAIN_MDU, 464);
719
720        // EXPL_LENGTH = 32 + 64 = 96
721        assert_eq!(EXPL_LENGTH, 96);
722
723        // IMPL_LENGTH = 64
724        assert_eq!(IMPL_LENGTH, 64);
725
726        // NAME_HASH_LENGTH = 80 bits = 10 bytes
727        assert_eq!(NAME_HASH_LENGTH / 8, 10);
728
729        // KEYSIZE = 512 bits = 64 bytes
730        assert_eq!(KEYSIZE / 8, 64);
731
732        // SIGLENGTH = 512 bits = 64 bytes
733        assert_eq!(SIGLENGTH / 8, 64);
734
735        // TRUNCATED_HASHLENGTH = 128 bits = 16 bytes
736        assert_eq!(TRUNCATED_HASHLENGTH / 8, 16);
737    }
738
739    #[test]
740    fn test_link_constants() {
741        assert_eq!(LINK_ECPUBSIZE, 64);
742        assert_eq!(LINK_MTU_SIZE, 3);
743        // LINK_MDU = floor((500 - 1 - 19 - 48) / 16) * 16 - 1 = floor(432/16)*16 - 1 = 27*16 - 1 = 431
744        assert_eq!(LINK_MDU, 431);
745        assert_eq!(CHANNEL_ENVELOPE_OVERHEAD, 6);
746        assert_eq!(STREAM_DATA_OVERHEAD, 8);
747        assert_eq!(STREAM_ID_MAX, 0x3FFF);
748    }
749
750    #[test]
751    fn test_transport_constants() {
752        // PATHFINDER_E = 7 days in seconds
753        assert_eq!(PATHFINDER_E, 60.0 * 60.0 * 24.0 * 7.0);
754
755        // AP_PATH_TIME = 1 day
756        assert_eq!(AP_PATH_TIME, 60.0 * 60.0 * 24.0);
757
758        // ROAMING_PATH_TIME = 6 hours
759        assert_eq!(ROAMING_PATH_TIME, 60.0 * 60.0 * 6.0);
760
761        assert_eq!(MODE_INTERNAL, 0x07);
762        assert!(DISCOVER_PATHS_FOR.contains(&MODE_INTERNAL));
763
764        // LINK_STALE_TIME = 2 * 360
765        assert_eq!(LINK_STALE_TIME, 720.0);
766
767        // LINK_TIMEOUT = STALE_TIME * 1.25
768        assert_eq!(LINK_TIMEOUT, LINK_STALE_TIME * 1.25);
769
770        // REVERSE_TIMEOUT = 8 minutes
771        assert_eq!(REVERSE_TIMEOUT, 8.0 * 60.0);
772
773        assert_eq!(PATH_REQUEST_GATE_TIMEOUT, 45.0);
774        assert_eq!(DISCOVERY_PR_TX_THROTTLE, 0.5);
775        assert_eq!(MAX_QUEUED_DISCOVERY_PRS, 32);
776        assert_eq!(MAX_PR_TAGS, 16_000);
777
778        // DESTINATION_TIMEOUT = 7 days
779        assert_eq!(DESTINATION_TIMEOUT, 60.0 * 60.0 * 24.0 * 7.0);
780    }
781
782    #[test]
783    fn test_resource_constants() {
784        // SDU = MDU = 464 (NOT ENCRYPTED_MDU)
785        assert_eq!(RESOURCE_SDU, 464);
786        assert_eq!(RESOURCE_SDU, MDU);
787
788        // FAST_RATE_THRESHOLD = WINDOW_MAX_SLOW - WINDOW - 2 = 10 - 4 - 2 = 4
789        assert_eq!(RESOURCE_FAST_RATE_THRESHOLD, 4);
790
791        // RATE_FAST = 50000 / 8 = 6250.0
792        assert_eq!(RESOURCE_RATE_FAST, 6250.0);
793
794        // RATE_VERY_SLOW = 2000 / 8 = 250.0
795        assert_eq!(RESOURCE_RATE_VERY_SLOW, 250.0);
796
797        // HASHMAP_MAX_LEN = floor((431 - 134) / 4) = floor(297/4) = 74
798        assert_eq!(RESOURCE_HASHMAP_MAX_LEN, 74);
799
800        // COLLISION_GUARD_SIZE = 2 * 75 + 74 = 224
801        assert_eq!(RESOURCE_COLLISION_GUARD_SIZE, 224);
802
803        // Window constants
804        assert_eq!(RESOURCE_WINDOW, 4);
805        assert_eq!(RESOURCE_WINDOW_MIN, 2);
806        assert_eq!(RESOURCE_WINDOW_MAX_SLOW, 10);
807        assert_eq!(RESOURCE_WINDOW_MAX_VERY_SLOW, 4);
808        assert_eq!(RESOURCE_WINDOW_MAX_FAST, 75);
809        assert_eq!(RESOURCE_WINDOW_MAX, 75);
810        assert_eq!(RESOURCE_WINDOW_FLEXIBILITY, 4);
811    }
812
813    #[test]
814    fn stamp_defaults_match_upstream_parameters() {
815        assert_eq!(STAMP_LENGTH, 14);
816        assert_eq!(EXT_HEADER_LENGTH, 2);
817        assert_eq!(EXT_PAYLOAD_LENGTH, 18);
818        assert_eq!(PR_EXT_PAYLOAD_LENGTH, 16);
819        assert_eq!(PATH_REQUEST_STAMPED_LENGTH, 64);
820        assert_eq!(EXT_PAYLOAD_MARKER, 0xC1);
821        assert_eq!(DESTINATION_STAMP_COST, StampCost::from_f64(23.5));
822        assert_eq!(DESTINATION_STAMP_VALUE, StampCost::from_f64(25.0));
823        assert_eq!(PATH_REQUEST_STAMP_COST, StampCost::from_f64(15.8));
824        assert_eq!(PATH_REQUEST_STAMP_VALUE, StampCost::from_f64(15.8));
825        assert_eq!(LINK_REQUEST_STAMP_COST, StampCost::from_f64(12.5));
826        assert_eq!(BLACKHOLE_LINK_COST, StampCost::from_f64(5.25));
827        assert_eq!(LINK_COST_CAP, 40);
828        assert_eq!(LINK_STAMP_TIMEOUT, 15);
829        assert_eq!(PATH_REQUEST_STAMP_TIMEOUT, 15);
830        assert_eq!(HOP_LIMITER, PATHFINDER_M);
831        // Generation, emission and enforcement stay opt-in in 1.6.0, except
832        // that link requests are stamped when the destination announced a
833        // cost (a8cc6cb2 turned STAMP_LRS back on).
834        let opt_in = [
835            STAMPS_ENFORCED,
836            LR_STAMPS_ENFORCED,
837            STAMP_DESTINATIONS,
838            STAMP_PRS,
839            ALWAYS_STAMP_LRS,
840        ];
841        assert_eq!((opt_in, STAMP_LRS), ([false; 5], true));
842    }
843}