diff options
| author | Bitflash <btf01@proton.me> | 2026-07-29 11:51:37 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-29 11:51:37 -0300 |
| commit | 7f6fdbab6c90360ab3b2d391a7b5aedc6f9721ac (patch) | |
| tree | 4a5504cb39d72d15b85e1a05f2493f6fa04b2eb3 /src | |
| parent | ba2de82b76aeeabe1c45877e02b0e8fe5b59bed3 (diff) | |
nostr: never advertise a rendezvous we are not registered at (#26)
BtfActiveRelay() fell back to vBtfMeetingRelays[0] whenever registration had
not completed yet. ThreadNostrSeed and ThreadBtfAccept start in parallel
(net.cpp), so at boot that fallback was exactly what PublishDescriptor wrote
into the descriptor -- including the one-time self-test, which publishes to
every relay in the list. Peers reading it dialed a rendezvous this node was
never registered at, and whenever the first seed happened to be down
(92.246.128.180 was, for part of today) every one of those dials was wasted.
BtfActiveRelay() now returns only the relay ThreadBtfAccept confirmed, and
PublishDescriptor skips publishing until there is one. The seed loop
republishes on every pass, so the only cost is the delay until registration
succeeds; the self-test waits up to 60s for it so it still measures the
publish/resolve chain instead of losing the race against it.
Also fix the WebSocket handshake reader. On headers past the cap it did a
bare break, leaving the socket parked in the middle of the header block --
and since the status line had already been read, the " 101 " check still
passed, so the caller got a "connected" socket whose next bytes were
leftover HTTP that the frame parser then read as garbage frames. It now
errors out instead, and the cap is 8192 because relays behind Cloudflare
send large header blocks.
Diffstat (limited to 'src')
| -rw-r--r-- | src/nostr.cpp | 36 | ||||
| -rw-r--r-- | src/nostr.h | 3 |
2 files changed, 32 insertions, 7 deletions
diff --git a/src/nostr.cpp b/src/nostr.cpp index 6f3b6a7..ec02c2e 100644 --- a/src/nostr.cpp +++ b/src/nostr.cpp | |||
| @@ -100,13 +100,17 @@ vector<string> vBtfMeetingRelays = { | |||
| 100 | string strBtfActiveRelay; | 100 | string strBtfActiveRelay; |
| 101 | static CCriticalSection cs_activeRelay; | 101 | static CCriticalSection cs_activeRelay; |
| 102 | 102 | ||
| 103 | // Only the rendezvous we actually registered at (BtfSetActiveRelay, called from | ||
| 104 | // ThreadBtfAccept once registration succeeds). Empty until then, on purpose: it | ||
| 105 | // used to fall back to vBtfMeetingRelays[0], and since ThreadNostrSeed and | ||
| 106 | // ThreadBtfAccept start in parallel (net.cpp), that fallback is what got | ||
| 107 | // published at boot -- a descriptor telling peers to dial a relay we were not | ||
| 108 | // registered at. When that first seed was down, every peer that read the | ||
| 109 | // descriptor burned a dial on a dead rendezvous. | ||
| 103 | string BtfActiveRelay() | 110 | string BtfActiveRelay() |
| 104 | { | 111 | { |
| 105 | CRITICAL_BLOCK(cs_activeRelay) | 112 | CRITICAL_BLOCK(cs_activeRelay) |
| 106 | if (!strBtfActiveRelay.empty()) | 113 | return strBtfActiveRelay; |
| 107 | return strBtfActiveRelay; | ||
| 108 | if (!vBtfMeetingRelays.empty()) | ||
| 109 | return vBtfMeetingRelays[0]; | ||
| 110 | return ""; | 114 | return ""; |
| 111 | } | 115 | } |
| 112 | 116 | ||
| @@ -542,7 +546,13 @@ public: | |||
| 542 | int n = RawRead(&c, 1); | 546 | int n = RawRead(&c, 1); |
| 543 | if (n <= 0) return error("Nostr: no handshake response from %s", host.c_str()); | 547 | if (n <= 0) return error("Nostr: no handshake response from %s", host.c_str()); |
| 544 | resp += c; | 548 | resp += c; |
| 545 | if (resp.size() > 4096) break; | 549 | // Bail out instead of breaking: breaking left the socket parked in |
| 550 | // the middle of the header block, and since the status line is read | ||
| 551 | // first the " 101 " check below still passed -- so the caller got a | ||
| 552 | // "connected" socket whose next bytes were leftover HTTP, which the | ||
| 553 | // frame parser then read as garbage frames. | ||
| 554 | if (resp.size() > 8192) | ||
| 555 | return error("Nostr: handshake headers too large from %s", host.c_str()); | ||
| 546 | } | 556 | } |
| 547 | if (resp.find(" 101 ") == string::npos) | 557 | if (resp.find(" 101 ") == string::npos) |
| 548 | return error("Nostr: handshake refused by %s", host.c_str()); | 558 | return error("Nostr: handshake refused by %s", host.c_str()); |
| @@ -749,7 +759,14 @@ static void PublishDescriptor(CWebSocket& ws, CNostrKey& key) | |||
| 749 | // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. | 759 | // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. |
| 750 | string meeting_node = BtfActiveRelay(); | 760 | string meeting_node = BtfActiveRelay(); |
| 751 | if (meeting_node.empty()) | 761 | if (meeting_node.empty()) |
| 752 | meeting_node = "rendezvous-pending"; | 762 | { |
| 763 | // Not registered anywhere yet. Publishing now would advertise a meeting | ||
| 764 | // node we can't be reached at, and peers would waste dials on it. The | ||
| 765 | // ThreadNostrSeed loop republishes on every pass, so skipping costs | ||
| 766 | // nothing but the delay until ThreadBtfAccept registers. | ||
| 767 | LogPrint("nostr", "Nostr: skipping descriptor publish, no rendezvous registered yet\n"); | ||
| 768 | return; | ||
| 769 | } | ||
| 753 | string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), | 770 | string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), |
| 754 | meeting_node, (uint64_t)GetTime()); | 771 | meeting_node, (uint64_t)GetTime()); |
| 755 | if (desc.empty()) | 772 | if (desc.empty()) |
| @@ -1564,6 +1581,13 @@ void ThreadNostrSeed(void* parg) | |||
| 1564 | LogPrint("nostr", "Nostr: node pubkey = %s\n", key.PubKeyHex().c_str()); | 1581 | LogPrint("nostr", "Nostr: node pubkey = %s\n", key.PubKeyHex().c_str()); |
| 1565 | LogPrint("nostr", "Nostr: node .btf address = %s\n", key.BtfAddress().c_str()); | 1582 | LogPrint("nostr", "Nostr: node .btf address = %s\n", key.BtfAddress().c_str()); |
| 1566 | 1583 | ||
| 1584 | // The self-test below publishes a real descriptor, and PublishDescriptor now | ||
| 1585 | // refuses to publish before ThreadBtfAccept has registered a rendezvous. Give | ||
| 1586 | // registration a bounded head start so the test measures the publish/resolve | ||
| 1587 | // chain instead of losing the race against it. | ||
| 1588 | for (int i = 0; i < 60 && !fShutdown && BtfActiveRelay().empty(); i++) | ||
| 1589 | Sleep(1000); | ||
| 1590 | |||
| 1567 | // One-time self-test of the rendezvous: publish our descriptor to a relay, | 1591 | // One-time self-test of the rendezvous: publish our descriptor to a relay, |
| 1568 | // then resolve our OWN .btf address back and verify it self-certifies. Proves | 1592 | // then resolve our OWN .btf address back and verify it self-certifies. Proves |
| 1569 | // the .btf publish/resolve/verify chain works against real public relays. | 1593 | // the .btf publish/resolve/verify chain works against real public relays. |
diff --git a/src/nostr.h b/src/nostr.h index 37b3b05..04149e3 100644 --- a/src/nostr.h +++ b/src/nostr.h | |||
| @@ -34,7 +34,8 @@ extern const int nNostrRelays; | |||
| 34 | extern std::vector<std::string> vBtfMeetingRelays; | 34 | extern std::vector<std::string> vBtfMeetingRelays; |
| 35 | // The relay this node is currently registered at (set by ThreadBtfAccept). | 35 | // The relay this node is currently registered at (set by ThreadBtfAccept). |
| 36 | extern std::string strBtfActiveRelay; | 36 | extern std::string strBtfActiveRelay; |
| 37 | // The active relay, or the first seed if none active yet ("" if the list is empty). | 37 | // The relay we are registered at, or "" if registration hasn't succeeded yet. |
| 38 | // Never guesses a seed: an unregistered node has no meeting node to advertise. | ||
| 38 | std::string BtfActiveRelay(); | 39 | std::string BtfActiveRelay(); |
| 39 | // Record the relay this node just registered its service at (thread-safe). | 40 | // Record the relay this node just registered its service at (thread-safe). |
| 40 | void BtfSetActiveRelay(const std::string& relay); | 41 | void BtfSetActiveRelay(const std::string& relay); |