From 41b2ba748531605a1afd84fdb967330c0efc529f Mon Sep 17 00:00:00 2001 From: Bitflash Date: Wed, 29 Jul 2026 14:11:58 -0300 Subject: net: exchange signed .btf descriptors between peers (#32) 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. --- src/main.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'src/main.cpp') diff --git a/src/main.cpp b/src/main.cpp index a75bf54..bb317d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1877,6 +1877,14 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) pfrom->PushMessage("getblocks", CBlockLocator(pindexBest), uint256(0)); } + // Introduce ourselves and hand over the .btf peers that answered us. + // Everything in here is self-certifying, so this is safe to send to a + // node we know nothing about, and safe for it to act on. + vector vPexOut; + BtfPexCollect(vPexOut); + if (!vPexOut.empty()) + pfrom->PushMessage("btfpeers", vPexOut); + if (LogAcceptsCategory("net")) printf("version addrMe = %s\n", addrMe.ToString().c_str()); } @@ -1888,6 +1896,29 @@ bool ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv) } + else if (strCommand == "btfpeers") + { + // Peer exchange. Nothing here is taken on trust: BtfPexAccept checks + // every descriptor's signature against the key its .btf address + // decodes to, and drops the rest. + int64 nNow = GetTime(); + if (nNow - pfrom->nLastPexRecv < PEX_MIN_INTERVAL) + { + if (LogAcceptsCategory("net")) + printf("btfpeers: ignoring, %s is sending them too fast\n", + pfrom->addr.ToString().c_str()); + return true; + } + pfrom->nLastPexRecv = nNow; + + vector vDesc; + vRecv >> vDesc; + int nKept = BtfPexAccept(vDesc); + LogPrint("net", "btfpeers: kept %d of %zu descriptor(s) from %s\n", + nKept, vDesc.size(), pfrom->addr.ToString().c_str()); + } + + else if (strCommand == "inv") { vector vInv; -- cgit v1.2.3