dig_pex/lib.rs
1//! # dig-pex — Peer Exchange (PEX) for the DIG Node peer network
2//!
3//! PEX lets a participant that already holds an **authenticated** link to another participant tell
4//! it, incrementally, which peers it knows **first-hand** — so the network's address books stay warm
5//! without polling and without a central directory. It adapts the proven mechanics of BitTorrent PEX
6//! (`ut_pex`): peers exchange **deltas of their first-hand known-peer set** over already-established
7//! connections, on a bounded periodic cadence, with hard per-message caps and **no third-party
8//! re-flooding**. This crate is the normative implementation of `SPEC.md` (wire version `1`).
9//!
10//! PEX runs in exactly two places (SPEC §1.1):
11//!
12//! 1. **Node ↔ Node** — over the mutual-TLS dig-nat mux stream transport ([`PexMessage::encode`] /
13//! [`PexMessage::decode`], a u32-BE length prefix + JSON body).
14//! 2. **Relay → Node** — by the `dig-relay` introducer, riding the existing `RelayMessage`
15//! WebSocket (RLY-008) as bare JSON text frames ([`PexMessage::to_json`] /
16//! [`PexMessage::from_json`]).
17//!
18//! ## What it is *not* (SPEC §1.3)
19//!
20//! - **Not a trust channel.** Every received entry is a *hint* — a candidate to dial and verify via
21//! the mTLS handshake, never an authenticated fact ([`PexEvent::Candidates`]).
22//! - **Not a gossip flood.** A participant advertises only what it knows **first-hand**; the
23//! [`Provenance`] type has no `"pex"` token, so a PEX-learned entry can never be re-advertised
24//! until independently verified.
25//! - **Not a payment authority.** An entry MAY carry a self-signed payment address
26//! ([`PeerEntry::verified_payment_address`], SPEC §3.4) so the incentive layer can pay the peer
27//! that earned it. The claim proves itself — it carries the peer's TLS SPKI and a signature this
28//! crate binds to `peer_id` — but PEX neither holds keys nor moves money.
29//! - **Not content discovery.** Locating which peers hold content is the DHT's job (`dig-dht`); PEX
30//! populates the pool of dialable peers underneath it.
31//!
32//! ## The engine (SPEC Appendix A)
33//!
34//! The crate ships a transport-agnostic, **sans-IO** [`PexEngine`]: you feed it link events, inbound
35//! messages, local peer-set changes, and clock ticks; it returns the messages to send and the events
36//! to act on. Both a DIG Node and the relay embed the same engine — only the I/O adapter differs.
37//!
38//! ```
39//! use dig_pex::{PexConfig, PexEngine, PexMessage, PeerEntry, Provenance, Address};
40//!
41//! let me = "a".repeat(64);
42//! let peer = "b".repeat(64);
43//! let mut engine = PexEngine::new(PexConfig::new(me, "mainnet").with_jitter(false));
44//!
45//! // A first-hand peer we know enters our advertise set.
46//! engine.upsert_known(
47//! PeerEntry::new("c".repeat(64), "mainnet", 1_000, Provenance::Direct)
48//! .with_address(Address::direct("203.0.113.7", 9444)),
49//! );
50//!
51//! // A link comes up → we emit our handshake + a snapshot of our first-hand set.
52//! let out = engine.link_up(&peer, 1_000_000);
53//! assert!(matches!(out[0], PexMessage::PexHandshake { .. }));
54//! assert!(matches!(out[1], PexMessage::PexSnapshot { .. }));
55//! ```
56//!
57//! ### DIG Node embedding (node↔node, SPEC §10.1)
58//!
59//! On each established peer connection call [`PexEngine::link_up`] and write the returned frames on a
60//! freshly opened mux stream (that stream is your sending direction). Feed each decoded inbound
61//! message to [`PexEngine::on_message`]; send its replies and honor a muting
62//! [`PexEvent::Violation`]. Drive [`PexEngine::tick`] ~1/s and write the returned deltas. Feed
63//! first-hand knowledge back with [`PexEngine::upsert_known`] / [`PexEngine::remove_known`], and on
64//! close call [`PexEngine::link_down`]. Route [`PexEvent::Candidates`] into the dig-gossip
65//! `AddressManager` as new-table candidates to dial + verify (SPEC §9.3).
66//!
67//! ### dig-relay embedding (relay→node, SPEC §10.2)
68//!
69//! Create one engine for the introducer role (flags `["introducer"]`). Only after a registered
70//! connection sends its `pex_handshake` do you [`PexEngine::link_up`] + [`PexEngine::on_message`] and
71//! reply as WebSocket text frames. Mirror the registry into the engine
72//! ([`PexEngine::upsert_known`] on register, [`PexEngine::remove_known`] on unregister); **never**
73//! fold inbound node PEX data into the registry — discard node-sent [`PexEvent::Candidates`] (the
74//! registry is registration-backed only, SPEC §10.2).
75
76#![forbid(unsafe_code)]
77#![warn(missing_docs)]
78
79pub mod caps;
80pub mod engine;
81pub mod entry;
82pub mod error;
83pub mod payment;
84pub mod state;
85pub mod timer;
86pub mod wire;
87
88pub use caps::{
89 PEX_ARRIVAL_GRACE, PEX_DEFAULT_INTERVAL, PEX_MAX_ADDED, PEX_MAX_ADDRESSES, PEX_MAX_DROPPED,
90 PEX_MAX_ENTRY_AGE, PEX_MAX_FLAGS, PEX_MAX_FLAG_LEN, PEX_MAX_FRAME, PEX_MAX_INTERVAL,
91 PEX_MAX_SNAPSHOT, PEX_MIN_INTERVAL, PEX_VERSION, PEX_VIOLATION_LIMIT,
92};
93pub use engine::{PexConfig, PexEngine, PexEvent, PexOutcome};
94pub use entry::{Address, AddressKind, PeerEntry, Provenance, ValidateCtx};
95pub use error::{EntrySkip, PexErrorCode};
96pub use payment::{
97 payment_signing_bytes, peer_id_for_spki, PaymentClaim, PaymentClaimError, SignatureVerifier,
98 PEX_MAX_PAYMENT_ADDRESS_LEN, PEX_MAX_PAYMENT_SIG_LEN, PEX_MAX_PAYMENT_SPKI_LEN,
99};
100pub use state::{LinkState, RecvPhase};
101pub use wire::PexMessage;