upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/fuzz_script.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-08-23 07:43:20 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-23 07:43:20 +0000
commit5892302449e1299f39812d0bb422c4184224b0f1 (patch)
treec9d382474a3212b756757a08c8020ffb99495129 /src/fuzz_script.cpp
parentc4754fb505be75b8eedee3ce230a8991762e18c5 (diff)
payout: do not write off a debt the wallet is only temporarily unable to pay
ForfeitPayoutRecipient permanently erases a miner's money: the amount leaves the ledger, the operator keeps the coin, and nothing on disk records that it was ever owed. It was reachable from two entirely ordinary conditions. The deferral gate compared GetBalance() against r.amount, ignoring the fee. A balance anywhere between amount and amount+fee therefore passed the gate, failed inside SendMoney, and was counted as a send FAILURE -- a retry. At one a minute, 1440 of those is 24 hours, and then the recipient is gone. A wallet left locked overnight did exactly the same thing: every attempt fails because nothing can be signed, and each failure spent budget. Three changes. The gate now requires amount + nTransactionFee, which is the part of the fee knowable before the transaction exists. It deliberately does not try to reserve GetMinFee(), which depends on the serialised size and so on how many inputs SelectCoins happens to pick: refusing to attempt a payment the wallet could actually have made is its own way of not paying a miner. A locked wallet is now a deferral at the same gate, for the same reason maturity is. Failures are classified after the fact instead of all counting as faults. The balance and lock state are re-read after SendMoney returns -- it holds cs_main and can block for a long time, so both may have moved -- and if the wallet is locked, or the balance no longer covers amount + fee + one fee tier of headroom, the attempt is recorded as a deferral. That headroom is where the size-dependent part of the fee lands, so a fee shortfall is a wait rather than a fault, which is what it is. The retry budget therefore now only counts failures that happened while the payment was funded, the wallet was unlocked, and the funding block was on the main chain. Reaching 1440 of those means the wallet refuses a payment it has every means to make, which is the only state in which the payment can be called impossible rather than merely blocked. The give-up log says which conditions held, rather than implying it. Finally, the forfeit is reconciled with the public record. Dropping the amount from the ledger while pool_rounds.json goes on advertising the address as owed with "txid": null left the pool's two accounts of the same round permanently disagreeing, with the operator holding the difference. PoolRoundProof gains a per-address forfeited/forfeitReason, written by MarkPoolRoundForfeited when the ledger writes the debt off, and pool_status.json counts those separately from genuinely unpaid ones instead of reporting them as still pending. On-disk: pool_rounds.json gains optional "forfeited" and "forfeitReason" keys inside a payout object, and pool_status.json gains "forfeitedPayouts" per round. Both are emitted only when a forfeit has actually happened, both are read back with defaults, and files written by any earlier build load unchanged.
Diffstat (limited to 'src/fuzz_script.cpp')
0 files changed, 0 insertions, 0 deletions