upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/btfrv.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 22:46:52 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-05 22:46:52 +0000
commitceae3d076b5ad11ee84729ae43f14e9312896646 (patch)
tree7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/btfrv.cpp
parentbe63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff)
parent66e0b9158d050c46b1ff2509faeee3f4573cac1c (diff)
Bring main onto the line this project actually ships
main had drifted to a history with no ancestor in common with the tree we build and deploy. It sat at be63e83 (2026-08-09) with 28 commits; the shipped line, deploy/soak-2026-08-21, had 383. The two shared nothing -- git merge-base returns no result at all -- so anyone cloning this repository landed on a default branch that was neither current nor related to the running node. The 28 commits on main were audited before this merge: every one is already present in the tree by content, including the two that are hardest to see because they were rewritten afterwards -- the payout reorg-safety work (PayoutBlockStillOnChain, since evolved to re-check on every retry rather than once at maturity) and the nostr relay certificate verification (SSL_set1_host, now with a Windows root-store path). Nothing on main is lost by this merge. Recorded as a merge rather than a force-push: main only ever moves forward, its old history stays reachable as the first parent, and this is revertible. The tree is taken wholesale from the merged branch, so main's content is now exactly what was built and tested. What this brings onto main: - compactblock: BIP152-style compact block relay ported from upstream 1.2.23, primitives and unit test only, with the RAND_bytes failure path closed and the reconstruction allocation bounded. - upstream/main 17741d9: isolated testnet parameters and genesis, optional 2 MB large pages for RandomX. Mainnet genesis verified byte-identical through upstream's BuildGenesisBlock() refactor. - deploy: every image built on bookworm so binaries run on Debian 12, and the container build now honours the RandomX and libsecp256k1 pins it had been silently ignoring since before 1.2.15. Gates at this tree: 15/15 selftests, compactblock's 17 checks, consensus constants unchanged against e4f898c apart from the pre-existing MAX_BLOCK_SIZE_GEN, mainnet genesis intact, money-log check ok.
Diffstat (limited to 'src/btfrv.cpp')
-rw-r--r--src/btfrv.cpp74
1 files changed, 69 insertions, 5 deletions
diff --git a/src/btfrv.cpp b/src/btfrv.cpp
index 905717f..7d705ef 100644
--- a/src/btfrv.cpp
+++ b/src/btfrv.cpp
@@ -5,6 +5,9 @@
5// Windows/Winsock implementation (Layer 6 will #ifdef this for Linux/BSD sockets). 5// Windows/Winsock implementation (Layer 6 will #ifdef this for Linux/BSD sockets).
6 6
7#include "btfrv.h" 7#include "btfrv.h"
8#ifdef BITFLASH_NODE_BUILD
9#include "proxy.h"
10#endif
8 11
9// Portable sockets: Winsock on Windows, BSD sockets on Linux/POSIX (for the 12// Portable sockets: Winsock on Windows, BSD sockets on Linux/POSIX (for the
10// headless relay `bitflashd` that runs on the VPS). 13// headless relay `bitflashd` that runs on the VPS).
@@ -37,6 +40,8 @@ typedef int SOCKET;
37#include <thread> 40#include <thread>
38#include <mutex> 41#include <mutex>
39#include <atomic> 42#include <atomic>
43#include <chrono>
44#include <system_error>
40 45
41namespace btf 46namespace btf
42{ 47{
@@ -92,6 +97,11 @@ static bool WriteN(SOCKET s, const void* buf, int n)
92 97
93static SOCKET ConnectTo(const char* host, unsigned short port) 98static SOCKET ConnectTo(const char* host, unsigned short port)
94{ 99{
100#ifdef BITFLASH_NODE_BUILD
101 SOCKET s = BtfConnectSocket(host, port, SOCK_RV_DIAL, 10);
102 if (s == INVALID_SOCKET)
103 return INVALID_SOCKET;
104#else
95 struct addrinfo hints, *res = NULL; 105 struct addrinfo hints, *res = NULL;
96 memset(&hints, 0, sizeof(hints)); 106 memset(&hints, 0, sizeof(hints));
97 hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 (some relays are v6-only) 107 hints.ai_family = AF_UNSPEC; // IPv4 or IPv6 (some relays are v6-only)
@@ -130,6 +140,7 @@ static SOCKET ConnectTo(const char* host, unsigned short port)
130 } 140 }
131 freeaddrinfo(res); 141 freeaddrinfo(res);
132 if (s == INVALID_SOCKET) return INVALID_SOCKET; 142 if (s == INVALID_SOCKET) return INVALID_SOCKET;
143#endif
133 144
134 // Clear the timeout -- the socket is now a live tunnel and must not 145 // Clear the timeout -- the socket is now a live tunnel and must not
135 // time out when idle. recv() will block until data arrives or peer closes. 146 // time out when idle. recv() will block until data arrives or peer closes.
@@ -200,8 +211,28 @@ static void HandleIncoming(SOCKET s)
200 bool a = WriteN(svc, &ok, 1); 211 bool a = WriteN(svc, &ok, 1);
201 bool b = WriteN(s, &ok, 1); 212 bool b = WriteN(s, &ok, 1);
202 if (!a || !b) { CLOSESOCK(svc); CLOSESOCK(s); return; } 213 if (!a || !b) { CLOSESOCK(svc); CLOSESOCK(s); return; }
203 std::thread(Forward, s, svc).detach(); 214 // One thread pumps s -> svc; this thread pumps svc -> s. Spawning a
204 std::thread(Forward, svc, s).detach(); 215 // thread per direction left no clean way to fail the second spawn:
216 // std::thread throws std::system_error when pthread_create is refused
217 // (NPROC limit, container pids cgroup, memory pressure), the first
218 // pump was already detached, and closing its sockets from here would
219 // race it -- so nothing caught the throw and it reached
220 // std::terminate, killing the relay over one pair it could not serve.
221 // Running one direction on this thread leaves exactly one spawn to
222 // guard, and a failure here has nothing running yet to race.
223 try
224 {
225 std::thread(Forward, s, svc).detach();
226 }
227 catch (const std::system_error& e)
228 {
229 fprintf(stderr, "rendezvous relay: cannot spawn forward thread (%s), pair dropped\n",
230 e.what());
231 CLOSESOCK(svc);
232 CLOSESOCK(s);
233 return;
234 }
235 Forward(svc, s);
205 } 236 }
206 else 237 else
207 { 238 {
@@ -227,6 +258,7 @@ bool RvRelayRun(unsigned short port)
227 if (bind(g_listen, (struct sockaddr*)&addr, sizeof(addr)) != 0) { CLOSESOCK(g_listen); return false; } 258 if (bind(g_listen, (struct sockaddr*)&addr, sizeof(addr)) != 0) { CLOSESOCK(g_listen); return false; }
228 if (listen(g_listen, 16) != 0) { CLOSESOCK(g_listen); return false; } 259 if (listen(g_listen, 16) != 0) { CLOSESOCK(g_listen); return false; }
229 260
261 int nSpawnFails = 0;
230 while (!g_stop) 262 while (!g_stop)
231 { 263 {
232 struct sockaddr_in cli; 264 struct sockaddr_in cli;
@@ -237,7 +269,27 @@ bool RvRelayRun(unsigned short port)
237 if (g_stop) break; 269 if (g_stop) break;
238 continue; 270 continue;
239 } 271 }
240 std::thread(HandleIncoming, s).detach(); 272 try
273 {
274 std::thread(HandleIncoming, s).detach();
275 nSpawnFails = 0;
276 }
277 catch (const std::system_error& e)
278 {
279 // A refused pthread_create is backpressure, not a reason to die:
280 // the condition behind it (NPROC, pids cgroup, memory) does not
281 // clear in milliseconds, and the connection count feeding it is
282 // attacker-influenced. Drop this one peer, stop accepting for a
283 // moment -- longer each time, capped -- and keep serving the
284 // connections already established.
285 fprintf(stderr, "rendezvous relay: cannot spawn connection thread (%s), connection dropped\n",
286 e.what());
287 CLOSESOCK(s);
288 nSpawnFails++;
289 int nWaitMs = nSpawnFails < 5 ? nSpawnFails * 1000 : 5000;
290 for (int i = 0; i < nWaitMs && !g_stop; i += 100)
291 std::this_thread::sleep_for(std::chrono::milliseconds(100));
292 }
241 } 293 }
242 CLOSESOCK(g_listen); 294 CLOSESOCK(g_listen);
243 g_listen = INVALID_SOCKET; 295 g_listen = INVALID_SOCKET;
@@ -325,17 +377,29 @@ bool RvServiceWaitPaired(RvSocket s, int nTimeoutSecs)
325} 377}
326 378
327RvSocket RvClientConnect(const char* relay_host, unsigned short port, 379RvSocket RvClientConnect(const char* relay_host, unsigned short port,
328 const unsigned char target_pubkey[32]) 380 const unsigned char target_pubkey[32],
381 RvClientFail* pFail)
329{ 382{
383 if (pFail) *pFail = RVC_FAIL_DIAL;
330 if (!RvInit()) return RV_INVALID; 384 if (!RvInit()) return RV_INVALID;
331 SOCKET s = ConnectTo(relay_host, port); 385 SOCKET s = ConnectTo(relay_host, port);
332 if (s == INVALID_SOCKET) return RV_INVALID; 386 if (s == INVALID_SOCKET) return RV_INVALID;
387 if (pFail) *pFail = RVC_FAIL_PAIR;
333 unsigned char hdr[33]; 388 unsigned char hdr[33];
334 hdr[0] = (unsigned char)'C'; 389 hdr[0] = (unsigned char)'C';
335 memcpy(hdr + 1, target_pubkey, 32); 390 memcpy(hdr + 1, target_pubkey, 32);
336 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; } 391 if (!WriteN(s, hdr, 33)) { CLOSESOCK(s); return RV_INVALID; }
337 unsigned char paired = 0; 392 unsigned char paired = 0;
338 if (!ReadN(s, &paired, 1) || paired != 0x01) { CLOSESOCK(s); return RV_INVALID; } 393 if (!ReadN(s, &paired, 1)) { CLOSESOCK(s); return RV_INVALID; }
394 if (paired != 0x01)
395 {
396 // The relay answered and declined: HandleIncoming writes 0x00 and
397 // closes when no service is parked under this pubkey right now.
398 if (pFail) *pFail = RVC_FAIL_REFUSED;
399 CLOSESOCK(s);
400 return RV_INVALID;
401 }
402 if (pFail) *pFail = RVC_FAIL_NONE;
339 return (RvSocket)s; 403 return (RvSocket)s;
340} 404}
341 405