Add NodeDiscovery: multi-hop peer crawl with UDP-ping preference

- Implement NodeDiscovery engine: known-peer table (DynArr) with a
  per-tick seed/ping/query/connect state machine driven by the node
  maintenance thread; bounded multi-hop crawl (FANOUT peers per node,
  hop-capped) that connects to reachable peers lowest-ping-first
- Add GET_PEERS/PEERS TCP opcodes for peer-list exchange, handled on
  both inbound and outbound connections
- Measure UDP round-trip time and pass it to the on_pong callback
  (previously the send timestamp was only used for retries)
- Advertise each node's listen port in HELLO/ACK_HELLO and store it
  per-connection, so inbound-only peers and non-default ports are
  discoverable (length-guarded parse; wire-compatible with old peers)
- Wire a udp_node_t + node_discovery_t into net_node_t: init/start in
  Node_Create, tick in the maintenance loop, teardown in Node_Destroy
  (stop UDP before destroying discovery to avoid callback races)
- Add Node_ConnListenEndpoint / Node_GetPeerEndpoints helpers to derive
  peers' listen endpoints (outbound: dialed port; inbound: advertised),
  with IPv4-mapped-IPv6 normalization and IP+port dedup
- Match ping pong/timeout callbacks by peer address (the UDP layer owns
  the nonce), fixing discovered peers stuck UNREACHABLE
- Ping the peer's listen port instead of the ephemeral TCP source port
  (fixes the original stub so pongs actually return)
- Add `peers` CLI command to dump the discovery table (endpoint/hop/
  state/ping)
- Add discovery tunables to constants.h (fanout, max hops, target
  connections, timeouts, caps)
This commit is contained in:
2026-07-22 23:18:57 +02:00
parent 1345ff7fa6
commit 21fe73fb01
12 changed files with 1234 additions and 10 deletions
+12
View File
@@ -13,6 +13,18 @@
#define MAX_CONS 32 // Some baseline for now
#define LISTEN_PORT 9393
#define ECHO_PEERS 1 // If non-zero, automatically attempt to connect back to any inbound peers (helps form bidirectional peering)
// Node discovery
#define DISCOVERY_FANOUT 2 // "A couple" - how many peers to query per round, and how many new peers to accept per PEERS response (keeps the crawl spread out)
#define DISCOVERY_MAX_HOPS 3 // How many hops away from us we keep crawling
#define DISCOVERY_TARGET_CONNECTIONS 8 // Desired outbound connection count discovery tries to reach (bounded by MAX_CONS)
#define DISCOVERY_MAX_KNOWN_PEERS 256 // Cap on the known-peer table size
#define DISCOVERY_PEERS_RESPONSE_CAP 8 // Max endpoints we put in a single PEERS response
#define DISCOVERY_MAX_PINGS_PER_TICK 8 // Cap on UDP pings sent per discovery tick
#define DISCOVERY_PING_TIMEOUT_MS 5000ULL // Backstop: a PINGED peer with no pong for this long is marked unreachable
#define DISCOVERY_PING_REFRESH_MS 60000ULL // Re-ping a reachable peer after this long to refresh its latency
#define DISCOVERY_QUERY_INTERVAL_MS 15000ULL // Minimum interval between GET_PEERS to the same peer
#define DISCOVERY_CONNECT_RETRY_MS 30000ULL // Minimum interval between connect attempts to the same discovered peer
#define TCP_THREAD_STACK_SIZE (512 * 1024) // 512 KB. We could get away with like 128 KB since it's mostly just recv bufs, but it's good having some breathing room.
// This is also for client threads. The server has the default (~8 MB on POSIX).