upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/net.cpp
diff options
context:
space:
mode:
authorBitflash <btf01@proton.me>2026-08-02 11:59:12 -0300
committerGitHub <noreply@github.com>2026-08-02 11:59:12 -0300
commit758ff4bc49af78b06e37fc1e99741cb5fa8e43d6 (patch)
tree4e9d4ed63430d7a442260d1be78d63878a2fc455 /src/net.cpp
parentfe04a2045c3080797c6f93bf7cd2e12192b8f68f (diff)
rendezvous: let a node park more than one registration (#113)
A registration was consumed by the first caller and registering again replaced the previous one, so a node was reachable to one peer at a time and unreachable for the gap between being paired and registering again. Measured on a live node: descriptors resolved 53/54, our own registrations paired 11/12, nothing paired and went silent -- and half of every outbound dial failed, 15 of 30 in eleven minutes. The relay keeps a queue per pubkey capped at four, pairs with the oldest still-alive registration and discards the dead; the node runs three accept threads, each holding its own registration. The wire protocol does not change, so either side can be deployed first. Bench with both relays built from source and waves of concurrent dialers: old+1 65%, new+1 58%, old+3 68%, new+3 98%. Each half alone lands within noise of doing nothing.
Diffstat (limited to 'src/net.cpp')
-rw-r--r--src/net.cpp194
1 files changed, 188 insertions, 6 deletions
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())