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-09-02 07:15:45 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-02 07:15:45 +0000
commitf094dc2f95df213f8c89d1d6f25dc864062623ae (patch)
tree06e9ac2db31590b2c9803e51f326105806d611d9 /src/rpc.cpp
parente4f898c4a6b9900553c64b9804889b5fdae3e55f (diff)
pool: refuse to build a job whose coinbase would burn the subsidy
GetKeyFromPool() returns an empty vector when the wallet is encrypted and locked, and when key generation itself fails. RebuildJob() fed whatever came back straight into the coinbase, and CScript::operator<< serialises an empty vector as a zero-length push, so the template's coinbase script became `<empty> OP_CHECKSIG` -- satisfiable by nobody. Every block the pool's miners found against such a job burned its whole subsidy: the coins exist and can never move, while QueuePayouts records the full obligation and the miners are then paid out of coin that block never contributed. An operator who locks the wallet overnight while the miners keep finding blocks pays twice for every one of them. BitcoinMiner has exactly this guard ("no wallet key available for coinbase; mining stopped"); this builder was missed. Mirror it: log the reason and return false before any job state is touched. Every caller already treats a false return as "no job this round" -- the submit handler and the new-block path log a warning and leave gHaveJob false, the no-job watchdog goes on retrying every few seconds, and the periodic nTime refresh keeps broadcasting the old job, whose key was drawn while the wallet could still produce one. Once the wallet can hand out a key again the next retry issues a spendable job unprompted.
Diffstat (limited to 'src/rpc.cpp')
-rw-r--r--src/rpc.cpp23
1 files changed, 23 insertions, 0 deletions
diff --git a/src/rpc.cpp b/src/rpc.cpp
index a4b53ca..cc3fffa 100644
--- a/src/rpc.cpp
+++ b/src/rpc.cpp
@@ -223,6 +223,29 @@ static bool RebuildJob()
223 // From the pool, so the template's payout key is in wallet.dat before 223 // From the pool, so the template's payout key is in wallet.dat before
224 // any miner is handed work against it. 224 // any miner is handed work against it.
225 vector<unsigned char> vchPubKey = GetKeyFromPool(); 225 vector<unsigned char> vchPubKey = GetKeyFromPool();
226 if (vchPubKey.empty()) {
227 // Empty means the wallet could not produce a key: encrypted and
228 // locked, or key generation itself failed. Serialised, an empty
229 // vector is a zero-length push, so the coinbase would become
230 // `<empty> OP_CHECKSIG` -- a script nobody can satisfy. Every
231 // block mined against such a job burns its whole subsidy, while
232 // QueuePayouts still records the full obligation and the miners
233 // are then paid out of coin that block never contributed.
234 // BitcoinMiner refuses to mine in this state; refuse to hand the
235 // pool's miners work in it too. printf and not LogPrint for the
236 // reason given above the deaf-node refusal, and throttled the
237 // same way -- the no-job watchdog retries every few seconds for
238 // as long as the condition lasts.
239 static int64 nLastSaidNoKey = 0;
240 if (GetTime() - nLastSaidNoKey > 60) {
241 nLastSaidNoKey = GetTime();
242 printf("RebuildJob: no wallet key available for the coinbase "
243 "(wallet locked, or key generation failed -- the "
244 "GetKeyFromPool line above says which) -- not issuing "
245 "jobs\n");
246 }
247 return false;
248 }
226 static std::atomic<uint32_t> sExtra{0}; 249 static std::atomic<uint32_t> sExtra{0};
227 uint32_t extraNonce = ++sExtra; 250 uint32_t extraNonce = ++sExtra;
228 251