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/net.cpp | 115 ++---------------------------------------------------------- 1 file changed, 2 insertions(+), 113 deletions(-) (limited to 'src/net.cpp') 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]) { -- cgit v1.2.3