diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-08 02:29:26 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 04:37:36 +0000 |
| commit | 6e4865c14c858d00c6cfa6967e66a86b31cd12f8 (patch) | |
| tree | e3988c6cfc0bd38f275c38191620730d3b269803 /src/main.cpp | |
| parent | a2592156207ed04671d6532cec4997fb1dab1595 (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/main.cpp')
| -rw-r--r-- | src/main.cpp | 318 |
1 files changed, 273 insertions, 45 deletions
diff --git a/src/main.cpp b/src/main.cpp index 0342650..32ab952 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -102,6 +102,9 @@ CAddress addrIncoming; | |||
| 102 | int nMineMode = MINE_RELAY; | 102 | int nMineMode = MINE_RELAY; |
| 103 | bool fMineModeFromCommandLine = false; | 103 | bool fMineModeFromCommandLine = false; |
| 104 | string strParticipantPool; // participant mode: pool .btf address | 104 | string strParticipantPool; // participant mode: pool .btf address |
| 105 | string strPayoutAddress; // participant: /payoutaddr override (mine without a wallet) | ||
| 106 | string strPayoutFile; // participant: /payoutfile with one address per line, rotated per reconnect | ||
| 107 | int nHashThreads = 0; // participant: /hashthreads override (0 = cores-1) | ||
| 105 | string strPoolName = "Bitflash Pool"; | 108 | string strPoolName = "Bitflash Pool"; |
| 106 | string strPoolDashboardUrl; | 109 | string strPoolDashboardUrl; |
| 107 | double dPoolFeePercent = 0.0; | 110 | double dPoolFeePercent = 0.0; |
| @@ -3488,6 +3491,102 @@ static bool StratumRecvLine(SOCKET s, std::string& buf, | |||
| 3488 | // How long to wait for a mining.notify after authorize before giving up | 3491 | // How long to wait for a mining.notify after authorize before giving up |
| 3489 | static const int64 STUCK_NO_JOB_TIMEOUT_SECS = 120; | 3492 | static const int64 STUCK_NO_JOB_TIMEOUT_SECS = 120; |
| 3490 | 3493 | ||
| 3494 | // ------------------------------------------------------------------ | ||
| 3495 | // Multi-threaded pool worker. The original loop hashed a single nonce | ||
| 3496 | // per blocking 50ms socket poll, capping one instance at ~20 H/s no | ||
| 3497 | // matter how many cores the machine has. The socket and job bookkeeping | ||
| 3498 | // stay on the calling thread; N hash threads grind nonce ranges on their | ||
| 3499 | // own RandomX VMs (all sharing the one ~2 GB dataset) and drop found | ||
| 3500 | // shares on a queue the I/O thread drains and submits. | ||
| 3501 | // ------------------------------------------------------------------ | ||
| 3502 | struct ParticipantJobShared | ||
| 3503 | { | ||
| 3504 | std::mutex mtx; | ||
| 3505 | unsigned char header[80]; | ||
| 3506 | uint256 target; | ||
| 3507 | std::string jobId; | ||
| 3508 | uint64 jobSeq; | ||
| 3509 | bool haveJob; | ||
| 3510 | bool stop; | ||
| 3511 | ParticipantJobShared() : jobSeq(0), haveJob(false), stop(false) | ||
| 3512 | { | ||
| 3513 | memset(header, 0, sizeof(header)); | ||
| 3514 | } | ||
| 3515 | }; | ||
| 3516 | |||
| 3517 | struct ParticipantShareHit | ||
| 3518 | { | ||
| 3519 | uint64 jobSeq; | ||
| 3520 | std::string jobId; | ||
| 3521 | unsigned int nonce; | ||
| 3522 | }; | ||
| 3523 | |||
| 3524 | static void ParticipantHashWorker(ParticipantJobShared* sh, int idx, int nWorkers, | ||
| 3525 | std::vector<ParticipantShareHit>* hits, | ||
| 3526 | std::mutex* hitsMtx) | ||
| 3527 | { | ||
| 3528 | void* vm = RandomXCreateMinerVM(); | ||
| 3529 | if (!vm) | ||
| 3530 | { | ||
| 3531 | LogPrint("worker", "[worker] hash thread %d: RandomX VM creation failed\n", idx); | ||
| 3532 | return; | ||
| 3533 | } | ||
| 3534 | |||
| 3535 | unsigned char header[80]; | ||
| 3536 | uint256 target; | ||
| 3537 | std::string jobId; | ||
| 3538 | uint64 mySeq = 0; | ||
| 3539 | bool active = false; | ||
| 3540 | unsigned int nNonce = 0; | ||
| 3541 | uint64 localHashes = 0; | ||
| 3542 | |||
| 3543 | while (!fShutdown) | ||
| 3544 | { | ||
| 3545 | { | ||
| 3546 | std::lock_guard<std::mutex> lk(sh->mtx); | ||
| 3547 | if (sh->stop) | ||
| 3548 | break; | ||
| 3549 | if (sh->haveJob && sh->jobSeq != mySeq) | ||
| 3550 | { | ||
| 3551 | memcpy(header, sh->header, 80); | ||
| 3552 | target = sh->target; | ||
| 3553 | jobId = sh->jobId; | ||
| 3554 | mySeq = sh->jobSeq; | ||
| 3555 | nNonce = (unsigned int)(idx + 1); | ||
| 3556 | active = true; | ||
| 3557 | } | ||
| 3558 | } | ||
| 3559 | if (!active) | ||
| 3560 | { | ||
| 3561 | Sleep(50); | ||
| 3562 | continue; | ||
| 3563 | } | ||
| 3564 | |||
| 3565 | memcpy(header + 76, &nNonce, 4); | ||
| 3566 | uint256 hash = RandomXHashWithVM(vm, header, 80); | ||
| 3567 | localHashes++; | ||
| 3568 | if ((localHashes & 0x3ff) == 0) | ||
| 3569 | gParticipantHashes.fetch_add(0x400); | ||
| 3570 | |||
| 3571 | if (hash <= target) | ||
| 3572 | { | ||
| 3573 | LogPrint("worker", "[worker] share found! nonce=%u job=%s (thread %d)\n", | ||
| 3574 | nNonce, jobId.c_str(), idx); | ||
| 3575 | std::lock_guard<std::mutex> lk(*hitsMtx); | ||
| 3576 | hits->push_back(ParticipantShareHit{mySeq, jobId, nNonce}); | ||
| 3577 | } | ||
| 3578 | |||
| 3579 | unsigned int next = nNonce + (unsigned int)nWorkers; | ||
| 3580 | if (next < nNonce) // wrapped past 2^32: range exhausted, wait for next job | ||
| 3581 | active = false; | ||
| 3582 | else | ||
| 3583 | nNonce = next; | ||
| 3584 | } | ||
| 3585 | |||
| 3586 | gParticipantHashes.fetch_add(localHashes & 0x3ff); | ||
| 3587 | RandomXDestroyMinerVM(vm); | ||
| 3588 | } | ||
| 3589 | |||
| 3491 | static bool PoolParticipantMiner() | 3590 | static bool PoolParticipantMiner() |
| 3492 | { | 3591 | { |
| 3493 | // ------------------------------------------------------------------ | 3592 | // ------------------------------------------------------------------ |
| @@ -3502,6 +3601,51 @@ static bool PoolParticipantMiner() | |||
| 3502 | LogPrint("worker", "[worker] connecting to pool %s\n", strParticipantPool.c_str()); | 3601 | LogPrint("worker", "[worker] connecting to pool %s\n", strParticipantPool.c_str()); |
| 3503 | SetParticipantMiningStatus("resolving pool address"); | 3602 | SetParticipantMiningStatus("resolving pool address"); |
| 3504 | 3603 | ||
| 3604 | // ------------------------------------------------------------------ | ||
| 3605 | // Steps 1-2: connect to the pool. Two address forms: | ||
| 3606 | // tcp:HOST:PORT -- plain TCP straight to the pool's stratum port | ||
| 3607 | // (e.g. our own operator node on this machine), | ||
| 3608 | // no .btf resolution or rendezvous tunnel involved | ||
| 3609 | // <name>.btf -- resolve via Nostr, dial through a rendezvous relay | ||
| 3610 | // ------------------------------------------------------------------ | ||
| 3611 | btf_socket_t s = INVALID_SOCKET; | ||
| 3612 | if (strParticipantPool.compare(0, 4, "tcp:") == 0) | ||
| 3613 | { | ||
| 3614 | std::string hp = strParticipantPool.substr(4); | ||
| 3615 | size_t colon = hp.rfind(':'); | ||
| 3616 | if (colon == std::string::npos) { | ||
| 3617 | LogPrint("worker", "[worker] bad tcp pool address '%s' (want tcp:host:port)\n", | ||
| 3618 | strParticipantPool.c_str()); | ||
| 3619 | SetParticipantMiningStatus("bad pool address"); | ||
| 3620 | return false; | ||
| 3621 | } | ||
| 3622 | std::string host = hp.substr(0, colon); | ||
| 3623 | int port = atoi(hp.substr(colon + 1).c_str()); | ||
| 3624 | unsigned long ip = inet_addr(host.c_str()); | ||
| 3625 | if (port <= 0 || port > 65535 || ip == INADDR_NONE) { | ||
| 3626 | LogPrint("worker", "[worker] bad tcp pool endpoint '%s'\n", hp.c_str()); | ||
| 3627 | SetParticipantMiningStatus("bad pool endpoint"); | ||
| 3628 | return false; | ||
| 3629 | } | ||
| 3630 | SetParticipantMiningStatus("connecting to pool (direct tcp)"); | ||
| 3631 | struct sockaddr_in sa; | ||
| 3632 | memset(&sa, 0, sizeof(sa)); | ||
| 3633 | sa.sin_family = AF_INET; | ||
| 3634 | sa.sin_port = htons((unsigned short)port); | ||
| 3635 | sa.sin_addr.s_addr = ip; | ||
| 3636 | SOCKET cs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); | ||
| 3637 | if (cs == INVALID_SOCKET || connect(cs, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR) { | ||
| 3638 | LogPrint("worker", "[worker] direct connect to %s FAILED\n", hp.c_str()); | ||
| 3639 | SetParticipantMiningStatus("pool connect failed"); | ||
| 3640 | if (cs != INVALID_SOCKET) closesocket(cs); | ||
| 3641 | return false; | ||
| 3642 | } | ||
| 3643 | LogPrint("worker", "[worker] connected directly to pool %s\n", hp.c_str()); | ||
| 3644 | s = (btf_socket_t)cs; | ||
| 3645 | goto connected; | ||
| 3646 | } | ||
| 3647 | |||
| 3648 | { | ||
| 3505 | unsigned char target_pubkey[32]; | 3649 | unsigned char target_pubkey[32]; |
| 3506 | if (!btf::ParseAddress(strParticipantPool, target_pubkey)) { | 3650 | if (!btf::ParseAddress(strParticipantPool, target_pubkey)) { |
| 3507 | LogPrint("worker", "[worker] invalid .btf address: %s\n", strParticipantPool.c_str()); | 3651 | LogPrint("worker", "[worker] invalid .btf address: %s\n", strParticipantPool.c_str()); |
| @@ -3536,11 +3680,11 @@ static bool PoolParticipantMiner() | |||
| 3536 | } | 3680 | } |
| 3537 | 3681 | ||
| 3538 | // ------------------------------------------------------------------ | 3682 | // ------------------------------------------------------------------ |
| 3539 | // Step 2: open encrypted tunnel to pool | 3683 | // Step 2b: open encrypted tunnel to pool |
| 3540 | // ------------------------------------------------------------------ | 3684 | // ------------------------------------------------------------------ |
| 3541 | SetParticipantMiningStatus("opening tunnel"); | 3685 | SetParticipantMiningStatus("opening tunnel"); |
| 3542 | btf_socket_t s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port, | 3686 | s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port, |
| 3543 | target_pubkey, service_enc_pub); | 3687 | target_pubkey, service_enc_pub); |
| 3544 | if (s == INVALID_SOCKET) { | 3688 | if (s == INVALID_SOCKET) { |
| 3545 | LogPrint("worker", "[worker] tunnel connect FAILED via %s" | 3689 | LogPrint("worker", "[worker] tunnel connect FAILED via %s" |
| 3546 | " (relay unreachable or pool not listening)\n", | 3690 | " (relay unreachable or pool not listening)\n", |
| @@ -3550,11 +3694,48 @@ static bool PoolParticipantMiner() | |||
| 3550 | } | 3694 | } |
| 3551 | LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str()); | 3695 | LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str()); |
| 3552 | SetParticipantMiningStatus("tunnel open"); | 3696 | SetParticipantMiningStatus("tunnel open"); |
| 3697 | } | ||
| 3698 | |||
| 3699 | connected: | ||
| 3553 | 3700 | ||
| 3554 | // ------------------------------------------------------------------ | 3701 | // ------------------------------------------------------------------ |
| 3555 | // Step 3: determine our payout address (mining username) | 3702 | // Step 3: determine our payout address (mining username). /payoutaddr |
| 3703 | // wins -- it lets a machine mine to an address whose keys live | ||
| 3704 | // elsewhere, so no wallet file is needed on the mining machine. | ||
| 3705 | // /payoutfile rotates through a list of such addresses: one is picked | ||
| 3706 | // per (re)connection, and the session self-limits (see rotation break | ||
| 3707 | // in the main loop) so consecutive rounds land on fresh addresses. | ||
| 3556 | // ------------------------------------------------------------------ | 3708 | // ------------------------------------------------------------------ |
| 3557 | std::string username; | 3709 | static std::vector<std::string> vPayoutAddrs; |
| 3710 | static size_t nPayoutRot = 0; | ||
| 3711 | if (!strPayoutFile.empty() && vPayoutAddrs.empty()) | ||
| 3712 | { | ||
| 3713 | FILE* f = fopen(strPayoutFile.c_str(), "r"); | ||
| 3714 | if (f) | ||
| 3715 | { | ||
| 3716 | char line[256]; | ||
| 3717 | while (fgets(line, sizeof(line), f)) | ||
| 3718 | { | ||
| 3719 | std::string a = line; | ||
| 3720 | while (!a.empty() && (a.back() == '\n' || a.back() == '\r' || | ||
| 3721 | a.back() == ' ' || a.back() == '\t')) | ||
| 3722 | a.pop_back(); | ||
| 3723 | if (!a.empty()) | ||
| 3724 | vPayoutAddrs.push_back(a); | ||
| 3725 | } | ||
| 3726 | fclose(f); | ||
| 3727 | LogPrint("worker", "[worker] loaded %zu payout addresses from %s\n", | ||
| 3728 | vPayoutAddrs.size(), strPayoutFile.c_str()); | ||
| 3729 | } | ||
| 3730 | else | ||
| 3731 | LogPrint("worker", "[worker] cannot open payout file %s\n", | ||
| 3732 | strPayoutFile.c_str()); | ||
| 3733 | } | ||
| 3734 | |||
| 3735 | std::string username = strPayoutAddress; | ||
| 3736 | if (username.empty() && !vPayoutAddrs.empty()) | ||
| 3737 | username = vPayoutAddrs[nPayoutRot++ % vPayoutAddrs.size()]; | ||
| 3738 | if (username.empty()) | ||
| 3558 | { | 3739 | { |
| 3559 | std::vector<unsigned char> vchPubKey; | 3740 | std::vector<unsigned char> vchPubKey; |
| 3560 | if (CWalletDB("r").ReadDefaultKey(vchPubKey)) | 3741 | if (CWalletDB("r").ReadDefaultKey(vchPubKey)) |
| @@ -3595,13 +3776,7 @@ static bool PoolParticipantMiner() | |||
| 3595 | unsigned int nThreads = std::thread::hardware_concurrency(); | 3776 | unsigned int nThreads = std::thread::hardware_concurrency(); |
| 3596 | RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1); | 3777 | RandomXInitDataset(nThreads > 0 ? (int)nThreads : 1); |
| 3597 | } | 3778 | } |
| 3598 | void* rxvm = RandomXCreateMinerVM(); | 3779 | LogPrint("worker", "[worker] RandomX dataset ready (%s)\n", |
| 3599 | if (!rxvm) { | ||
| 3600 | LogPrint("worker", "[worker] failed to create RandomX VM\n"); | ||
| 3601 | SetParticipantMiningStatus("RandomX init failed"); | ||
| 3602 | closesocket(s); return false; | ||
| 3603 | } | ||
| 3604 | LogPrint("worker", "[worker] RandomX VM ready (%s)\n", | ||
| 3605 | RandomXFastReady() ? "fast 2GB" : "light 256MB"); | 3780 | RandomXFastReady() ? "fast 2GB" : "light 256MB"); |
| 3606 | 3781 | ||
| 3607 | // ------------------------------------------------------------------ | 3782 | // ------------------------------------------------------------------ |
| @@ -3619,7 +3794,6 @@ static bool PoolParticipantMiner() | |||
| 3619 | {"params",json::array()}})) { | 3794 | {"params",json::array()}})) { |
| 3620 | LogPrint("worker", "[worker] subscribe send FAILED -- pool dropped connection\n"); | 3795 | LogPrint("worker", "[worker] subscribe send FAILED -- pool dropped connection\n"); |
| 3621 | SetParticipantMiningStatus("subscribe send failed"); | 3796 | SetParticipantMiningStatus("subscribe send failed"); |
| 3622 | RandomXDestroyMinerVM(rxvm); | ||
| 3623 | closesocket(s); return false; | 3797 | closesocket(s); return false; |
| 3624 | } | 3798 | } |
| 3625 | 3799 | ||
| @@ -3629,11 +3803,30 @@ static bool PoolParticipantMiner() | |||
| 3629 | {"params",json::array({username,"x"})}})) { | 3803 | {"params",json::array({username,"x"})}})) { |
| 3630 | LogPrint("worker", "[worker] authorize send FAILED -- pool dropped connection\n"); | 3804 | LogPrint("worker", "[worker] authorize send FAILED -- pool dropped connection\n"); |
| 3631 | SetParticipantMiningStatus("authorize send failed"); | 3805 | SetParticipantMiningStatus("authorize send failed"); |
| 3632 | RandomXDestroyMinerVM(rxvm); | ||
| 3633 | closesocket(s); return false; | 3806 | closesocket(s); return false; |
| 3634 | } | 3807 | } |
| 3635 | 3808 | ||
| 3636 | // ------------------------------------------------------------------ | 3809 | // ------------------------------------------------------------------ |
| 3810 | // Step 6: spawn hash threads (each with its own RandomX VM on the | ||
| 3811 | // shared dataset), then run the I/O loop below: receive pool messages, | ||
| 3812 | // publish jobs to the threads, submit the shares they find. | ||
| 3813 | // ------------------------------------------------------------------ | ||
| 3814 | int nWorkers = nHashThreads > 0 ? nHashThreads | ||
| 3815 | : (int)std::thread::hardware_concurrency(); | ||
| 3816 | if (nWorkers < 2) nWorkers = 2; | ||
| 3817 | if (nHashThreads <= 0) nWorkers -= 1; // leave one core for node + I/O threads | ||
| 3818 | ParticipantJobShared jobShared; | ||
| 3819 | std::vector<ParticipantShareHit> shareHits; | ||
| 3820 | std::mutex shareHitsMtx; | ||
| 3821 | std::vector<std::thread> hashThreads; | ||
| 3822 | for (int i = 0; i < nWorkers; i++) | ||
| 3823 | hashThreads.emplace_back(ParticipantHashWorker, &jobShared, i, nWorkers, | ||
| 3824 | &shareHits, &shareHitsMtx); | ||
| 3825 | LogPrint("worker", "[worker] spawned %d hash threads on shared RandomX dataset\n", | ||
| 3826 | nWorkers); | ||
| 3827 | const uint64 sessionStartHashes = gParticipantHashes.load(); | ||
| 3828 | |||
| 3829 | // ------------------------------------------------------------------ | ||
| 3637 | // Step 6: main loop -- receive messages, hash, submit shares | 3830 | // Step 6: main loop -- receive messages, hash, submit shares |
| 3638 | // ------------------------------------------------------------------ | 3831 | // ------------------------------------------------------------------ |
| 3639 | std::string jobId; | 3832 | std::string jobId; |
| @@ -3650,6 +3843,8 @@ static bool PoolParticipantMiner() | |||
| 3650 | uint64 sessionSharesSent = 0; // this connection only | 3843 | uint64 sessionSharesSent = 0; // this connection only |
| 3651 | uint64 sessionSharesAccepted = 0; | 3844 | uint64 sessionSharesAccepted = 0; |
| 3652 | int64 sampleStart = GetTime(); | 3845 | int64 sampleStart = GetTime(); |
| 3846 | int64 connStart = GetTime(); | ||
| 3847 | const int64 PAYOUT_ROTATE_SECS = 300; // reconnect with next /payoutfile address | ||
| 3653 | 3848 | ||
| 3654 | SetParticipantMiningStatus("waiting for pool response"); | 3849 | SetParticipantMiningStatus("waiting for pool response"); |
| 3655 | 3850 | ||
| @@ -3757,6 +3952,14 @@ static bool PoolParticipantMiner() | |||
| 3757 | memcpy(&target, tgtBytes.data(), 32); | 3952 | memcpy(&target, tgtBytes.data(), 32); |
| 3758 | haveJob = true; | 3953 | haveJob = true; |
| 3759 | nNonce = 1; | 3954 | nNonce = 1; |
| 3955 | { | ||
| 3956 | std::lock_guard<std::mutex> lk(jobShared.mtx); | ||
| 3957 | memcpy(jobShared.header, header, 80); | ||
| 3958 | jobShared.target = target; | ||
| 3959 | jobShared.jobId = jobId; | ||
| 3960 | jobShared.jobSeq++; | ||
| 3961 | jobShared.haveJob = true; | ||
| 3962 | } | ||
| 3760 | LogPrint("worker", "[pool->worker] new job %s received\n", | 3963 | LogPrint("worker", "[pool->worker] new job %s received\n", |
| 3761 | jobId.c_str()); | 3964 | jobId.c_str()); |
| 3762 | SetParticipantMiningStatus("hashing job " + jobId); | 3965 | SetParticipantMiningStatus("hashing job " + jobId); |
| @@ -3812,46 +4015,71 @@ static bool PoolParticipantMiner() | |||
| 3812 | } | 4015 | } |
| 3813 | } | 4016 | } |
| 3814 | 4017 | ||
| 4018 | // -- Payout address rotation: end this session cleanly so the outer | ||
| 4019 | // loop reconnects and authorizes the next /payoutfile address -- | ||
| 4020 | if (!vPayoutAddrs.empty() && GetTime() - connStart > PAYOUT_ROTATE_SECS) | ||
| 4021 | { | ||
| 4022 | LogPrint("worker", "[worker] payout rotation interval reached" | ||
| 4023 | " -- reconnecting with next address\n"); | ||
| 4024 | break; | ||
| 4025 | } | ||
| 4026 | |||
| 3815 | if (!haveJob) { Sleep(100); continue; } | 4027 | if (!haveJob) { Sleep(100); continue; } |
| 3816 | 4028 | ||
| 3817 | // -- Hash one nonce -- | 4029 | // -- Hash rate accounting (hashing happens on the worker threads) -- |
| 3818 | memcpy(header+76, &nNonce, 4); | 4030 | hashesSinceSample = gParticipantHashes.load() - sessionStartHashes; |
| 3819 | uint256 hash = RandomXHashWithVM(rxvm, header, 80); | 4031 | UpdateParticipantHashRate(hashesSinceSample, sampleStart); |
| 3820 | hashesSinceSample++; | ||
| 3821 | gParticipantHashes.fetch_add(1); | ||
| 3822 | if ((hashesSinceSample & 0x3ff) == 0) | ||
| 3823 | UpdateParticipantHashRate(hashesSinceSample, sampleStart); | ||
| 3824 | |||
| 3825 | if (hash <= target) { | ||
| 3826 | LogPrint("worker", "[worker] share found! nonce=%u job=%s difficulty=%.4f\n", | ||
| 3827 | nNonce, jobId.c_str(), currentDifficulty); | ||
| 3828 | SetParticipantMiningStatus("share found; submitting"); | ||
| 3829 | int submitId = msgId++; | ||
| 3830 | pendingSubmitIds.insert(submitId); | ||
| 3831 | gParticipantSharesSent.fetch_add(1); | ||
| 3832 | sessionSharesSent++; | ||
| 3833 | json submit = {{"id",submitId},{"method","mining.submit"}, | ||
| 3834 | {"params",json::array({username, jobId, ToHexStr(&nNonce,4)})}}; | ||
| 3835 | if (!StratumSendLine(s, submit)) { | ||
| 3836 | LogPrint("worker", "[worker] share submit send FAILED" | ||
| 3837 | " -- pool connection lost\n"); | ||
| 3838 | break; | ||
| 3839 | } | ||
| 3840 | } | ||
| 3841 | 4032 | ||
| 3842 | nNonce++; | 4033 | // -- Submit any shares the hash threads found -- |
| 3843 | if (nNonce == 0) { | 4034 | bool submitFailed = false; |
| 3844 | LogPrint("worker", "[worker] nonce space exhausted for job %s" | 4035 | { |
| 3845 | " -- waiting for new job\n", jobId.c_str()); | 4036 | std::vector<ParticipantShareHit> pending; |
| 3846 | haveJob = false; | 4037 | { |
| 4038 | std::lock_guard<std::mutex> lk(shareHitsMtx); | ||
| 4039 | pending.swap(shareHits); | ||
| 4040 | } | ||
| 4041 | for (ParticipantShareHit& hit : pending) | ||
| 4042 | { | ||
| 4043 | if (hit.jobId != jobId) | ||
| 4044 | { | ||
| 4045 | LogPrint("worker", "[worker] dropping stale share for old job %s\n", | ||
| 4046 | hit.jobId.c_str()); | ||
| 4047 | continue; | ||
| 4048 | } | ||
| 4049 | LogPrint("worker", "[worker] share found! nonce=%u job=%s difficulty=%.4f\n", | ||
| 4050 | hit.nonce, hit.jobId.c_str(), currentDifficulty); | ||
| 4051 | SetParticipantMiningStatus("share found; submitting"); | ||
| 4052 | int submitId = msgId++; | ||
| 4053 | pendingSubmitIds.insert(submitId); | ||
| 4054 | gParticipantSharesSent.fetch_add(1); | ||
| 4055 | sessionSharesSent++; | ||
| 4056 | json submit = {{"id",submitId},{"method","mining.submit"}, | ||
| 4057 | {"params",json::array({username, hit.jobId, | ||
| 4058 | ToHexStr(&hit.nonce,4)})}}; | ||
| 4059 | if (!StratumSendLine(s, submit)) { | ||
| 4060 | LogPrint("worker", "[worker] share submit send FAILED" | ||
| 4061 | " -- pool connection lost\n"); | ||
| 4062 | submitFailed = true; | ||
| 4063 | break; | ||
| 4064 | } | ||
| 4065 | } | ||
| 3847 | } | 4066 | } |
| 4067 | if (submitFailed) | ||
| 4068 | break; | ||
| 3848 | } | 4069 | } |
| 3849 | 4070 | ||
| 3850 | // ------------------------------------------------------------------ | 4071 | // ------------------------------------------------------------------ |
| 3851 | // Cleanup | 4072 | // Cleanup: stop the hash threads, then drop the pool connection |
| 3852 | // ------------------------------------------------------------------ | 4073 | // ------------------------------------------------------------------ |
| 3853 | RandomXDestroyMinerVM(rxvm); | 4074 | { |
| 4075 | std::lock_guard<std::mutex> lk(jobShared.mtx); | ||
| 4076 | jobShared.stop = true; | ||
| 4077 | jobShared.haveJob = false; | ||
| 4078 | } | ||
| 4079 | for (std::thread& t : hashThreads) | ||
| 4080 | t.join(); | ||
| 3854 | closesocket(s); | 4081 | closesocket(s); |
| 4082 | hashesSinceSample = gParticipantHashes.load() - sessionStartHashes; | ||
| 3855 | UpdateParticipantHashRate(hashesSinceSample, sampleStart); | 4083 | UpdateParticipantHashRate(hashesSinceSample, sampleStart); |
| 3856 | SetParticipantMiningStatus("disconnected"); | 4084 | SetParticipantMiningStatus("disconnected"); |
| 3857 | LogPrint("worker", "[worker] disconnected from pool" | 4085 | LogPrint("worker", "[worker] disconnected from pool" |