upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/nostr.cpp
diff options
context:
space:
mode:
authorNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-13 11:11:59 -0300
committerNsable-sh <nsable-sh@noreply.git.bitflash.network>2026-08-13 11:11:59 -0300
commitd2b86473a267c960a1f0c012a983176e5d962187 (patch)
tree002423e5987a3c544f469befae83519ccb29fffb /src/nostr.cpp
parent851811b43be38bea48ba5d04d16987bd541dca53 (diff)
net: add signed direct onion peer endpoints
Diffstat (limited to 'src/nostr.cpp')
-rw-r--r--src/nostr.cpp76
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};
113string strBtfActiveRelay; 113string strBtfActiveRelay;
114static CCriticalSection cs_activeRelay; 114static CCriticalSection cs_activeRelay;
115static string strBtfOnionEndpoint;
116static 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
138bool 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
151string 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).
137string strBtfAnnounceRelay; 159string 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.
1020static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, 1052static 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.
1059bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort, 1095bool 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)