upleb.uk

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

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--relay/btfrv.cpp97
-rw-r--r--src/net.cpp194
-rw-r--r--src/net.h22
-rw-r--r--src/nostr.cpp4
4 files changed, 287 insertions, 30 deletions
diff --git a/relay/btfrv.cpp b/relay/btfrv.cpp
index b1eee55..833c47c 100644
--- a/relay/btfrv.cpp
+++ b/relay/btfrv.cpp
@@ -31,6 +31,7 @@ typedef int SOCKET;
31#include <cstring> 31#include <cstring>
32#include <cstdio> 32#include <cstdio>
33#include <string> 33#include <string>
34#include <deque>
34#include <map> 35#include <map>
35#include <thread> 36#include <thread>
36#include <mutex> 37#include <mutex>
@@ -44,7 +45,24 @@ static std::atomic<bool> g_stop(false);
44static SOCKET g_listen = INVALID_SOCKET; 45static SOCKET g_listen = INVALID_SOCKET;
45 46
46// Services that have registered and are waiting for a client, keyed by pubkey. 47// Services that have registered and are waiting for a client, keyed by pubkey.
47static std::map<std::string, SOCKET> g_waiting; 48// More than one registration per node, on purpose.
49//
50// This used to be one socket per pubkey, and a dial *consumed* it: the first
51// caller took the registration and everyone else was told "not found" until the
52// node noticed and registered again. A node re-registers only after its pairing
53// has been handed off, so between the two there is a window where it is
54// unreachable to the whole network. Measured from a node's own counters: half
55// of all outbound dials failed that way -- 15 of 30 in eleven minutes -- while
56// descriptors resolved 53 times out of 54 and the node's own registrations
57// paired 11 times out of 12. The rendezvous was not losing anybody; it was
58// letting one caller in at a time.
59//
60// A queue per key lets a node keep spare registrations parked, so a dial that
61// arrives while another is being paired finds the next one instead of a closed
62// door. Old nodes register exactly as before and simply keep one entry: the
63// wire protocol does not change, so a relay running this serves both.
64static const size_t MAX_WAITING_PER_KEY = 4;
65static std::map<std::string, std::deque<SOCKET> > g_waiting;
48static std::mutex g_mtx; 66static std::mutex g_mtx;
49 67
50// How often the reaper walks the waiting map. 68// How often the reaper walks the waiting map.
@@ -150,16 +168,24 @@ static void ReapDeadRegistrations()
150 int nBefore = 0, nReaped = 0; 168 int nBefore = 0, nReaped = 0;
151 { 169 {
152 std::lock_guard<std::mutex> lk(g_mtx); 170 std::lock_guard<std::mutex> lk(g_mtx);
153 nBefore = (int)g_waiting.size(); 171 std::map<std::string, std::deque<SOCKET> >::iterator it = g_waiting.begin();
154 std::map<std::string, SOCKET>::iterator it = g_waiting.begin();
155 while (it != g_waiting.end()) 172 while (it != g_waiting.end())
156 { 173 {
157 if (RegistrationDead(it->second)) 174 std::deque<SOCKET>& q = it->second;
175 nBefore += (int)q.size();
176 for (std::deque<SOCKET>::iterator jt = q.begin(); jt != q.end();)
158 { 177 {
159 CLOSESOCK(it->second); 178 if (RegistrationDead(*jt))
160 g_waiting.erase(it++); 179 {
161 nReaped++; 180 CLOSESOCK(*jt);
181 jt = q.erase(jt);
182 nReaped++;
183 }
184 else
185 ++jt;
162 } 186 }
187 if (q.empty())
188 g_waiting.erase(it++);
163 else 189 else
164 ++it; 190 ++it;
165 } 191 }
@@ -209,34 +235,57 @@ static void HandleIncoming(SOCKET s)
209 { 235 {
210 // Register and wait; a future client will pair us. 236 // Register and wait; a future client will pair us.
211 std::lock_guard<std::mutex> lk(g_mtx); 237 std::lock_guard<std::mutex> lk(g_mtx);
212 // If a stale service for this key exists, drop it. 238 std::deque<SOCKET>& q = g_waiting[pubkey];
213 std::map<std::string, SOCKET>::iterator it = g_waiting.find(pubkey); 239
214 if (it != g_waiting.end()) CLOSESOCK(it->second); 240 // Drop registrations this node has already lost the far end of, rather
215 g_waiting[pubkey] = s; 241 // than dropping the previous one on principle. The old code closed the
216 // leave the socket open in the map; do not close here. 242 // prior registration on every new one, which is why a node could never
243 // hold a spare.
244 for (std::deque<SOCKET>::iterator it = q.begin(); it != q.end();)
245 {
246 if (RegistrationDead(*it)) { CLOSESOCK(*it); it = q.erase(it); }
247 else ++it;
248 }
249
250 // A ceiling, because a stranger can open these. Oldest goes first: it
251 // is the one most likely to be stale.
252 while (q.size() >= MAX_WAITING_PER_KEY)
253 {
254 CLOSESOCK(q.front());
255 q.pop_front();
256 }
257 q.push_back(s);
258 // leave the socket open in the queue; do not close here.
217 } 259 }
218 else if (role == 'C') 260 else if (role == 'C')
219 { 261 {
220 SOCKET svc = INVALID_SOCKET; 262 SOCKET svc = INVALID_SOCKET;
221 { 263 {
222 std::lock_guard<std::mutex> lk(g_mtx); 264 std::lock_guard<std::mutex> lk(g_mtx);
223 std::map<std::string, SOCKET>::iterator it = g_waiting.find(pubkey); 265 std::map<std::string, std::deque<SOCKET> >::iterator it = g_waiting.find(pubkey);
224 if (it != g_waiting.end()) 266 if (it != g_waiting.end())
225 { 267 {
226 // Check before handing it over. Pairing a client with a dead 268 std::deque<SOCKET>& q = it->second;
227 // registration produces a tunnel that fails after both sides 269 // Take the oldest one that is still alive, discarding the dead
228 // think they succeeded, which is the failure the dialling node 270 // as we go. Checking before handing it over matters: pairing a
229 // reports as "tunnel to X failed" with nothing to explain it. 271 // client with a dead registration produces a tunnel that fails
230 if (RegistrationDead(it->second)) 272 // after both sides think they succeeded, which is the failure
273 // the dialling node reports as "tunnel to X failed" with
274 // nothing to explain it.
275 while (!q.empty())
231 { 276 {
232 CLOSESOCK(it->second); 277 SOCKET candidate = q.front();
233 g_waiting.erase(it); 278 q.pop_front();
279 if (RegistrationDead(candidate))
280 {
281 CLOSESOCK(candidate);
282 continue;
283 }
284 svc = candidate;
285 break;
234 } 286 }
235 else 287 if (q.empty())
236 {
237 svc = it->second;
238 g_waiting.erase(it); 288 g_waiting.erase(it);
239 }
240 } 289 }
241 } 290 }
242 if (svc == INVALID_SOCKET) 291 if (svc == INVALID_SOCKET)
diff --git a/src/net.cpp b/src/net.cpp
index 7091981..449d91c 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -51,6 +51,103 @@ int64 nBlocksReceived = 0;
51int64 nBlocksWithoutParent = 0; 51int64 nBlocksWithoutParent = 0;
52static int64 nNodeStartTime = 0; 52static int64 nNodeStartTime = 0;
53 53
54static CCriticalSection cs_btfChurn;
55static int64 nBtfResolveAttempts = 0;
56static int64 nBtfResolveOk = 0;
57static int64 nBtfResolveMiss = 0;
58static int64 nBtfDialAttempts = 0;
59static int64 nBtfDialOk = 0;
60static int64 nBtfDialFailed = 0;
61static int64 nBtfRegisterOk = 0;
62static int64 nBtfRegisterFailed = 0;
63static int64 nBtfPairOk = 0;
64static int64 nBtfPairFailed = 0;
65static int64 nBtfHandshakeNoRecv = 0;
66static int64 nBtfHandshakeNoSend = 0;
67static int64 nBtfHandshakeSilent = 0;
68static string strBtfLastDialFail;
69static string strBtfLastHandshakeTimeout;
70
71void BtfChurnNoteResolveAttempt()
72{
73 CRITICAL_BLOCK(cs_btfChurn)
74 nBtfResolveAttempts++;
75}
76
77void BtfChurnNoteResolveResult(bool fOk)
78{
79 CRITICAL_BLOCK(cs_btfChurn)
80 {
81 if (fOk) nBtfResolveOk++;
82 else nBtfResolveMiss++;
83 }
84}
85
86void BtfChurnNoteDialAttempt(const string& strBtfAddr, const string& strMeeting)
87{
88 CRITICAL_BLOCK(cs_btfChurn)
89 nBtfDialAttempts++;
90 LogPrint("net", "btfchurn: dial attempt addr=%s meeting=%s\n",
91 strBtfAddr.c_str(), strMeeting.c_str());
92}
93
94void BtfChurnNoteDialResult(const string& strBtfAddr, const string& strMeeting, bool fOk)
95{
96 CRITICAL_BLOCK(cs_btfChurn)
97 {
98 if (fOk)
99 nBtfDialOk++;
100 else
101 {
102 nBtfDialFailed++;
103 strBtfLastDialFail = strBtfAddr + " via " + strMeeting;
104 }
105 }
106 LogPrint("net", "btfchurn: dial %s addr=%s meeting=%s\n",
107 fOk ? "ok" : "failed", strBtfAddr.c_str(), strMeeting.c_str());
108}
109
110void BtfChurnNoteRegisterResult(const string& strMeeting, bool fOk)
111{
112 CRITICAL_BLOCK(cs_btfChurn)
113 {
114 if (fOk) nBtfRegisterOk++;
115 else nBtfRegisterFailed++;
116 }
117 LogPrint("net", "btfchurn: register %s meeting=%s\n",
118 fOk ? "ok" : "failed", strMeeting.c_str());
119}
120
121void BtfChurnNotePairResult(const string& strMeeting, bool fOk)
122{
123 CRITICAL_BLOCK(cs_btfChurn)
124 {
125 if (fOk) nBtfPairOk++;
126 else nBtfPairFailed++;
127 }
128 LogPrint("net", "btfchurn: pair %s meeting=%s\n",
129 fOk ? "ok" : "timeout-or-drop", strMeeting.c_str());
130}
131
132void BtfChurnNoteHandshakeTimeout(const string& strBtfAddr, const string& strMeeting,
133 bool fRecv, bool fSend)
134{
135 CRITICAL_BLOCK(cs_btfChurn)
136 {
137 if (!fRecv && !fSend)
138 nBtfHandshakeSilent++;
139 else
140 {
141 if (!fRecv) nBtfHandshakeNoRecv++;
142 if (!fSend) nBtfHandshakeNoSend++;
143 }
144 strBtfLastHandshakeTimeout = strprintf("%s via %s recv=%d send=%d",
145 strBtfAddr.empty() ? "unknown" : strBtfAddr.c_str(),
146 strMeeting.empty() ? "unknown" : strMeeting.c_str(),
147 fRecv, fSend);
148 }
149}
150
54// --- Socket accounting (implementation lives in sockcount.h) -------------- 151// --- Socket accounting (implementation lives in sockcount.h) --------------
55static const char* pszSockSite[SOCK_SITES] = { 152static const char* pszSockSite[SOCK_SITES] = {
56 "external-ip probe", "listen socket", "inbound accept", 153 "external-ip probe", "listen socket", "inbound accept",
@@ -199,19 +296,64 @@ string GetDiagnosticsText()
199 296
200 str += SockAccountingText(); 297 str += SockAccountingText();
201 298
202 str += "\n peer dir height last recv last send unsent\n"; 299 int64 nResolveAttempts, nResolveOk, nResolveMiss;
300 int64 nDialAttempts, nDialOk, nDialFailed;
301 int64 nRegisterOk, nRegisterFailed, nPairOk, nPairFailed;
302 int64 nHandshakeNoRecv, nHandshakeNoSend, nHandshakeSilent;
303 string strLastDialFail, strLastHandshakeTimeout;
304 CRITICAL_BLOCK(cs_btfChurn)
305 {
306 nResolveAttempts = nBtfResolveAttempts;
307 nResolveOk = nBtfResolveOk;
308 nResolveMiss = nBtfResolveMiss;
309 nDialAttempts = nBtfDialAttempts;
310 nDialOk = nBtfDialOk;
311 nDialFailed = nBtfDialFailed;
312 nRegisterOk = nBtfRegisterOk;
313 nRegisterFailed = nBtfRegisterFailed;
314 nPairOk = nBtfPairOk;
315 nPairFailed = nBtfPairFailed;
316 nHandshakeNoRecv = nBtfHandshakeNoRecv;
317 nHandshakeNoSend = nBtfHandshakeNoSend;
318 nHandshakeSilent = nBtfHandshakeSilent;
319 strLastDialFail = strBtfLastDialFail;
320 strLastHandshakeTimeout = strBtfLastHandshakeTimeout;
321 }
322 str += "\n .btf churn\n";
323 str += strprintf(" resolves %lld attempts, %lld ok, %lld no descriptor\n",
324 (long long)nResolveAttempts, (long long)nResolveOk,
325 (long long)nResolveMiss);
326 str += strprintf(" outbound dials %lld attempts, %lld ok, %lld failed\n",
327 (long long)nDialAttempts, (long long)nDialOk,
328 (long long)nDialFailed);
329 str += strprintf(" rendezvous local register ok/fail %lld/%lld, pair ok/drop %lld/%lld\n",
330 (long long)nRegisterOk, (long long)nRegisterFailed,
331 (long long)nPairOk, (long long)nPairFailed);
332 str += strprintf(" handshake grace silent %lld, no recv %lld, no send %lld\n",
333 (long long)nHandshakeSilent, (long long)nHandshakeNoRecv,
334 (long long)nHandshakeNoSend);
335 if (!strLastDialFail.empty())
336 str += strprintf(" last dial failure %s\n", strLastDialFail.c_str());
337 if (!strLastHandshakeTimeout.empty())
338 str += strprintf(" last handshake %s\n", strLastHandshakeTimeout.c_str());
339
340 str += "\n peer dir height last recv last send unsent via\n";
203 foreach(CNode* pnode, vCopy) 341 foreach(CNode* pnode, vCopy)
204 { 342 {
205 int nSendSize = 0; 343 int nSendSize = 0;
206 TRY_CRITICAL_BLOCK(pnode->cs_vSend) 344 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
207 nSendSize = (int)pnode->vSend.size(); 345 nSendSize = (int)pnode->vSend.size();
208 str += strprintf(" %-28s %-4s %6d %10s %10s %7d\n", 346 string strVia;
347 if (!pnode->strBtfMeeting.empty())
348 strVia = pnode->strBtfMeeting;
349 str += strprintf(" %-28s %-4s %6d %10s %10s %7d %s\n",
209 pnode->addr.ToString().substr(0, 28).c_str(), 350 pnode->addr.ToString().substr(0, 28).c_str(),
210 pnode->fInbound ? "in" : "out", 351 pnode->fInbound ? "in" : "out",
211 pnode->nStartingHeight, 352 pnode->nStartingHeight,
212 FormatAge(pnode->nLastRecv ? nNow - pnode->nLastRecv : -1).c_str(), 353 FormatAge(pnode->nLastRecv ? nNow - pnode->nLastRecv : -1).c_str(),
213 FormatAge(pnode->nLastSend ? nNow - pnode->nLastSend : -1).c_str(), 354 FormatAge(pnode->nLastSend ? nNow - pnode->nLastSend : -1).c_str(),
214 nSendSize); 355 nSendSize,
356 strVia.substr(0, 28).c_str());
215 } 357 }
216 return str; 358 return str;
217} 359}
@@ -814,30 +956,41 @@ static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char p
814 return pnode; 956 return pnode;
815 } 957 }
816 958
959 BtfChurnNoteDialAttempt(strBtfAddr, strMeeting);
817 size_t colon = strMeeting.rfind(':'); 960 size_t colon = strMeeting.rfind(':');
818 if (colon == string::npos) 961 if (colon == string::npos)
962 {
963 BtfChurnNoteDialResult(strBtfAddr, strMeeting, false);
819 return NULL; 964 return NULL;
965 }
820 string strHost = strMeeting.substr(0, colon); 966 string strHost = strMeeting.substr(0, colon);
821 int nPort = atoi(strMeeting.substr(colon + 1).c_str()); 967 int nPort = atoi(strMeeting.substr(colon + 1).c_str());
822 if (nPort <= 0 || nPort > 65535) 968 if (nPort <= 0 || nPort > 65535)
969 {
970 BtfChurnNoteDialResult(strBtfAddr, strMeeting, false);
823 return NULL; 971 return NULL;
972 }
824 973
825 btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub); 974 btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub);
826 if (hSocket == INVALID_SOCKET) 975 if (hSocket == INVALID_SOCKET)
827 { 976 {
828 if (fDebug) 977 if (fDebug)
829 LogPrint("net", "ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); 978 LogPrint("net", "ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str());
979 BtfChurnNoteDialResult(strBtfAddr, strMeeting, false);
830 return NULL; 980 return NULL;
831 } 981 }
832 982
833 if (fDebug) 983 if (fDebug)
834 LogPrint("net", "connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); 984 LogPrint("net", "connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str());
985 BtfChurnNoteDialResult(strBtfAddr, strMeeting, true);
835 986
836 // This one answered -- worth trying first next time we start. 987 // This one answered -- worth trying first next time we start.
837 RememberBtfPeer(strBtfAddr, strMeeting, enc_pub); 988 RememberBtfPeer(strBtfAddr, strMeeting, enc_pub);
838 989
839 // Add node 990 // Add node
840 pnode = new CNode(hSocket, addr, false); 991 pnode = new CNode(hSocket, addr, false);
992 pnode->strBtfAddr = strBtfAddr;
993 pnode->strBtfMeeting = strMeeting;
841 pnode->AddRef(); 994 pnode->AddRef();
842 CRITICAL_BLOCK(cs_vNodes) 995 CRITICAL_BLOCK(cs_vNodes)
843 vNodes.push_back(pnode); 996 vNodes.push_back(pnode);
@@ -863,12 +1016,15 @@ CNode* ConnectNodeBtf(const string& strBtfAddr)
863 1016
864 string strMeeting; 1017 string strMeeting;
865 unsigned char enc_pub[32]; 1018 unsigned char enc_pub[32];
1019 BtfChurnNoteResolveAttempt();
866 if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) 1020 if (!BtfResolve(strBtfAddr, strMeeting, enc_pub))
867 { 1021 {
1022 BtfChurnNoteResolveResult(false);
868 if (fDebug) 1023 if (fDebug)
869 LogPrint("net", "ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); 1024 LogPrint("net", "ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str());
870 return NULL; 1025 return NULL;
871 } 1026 }
1027 BtfChurnNoteResolveResult(true);
872 return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, enc_pub); 1028 return ConnectNodeBtfTail(strBtfAddr, pk, strMeeting, enc_pub);
873} 1029}
874 1030
@@ -951,12 +1107,14 @@ void ThreadBtfAccept(void* parg)
951 } 1107 }
952 if (rv == btf::RV_INVALID) 1108 if (rv == btf::RV_INVALID)
953 { 1109 {
1110 BtfChurnNoteRegisterResult(strMeeting, false);
954 LogPrint("net", "rendezvous: could not register at %s, trying another\n", 1111 LogPrint("net", "rendezvous: could not register at %s, trying another\n",
955 strMeeting.c_str()); 1112 strMeeting.c_str());
956 iRelay++; // this relay is down/attacked -> try the next one 1113 iRelay++; // this relay is down/attacked -> try the next one
957 Sleep(3000); 1114 Sleep(3000);
958 continue; 1115 continue;
959 } 1116 }
1117 BtfChurnNoteRegisterResult(strMeeting, true);
960 1118
961 // Registered. Advertise THIS relay now, while we are listed and before 1119 // Registered. Advertise THIS relay now, while we are listed and before
962 // anybody dials -- a descriptor naming it is what makes a dial possible 1120 // anybody dials -- a descriptor naming it is what makes a dial possible
@@ -971,7 +1129,9 @@ void ThreadBtfAccept(void* parg)
971 // here, the node stopped being reachable, and nothing in the log said 1129 // here, the node stopped being reachable, and nothing in the log said
972 // so. Re-registering every few minutes when nobody has dialled costs 1130 // so. Re-registering every few minutes when nobody has dialled costs
973 // one reconnect and removes the whole failure mode. 1131 // one reconnect and removes the whole failure mode.
974 if (!btf::RvServiceWaitPaired(rv, BTF_RENDEZVOUS_WAIT_SECS)) 1132 bool fPaired = btf::RvServiceWaitPaired(rv, BTF_RENDEZVOUS_WAIT_SECS);
1133 BtfChurnNotePairResult(strMeeting, fPaired);
1134 if (!fPaired)
975 { 1135 {
976 btf::RvClose(rv); 1136 btf::RvClose(rv);
977 LogPrint("net", "rendezvous: no dial at %s within %ds (or it dropped us), " 1137 LogPrint("net", "rendezvous: no dial at %s within %ds (or it dropped us), "
@@ -981,7 +1141,11 @@ void ThreadBtfAccept(void* parg)
981 1141
982 btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk); 1142 btf_socket_t hSocket = btf::BtfServiceWrap(rv, sk);
983 if (hSocket == INVALID_SOCKET) 1143 if (hSocket == INVALID_SOCKET)
1144 {
1145 LogPrint("net", "rendezvous: paired at %s but channel setup failed\n",
1146 strMeeting.c_str());
984 continue; 1147 continue;
1148 }
985 1149
986 // The dialer is anonymous (its pubkey never reaches us), so tag the 1150 // The dialer is anonymous (its pubkey never reaches us), so tag the
987 // connection with a random marker address. 1151 // connection with a random marker address.
@@ -991,6 +1155,8 @@ void ThreadBtfAccept(void* parg)
991 1155
992 LogPrint("net", "accepted .btf connection via rendezvous %s\n", strMeeting.c_str()); 1156 LogPrint("net", "accepted .btf connection via rendezvous %s\n", strMeeting.c_str());
993 CNode* pnode = new CNode(hSocket, addr, true); 1157 CNode* pnode = new CNode(hSocket, addr, true);
1158 pnode->strBtfAddr = "inbound";
1159 pnode->strBtfMeeting = strMeeting;
994 pnode->AddRef(); 1160 pnode->AddRef();
995 CRITICAL_BLOCK(cs_vNodes) 1161 CRITICAL_BLOCK(cs_vNodes)
996 vNodes.push_back(pnode); 1162 vNodes.push_back(pnode);
@@ -1457,6 +1623,9 @@ void ThreadSocketHandler2(void* parg)
1457 { 1623 {
1458 if (pnode->nLastRecv == 0 || pnode->nLastSend == 0) 1624 if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
1459 { 1625 {
1626 if (!pnode->strBtfMeeting.empty())
1627 BtfChurnNoteHandshakeTimeout(pnode->strBtfAddr, pnode->strBtfMeeting,
1628 pnode->nLastRecv != 0, pnode->nLastSend != 0);
1460 LogPrint("net", "socket no message in first %d seconds, recv=%d send=%d\n", 1629 LogPrint("net", "socket no message in first %d seconds, recv=%d send=%d\n",
1461 BTF_HANDSHAKE_GRACE_SECS, pnode->nLastRecv != 0, pnode->nLastSend != 0); 1630 BTF_HANDSHAKE_GRACE_SECS, pnode->nLastRecv != 0, pnode->nLastSend != 0);
1462 pnode->fDisconnect = true; 1631 pnode->fDisconnect = true;
@@ -1803,9 +1972,22 @@ bool StartNode(string& strError)
1803 1972
1804 // Anonymous inbound: this node's .btf hidden service, reachable through the 1973 // Anonymous inbound: this node's .btf hidden service, reachable through the
1805 // meeting relay without exposing our IP or needing a public port. 1974 // meeting relay without exposing our IP or needing a public port.
1975 //
1976 // Several of these, not one. Each holds its own registration at a
1977 // rendezvous, and a registration is consumed the moment a caller is paired
1978 // with it -- so with a single thread the node is unreachable for the whole
1979 // gap between being paired and registering again. Measured from a node's
1980 // own counters: half of all outbound dials failed for exactly that reason,
1981 // 15 of 30 in eleven minutes, while descriptors resolved 53 times out of
1982 // 54. Spares parked at the relay close that gap.
1983 //
1984 // Needs a relay that keeps more than one registration per node; against an
1985 // older relay the extra threads are harmless, because it drops the previous
1986 // registration on each new one and the node ends up where it started.
1806 if (!vBtfMeetingRelays.empty()) 1987 if (!vBtfMeetingRelays.empty())
1807 if (_beginthread(ThreadBtfAccept, 0, NULL) == -1) 1988 for (int i = 0; i < BTF_ACCEPT_THREADS; i++)
1808 printf("Error: _beginthread(ThreadBtfAccept) failed\n"); 1989 if (_beginthread(ThreadBtfAccept, 0, NULL) == -1)
1990 printf("Error: _beginthread(ThreadBtfAccept) failed\n");
1809 1991
1810 // Anonymous outbound: keep a connection to a specific .btf peer, if asked. 1992 // Anonymous outbound: keep a connection to a specific .btf peer, if asked.
1811 if (!strBtfConnect.empty()) 1993 if (!strBtfConnect.empty())
diff --git a/src/net.h b/src/net.h
index 37f335e..59a8028 100644
--- a/src/net.h
+++ b/src/net.h
@@ -54,6 +54,12 @@ static const int64 PEX_MIN_INTERVAL = 60;
54// traffic is nothing: one per relay per five minutes. 54// traffic is nothing: one per relay per five minutes.
55static const int BTF_RENDEZVOUS_WAIT_SECS = 300; 55static const int BTF_RENDEZVOUS_WAIT_SECS = 300;
56 56
57// Registrations this node parks at a rendezvous at once. One is what the
58// protocol needs; more is what keeps the node reachable while one of them is
59// being paired, since pairing consumes the registration it used. Four matches
60// the relay's ceiling per node.
61static const int BTF_ACCEPT_THREADS = 3;
62
57// Liveness of an established peer connection. BTF_RENDEZVOUS_WAIT_SECS bounds 63// Liveness of an established peer connection. BTF_RENDEZVOUS_WAIT_SECS bounds
58// the wait for a dial to arrive; these bound the connection that comes out of 64// the wait for a dial to arrive; these bound the connection that comes out of
59// it, which has the same failure mode once it goes quiet. 65// it, which has the same failure mode once it goes quiet.
@@ -139,6 +145,18 @@ extern int nBestHeight;
139// absurd height cannot move it. 145// absurd height cannot move it.
140int GetPeerMedianHeight(); 146int GetPeerMedianHeight();
141 147
148// Lightweight .btf churn accounting. These counters are diagnostics only: they
149// let an operator tell stale descriptors, relay pairing failures, and paired
150// tunnels that never speak apart before changing behaviour.
151void BtfChurnNoteResolveAttempt();
152void BtfChurnNoteResolveResult(bool fOk);
153void BtfChurnNoteDialAttempt(const string& strBtfAddr, const string& strMeeting);
154void BtfChurnNoteDialResult(const string& strBtfAddr, const string& strMeeting, bool fOk);
155void BtfChurnNoteRegisterResult(const string& strMeeting, bool fOk);
156void BtfChurnNotePairResult(const string& strMeeting, bool fOk);
157void BtfChurnNoteHandshakeTimeout(const string& strBtfAddr, const string& strMeeting,
158 bool fRecv, bool fSend);
159
142// Descriptors to hand a peer: ours first, then peers that answered us. 160// Descriptors to hand a peer: ours first, then peers that answered us.
143void BtfPexCollect(std::vector<std::string>& vDescOut); 161void BtfPexCollect(std::vector<std::string>& vDescOut);
144// Verify descriptors a peer sent and remember the good ones. Returns how many. 162// Verify descriptors a peer sent and remember the good ones. Returns how many.
@@ -606,6 +624,8 @@ public:
606 int64 nIncompleteMessageStart; 624 int64 nIncompleteMessageStart;
607 unsigned int nIncompleteMessageSize; 625 unsigned int nIncompleteMessageSize;
608 string strIncompleteMessageCommand; 626 string strIncompleteMessageCommand;
627 string strBtfAddr;
628 string strBtfMeeting;
609 629
610 // Height this peer announced in its version message, or -1 if it sent a 630 // Height this peer announced in its version message, or -1 if it sent a
611 // version message that predates the field. Knowing how far along everyone 631 // version message that predates the field. Knowing how far along everyone
@@ -639,6 +659,8 @@ public:
639 nIncompleteMessageStart = 0; 659 nIncompleteMessageStart = 0;
640 nIncompleteMessageSize = 0; 660 nIncompleteMessageSize = 0;
641 strIncompleteMessageCommand.clear(); 661 strIncompleteMessageCommand.clear();
662 strBtfAddr.clear();
663 strBtfMeeting.clear();
642 nStartingHeight = -1; 664 nStartingHeight = -1;
643 vfSubscribe.assign(256, false); 665 vfSubscribe.assign(256, false);
644 666
diff --git a/src/nostr.cpp b/src/nostr.cpp
index dc96be2..1fd60ad 100644
--- a/src/nostr.cpp
+++ b/src/nostr.cpp
@@ -1596,6 +1596,8 @@ static void ConnectDiscoveredBtfPeers()
1596 // handshakes refused, which then looks exactly like "dead peers" even 1596 // handshakes refused, which then looks exactly like "dead peers" even
1597 // though the descriptors were perfectly resolvable. 1597 // though the descriptors were perfectly resolvable.
1598 map<string, BtfResolvedPeer> resolved; 1598 map<string, BtfResolvedPeer> resolved;
1599 for (const string& addr : candidates)
1600 BtfChurnNoteResolveAttempt();
1599 BtfResolveMany(candidates, resolved); 1601 BtfResolveMany(candidates, resolved);
1600 1602
1601 // Candidates whose descriptor didn't resolve this pass are recorded as a 1603 // Candidates whose descriptor didn't resolve this pass are recorded as a
@@ -1608,9 +1610,11 @@ static void ConnectDiscoveredBtfPeers()
1608 { 1610 {
1609 if (resolved.count(addr)) 1611 if (resolved.count(addr))
1610 { 1612 {
1613 BtfChurnNoteResolveResult(true);
1611 toDial.push_back(addr); 1614 toDial.push_back(addr);
1612 continue; 1615 continue;
1613 } 1616 }
1617 BtfChurnNoteResolveResult(false);
1614 int n = ++g_btfPeerFails[addr]; 1618 int n = ++g_btfPeerFails[addr];
1615 int64 cap = (n >= BTF_CHRONIC_FAIL_THRESHOLD) ? BTF_BACKOFF_CAP_SECS_DEAD 1619 int64 cap = (n >= BTF_CHRONIC_FAIL_THRESHOLD) ? BTF_BACKOFF_CAP_SECS_DEAD
1616 : BTF_BACKOFF_CAP_SECS; 1620 : BTF_BACKOFF_CAP_SECS;