From 137a166fe0b95ccdef444041e907358bd26bc226 Mon Sep 17 00:00:00 2001 From: Bitflash Date: Wed, 29 Jul 2026 12:05:12 -0300 Subject: Remove peer exchange from main; it was merged unreviewed (#27) PR #25 was opened, reviewed and approved as a single-file change to net.cpp: the .btf peer cache, +169/-0. I then pushed peer exchange and a /port option onto that same branch while the PR was open, which silently widened it to six files. The merge carried all of it in. Nothing about the peer exchange had been validated. Its two-node test never completed -- node B could not dial node A at all -- so what landed on main is unproven network protocol code that no one agreed to take. This restores main to what #25 actually described. The peer cache stays, with its runtime numbers (36.3s to 2.1s to first peer). #26 stays. What comes out: - the "btfpeers" message, both directions - BtfPexCollect / BtfPexAccept and the per-message caps - CNode::nLastPexRecv - the `desc` field on the cached peer record - BtfLocalDescriptor / BtfSecpContext - /port Peer exchange returns as its own pull request once its test passes. The /port option is worth having on its own -- two nodes cannot share a machine without it, which is what blocked the test -- and should go up separately rather than ride along again. Release v1.2.2 is unaffected: it was built from ba2de82, before either merge. --- src/main.cpp | 31 --------------- src/main_gui.cpp | 18 --------- src/net.cpp | 115 +------------------------------------------------------ src/net.h | 19 --------- src/nostr.cpp | 30 --------------- src/nostr.h | 8 ---- 6 files changed, 2 insertions(+), 219 deletions(-) (limited to 'src') diff --git a/src/main.cpp b/src/main.cpp index bb317d2..a75bf54 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1877,14 +1877,6 @@ 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()); } @@ -1896,29 +1888,6 @@ 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; diff --git a/src/main_gui.cpp b/src/main_gui.cpp index edc772d..842b0d5 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -58,7 +58,6 @@ static void PrintUsage() printf(" /connectbtf=PEER_BTF_ADDRESS\n"); printf(" /rvrelay=HOST:PORT\n"); printf(" /announcerelay=HOST:PORT\n"); - printf(" /port=N (P2P listen port, default 8433)\n"); printf("\n"); printf("Each option also accepts '-' instead of '/'.\n"); } @@ -114,23 +113,6 @@ static void ParseStartupArguments(int argc, char* argv[]) string announceRelay = argval2(argc, argv, "/announcerelay", "-announcerelay"); if (!announceRelay.empty()) strBtfAnnounceRelay = announceRelay; - - // net.cpp has claimed nListenPort was "tunable via /port" since it was - // written, but nothing ever read the option. Without it a second node - // cannot start on a machine that already runs one -- which is exactly what - // testing peer exchange needs. - string strPort = argval2(argc, argv, "/port", "-port"); - if (!strPort.empty()) - { - int nPort = atoi(strPort.c_str()); - if (nPort <= 0 || nPort > 65535) - fprintf(stderr, "Ignoring /port=%s: not a port number\n", strPort.c_str()); - else - { - nListenPort = htons((unsigned short)nPort); - addrLocalHost.port = nListenPort; // or we advertise a port we never bound - } - } } int main(int argc, char* argv[]) diff --git a/src/net.cpp b/src/net.cpp index dfeb4a2..7b2ef83 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -285,13 +285,6 @@ struct CachedBtfPeer string meeting; string encHex; int64 lastSeen; - // The peer's own signed descriptor, as it announced itself. Kept verbatim - // because that signature is what lets us hand this peer on to somebody - // else: the receiver checks it against the key the address decodes to and - // never has to take our word for anything. Empty for entries learned - // before peer exchange existed, or resolved through Nostr -- those are - // still dialable, just not relayable. - string desc; }; static string BtfPeerCachePath() { return GetAppDir() + "/btfpeers.json"; } @@ -343,8 +336,6 @@ void LoadCachedBtfPeers(vector& out) p.meeting = o["meeting"].get(); p.encHex = o["enc"].get(); p.lastSeen = o.value("seen", (int64)0); - if (o.contains("desc") && o["desc"].is_string()) - p.desc = o["desc"].get(); if (nNow - p.lastSeen > CACHED_BTF_PEER_TTL) continue; // long gone out.push_back(p); } @@ -358,35 +349,22 @@ void LoadCachedBtfPeers(vector& out) static CCriticalSection cs_btfPeerCache; -// strDesc is the peer's own signed descriptor when we have it (it announced -// itself over peer exchange), "" when we only resolved it through Nostr. An -// empty one never clears a descriptor already on file: dialing a peer we first -// learned about by exchange must not cost us the ability to pass it on. static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, - const unsigned char enc_pub[32], - const string& strDesc = string()) + const unsigned char enc_pub[32]) { CRITICAL_BLOCK(cs_btfPeerCache) { vector peers; LoadCachedBtfPeers(peers); - string strKeepDesc = strDesc; for (size_t i = 0; i < peers.size(); i++) - if (peers[i].btfAddr == strBtfAddr) - { - if (strKeepDesc.empty()) - strKeepDesc = peers[i].desc; - peers.erase(peers.begin() + i); - break; - } + if (peers[i].btfAddr == strBtfAddr) { peers.erase(peers.begin() + i); break; } CachedBtfPeer p; p.btfAddr = strBtfAddr; p.meeting = strMeeting; p.encHex = BytesToHex(enc_pub, 32); p.lastSeen = GetTime(); - p.desc = strKeepDesc; peers.insert(peers.begin(), p); // most recent first if (peers.size() > MAX_CACHED_BTF_PEERS) @@ -400,8 +378,6 @@ static void RememberBtfPeer(const string& strBtfAddr, const string& strMeeting, o["meeting"] = q.meeting; o["enc"] = q.encHex; o["seen"] = q.lastSeen; - if (!q.desc.empty()) - o["desc"] = q.desc; arr.push_back(o); } FILE* f = fopen(BtfPeerCachePath().c_str(), "w"); @@ -443,93 +419,6 @@ void ThreadReconnectCachedBtfPeers(void* parg) nConnected, peers.size()); } -// -// .btf peer exchange -// -// Until now discovery 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. -// -// What travels here is each peer's own signed descriptor. The receiver checks -// the Schnorr signature against the key the `.btf` address decodes to, so the -// sender is trusted for nothing -- forging an entry would need somebody else's -// secret key. That leaves flooding: valid descriptors for keys the sender -// generated itself. The caps below bound one message, and the cache prefers -// peers that actually answered, so a flood costs a Sybil more than it costs us. -// - -void BtfPexCollect(vector& vDescOut) -{ - vDescOut.clear(); - - // Ours first -- the node on the other end may know nobody but us. - string strMine = BtfLocalDescriptor(); - if (!strMine.empty()) - vDescOut.push_back(strMine); - - vector peers; - CRITICAL_BLOCK(cs_btfPeerCache) - LoadCachedBtfPeers(peers); - - // Most recently seen first: these answered us, they are not names copied - // off a relay listing. - foreach(const CachedBtfPeer& p, peers) - { - if (vDescOut.size() >= MAX_PEX_DESCRIPTORS) break; - if (p.desc.empty()) continue; // nothing provable to pass on - if (p.desc.size() > MAX_PEX_DESCRIPTOR_BYTES) continue; - vDescOut.push_back(p.desc); - } -} - -int BtfPexAccept(const vector& vDesc) -{ - void* ctx = BtfSecpContext(); - if (!ctx) - return 0; - - string strSelf = BtfLocalAddress(); - int nKept = 0; - unsigned int nSeen = 0; - - foreach(const string& strDesc, vDesc) - { - if (++nSeen > MAX_PEX_DESCRIPTORS) break; - if (strDesc.empty() || strDesc.size() > MAX_PEX_DESCRIPTOR_BYTES) continue; - - // The descriptor names the key it belongs to, and taking that key from - // the blob is safe precisely because the signature must verify under - // it: nobody can produce a valid descriptor for a key they don't hold. - unsigned char pubkey[32]; - try - { - nlohmann::json v = nlohmann::json::parse(strDesc); - if (!v.is_object() || !v.contains("pubkey") || !v["pubkey"].is_string()) - continue; - if (!HexToBytes(v["pubkey"].get(), pubkey, 32)) - continue; - } - catch (...) { continue; } // malformed JSON from an untrusted peer - - btf::Descriptor d; - if (!btf::VerifyDescriptor(ctx, strDesc, pubkey, d)) - continue; - - string strAddr = btf::Address(pubkey); - if (strAddr.empty() || strAddr == strSelf) - continue; // ourselves, nothing to learn - - unsigned char enc[32]; - if (!HexToBytes(d.enc, enc, 32)) - continue; - - RememberBtfPeer(strAddr, d.meeting_node, enc, strDesc); - nKept++; - } - return nKept; -} - static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char pk[32], const string& strMeeting, const unsigned char enc_pub[32]) { diff --git a/src/net.h b/src/net.h index 7d70721..f028c3b 100644 --- a/src/net.h +++ b/src/net.h @@ -29,21 +29,6 @@ CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting void ThreadBtfAccept(void* parg); void ThreadBtfConnect(void* parg); extern string strBtfConnect; - -// --- .btf peer exchange --------------------------------------------------- -// Peers gossip signed descriptors so discovery survives a relay outage. Both -// caps apply to a single "btfpeers" message; the generic header limit is 256 MB, -// which is no protection at all for something a stranger can send unprompted. -static const unsigned int MAX_PEX_DESCRIPTORS = 20; -static const unsigned int MAX_PEX_DESCRIPTOR_BYTES = 1024; -// Ignore a peer's exchange more often than this. One announcement per side per -// connection is the intended traffic; anything faster is someone else's idea. -static const int64 PEX_MIN_INTERVAL = 60; - -// Descriptors to hand a peer: ours first, then peers that answered us. -void BtfPexCollect(std::vector& vDescOut); -// Verify descriptors a peer sent and remember the good ones. Returns how many. -int BtfPexAccept(const std::vector& vDesc); void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); bool AnySubscribed(unsigned int nChannel); void ThreadBitcoinMiner(void* parg); @@ -482,9 +467,6 @@ public: // publish and subscription vector vfSubscribe; - // Last "btfpeers" we accepted from this node, to rate-limit the exchange. - int64 nLastPexRecv; - CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) { @@ -501,7 +483,6 @@ public: fDisconnect = false; nRefCount = 0; nReleaseTime = 0; - nLastPexRecv = 0; vfSubscribe.assign(256, false); // Push a version message diff --git a/src/nostr.cpp b/src/nostr.cpp index 482a835..ec02c2e 100644 --- a/src/nostr.cpp +++ b/src/nostr.cpp @@ -342,36 +342,6 @@ std::string BtfLocalAddress() return g_nostrKey.BtfAddress(); } -// This node's own descriptor, signed and self-certifying -- the same blob -// PublishDescriptor puts on Nostr, but handed straight to a peer instead. -// -// Reads strBtfActiveRelay directly rather than going through BtfActiveRelay(): -// only a rendezvous we actually registered at is worth announcing. Handing out -// a meeting node we are not reachable at would make every peer that receives -// it burn a dial, and over peer exchange that mistake travels further than it -// does over Nostr. -std::string BtfLocalDescriptor() -{ - if (!EnsureNostrKey()) - return ""; - string meeting; - CRITICAL_BLOCK(cs_activeRelay) - meeting = strBtfActiveRelay; - if (meeting.empty()) - return ""; - return btf::SignDescriptor(g_nostrKey.ctx, g_nostrKey.seckey, - g_nostrKey.EncPubHex(), meeting, (uint64_t)GetTime()); -} - -// The shared secp256k1 context, so net.cpp can verify descriptors that arrive -// from peers. NULL if the identity could not be loaded. -void* BtfSecpContext() -{ - if (!EnsureNostrKey()) - return NULL; - return (void*)g_nostrKey.ctx; -} - int GetDiscoveredPeerCount() { int n = 0; diff --git a/src/nostr.h b/src/nostr.h index b5eb5e3..04149e3 100644 --- a/src/nostr.h +++ b/src/nostr.h @@ -82,14 +82,6 @@ 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