upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp106
1 files changed, 95 insertions, 11 deletions
diff --git a/src/main.cpp b/src/main.cpp
index 681673c..6b8e0de 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -3970,6 +3970,14 @@ static void ParticipantHashWorker(ParticipantJobShared* sh, int idx, int nWorker
3970 RandomXDestroyMinerVM(vm); 3970 RandomXDestroyMinerVM(vm);
3971} 3971}
3972 3972
3973// Run one participant session: resolve the pool, open the tunnel, subscribe,
3974// hash, submit shares. Returns true only if the pool actually handed out work
3975// -- at least one well-formed mining.notify arrived. Everything else is false,
3976// including a tunnel that opened and was dropped before any job.
3977//
3978// The caller's reconnect backoff keys off this return value, so any path added
3979// here that leaves the session without work MUST return false. "The session
3980// ended tidily" is not the question being asked.
3973static bool PoolParticipantMiner() 3981static bool PoolParticipantMiner()
3974{ 3982{
3975 // ------------------------------------------------------------------ 3983 // ------------------------------------------------------------------
@@ -4047,6 +4055,33 @@ static bool PoolParticipantMiner()
4047 } 4055 }
4048 LogPrint("worker", "[worker] pool resolved to meeting node %s\n", meetingHostPort.c_str()); 4056 LogPrint("worker", "[worker] pool resolved to meeting node %s\n", meetingHostPort.c_str());
4049 4057
4058 // The descriptor names ONE meeting node, but the pool parks a
4059 // registration at every relay it knows (one accept thread per relay,
4060 // rpc.cpp:AcceptOneFn), so any seed relay can pair us. Insisting on the
4061 // advertised entry forever turns one bad path into a miner that idles
4062 // indefinitely while a working path exists (issue #17); after a few
4063 // consecutive tunnel failures, rotate the seed relays into the attempts
4064 // too. Only ONE participant session runs (StartMinerThreads), so these
4065 // statics are single-threaded.
4066 static const int TUNNEL_FAILOVER_AFTER = 3;
4067 static int nTunnelFails = 0; // consecutive tunnel failures, this pool
4068 static size_t iFailoverRelay = 0; // rotation cursor over vBtfMeetingRelays
4069 static std::string strFailPool; // pool the counters belong to
4070 if (strFailPool != strParticipantPool) {
4071 strFailPool = strParticipantPool;
4072 nTunnelFails = 0;
4073 iFailoverRelay = 0;
4074 }
4075 if (nTunnelFails >= TUNNEL_FAILOVER_AFTER && !vBtfMeetingRelays.empty()) {
4076 std::string alt = vBtfMeetingRelays[iFailoverRelay++ % vBtfMeetingRelays.size()];
4077 if (alt != meetingHostPort) {
4078 LogPrint("worker", "[worker] advertised meeting node %s failed %d"
4079 " times in a row -- trying seed relay %s instead\n",
4080 meetingHostPort.c_str(), nTunnelFails, alt.c_str());
4081 meetingHostPort = alt;
4082 }
4083 }
4084
4050 size_t colon = meetingHostPort.rfind(':'); 4085 size_t colon = meetingHostPort.rfind(':');
4051 if (colon == std::string::npos) { 4086 if (colon == std::string::npos) {
4052 LogPrint("worker", "[worker] bad meeting node format '%s'\n", meetingHostPort.c_str()); 4087 LogPrint("worker", "[worker] bad meeting node format '%s'\n", meetingHostPort.c_str());
@@ -4066,15 +4101,18 @@ static bool PoolParticipantMiner()
4066 // Step 2b: open encrypted tunnel to pool 4101 // Step 2b: open encrypted tunnel to pool
4067 // ------------------------------------------------------------------ 4102 // ------------------------------------------------------------------
4068 SetParticipantMiningStatus("opening tunnel"); 4103 SetParticipantMiningStatus("opening tunnel");
4104 btf::TunnelFail tunnelFail = btf::TUNNEL_FAIL_NONE;
4069 s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port, 4105 s = btf::BtfClientTunnel(host.c_str(), (unsigned short)port,
4070 target_pubkey, service_enc_pub); 4106 target_pubkey, service_enc_pub, &tunnelFail);
4071 if (s == INVALID_SOCKET) { 4107 if (s == INVALID_SOCKET) {
4072 LogPrint("worker", "[worker] tunnel connect FAILED via %s" 4108 nTunnelFails++;
4073 " (relay unreachable or pool not listening)\n", 4109 LogPrint("worker", "[worker] tunnel connect FAILED via %s: %s\n",
4074 meetingHostPort.c_str()); 4110 meetingHostPort.c_str(), btf::TunnelFailText(tunnelFail));
4075 SetParticipantMiningStatus("tunnel connect failed"); 4111 SetParticipantMiningStatus(std::string("tunnel connect failed: ")
4112 + btf::TunnelFailText(tunnelFail));
4076 return false; 4113 return false;
4077 } 4114 }
4115 nTunnelFails = 0;
4078 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str()); 4116 LogPrint("worker", "[worker] tunnel open via %s\n", meetingHostPort.c_str());
4079 SetParticipantMiningStatus("tunnel open"); 4117 SetParticipantMiningStatus("tunnel open");
4080 } 4118 }
@@ -4253,6 +4291,7 @@ connected:
4253 int nBestHeightResolved = -1; // nBestHeight when we last resolved 4291 int nBestHeightResolved = -1; // nBestHeight when we last resolved
4254 double currentDifficulty = 1.0; // updated by mining.set_difficulty from pool 4292 double currentDifficulty = 1.0; // updated by mining.set_difficulty from pool
4255 bool haveJob = false; 4293 bool haveJob = false;
4294 bool gotAnyJob = false; // any job at all this session -- the return value
4256 bool gotSubscribeAck = false; 4295 bool gotSubscribeAck = false;
4257 bool gotAuthorizeAck = false; 4296 bool gotAuthorizeAck = false;
4258 int64 authWaitStart = 0; 4297 int64 authWaitStart = 0;
@@ -4369,8 +4408,9 @@ connected:
4369 jobId = newJobId; 4408 jobId = newJobId;
4370 memcpy(header, hdrBytes.data(), 80); 4409 memcpy(header, hdrBytes.data(), 80);
4371 memcpy(&target, tgtBytes.data(), 32); 4410 memcpy(&target, tgtBytes.data(), 32);
4372 haveJob = true; 4411 haveJob = true;
4373 nNonce = 1; 4412 gotAnyJob = true;
4413 nNonce = 1;
4374 { 4414 {
4375 std::lock_guard<std::mutex> lk(jobShared.mtx); 4415 std::lock_guard<std::mutex> lk(jobShared.mtx);
4376 memcpy(jobShared.header, header, 80); 4416 memcpy(jobShared.header, header, 80);
@@ -4562,7 +4602,11 @@ connected:
4562 (unsigned long long)hashesSinceSample, 4602 (unsigned long long)hashesSinceSample,
4563 (unsigned long long)sessionSharesSent, 4603 (unsigned long long)sessionSharesSent,
4564 (unsigned long long)sessionSharesAccepted); 4604 (unsigned long long)sessionSharesAccepted);
4565 return true; 4605 // Not "we got here", which is true of a tunnel the pool accepted and then
4606 // dropped without ever sending a job -- exactly the case the caller's
4607 // backoff exists for. Reaching the end of the session is not evidence the
4608 // session was worth anything; a job is.
4609 return gotAnyJob;
4566} 4610}
4567 4611
4568// --------------------------------------------------------------------------- 4612// ---------------------------------------------------------------------------
@@ -4759,11 +4803,51 @@ bool BitcoinMiner(int nThreadId)
4759 // Participant mode: connect to external pool instead of mining solo/operator 4803 // Participant mode: connect to external pool instead of mining solo/operator
4760 if (nMineMode == MINE_PARTICIPANT) 4804 if (nMineMode == MINE_PARTICIPANT)
4761 { 4805 {
4806 // An attempt that never got work backs off (10s doubling to 120s)
4807 // instead of hammering the same path every flat 10s, and a streak
4808 // lasting minutes is said at printf level: the identical once-per-10s
4809 // LogPrint line is how a worker idled all night unnoticed (issue #17).
4810 //
4811 // Only a session the pool actually sent a job to resets either. The
4812 // weaker test -- "the session returned" -- was gameable: a pool that
4813 // completes the tunnel and drops without ever sending work leaves by
4814 // the same tidy cleanup path a healthy one does, which would pin the
4815 // retry at 10s forever and defeat the backoff in exactly the scenario
4816 // it exists for.
4817 int nRetrySecs = 10;
4818 int64 nFailingSince = 0;
4762 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) { 4819 while (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) {
4763 PoolParticipantMiner(); 4820 bool fGotWork = PoolParticipantMiner();
4764 if (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) { 4821 if (fGenerateBitcoins && nMineMode == MINE_PARTICIPANT && !fShutdown) {
4765 LogPrint("worker", "PoolParticipantMiner: reconnecting in 10s\n"); 4822 if (fGotWork) {
4766 for (int i = 0; i < 10 && !fShutdown; i++) Sleep(1000); 4823 nRetrySecs = 10;
4824 nFailingSince = 0;
4825 } else {
4826 if (nFailingSince == 0)
4827 nFailingSince = GetTime();
4828 int64 nMins = (GetTime() - nFailingSince) / 60;
4829 if (nMins >= 1)
4830 printf("[worker] connection to pool %s has been failing"
4831 " for %lld minutes\n",
4832 strParticipantPool.c_str(), (long long)nMins);
4833 }
4834 LogPrint("worker", "PoolParticipantMiner: reconnecting in %ds\n",
4835 nRetrySecs);
4836 // Poll everything the enclosing while polls, not just
4837 // fShutdown. Raising the ceiling to 120s made a
4838 // shutdown-only predicate expensive: after the operator
4839 // clears Generate or switches to Relay, the session -- and
4840 // the vfThreadRunning[3] slot that gates restarting it --
4841 // would stay held for up to two minutes. Measured 119s
4842 // before, 1s after.
4843 for (int i = 0; i < nRetrySecs && !fShutdown && fGenerateBitcoins
4844 && nMineMode == MINE_PARTICIPANT; i++)
4845 Sleep(1000);
4846 if (!fGotWork && nRetrySecs < 120) {
4847 nRetrySecs *= 2;
4848 if (nRetrySecs > 120)
4849 nRetrySecs = 120;
4850 }
4767 } 4851 }
4768 } 4852 }
4769 return true; 4853 return true;