diff options
Diffstat (limited to 'src/rpc.cpp')
| -rw-r--r-- | src/rpc.cpp | 95 |
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() | |||
| 218 | struct PendingPayout { | 232 | struct 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 | }; |
| 222 | static std::vector<PendingPayout> gPendingPayouts; | 238 | static 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. | ||
| 593 | static 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 | ||