upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/rpc.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-08-08 02:29:26 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-19 22:48:26 +0000
commit541223daa6e6510daf3bdfdcb4dd4f6b0e38d877 (patch)
treeec09f2f0bf24cc737bf978b4152df8a5a2e7fc07 /src/rpc.cpp
parent68de575503c5df5e944278cecd3129cd10f2546e (diff)
pool: sync-gate job creation, retry failed payouts; widen nostr relay set
- RebuildJob: no jobs with zero peers or a tip older than 30min; mining unsynced produced orphaned blocks and payouts that can never be paid (observed in production: 8 orphan 'found blocks') - payout exec: on SendMoney failure requeue with 60s backoff, up to 30 attempts, instead of silently forfeiting the payment - nostr: add oxtr.dev, nos.social, fmt.wiz.biz, current.fyi, nostr.bg (12 relays total) for announcement redundancy (cherry picked from commit f1986cd7e4a6f69469a2256eb96ff03f3f146792)
Diffstat (limited to 'src/rpc.cpp')
-rw-r--r--src/rpc.cpp95
1 files changed, 91 insertions, 4 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp
index 41c0328..98b6208 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -143,6 +143,20 @@ static bool RebuildJob()
143 return false; 143 return false;
144 } 144 }
145 145
146 // Don't issue jobs until the chain is synced: mining against a stale
147 // tip produces blocks the network will orphan, and payout entries
148 // that can never be paid (their coinbases never exist on the main
149 // chain). Require at least one peer and a fresh tip.
150 if (vNodes.empty()) {
151 LogPrint("pool", "RebuildJob: no peers yet -- not issuing jobs\n");
152 return false;
153 }
154 if (GetAdjustedTime() - (int64)pindexBest->nTime > 30*60) {
155 LogPrint("pool", "RebuildJob: tip is %llds old -- still syncing, not issuing jobs\n",
156 (long long)(GetAdjustedTime() - (int64)pindexBest->nTime));
157 return false;
158 }
159
146 CBlockIndex* pindexPrev = pindexBest; 160 CBlockIndex* pindexPrev = pindexBest;
147 unsigned int nBits = GetNextWorkRequired(pindexPrev); 161 unsigned int nBits = GetNextWorkRequired(pindexPrev);
148 162
@@ -218,6 +232,8 @@ static bool RebuildJob()
218struct PendingPayout { 232struct PendingPayout {
219 int matureAtHeight; 233 int matureAtHeight;
220 std::map<std::string, int64> amounts; // address -> satoshis 234 std::map<std::string, int64> amounts; // address -> satoshis
235 int retries = 0; // exec-thread only: SendMoney attempts so far
236 int64 retryAfter = 0; // exec-thread only: unix time, 0 = ready now
221}; 237};
222static std::vector<PendingPayout> gPendingPayouts; 238static std::vector<PendingPayout> gPendingPayouts;
223 239
@@ -403,6 +419,11 @@ static void PayoutThreadFn(void*)
403 } 419 }
404 420
405 for (auto& pp : batch) { 421 for (auto& pp : batch) {
422 if (pp.retryAfter > GetTime()) {
423 std::lock_guard<std::mutex> lk(gPayoutExecMutex);
424 gPayoutExecQueue.push_back(pp);
425 continue;
426 }
406 LogPrint("payout", "[payout] executing payments for block matured at %d" 427 LogPrint("payout", "[payout] executing payments for block matured at %d"
407 " (%zu miners)\n", pp.matureAtHeight, pp.amounts.size()); 428 " (%zu miners)\n", pp.matureAtHeight, pp.amounts.size());
408 for (auto& kv : pp.amounts) { 429 for (auto& kv : pp.amounts) {
@@ -418,13 +439,34 @@ static void PayoutThreadFn(void*)
418 CWalletTx wtx; 439 CWalletTx wtx;
419 wtx.mapValue["comment"] = strprintf("Pool payout height %d", 440 wtx.mapValue["comment"] = strprintf("Pool payout height %d",
420 pp.matureAtHeight); 441 pp.matureAtHeight);
421 if (SendMoney(sc, kv.second, wtx)) 442 if (SendMoney(sc, kv.second, wtx)) {
422 LogPrint("payout", "[payout] paid %s to %s\n", 443 LogPrint("payout", "[payout] paid %s to %s\n",
423 FormatMoney(kv.second).c_str(), kv.first.c_str()); 444 FormatMoney(kv.second).c_str(), kv.first.c_str());
424 else 445 } else if (pp.retries < 30) {
446 // Transient failures (immature coinbase, reorg, locked
447 // wallet) must not silently burn the payment -- requeue
448 // with backoff. Permanent cases (orphaned coinbase) give
449 // up loudly after 30 tries (~30 min).
450 PendingPayout rp;
451 rp.matureAtHeight = pp.matureAtHeight;
452 rp.amounts[kv.first] = kv.second;
453 rp.retries = pp.retries + 1;
454 rp.retryAfter = GetTime() + 60;
455 {
456 std::lock_guard<std::mutex> lk(gPayoutExecMutex);
457 gPayoutExecQueue.push_back(std::move(rp));
458 }
425 LogPrint("payout", "[payout] FAILED to pay %s to %s" 459 LogPrint("payout", "[payout] FAILED to pay %s to %s"
426 " (insufficient funds or wallet locked?)\n", 460 " -- retry %d/30 in 60s\n",
427 FormatMoney(kv.second).c_str(), kv.first.c_str()); 461 FormatMoney(kv.second).c_str(), kv.first.c_str(),
462 pp.retries + 1);
463 } else {
464 LogPrint("payout", "[payout] GIVING UP on %s to %s after %d"
465 " retries (orphaned coinbase or persistent wallet"
466 " error?) -- payment forfeited\n",
467 FormatMoney(kv.second).c_str(), kv.first.c_str(),
468 pp.retries);
469 }
428 } 470 }
429 } 471 }
430 472
@@ -543,6 +585,45 @@ static void PushReady(btf_socket_t s)
543 gReadyQueue.push_back(s); 585 gReadyQueue.push_back(s);
544} 586}
545 587
588// Loopback stratum listener: lets a miner running on the SAME machine reach
589// this pool over plain TCP (127.0.0.1:8334), skipping the .btf rendezvous
590// entirely. The rendezvous relay only allows one service registration per
591// pubkey, so the pool service and the node's own peer-accept fight over the
592// node identity at the relay -- loopback avoids that fight for local miners.
593static void LocalAcceptFn(void*)
594{
595 SOCKET ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
596 if (ls == INVALID_SOCKET)
597 return;
598 int yes = 1;
599 setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
600 struct sockaddr_in addr;
601 memset(&addr, 0, sizeof(addr));
602 addr.sin_family = AF_INET;
603 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
604 addr.sin_port = htons(8334);
605 if (bind(ls, (struct sockaddr*)&addr, sizeof(addr)) != 0 || listen(ls, 16) != 0)
606 {
607 LogPrint("pool", "[pool] loopback stratum bind/listen failed on 127.0.0.1:8334\n");
608 closesocket(ls);
609 return;
610 }
611 LogPrint("pool", "[pool] loopback stratum listening on 127.0.0.1:8334\n");
612 while (!fShutdown && gPoolRunning)
613 {
614 SOCKET c = accept(ls, NULL, NULL);
615 if (c == INVALID_SOCKET)
616 {
617 if (fShutdown || !gPoolRunning)
618 break;
619 continue;
620 }
621 LogPrint("pool", "[pool] miner arrived via loopback -- pushing to event loop\n");
622 PushReady((btf_socket_t)c);
623 }
624 closesocket(ls);
625}
626
546// --------------------------------------------------------------------------- 627// ---------------------------------------------------------------------------
547// I/O helpers -- event loop thread only 628// I/O helpers -- event loop thread only
548// --------------------------------------------------------------------------- 629// ---------------------------------------------------------------------------
@@ -1307,6 +1388,11 @@ static void AcceptOneFn(void* arg)
1307 LogPrint("pool", "[pool] accept thread started for relay %s\n", relay.c_str()); 1388 LogPrint("pool", "[pool] accept thread started for relay %s\n", relay.c_str());
1308 1389
1309 while (!fShutdown && gPoolRunning) { 1390 while (!fShutdown && gPoolRunning) {
1391 // Advertise this relay as the pool's meeting node BEFORE registering:
1392 // RvServiceRegister blocks until a miner pairs, and miners can only
1393 // find us if the published descriptor already names a relay where the
1394 // pool (not the node's peer-accept) holds the registration.
1395 BtfSetActiveRelay(relay);
1310 // Blocks until a miner connects to us at this relay 1396 // Blocks until a miner connects to us at this relay
1311 btf::RvSocket rv = btf::RvServiceRegister(host.c_str(), (unsigned short)port, pk); 1397 btf::RvSocket rv = btf::RvServiceRegister(host.c_str(), (unsigned short)port, pk);
1312 1398
@@ -1408,6 +1494,7 @@ void ThreadRPCServer(void*)
1408 LoadPendingPayouts(); 1494 LoadPendingPayouts();
1409 1495
1410 _beginthread(AcceptLoopFn, 0, NULL); 1496 _beginthread(AcceptLoopFn, 0, NULL);
1497 _beginthread(LocalAcceptFn, 0, NULL);
1411 _beginthread(ThreadBtfPoolAnnouncer, 0, NULL); 1498 _beginthread(ThreadBtfPoolAnnouncer, 0, NULL);
1412 _beginthread(PayoutThreadFn, 0, NULL); 1499 _beginthread(PayoutThreadFn, 0, NULL);
1413 1500