diff options
Diffstat (limited to 'src/nostr.cpp')
| -rw-r--r-- | src/nostr.cpp | 76 |
1 files changed, 62 insertions, 14 deletions
diff --git a/src/nostr.cpp b/src/nostr.cpp index d286767..0c1b4b6 100644 --- a/src/nostr.cpp +++ b/src/nostr.cpp | |||
| @@ -112,6 +112,8 @@ vector<string> vBtfMeetingRelays = { | |||
| 112 | }; | 112 | }; |
| 113 | string strBtfActiveRelay; | 113 | string strBtfActiveRelay; |
| 114 | static CCriticalSection cs_activeRelay; | 114 | static CCriticalSection cs_activeRelay; |
| 115 | static string strBtfOnionEndpoint; | ||
| 116 | static CCriticalSection cs_onionEndpoint; | ||
| 115 | 117 | ||
| 116 | // Only the rendezvous we actually registered at (BtfSetActiveRelay, called from | 118 | // Only the rendezvous we actually registered at (BtfSetActiveRelay, called from |
| 117 | // ThreadBtfAccept once registration succeeds). Empty until then, on purpose: it | 119 | // ThreadBtfAccept once registration succeeds). Empty until then, on purpose: it |
| @@ -133,6 +135,26 @@ void BtfSetActiveRelay(const string& relay) | |||
| 133 | strBtfActiveRelay = relay; | 135 | strBtfActiveRelay = relay; |
| 134 | } | 136 | } |
| 135 | 137 | ||
| 138 | bool BtfSetLocalOnionEndpoint(const string& endpoint, string& errOut) | ||
| 139 | { | ||
| 140 | string norm; | ||
| 141 | if (!btf::NormalizeOnionEndpoint(endpoint, norm)) | ||
| 142 | { | ||
| 143 | errOut = "expected v3 onion endpoint HOST.onion:PORT"; | ||
| 144 | return false; | ||
| 145 | } | ||
| 146 | CRITICAL_BLOCK(cs_onionEndpoint) | ||
| 147 | strBtfOnionEndpoint = norm; | ||
| 148 | return true; | ||
| 149 | } | ||
| 150 | |||
| 151 | string BtfLocalOnionEndpoint() | ||
| 152 | { | ||
| 153 | CRITICAL_BLOCK(cs_onionEndpoint) | ||
| 154 | return strBtfOnionEndpoint; | ||
| 155 | return ""; | ||
| 156 | } | ||
| 157 | |||
| 136 | // A relay this node advertises on Nostr for others to discover (/announcerelay). | 158 | // A relay this node advertises on Nostr for others to discover (/announcerelay). |
| 137 | string strBtfAnnounceRelay; | 159 | string strBtfAnnounceRelay; |
| 138 | // Relays learned from other operators' Nostr announcements. | 160 | // Relays learned from other operators' Nostr announcements. |
| @@ -370,10 +392,16 @@ std::string BtfLocalDescriptor() | |||
| 370 | string meeting; | 392 | string meeting; |
| 371 | CRITICAL_BLOCK(cs_activeRelay) | 393 | CRITICAL_BLOCK(cs_activeRelay) |
| 372 | meeting = strBtfActiveRelay; | 394 | meeting = strBtfActiveRelay; |
| 395 | string onion = BtfLocalOnionEndpoint(); | ||
| 373 | if (meeting.empty()) | 396 | if (meeting.empty()) |
| 374 | return ""; | 397 | { |
| 398 | if (onion.empty()) | ||
| 399 | return ""; | ||
| 400 | meeting = "rendezvous-pending"; | ||
| 401 | } | ||
| 375 | return btf::SignDescriptor(g_nostrKey.ctx, g_nostrKey.seckey, | 402 | return btf::SignDescriptor(g_nostrKey.ctx, g_nostrKey.seckey, |
| 376 | g_nostrKey.EncPubHex(), meeting, (uint64_t)GetTime()); | 403 | g_nostrKey.EncPubHex(), meeting, (uint64_t)GetTime(), |
| 404 | onion); | ||
| 377 | } | 405 | } |
| 378 | 406 | ||
| 379 | // The shared secp256k1 context, so net.cpp can verify descriptors that arrive | 407 | // The shared secp256k1 context, so net.cpp can verify descriptors that arrive |
| @@ -806,17 +834,21 @@ static void PublishDescriptor(CWebSocket& ws, CNostrKey& key) | |||
| 806 | // meeting_node is the rendezvous relay this node's hidden service | 834 | // meeting_node is the rendezvous relay this node's hidden service |
| 807 | // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. | 835 | // (ThreadBtfAccept in net.cpp) is registered at -- where clients dial us. |
| 808 | string meeting_node = BtfActiveRelay(); | 836 | string meeting_node = BtfActiveRelay(); |
| 837 | string onion = BtfLocalOnionEndpoint(); | ||
| 809 | if (meeting_node.empty()) | 838 | if (meeting_node.empty()) |
| 810 | { | 839 | { |
| 811 | // Not registered anywhere yet. Publishing now would advertise a meeting | 840 | if (onion.empty()) |
| 812 | // node we can't be reached at, and peers would waste dials on it. The | 841 | { |
| 813 | // ThreadNostrSeed loop republishes on every pass, so skipping costs | 842 | // Not registered anywhere yet, and no direct onion endpoint exists. |
| 814 | // nothing but the delay until ThreadBtfAccept registers. | 843 | // Publishing now would advertise a meeting node we can't be reached at. |
| 815 | LogPrint("nostr", "Nostr: skipping descriptor publish, no rendezvous registered yet\n"); | 844 | LogPrint("nostr", "Nostr: skipping descriptor publish, no rendezvous registered yet\n"); |
| 816 | return; | 845 | return; |
| 846 | } | ||
| 847 | meeting_node = "rendezvous-pending"; | ||
| 817 | } | 848 | } |
| 818 | string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), | 849 | string desc = btf::SignDescriptor(key.ctx, key.seckey, key.EncPubHex(), |
| 819 | meeting_node, (uint64_t)GetTime()); | 850 | meeting_node, (uint64_t)GetTime(), |
| 851 | onion); | ||
| 820 | if (desc.empty()) | 852 | if (desc.empty()) |
| 821 | return; | 853 | return; |
| 822 | json tags = json::array({ json::array({ "d", BTF_DESC_DTAG }) }); | 854 | json tags = json::array({ json::array({ "d", BTF_DESC_DTAG }) }); |
| @@ -1018,7 +1050,7 @@ static void HandlePoolAnnouncement(const json& ev) | |||
| 1018 | // Resolve a `.btf` address via a relay: fetch the descriptor published by that | 1050 | // Resolve a `.btf` address via a relay: fetch the descriptor published by that |
| 1019 | // address's key and verify it self-certifies under the decoded pubkey. | 1051 | // address's key and verify it self-certifies under the decoded pubkey. |
| 1020 | static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, | 1052 | static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, |
| 1021 | btf::Descriptor& out) | 1053 | btf::Descriptor& out, string* rawOut = NULL) |
| 1022 | { | 1054 | { |
| 1023 | unsigned char pk[32]; | 1055 | unsigned char pk[32]; |
| 1024 | if (!btf::ParseAddress(btfAddr, pk)) | 1056 | if (!btf::ParseAddress(btfAddr, pk)) |
| @@ -1045,7 +1077,11 @@ static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, | |||
| 1045 | const json& ev = j[2]; | 1077 | const json& ev = j[2]; |
| 1046 | if (ev.contains("content") && | 1078 | if (ev.contains("content") && |
| 1047 | btf::VerifyDescriptor(ctx, ev["content"].get<string>(), pk, out)) | 1079 | btf::VerifyDescriptor(ctx, ev["content"].get<string>(), pk, out)) |
| 1080 | { | ||
| 1081 | if (rawOut) | ||
| 1082 | *rawOut = ev["content"].get<string>(); | ||
| 1048 | return true; | 1083 | return true; |
| 1084 | } | ||
| 1049 | } | 1085 | } |
| 1050 | else if (t == "EOSE") | 1086 | else if (t == "EOSE") |
| 1051 | break; | 1087 | break; |
| @@ -1057,8 +1093,10 @@ static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, | |||
| 1057 | // public discovery relays for the owner's self-certified descriptor. Used by | 1093 | // public discovery relays for the owner's self-certified descriptor. Used by |
| 1058 | // ConnectNodeBtf in net.cpp. | 1094 | // ConnectNodeBtf in net.cpp. |
| 1059 | bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, | 1095 | bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, |
| 1060 | unsigned char enc_pub[32]) | 1096 | unsigned char enc_pub[32], std::string* onionHostPort) |
| 1061 | { | 1097 | { |
| 1098 | if (onionHostPort) | ||
| 1099 | onionHostPort->clear(); | ||
| 1062 | if (!EnsureNostrKey()) | 1100 | if (!EnsureNostrKey()) |
| 1063 | return false; | 1101 | return false; |
| 1064 | for (int i = 0; i < nNostrRelays && !fShutdown; i++) | 1102 | for (int i = 0; i < nNostrRelays && !fShutdown; i++) |
| @@ -1076,10 +1114,15 @@ bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, | |||
| 1076 | } | 1114 | } |
| 1077 | lease.fOk = true; | 1115 | lease.fOk = true; |
| 1078 | if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") | 1116 | if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") |
| 1079 | continue; // descriptor predates the owner's relay config | 1117 | { |
| 1118 | if (!onionHostPort || d.onion.empty()) | ||
| 1119 | continue; // caller needs rendezvous, or descriptor has no direct path | ||
| 1120 | } | ||
| 1080 | if (!HexDecode(d.enc, enc_pub, 32)) | 1121 | if (!HexDecode(d.enc, enc_pub, 32)) |
| 1081 | continue; | 1122 | continue; |
| 1082 | meetingHostPort = d.meeting_node; | 1123 | meetingHostPort = d.meeting_node; |
| 1124 | if (onionHostPort) | ||
| 1125 | *onionHostPort = d.onion; | ||
| 1083 | return true; | 1126 | return true; |
| 1084 | } | 1127 | } |
| 1085 | CATCH_PRINT_EXCEPTION("BtfResolve") | 1128 | CATCH_PRINT_EXCEPTION("BtfResolve") |
| @@ -1168,12 +1211,15 @@ bool BtfResolveMany(const std::vector<std::string>& btfAddrs, | |||
| 1168 | btf::Descriptor d; | 1211 | btf::Descriptor d; |
| 1169 | if (!btf::VerifyDescriptor(g_nostrKey.ctx, ev["content"].get<string>(), pk, d)) | 1212 | if (!btf::VerifyDescriptor(g_nostrKey.ctx, ev["content"].get<string>(), pk, d)) |
| 1170 | continue; | 1213 | continue; |
| 1171 | if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") | 1214 | if ((d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") && |
| 1215 | d.onion.empty()) | ||
| 1172 | continue; | 1216 | continue; |
| 1173 | BtfResolvedPeer rp; | 1217 | BtfResolvedPeer rp; |
| 1174 | if (!HexDecode(d.enc, rp.enc_pub, 32)) | 1218 | if (!HexDecode(d.enc, rp.enc_pub, 32)) |
| 1175 | continue; | 1219 | continue; |
| 1176 | rp.meetingHostPort = d.meeting_node; | 1220 | rp.meetingHostPort = d.meeting_node; |
| 1221 | rp.onionHostPort = d.onion; | ||
| 1222 | rp.descriptor = ev["content"].get<string>(); | ||
| 1177 | out[ai->second] = rp; | 1223 | out[ai->second] = rp; |
| 1178 | pending.erase(ai->second); | 1224 | pending.erase(ai->second); |
| 1179 | } | 1225 | } |
| @@ -1650,11 +1696,13 @@ static void ConnectDiscoveredBtfPeers() | |||
| 1650 | DialCtx* ctx = (DialCtx*)arg; | 1696 | DialCtx* ctx = (DialCtx*)arg; |
| 1651 | string addr = ctx->addr; | 1697 | string addr = ctx->addr; |
| 1652 | string meeting = ctx->rp.meetingHostPort; | 1698 | string meeting = ctx->rp.meetingHostPort; |
| 1699 | string onion = ctx->rp.onionHostPort; | ||
| 1700 | string desc = ctx->rp.descriptor; | ||
| 1653 | unsigned char enc_pub[32]; | 1701 | unsigned char enc_pub[32]; |
| 1654 | memcpy(enc_pub, ctx->rp.enc_pub, 32); | 1702 | memcpy(enc_pub, ctx->rp.enc_pub, 32); |
| 1655 | delete ctx; | 1703 | delete ctx; |
| 1656 | 1704 | ||
| 1657 | CNode* pnode = ConnectNodeBtfResolved(addr, meeting, enc_pub); | 1705 | CNode* pnode = ConnectNodeBtfResolved(addr, meeting, onion, enc_pub, desc); |
| 1658 | CRITICAL_BLOCK(cs_btfPeerState) | 1706 | CRITICAL_BLOCK(cs_btfPeerState) |
| 1659 | { | 1707 | { |
| 1660 | if (pnode) | 1708 | if (pnode) |