diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-26 22:41:53 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-26 22:41:53 +0000 |
| commit | bbe04dc8c96b4c4766d2771d3438233bf82687c0 (patch) | |
| tree | 62e5a1febfb8e9bc38d133ac7981e9ef93918484 /src/nostr.cpp | |
| parent | 991853fa55a749d28d6f9e507ce72b289df92785 (diff) | |
| parent | 9e39a50d1f110020b0841d31e4b931aa877cc448 (diff) | |
Merge upstream v1.2.19 into the fork
Merges upstream tag v1.2.19 (9e39a50) into merge/upstream-1.2.18 (991853f).
Unlike the 1.2.15->1.2.18 replay, bc3de30 is a true ancestor of our branch via
a9b4f89, so this is an ordinary three-way merge with merge-base bc3de30.
Delta: 36 files, +3265/-163, the Tor onion transport (managed Tor, signed
direct onion endpoints, onion-only dial mode) and the nHDNext restore fix.
src/main.cpp, main.h, script.cpp/h and db.cpp/h are byte-identical between the
two tags, and the poll() socket loop carries no diff hunks, so the merge is
consensus-neutral and the soak-tested socket paths are unchanged. relay/ is
untouched; the payout ledger (rpc.cpp) is untouched.
7 conflict blocks in 6 files, resolved as follows.
Makefile -- VERSION. Ours: stays 1.2.19+sec with the naming comment updated,
so artifacts from this tree cannot be mistaken for the upstream release.
src/Makefile, src/makefile.mingw -- HEADERS list. Ours (no market.h) plus
their tor.h. NODE_OBJS auto-merged correctly: no market.o, tor.o present.
src/selftest.cpp -- known-tests list. Union: payout-ledger (ours) and
managed-tor (theirs).
src/main_gui.cpp -- one block. Our /dumpaddresses kept; their
BtfStopManagedTor() on the StartNode failure path taken. The /rvrelay pin,
/noannounce and the marketplace removal auto-merged and were verified intact.
src/nostr.cpp -- two blocks in ResolveDescriptor. Signature takes both
out-params (our fExchangeOk, their rawOut). The body keeps our
drain-to-EOSE read -- returning early poisons the pooled socket for the next
caller -- and adds their rawOut capture. Our TLS 1.2 floor
(SSL_CTX_set_min_proto_version) is untouched by this merge; upstream 1.2.19
still has no floor of its own.
Upstream's release commit also added four build logs and a compiled Python
cache (build-1219.log, build-17.log, build-appimage.log, build-node.log,
scripts/__pycache__/bitflash_chain.cpython-313.pyc). Removed here; they are
build output, not source.
Diffstat (limited to 'src/nostr.cpp')
| -rw-r--r-- | src/nostr.cpp | 88 |
1 files changed, 69 insertions, 19 deletions
diff --git a/src/nostr.cpp b/src/nostr.cpp index 32cea18..e2d839c 100644 --- a/src/nostr.cpp +++ b/src/nostr.cpp | |||
| @@ -132,6 +132,8 @@ vector<string> vBtfMeetingRelays = { | |||
| 132 | bool fBtfRelayPinned = false; | 132 | bool fBtfRelayPinned = false; |
| 133 | string strBtfActiveRelay; | 133 | string strBtfActiveRelay; |
| 134 | static CCriticalSection cs_activeRelay; | 134 | static CCriticalSection cs_activeRelay; |
| 135 | static string strBtfOnionEndpoint; | ||
| 136 | static CCriticalSection cs_onionEndpoint; | ||
| 135 | 137 | ||
| 136 | // Only the rendezvous we actually registered at (BtfSetActiveRelay, called from | 138 | // Only the rendezvous we actually registered at (BtfSetActiveRelay, called from |
| 137 | // ThreadBtfAccept once registration succeeds). Empty until then, on purpose: it | 139 | // ThreadBtfAccept once registration succeeds). Empty until then, on purpose: it |
| @@ -153,6 +155,26 @@ void BtfSetActiveRelay(const string& relay) | |||
| 153 | strBtfActiveRelay = relay; | 155 | strBtfActiveRelay = relay; |
| 154 | } | 156 | } |
| 155 | 157 | ||
| 158 | bool BtfSetLocalOnionEndpoint(const string& endpoint, string& errOut) | ||
| 159 | { | ||
| 160 | string norm; | ||
| 161 | if (!btf::NormalizeOnionEndpoint(endpoint, norm)) | ||
| 162 | { | ||
| 163 | errOut = "expected v3 onion endpoint HOST.onion:PORT"; | ||
| 164 | return false; | ||
| 165 | } | ||
| 166 | CRITICAL_BLOCK(cs_onionEndpoint) | ||
| 167 | strBtfOnionEndpoint = norm; | ||
| 168 | return true; | ||
| 169 | } | ||
| 170 | |||
| 171 | string BtfLocalOnionEndpoint() | ||
| 172 | { | ||
| 173 | CRITICAL_BLOCK(cs_onionEndpoint) | ||
| 174 | return strBtfOnionEndpoint; | ||
| 175 | return ""; | ||
| 176 | } | ||
| 177 | |||
| 156 | // A relay this node advertises on Nostr for others to discover (/announcerelay). | 178 | // A relay this node advertises on Nostr for others to discover (/announcerelay). |
| 157 | string strBtfAnnounceRelay; | 179 | string strBtfAnnounceRelay; |
| 158 | // Relays learned from other operators' Nostr announcements. | 180 | // Relays learned from other operators' Nostr announcements. |
| @@ -392,10 +414,16 @@ std::string BtfLocalDescriptor() | |||
| 392 | string meeting; | 414 | string meeting; |
| 393 | CRITICAL_BLOCK(cs_activeRelay) | 415 | CRITICAL_BLOCK(cs_activeRelay) |
| 394 | meeting = strBtfActiveRelay; | 416 | meeting = strBtfActiveRelay; |
| 417 | string onion = BtfLocalOnionEndpoint(); | ||
| 395 | if (meeting.empty()) | 418 | if (meeting.empty()) |
| 396 | return ""; | 419 | { |
| 420 | if (onion.empty()) | ||
| 421 | return ""; | ||
| 422 | meeting = "rendezvous-pending"; | ||
| 423 | } | ||
| 397 | return btf::SignDescriptor(g_nostrKey.ctx, g_nostrKey.seckey, | 424 | return btf::SignDescriptor(g_nostrKey.ctx, g_nostrKey.seckey, |
| 398 | g_nostrKey.EncPubHex(), meeting, (uint64_t)GetTime()); | 425 | g_nostrKey.EncPubHex(), meeting, (uint64_t)GetTime(), |
| 426 | onion); | ||
| 399 | } | 427 | } |
| 400 | 428 | ||
| 401 | // The shared secp256k1 context, so net.cpp can verify descriptors that arrive | 429 | // The shared secp256k1 context, so net.cpp can verify descriptors that arrive |
| @@ -854,17 +882,21 @@ static void PublishDescriptor(CWebSocket& ws, CNostrKey& key) | |||
| 854 | // meeting_node is the rendezvous relay this node's hidden service | 882 | // meeting_node is the rendezvous relay this node's hidden service |
| 855 | // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. | 883 | // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. |
| 856 | string meeting_node = BtfActiveRelay(); | 884 | string meeting_node = BtfActiveRelay(); |
| 885 | string onion = BtfLocalOnionEndpoint(); | ||
| 857 | if (meeting_node.empty()) | 886 | if (meeting_node.empty()) |
| 858 | { | 887 | { |
| 859 | // Not registered anywhere yet. Publishing now would advertise a meeting | 888 | if (onion.empty()) |
| 860 | // node we can't be reached at, and peers would waste dials on it. The | 889 | { |
| 861 | // ThreadNostrSeed loop republishes on every pass, so skipping costs | 890 | // Not registered anywhere yet, and no direct onion endpoint exists. |
| 862 | // nothing but the delay until ThreadBtfAccept registers. | 891 | // Publishing now would advertise a meeting node we can't be reached at. |
| 863 | LogPrint("nostr", "Nostr: skipping descriptor publish, no rendezvous registered yet\n"); | 892 | LogPrint("nostr", "Nostr: skipping descriptor publish, no rendezvous registered yet\n"); |
| 864 | return; | 893 | return; |
| 894 | } | ||
| 895 | meeting_node = "rendezvous-pending"; | ||
| 865 | } | 896 | } |
| 866 | string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), | 897 | string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), |
| 867 | meeting_node, (uint64_t)GetTime()); | 898 | meeting_node, (uint64_t)GetTime(), |
| 899 | onion); | ||
| 868 | if (desc.empty()) | 900 | if (desc.empty()) |
| 869 | return; | 901 | return; |
| 870 | json tags = json::array({ json::array({ "d", BTF_DESC_DTAG }) }); | 902 | json tags = json::array({ json::array({ "d", BTF_DESC_DTAG }) }); |
| @@ -1070,7 +1102,8 @@ static void HandlePoolAnnouncement(const json& ev) | |||
| 1070 | // this socket for reuse, and only a completed exchange leaves it in a state | 1102 | // this socket for reuse, and only a completed exchange leaves it in a state |
| 1071 | // the next caller can trust. | 1103 | // the next caller can trust. |
| 1072 | static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, | 1104 | static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, |
| 1073 | btf::Descriptor& out, bool& fExchangeOk) | 1105 | btf::Descriptor& out, bool& fExchangeOk, |
| 1106 | string* rawOut = NULL) | ||
| 1074 | { | 1107 | { |
| 1075 | fExchangeOk = false; | 1108 | fExchangeOk = false; |
| 1076 | unsigned char pk[32]; | 1109 | unsigned char pk[32]; |
| @@ -1106,7 +1139,11 @@ static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, | |||
| 1106 | const json& ev = j[2]; | 1139 | const json& ev = j[2]; |
| 1107 | if (!fFound && ev.contains("content") && | 1140 | if (!fFound && ev.contains("content") && |
| 1108 | btf::VerifyDescriptor(ctx, ev["content"].get<string>(), pk, out)) | 1141 | btf::VerifyDescriptor(ctx, ev["content"].get<string>(), pk, out)) |
| 1142 | { | ||
| 1109 | fFound = true; | 1143 | fFound = true; |
| 1144 | if (rawOut) | ||
| 1145 | *rawOut = ev["content"].get<string>(); | ||
| 1146 | } | ||
| 1110 | // Deliberately keep reading to EOSE rather than returning here. | 1147 | // Deliberately keep reading to EOSE rather than returning here. |
| 1111 | // Returning early leaves the rest of the relay's answer queued on a | 1148 | // Returning early leaves the rest of the relay's answer queued on a |
| 1112 | // socket that goes straight back into the pool, and the next caller | 1149 | // socket that goes straight back into the pool, and the next caller |
| @@ -1126,8 +1163,10 @@ static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, | |||
| 1126 | // public discovery relays for the owner's self-certified descriptor. Used by | 1163 | // public discovery relays for the owner's self-certified descriptor. Used by |
| 1127 | // ConnectNodeBtf in net.cpp. | 1164 | // ConnectNodeBtf in net.cpp. |
| 1128 | bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, | 1165 | bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, |
| 1129 | unsigned char enc_pub[32]) | 1166 | unsigned char enc_pub[32], std::string* onionHostPort) |
| 1130 | { | 1167 | { |
| 1168 | if (onionHostPort) | ||
| 1169 | onionHostPort->clear(); | ||
| 1131 | if (!EnsureNostrKey()) | 1170 | if (!EnsureNostrKey()) |
| 1132 | return false; | 1171 | return false; |
| 1133 | for (int i = 0; i < nNostrRelays && !fShutdown; i++) | 1172 | for (int i = 0; i < nNostrRelays && !fShutdown; i++) |
| @@ -1155,10 +1194,15 @@ bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, | |||
| 1155 | } | 1194 | } |
| 1156 | lease.fOk = fExchangeOk; | 1195 | lease.fOk = fExchangeOk; |
| 1157 | if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") | 1196 | if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") |
| 1158 | continue; // descriptor predates the owner's relay config | 1197 | { |
| 1198 | if (!onionHostPort || d.onion.empty()) | ||
| 1199 | continue; // caller needs rendezvous, or descriptor has no direct path | ||
| 1200 | } | ||
| 1159 | if (!HexDecode(d.enc, enc_pub, 32)) | 1201 | if (!HexDecode(d.enc, enc_pub, 32)) |
| 1160 | continue; | 1202 | continue; |
| 1161 | meetingHostPort = d.meeting_node; | 1203 | meetingHostPort = d.meeting_node; |
| 1204 | if (onionHostPort) | ||
| 1205 | *onionHostPort = d.onion; | ||
| 1162 | return true; | 1206 | return true; |
| 1163 | } | 1207 | } |
| 1164 | CATCH_PRINT_EXCEPTION("BtfResolve") | 1208 | CATCH_PRINT_EXCEPTION("BtfResolve") |
| @@ -1247,12 +1291,15 @@ bool BtfResolveMany(const std::vector<std::string>& btfAddrs, | |||
| 1247 | btf::Descriptor d; | 1291 | btf::Descriptor d; |
| 1248 | if (!btf::VerifyDescriptor(g_nostrKey.ctx, ev["content"].get<string>(), pk, d)) | 1292 | if (!btf::VerifyDescriptor(g_nostrKey.ctx, ev["content"].get<string>(), pk, d)) |
| 1249 | continue; | 1293 | continue; |
| 1250 | if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") | 1294 | if ((d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") && |
| 1295 | d.onion.empty()) | ||
| 1251 | continue; | 1296 | continue; |
| 1252 | BtfResolvedPeer rp; | 1297 | BtfResolvedPeer rp; |
| 1253 | if (!HexDecode(d.enc, rp.enc_pub, 32)) | 1298 | if (!HexDecode(d.enc, rp.enc_pub, 32)) |
| 1254 | continue; | 1299 | continue; |
| 1255 | rp.meetingHostPort = d.meeting_node; | 1300 | rp.meetingHostPort = d.meeting_node; |
| 1301 | rp.onionHostPort = d.onion; | ||
| 1302 | rp.descriptor = ev["content"].get<string>(); | ||
| 1256 | out[ai->second] = rp; | 1303 | out[ai->second] = rp; |
| 1257 | pending.erase(ai->second); | 1304 | pending.erase(ai->second); |
| 1258 | } | 1305 | } |
| @@ -1729,11 +1776,13 @@ static void ConnectDiscoveredBtfPeers() | |||
| 1729 | DialCtx* ctx = (DialCtx*)arg; | 1776 | DialCtx* ctx = (DialCtx*)arg; |
| 1730 | string addr = ctx->addr; | 1777 | string addr = ctx->addr; |
| 1731 | string meeting = ctx->rp.meetingHostPort; | 1778 | string meeting = ctx->rp.meetingHostPort; |
| 1779 | string onion = ctx->rp.onionHostPort; | ||
| 1780 | string desc = ctx->rp.descriptor; | ||
| 1732 | unsigned char enc_pub[32]; | 1781 | unsigned char enc_pub[32]; |
| 1733 | memcpy(enc_pub, ctx->rp.enc_pub, 32); | 1782 | memcpy(enc_pub, ctx->rp.enc_pub, 32); |
| 1734 | delete ctx; | 1783 | delete ctx; |
| 1735 | 1784 | ||
| 1736 | CNode* pnode = ConnectNodeBtfResolved(addr, meeting, enc_pub); | 1785 | CNode* pnode = ConnectNodeBtfResolved(addr, meeting, onion, enc_pub, desc); |
| 1737 | CRITICAL_BLOCK(cs_btfPeerState) | 1786 | CRITICAL_BLOCK(cs_btfPeerState) |
| 1738 | { | 1787 | { |
| 1739 | if (pnode) | 1788 | if (pnode) |
| @@ -1795,11 +1844,12 @@ void ThreadNostrSeed(void* parg) | |||
| 1795 | LogPrint("nostr", "Nostr: node pubkey = %s\n", key.PubKeyHex().c_str()); | 1844 | LogPrint("nostr", "Nostr: node pubkey = %s\n", key.PubKeyHex().c_str()); |
| 1796 | LogPrint("nostr", "Nostr: node .btf address = %s\n", key.BtfAddress().c_str()); | 1845 | LogPrint("nostr", "Nostr: node .btf address = %s\n", key.BtfAddress().c_str()); |
| 1797 | 1846 | ||
| 1798 | // The self-test below publishes a real descriptor, and PublishDescriptor now | 1847 | // The self-test below publishes a real descriptor. Rendezvous-only nodes |
| 1799 | // refuses to publish before ThreadBtfAccept has registered a rendezvous. Give | 1848 | // need ThreadBtfAccept to register first; Tor nodes can publish as soon as |
| 1800 | // registration a bounded head start so the test measures the publish/resolve | 1849 | // their signed onion endpoint exists, with rendezvous marked pending. |
| 1801 | // chain instead of losing the race against it. | 1850 | for (int i = 0; i < 60 && !fShutdown && |
| 1802 | for (int i = 0; i < 60 && !fShutdown && BtfActiveRelay().empty(); i++) | 1851 | BtfActiveRelay().empty() && |
| 1852 | BtfLocalOnionEndpoint().empty(); i++) | ||
| 1803 | Sleep(1000); | 1853 | Sleep(1000); |
| 1804 | 1854 | ||
| 1805 | // One-time self-test of the rendezvous: publish our descriptor to a relay, | 1855 | // One-time self-test of the rendezvous: publish our descriptor to a relay, |