Skip to main content

dig_pex/
caps.rs

1//! The frozen version-1 protocol constants and the message-level cap checks (SPEC §7.1, §7.2).
2//!
3//! These are the **wire contract** — an implementation is conformant only if it enforces exactly
4//! these values. Senders MUST never exceed the list caps; receivers MUST reject (never truncate) an
5//! over-cap message with a violation strike. The [`PexEngine`](crate::PexEngine) enforces both
6//! directions: it caps its own outgoing messages, and it rejects over-cap inbound ones.
7
8/// The PEX wire version this crate implements (SPEC §7.1).
9pub const PEX_VERSION: u32 = 1;
10
11/// Maximum entries in a `pex_delta.added` list.
12pub const PEX_MAX_ADDED: usize = 50;
13
14/// Maximum ids in a `pex_delta.dropped` list.
15pub const PEX_MAX_DROPPED: usize = 50;
16
17/// Maximum entries in a `pex_snapshot.peers` list.
18pub const PEX_MAX_SNAPSHOT: usize = 200;
19
20/// Maximum `addresses` per peer entry.
21pub const PEX_MAX_ADDRESSES: usize = 8;
22
23/// Maximum `flags` per peer entry (and per handshake).
24pub const PEX_MAX_FLAGS: usize = 8;
25
26/// Maximum characters per flag token.
27pub const PEX_MAX_FLAG_LEN: usize = 32;
28
29/// Maximum message body bytes (256 KiB) — matches the DHT / dig-nat wire bound. A frame claiming a
30/// larger body MUST be rejected before allocating or reading the body (SPEC §4.1, §7.2).
31pub const PEX_MAX_FRAME: usize = 262_144;
32
33/// Default declared send interval, in seconds (SPEC §6.2).
34pub const PEX_DEFAULT_INTERVAL: u32 = 60;
35
36/// Hard interval floor, in seconds — a sender MUST NOT declare (nor be enforced) below this.
37pub const PEX_MIN_INTERVAL: u32 = 30;
38
39/// Interval ceiling for declarations, in seconds.
40pub const PEX_MAX_INTERVAL: u32 = 3600;
41
42/// The receiver's enforcement tolerance, in seconds — absorbs scheduling + clock skew (SPEC §6.4).
43pub const PEX_ARRIVAL_GRACE: u32 = 5;
44
45/// Maximum `last_seen` age (seconds) an entry may be advertised with; older entries are not
46/// advertised (sender) and skipped on receive (SPEC §3.3, §8.2).
47pub const PEX_MAX_ENTRY_AGE: u64 = 1800;
48
49/// Strikes on a direction before it is muted / the peer may be disconnected (SPEC §7.1, §11.2).
50pub const PEX_VIOLATION_LIMIT: u32 = 3;
51
52/// Hard ceiling on a single link's `received` accumulator (SPEC §9.2, §11.3) — the set of `peer_id`s
53/// that link has told us, kept for `dropped` attribution. Bounds per-link memory from an authenticated
54/// peer that streams an unbounded number of distinct fresh `peer_id`s over the link's lifetime; a
55/// single message is already capped by [`PEX_MAX_ADDED`]/[`PEX_MAX_SNAPSHOT`], but that does not bound
56/// the cumulative total across many messages. Oldest-`last_seen` entries are evicted first once the
57/// cap is reached.
58pub const PEX_MAX_RECEIVED_PER_LINK: usize = 4096;
59
60/// Hard ceiling on the engine-global `hints` map (SPEC §9.2, §11.3) — the deduplicated best hint per
61/// `peer_id` across all links. Bounds total memory when many links each contribute distinct
62/// `peer_id`s. Oldest-`last_seen` entries are evicted first once the cap is reached.
63pub const PEX_MAX_HINTS: usize = 16_384;
64
65/// Whether a frame body of `len` bytes is within [`PEX_MAX_FRAME`]. A caller MUST check this
66/// **before** allocating or reading the body (the stream binding checks the length prefix; the relay
67/// binding checks the WebSocket payload length).
68#[must_use]
69pub fn frame_within_bound(len: usize) -> bool {
70    len <= PEX_MAX_FRAME
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn frozen_v1_constants() {
79        // These values are the wire contract (SPEC §7.1) — pinned so a change is a deliberate,
80        // reviewed protocol event, never an accident.
81        assert_eq!(PEX_VERSION, 1);
82        assert_eq!(PEX_MAX_ADDED, 50);
83        assert_eq!(PEX_MAX_DROPPED, 50);
84        assert_eq!(PEX_MAX_SNAPSHOT, 200);
85        assert_eq!(PEX_MAX_ADDRESSES, 8);
86        assert_eq!(PEX_MAX_FLAGS, 8);
87        assert_eq!(PEX_MAX_FLAG_LEN, 32);
88        assert_eq!(PEX_MAX_FRAME, 262_144);
89        assert_eq!(PEX_DEFAULT_INTERVAL, 60);
90        assert_eq!(PEX_MIN_INTERVAL, 30);
91        assert_eq!(PEX_MAX_INTERVAL, 3600);
92        assert_eq!(PEX_ARRIVAL_GRACE, 5);
93        assert_eq!(PEX_MAX_ENTRY_AGE, 1800);
94        assert_eq!(PEX_VIOLATION_LIMIT, 3);
95        assert_eq!(PEX_MAX_RECEIVED_PER_LINK, 4096);
96        assert_eq!(PEX_MAX_HINTS, 16_384);
97    }
98
99    #[test]
100    fn frame_bound_edges() {
101        assert!(frame_within_bound(0));
102        assert!(frame_within_bound(PEX_MAX_FRAME));
103        assert!(!frame_within_bound(PEX_MAX_FRAME + 1));
104    }
105}