Skip to content

Conversation

@l0rinc
Copy link
Contributor

@l0rinc l0rinc commented Jan 6, 2026

Summary

::GetCoin() is an interface for querying the UTXO set, so production implementations should only ever return unspent coins. Tests should mimic this to provide useful feedback.

Context

This PR is split out from #33018 to keep that PR focused on removing the FRESH-but-not-DIRTY cache state.

Changes:

  • Add a fail-fast assertion that CCoinsViewDB::GetCoin() never returns a spent coin.
  • Align unit tests and fuzz simulations with the production GetCoin() contract by never returning spent coins.
  • Replace the unreachable “spent coin returned by parent” handling in CCoinsViewCache::FetchCoin() with Assert(!coin.IsSpent()), drop outdated spent+FRESH docs, and tighten SanityCheck() invariants.

Behavior is unchanged, it just aligns our tests to exercise valid states.

l0rinc and others added 3 commits January 6, 2026 12:16
The chainstate UTXO database only stores unspent outputs; spent entries are removed.

Assert after reading a `Coin` so corruption or misuse cannot propagate a spent coin through the `GetCoin()` interface.
Production `GetCoin()` implementations only return unspent coins.

Update the `CCoinsView` test backend to match that contract, so tests stop exercising cache states that cannot occur with `CCoinsViewCache` or `CCoinsViewDB`.

Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
`CoinsViewBottom` roughly simulates a memory-backed `CCoinsViewDB`, which never stores spent coins.

Stop returning spent coins from `GetCoin()`, erase spent entries in `BatchWrite()`, and tighten comparisons to expect `std::nullopt` when the simulator has no coin.

Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
@DrahtBot
Copy link
Contributor

DrahtBot commented Jan 6, 2026

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/34207.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK andrewtoth, optout21
Stale ACK w0xlt, bensig

If your review is incorrectly listed, please copy-paste <!--meta-tag:bot-skip--> into the comment that the bot should ignore.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #34132 (refactor: inline CCoinsViewErrorCatcher into CCoinsViewDB by l0rinc)
  • #33512 (coins: use number of dirty cache entries in flush warnings/logs by l0rinc)
  • #33018 (coins: remove SetFresh method from CCoinsCacheEntry by andrewtoth)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

Copy link
Contributor

@w0xlt w0xlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 712bd86

@bensig
Copy link
Contributor

bensig commented Jan 7, 2026

ACK 712bd86

Tested.

Copy link
Contributor

@andrewtoth andrewtoth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 712bd86

`CCoinsViewCache::FetchCoin()` had special handling for a spent `Coin` returned by the parent view.
Production parents (`CCoinsViewCache` and `CCoinsViewDB`) do not return spent coins, so this path is unreachable.

Replace it with an `Assume(!coin.IsSpent())`, drop outdated documentation about spent+FRESH cache entries, and simplify `SanityCheck()` to assert the remaining possible state invariants.
This is safe because it does not change behavior for valid backends and will fail fast if the `GetCoin()` contract is violated.

Co-authored-by: Lőrinc <pap.lorinc@gmail.com>
@l0rinc l0rinc force-pushed the l0rinc/getcoin-unspent branch from 712bd86 to 2ee7f9b Compare January 11, 2026 20:33
@l0rinc l0rinc requested a review from w0xlt January 11, 2026 20:34
@l0rinc l0rinc mentioned this pull request Jan 14, 2026
24 tasks
@andrewtoth
Copy link
Contributor

re-ACK 2ee7f9b

Changes since last ACK:

@optout21
Copy link
Contributor

crACK 2ee7f9b

I did not ensure via code review that indeed spent coins are never returned. If that happened, that would be a serious error, the Assert is warranted. Reviewed code, checked unit tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants