From e0c00d3e1b3192efde01fba85d0831470a8fb89c Mon Sep 17 00:00:00 2001 From: Bitflash Date: Tue, 28 Jul 2026 15:25:02 -0300 Subject: rpc: the payout comment claims a guarantee the code does not provide (#22) It reads: Save BEFORE moving to exec queue: if we crash between the save and SendMoney, LoadPendingPayouts re-queues them from the exec-queue section of the file. There is no exec-queue section. SavePendingPayouts serialises gPendingPayouts and nothing else, and a matured payout is erased from it and written out before ever reaching the queue, which lives only in memory. Crash in between and those miners are never paid, with no record they were owed anything. Only the comment changes here. The behaviour needs idempotent payment -- record the txid, look for it in the wallet on restart -- because simply persisting the queue and re-running it would double-pay anyone already sent to, which is the worse failure. --- src/rpc.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/rpc.cpp b/src/rpc.cpp index 3b2dae9..6f81153 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -341,8 +341,18 @@ static void QueuePayouts(int blockHeight, } // Called from event loop: move matured entries to the exec queue (fast). -// Save BEFORE moving to exec queue: if we crash between the save and SendMoney, -// LoadPendingPayouts re-queues them from the exec-queue section of the file. +// NOT crash-safe, despite the ordering below looking like it is. +// +// A matured payout is erased from gPendingPayouts, the file is rewritten +// without it, and only then does it reach the exec queue -- which lives in +// memory alone. SavePendingPayouts writes gPendingPayouts and nothing else; +// there is no exec-queue section of the file to recover from. Die anywhere +// between the rewrite and SendMoney and those miners are simply never paid, +// with no record that they were owed anything. +// +// Making this safe needs the payment to be idempotent -- record the txid and +// check the wallet for it on restart -- rather than a reordering, since +// re-queueing blindly would double-pay whoever was already sent to. static void FlushMaturePayouts() { std::vector ready; -- cgit v1.2.3