diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 22:46:52 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 22:46:52 +0000 |
| commit | ceae3d076b5ad11ee84729ae43f14e9312896646 (patch) | |
| tree | 7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/btfrv.cpp | |
| parent | be63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff) | |
| parent | 66e0b9158d050c46b1ff2509faeee3f4573cac1c (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.cpp | 74 |
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 | ||
| 41 | namespace btf | 46 | namespace btf |
| 42 | { | 47 | { |
| @@ -92,6 +97,11 @@ static bool WriteN(SOCKET s, const void* buf, int n) | |||
| 92 | 97 | ||
| 93 | static SOCKET ConnectTo(const char* host, unsigned short port) | 98 | static 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 | ||
| 327 | RvSocket RvClientConnect(const char* relay_host, unsigned short port, | 379 | RvSocket 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 | ||