From c9353e8f8a4d95c827497abe5b30ac951dd7dda5 Mon Sep 17 00:00:00 2001 From: bitflash-maintainer Date: Sun, 23 Aug 2026 07:58:52 +0000 Subject: payout: indent the blocks introduced by the publish/serialise split No behaviour change. Moving the file writes out from under the data mutexes put a new scope around the locked sections of ReconcilePayouts and FlushMaturePayouts, and their bodies were left at the old indentation, which reads as if the lock is not held. Re-indented, with the comments and the concatenated log strings inside them re-wrapped to fit -- `git show -w` on this commit is comments and string line breaks only, and the strings concatenate to the same text. --- src/rpc.cpp | 156 ++++++++++++++++++++++++++++++------------------------------ 1 file changed, 79 insertions(+), 77 deletions(-) (limited to 'src/rpc.cpp') diff --git a/src/rpc.cpp b/src/rpc.cpp index e4939b3..f853e78 100644 --- a/src/rpc.cpp +++ b/src/rpc.cpp @@ -1125,52 +1125,54 @@ static void ReconcilePayouts() { VersionedBlob payingSnap; { - std::lock_guard exec(gPayoutExecMutex); - std::lock_guard ledger(gPayoutLedgerMutex); + std::lock_guard exec(gPayoutExecMutex); + std::lock_guard ledger(gPayoutLedgerMutex); - size_t settled = 0; - int64 settledAmount = 0; - for (auto it = gPaying.begin(); it != gPaying.end(); ) { - bool paid = false; - for (const PendingPayout& pp : gPendingPayouts) - if (SamePayoutRound(pp.blockHeight, pp.blockHash, - it->blockHeight, it->blockHash) - && pp.paidTxids.count(it->address)) { - paid = true; - break; + size_t settled = 0; + int64 settledAmount = 0; + for (auto it = gPaying.begin(); it != gPaying.end(); ) { + bool paid = false; + for (const PendingPayout& pp : gPendingPayouts) + if (SamePayoutRound(pp.blockHeight, pp.blockHash, + it->blockHeight, it->blockHash) + && pp.paidTxids.count(it->address)) { + paid = true; + break; + } + if (paid) { + settledAmount += it->amount; + settled++; + it = gPaying.erase(it); + } else { + // Every restored record gets one wallet probe before it is + // allowed to send: this is precisely the window where the + // wallet may hold a transaction the ledger has not recorded. + it->fProbeWallet = true; + ++it; } - if (paid) { - settledAmount += it->amount; - settled++; - it = gPaying.erase(it); - } else { - // Every restored record gets one wallet probe before it is allowed - // to send: this is precisely the window where the wallet may hold - // a transaction the ledger has not recorded yet. - it->fProbeWallet = true; - ++it; } - } - if (settled) { - LogPrint("payout", "[payout] %zu in-flight payment(s) totalling %s are" - " already recorded paid in the ledger -- not resending\n", - settled, FormatMoney(settledAmount).c_str()); - } + if (settled) { + LogPrint("payout", "[payout] %zu in-flight payment(s) totalling %s" + " are already recorded paid in the ledger -- not" + " resending\n", + settled, FormatMoney(settledAmount).c_str()); + } - // Rebuild the "already exploded" set from what survived. Without this the - // first flush after a restart re-explodes every round still in flight. - gQueuedPayouts.clear(); - for (const PayingRecord& r : gPaying) - gQueuedPayouts.insert(PayoutIdForBlock(r.blockHeight, r.blockHash)); + // Rebuild the "already exploded" set from what survived. Without this + // the first flush after a restart re-explodes every round in flight. + gQueuedPayouts.clear(); + for (const PayingRecord& r : gPaying) + gQueuedPayouts.insert(PayoutIdForBlock(r.blockHeight, r.blockHash)); - payingSnap = SerializePayingLocked(); + payingSnap = SerializePayingLocked(); - if (!gPaying.empty()) { - int64 owed = 0; - for (const auto& r : gPaying) owed += r.amount; - LogPrint("payout", "[payout] resuming %zu in-flight payment(s) totalling %s\n", - gPaying.size(), FormatMoney(owed).c_str()); - } + if (!gPaying.empty()) { + int64 owed = 0; + for (const auto& r : gPaying) owed += r.amount; + LogPrint("payout", "[payout] resuming %zu in-flight payment(s)" + " totalling %s\n", + gPaying.size(), FormatMoney(owed).c_str()); + } } PublishPaying(payingSnap); } @@ -1606,44 +1608,44 @@ static void FlushMaturePayouts() VersionedBlob payingSnap; { - std::lock_guard queue(gPayoutExecMutex); - size_t added = 0; - for (const PendingPayout& pp : ready) { - const std::string id = PayoutIdForBlock(pp.blockHeight, pp.blockHash); - if (gQueuedPayouts.count(id)) - continue; // already exploded into gPaying and still in flight - size_t n = 0; - for (const auto& kv : pp.amounts) { - // Upstream's per-recipient dedupe: a recipient already carrying a - // txid is done, even though the entry as a whole is not. - if (pp.paidTxids.count(kv.first)) - continue; - // The invariant, checked against the thing it is an invariant of: - // at most one in-flight record per (round, address). The round-level - // set above is an optimisation that can be wrong -- it is memory - // only, it is rebuilt from gPaying at startup, and a stale `ready` - // snapshot can race a round finishing -- whereas gPaying is the - // list being protected. A second record here is a second standing - // order to send the same money. - if (PaymentInFlightLocked(pp.blockHeight, pp.blockHash, kv.first)) - continue; - PayingRecord r; - r.id = ++gPayingNextId; - r.blockHeight = pp.blockHeight; - r.blockHash = pp.blockHash; - r.matureAtHeight = pp.matureAtHeight; - r.address = kv.first; - r.amount = kv.second; - gPaying.push_back(r); - n++; + std::lock_guard queue(gPayoutExecMutex); + size_t added = 0; + for (const PendingPayout& pp : ready) { + const std::string id = PayoutIdForBlock(pp.blockHeight, pp.blockHash); + if (gQueuedPayouts.count(id)) + continue; // already exploded into gPaying and still in flight + size_t n = 0; + for (const auto& kv : pp.amounts) { + // Upstream's per-recipient dedupe: a recipient already carrying + // a txid is done, even though the entry as a whole is not. + if (pp.paidTxids.count(kv.first)) + continue; + // The invariant, checked against the thing it is an invariant + // of: at most one in-flight record per (round, address). The + // round-level set above is an optimisation that can be wrong -- + // it is memory only, it is rebuilt from gPaying at startup, and + // a stale `ready` snapshot can race a round finishing -- + // whereas gPaying is the list being protected. A second record + // here is a second standing order to send the same money. + if (PaymentInFlightLocked(pp.blockHeight, pp.blockHash, kv.first)) + continue; + PayingRecord r; + r.id = ++gPayingNextId; + r.blockHeight = pp.blockHeight; + r.blockHash = pp.blockHash; + r.matureAtHeight = pp.matureAtHeight; + r.address = kv.first; + r.amount = kv.second; + gPaying.push_back(r); + n++; + } + gQueuedPayouts.insert(id); + added += n; + LogPrint("payout", "[payout] block height %d matured -- queuing" + " payout %s (%zu of %zu recipients still owed)\n", + pp.matureAtHeight, id.c_str(), n, pp.amounts.size()); } - gQueuedPayouts.insert(id); - added += n; - LogPrint("payout", "[payout] block height %d matured -- queuing payout %s" - " (%zu of %zu recipients still owed)\n", - pp.matureAtHeight, id.c_str(), n, pp.amounts.size()); - } - if (added) payingSnap = SerializePayingLocked(); + if (added) payingSnap = SerializePayingLocked(); } PublishPaying(payingSnap); } -- cgit v1.2.3