upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nostr.cpp56
1 files changed, 45 insertions, 11 deletions
diff --git a/src/nostr.cpp b/src/nostr.cpp
index b3e4a7c..3f43302 100644
--- a/src/nostr.cpp
+++ b/src/nostr.cpp
@@ -1057,9 +1057,14 @@ static void HandlePoolAnnouncement(const json& ev)
1057 1057
1058// Resolve a `.btf` address via a relay: fetch the descriptor published by that 1058// Resolve a `.btf` address via a relay: fetch the descriptor published by that
1059// address's key and verify it self-certifies under the decoded pubkey. 1059// address's key and verify it self-certifies under the decoded pubkey.
1060// fExchangeOk reports whether the relay actually finished the conversation
1061// (an EOSE arrived) as opposed to the socket dying mid-read. The caller pools
1062// this socket for reuse, and only a completed exchange leaves it in a state
1063// the next caller can trust.
1060static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr, 1064static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr,
1061 btf::Descriptor& out) 1065 btf::Descriptor& out, bool& fExchangeOk)
1062{ 1066{
1067 fExchangeOk = false;
1063 unsigned char pk[32]; 1068 unsigned char pk[32];
1064 if (!btf::ParseAddress(btfAddr, pk)) 1069 if (!btf::ParseAddress(btfAddr, pk))
1065 return false; 1070 return false;
@@ -1070,26 +1075,42 @@ static bool ResolveDescriptor(CWebSocket& ws, void* ctx, const string& btfAddr,
1070 json req = json::array({ "REQ", "btf-resolve", filter }); 1075 json req = json::array({ "REQ", "btf-resolve", filter });
1071 if (!ws.SendText(req.dump())) 1076 if (!ws.SendText(req.dump()))
1072 return false; 1077 return false;
1078 bool fFound = false;
1073 for (;;) 1079 for (;;)
1074 { 1080 {
1075 string msg; 1081 string msg;
1076 if (!ws.RecvText(msg)) 1082 if (!ws.RecvText(msg))
1077 break; 1083 return fFound; // socket died mid-exchange; fExchangeOk stays false
1078 json j; 1084 json j;
1079 try { j = json::parse(msg); } catch (...) { continue; } 1085 try { j = json::parse(msg); } catch (...) { continue; }
1080 if (!j.is_array() || j.empty() || !j[0].is_string()) continue; 1086 if (!j.is_array() || j.empty() || !j[0].is_string()) continue;
1081 string t = j[0].get<string>(); 1087 string t = j[0].get<string>();
1088 // Every relay message names the subscription it belongs to. This socket
1089 // is pooled and reused, so a message left over from an earlier exchange
1090 // is the ordinary case here, not an exotic one -- and an unmatched EOSE
1091 // ends this query before it has begun, which is indistinguishable from
1092 // "that descriptor does not exist". Check the id.
1093 if (j.size() >= 2 && j[1].is_string() && j[1].get<string>() != "btf-resolve")
1094 continue;
1082 if (t == "EVENT" && j.size() >= 3) 1095 if (t == "EVENT" && j.size() >= 3)
1083 { 1096 {
1084 const json& ev = j[2]; 1097 const json& ev = j[2];
1085 if (ev.contains("content") && 1098 if (!fFound && ev.contains("content") &&
1086 btf::VerifyDescriptor(ctx, ev["content"].get<string>(), pk, out)) 1099 btf::VerifyDescriptor(ctx, ev["content"].get<string>(), pk, out))
1087 return true; 1100 fFound = true;
1101 // Deliberately keep reading to EOSE rather than returning here.
1102 // Returning early leaves the rest of the relay's answer queued on a
1103 // socket that goes straight back into the pool, and the next caller
1104 // reads our leftovers as its own -- one message out of step, for
1105 // the life of the process.
1088 } 1106 }
1089 else if (t == "EOSE") 1107 else if (t == "EOSE")
1108 {
1109 fExchangeOk = true; // relay said "that is all": socket is clean
1090 break; 1110 break;
1111 }
1091 } 1112 }
1092 return false; 1113 return fFound;
1093} 1114}
1094 1115
1095// Resolve a `.btf` address into its rendezvous coordinates by querying the 1116// Resolve a `.btf` address into its rendezvous coordinates by querying the
@@ -1108,12 +1129,22 @@ bool BtfResolve(const std::string& btfAddr, std::string& meetingHostPort,
1108 if (!lease.Connected()) 1129 if (!lease.Connected())
1109 continue; 1130 continue;
1110 btf::Descriptor d; 1131 btf::Descriptor d;
1111 if (!ResolveDescriptor(lease.Ws(), g_nostrKey.ctx, btfAddr, d)) 1132 bool fExchangeOk = false;
1133 if (!ResolveDescriptor(lease.Ws(), g_nostrKey.ctx, btfAddr, d, fExchangeOk))
1112 { 1134 {
1113 lease.fOk = true; // exchange completed, just no match 1135 // Only keep the pooled socket if the relay actually finished
1136 // the conversation. This used to say "exchange completed, just
1137 // no match" on every failure -- including a dead socket -- so
1138 // ~CRelayLease never closed it and fConnected was never
1139 // cleared. A relay connection that died was then pinned in the
1140 // pool, reported healthy, and reused for every later lookup,
1141 // which is why "no Nostr descriptor found" persisted for a
1142 // descriptor that was demonstrably published, and why only a
1143 // restart cleared it.
1144 lease.fOk = fExchangeOk;
1114 continue; 1145 continue;
1115 } 1146 }
1116 lease.fOk = true; 1147 lease.fOk = fExchangeOk;
1117 if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending") 1148 if (d.meeting_node.empty() || d.meeting_node == "rendezvous-pending")
1118 continue; // descriptor predates the owner's relay config 1149 continue; // descriptor predates the owner's relay config
1119 if (!HexDecode(d.enc, enc_pub, 32)) 1150 if (!HexDecode(d.enc, enc_pub, 32))
@@ -1761,14 +1792,17 @@ void ThreadNostrSeed(void* parg)
1761 PublishDescriptor(ws, key); 1792 PublishDescriptor(ws, key);
1762 Sleep(700); // let the relay store the replaceable event 1793 Sleep(700); // let the relay store the replaceable event
1763 btf::Descriptor d; 1794 btf::Descriptor d;
1764 if (ResolveDescriptor(ws, key.ctx, key.BtfAddress(), d)) 1795 bool fExchangeOk = false;
1796 if (ResolveDescriptor(ws, key.ctx, key.BtfAddress(), d, fExchangeOk))
1765 { 1797 {
1766 LogPrint("nostr", "Nostr: .btf self-resolve OK via %s (meeting_node=%s, enc=%s...)\n", 1798 LogPrint("nostr", "Nostr: .btf self-resolve OK via %s (meeting_node=%s, enc=%s...)\n",
1767 pszNostrRelays[i], d.meeting_node.c_str(), d.enc.substr(0, 16).c_str()); 1799 pszNostrRelays[i], d.meeting_node.c_str(), d.enc.substr(0, 16).c_str());
1768 lease.fOk = true; 1800 lease.fOk = fExchangeOk;
1769 break; 1801 break;
1770 } 1802 }
1771 lease.fOk = true; 1803 // Same rule as BtfResolve: a socket only goes back in the pool if
1804 // the relay finished the conversation on it.
1805 lease.fOk = fExchangeOk;
1772 } 1806 }
1773 CATCH_PRINT_EXCEPTION("btf self-test") 1807 CATCH_PRINT_EXCEPTION("btf self-test")
1774 } 1808 }