From 96a8e42bbc313f96d54377ae5886da84298e61a3 Mon Sep 17 00:00:00 2001 From: Bitflash Date: Wed, 29 Jul 2026 11:52:12 -0300 Subject: Remember .btf peers that answered, and dial them first on start (#25) * net: remember .btf peers that answered, and dial them first on start Discovery depends entirely on the Nostr relays. A node that has been up for hours knows exactly which peers respond, and throws all of it away on exit; the next start reaches for the relays again and walks the same descriptor list, of which roughly 96% is dead. That is what makes a restart take minutes before the first tunnel opens -- measured today at about seven. Every peer that completes a tunnel is now written to btfpeers.json with its meeting node and encryption key, which is everything needed to redial it. On start a thread tries those before the relays have answered anything, stopping once eight are connected. Entries older than a week are dropped, and the file holds at most fifty. Cache only: nothing here changes how a first-ever run bootstraps, and a missing or corrupt file just falls through to relay discovery. * net: exchange signed .btf descriptors between peers Discovery has depended entirely on the Nostr relays. Two connected nodes never told each other who else existed, so a relay outage left a running network unable to grow, and a node that found one stale peer had no way to learn there was anything better. The old `addr` gossip went out with the IRC seed. Peers now trade signed descriptors over a new "btfpeers" message: our own first, then peers that actually answered us. The receiver checks each Schnorr signature against the key the `.btf` address decodes to, so the sender is trusted for nothing -- forging an entry needs somebody else's secret key. That leaves flooding with self-generated keys, which the per-message caps and the "peers that answered" ordering are there to blunt. Unknown commands are ignored (main.cpp), so nodes without this just drop the message; nothing here is a protocol break. The cache from the peer-cache change now keeps each peer's descriptor verbatim alongside the parsed fields, because that signature is what makes a peer relayable. Entries without one -- resolved through Nostr, or written before this -- stay dialable, just not passed on. A dial never clears a descriptor already on file. * main_gui: implement /port, which net.cpp already documented net.cpp has said nListenPort is "tunable via /port" since it was written, but nothing ever parsed the option. Two nodes could not share a machine, which is exactly what testing peer exchange needs. Sets addrLocalHost.port too, so we do not advertise a port we never bound. --- src/nostr.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/nostr.h') diff --git a/src/nostr.h b/src/nostr.h index 04149e3..b5eb5e3 100644 --- a/src/nostr.h +++ b/src/nostr.h @@ -82,6 +82,14 @@ bool BtfGetIdentity(unsigned char pubkey[32], unsigned char enc_sk[32]); // This node's printable .btf address, or "" if the identity failed to load. std::string BtfLocalAddress(); +// This node's own signed descriptor, ready to hand to a peer over the wire. +// "" until ThreadBtfAccept has registered at a rendezvous. +std::string BtfLocalDescriptor(); + +// The shared secp256k1 context, for btf::VerifyDescriptor (which takes it as +// void* to keep headers free of secp). NULL if the identity failed to load. +void* BtfSecpContext(); + // Resolve a .btf address via the public discovery relays: fetch the owner's // self-certified descriptor and return its meeting node ("host:port") and // x25519 public key for the end-to-end channel. -- cgit v1.2.3