-
Notifications
You must be signed in to change notification settings - Fork 38.7k
Description
bitcoin/src/index/coinstatsindex.h
Lines 33 to 35 in 6d40484
| CAmount m_total_prevout_spent_amount{0}; | |
| CAmount m_total_new_outputs_ex_coinbase_amount{0}; | |
| CAmount m_total_coinbase_amount{0}; |
bitcoin/src/kernel/coinstats.h
Lines 54 to 59 in 6d40484
| //! Total cumulative amount of prevouts spent up to and including this block | |
| CAmount total_prevout_spent_amount{0}; | |
| //! Total cumulative amount of outputs created up to and including this block | |
| CAmount total_new_outputs_ex_coinbase_amount{0}; | |
| //! Total cumulative amount of coinbase outputs up to and including this block | |
| CAmount total_coinbase_amount{0}; |
The values:
m_total_prevout_spent_amountm_total_new_outputs_ex_coinbase_amountm_total_coinbase_amount
are all subject to overflowing an int64_t (ie, CAmount). This is visible in practice as of signet block 112516; with total_prevout_spent rolling over to -92230771076.81494784 (from from 92232270378.54920553 BTC at block 112515). With debug builds, this will cause a crash due to the -ftrapv compiler option.
Making these just be per-block totals instead of cumulative totals should fix this in practice. I think that would require a full index rebuild, though?
Even then I think it would still be technically possible to overflow m_block_prevout_spent_amount or m_total_new_outputs_ex_coinbase_amount with a single block: you can fit over 15,000 65-byte transactions spending to padding DROP TRUE in a block, and if you start with a utxo containing 6.15M BTC and chain it through 15,000 txs, then that will overflow a signed 64 bit counter. That's not reproducible on regtest, though, since the halving schedule there limits the total supply to ~15,000 regtest-BTC, which means you need about 6.15M transactions to trigger the same overflow.
Since we don't actually expose the cumulative totals; I observed the values above by patching gettxoutsetinfo in rpc/blockchain to add:
/*line ~877*/ {RPCResult::Type::STR_AMOUNT, "total_prevout_spent", "Total amount of all prevouts spent up to this block"},
/*line ~981*/ block_info.pushKV("total_prevout_spent", ValueFromAmount(stats.total_prevout_spent_amount));