diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 07:40:33 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-23 07:40:33 +0000 |
| commit | c4754fb505be75b8eedee3ce230a8991762e18c5 (patch) | |
| tree | 4d9e0007b1b56f6fac69fe143f40d9c58ef18c8d /src/script.cpp | |
| parent | 489be141ee3be1c355650e1429c3f830eb8e442d (diff) | |
payout: recognise a payment a pre-upgrade build already made
FindRecordedPoolPayout matches on pool_payout_id / pool_payout_address /
pool_payout_amount. That metadata is written only by this build. Every payout
the pool has actually sent, in the wallet right now, carries nothing but a
"comment".
So the first boot after this upgrade is the dangerous one: swap the binary
after an unclean shutdown with payments in flight, and the recovery probe
finds no evidence for any of them and pays every one of those recipients a
second time. The deleted WalletAlreadyPaid() matched on what the payment WAS
-- the comment plus an output paying exactly that amount to exactly that
script -- and that evidence is still sitting in the wallet. It just stopped
being read.
The probe now answers with which kind of evidence it found, and the legacy
form is restored as a fallback under three constraints that keep it from
answering for a round that is not the one being asked about:
1. it is consulted only when the structured lookup found nothing, so the
exact path stays primary and strictly better going forward;
2. any transaction carrying pool_payout_id is excluded from it outright.
That key means this build wrote the transaction, and this build's
transactions are adjudicated exactly -- so a newer round's payment can
never be offered as proof for an older one. Only pre-upgrade
transactions, which is the entire population the fallback exists for,
are eligible;
3. a txid already credited to a DIFFERENT (round, address) is excluded --
from the ledger's paidTxids, which is durable across restarts, and from
what this process has already claimed. One legacy transaction therefore
settles at most one debt, so N transactions can never discharge more than
N obligations. Two debts with one transaction pays the second, which is
the correct reading of the evidence.
What cannot be resolved from a comment alone is two PRE-UPGRADE rounds at the
same height paying the same address the same amount, because the comment
carries only the height. That requires a reorg, and the losing round's ledger
entry is voided by the orphan check before it can be paid, so the two cannot
both be live. Where the evidence genuinely runs out the fallback does not
match and the payment is made: paying a debt that cannot be proven settled is
the correct default for a pool, and it is the direction that does not keep a
miner's money.
Recovery on legacy evidence logs loudly and says exactly what it matched on,
because it is a judgement made about a binary that is no longer running.
The two lookups share one pass over mapWallet. The structured path used to
scan alone; adding a second scan would have doubled a linear walk taken under
cs_mapWallet, which the whole node contends on.
No on-disk format change.
Diffstat (limited to 'src/script.cpp')
0 files changed, 0 insertions, 0 deletions