Skip to content

signed overflow in coinstats index #26362

@ajtowns

Description

@ajtowns

CAmount m_total_prevout_spent_amount{0};
CAmount m_total_new_outputs_ex_coinbase_amount{0};
CAmount m_total_coinbase_amount{0};

//! 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_amount
  • m_total_new_outputs_ex_coinbase_amount
  • m_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));

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions