#35187 kernel: Block validation without a complete UTXO set

ReviewabilityReviewsAgreementSize
#35187 sedited kernel: Block validation without a complete UTXO setReady0 (+1)Disputed242 + 119 tests
  • Validate blocks in libbitcoinkernel without requiring a local on-disk chainstate database
  • Allow lightweight verifiers and external UTXO databases to validate block transactions directly
  • Ready to review
  • Broad concept support, but contested objections remain after a rebase (ajtowns, w0xlt)
  • Contested objection: caller callback enables non-deterministic lookups and drops UTXO changes (ajtowns)
  • Contested objection: script cache poisoning could let caller-supplied coins validate invalid blocks (w0xlt)
  • Contested objection: impossible coin confirmation heights can cause assertion crashes in release builds (w0xlt)
  • Unaddressed objection: assumevalid causes ancestor blocks to skip script checks (nervana21)
  • 242 lines added or modified outside tests
  • 119 lines added or modified in tests
  • 0 lines removed in total
  • 6 files, 5 commits
Kernel (libbitcoinkernel)#4 of 18
P2 · new feature
  • P2 because it adds a major capability to libbitcoinkernel for block validation without an on-disk UTXO set
  • P2 because it advances API completeness for external consumers and lightweight verification tools
Validation#44 of 53
P3 · new feature
  • P3 after comparing with the other PRs here (assessed alone as P2): Lowered to P3: primarily a libbitcoinkernel API addition for external callers; node-side validation behavior is unchanged, so it ranks below in-node validation work
  • P2 because it adds a new validation entry point that checks blocks against caller-supplied coins
  • P2 because it alters how block validation interacts with script caching and validity invariants
Reviewability: ReadyStalePaused
Agreement: StrongPositivePositive w/ caveatsNeutralMildDisputedBlockedCrickets
Reviews: current code-review ACKs, then (+stale ACKs) and -NACKs; greener = more ACKs.
Size: lines added or modified outside tests, then in tests; greener = smaller.

Goal

Adds a C API endpoint and C++ wrapper in libbitcoinkernel to validate blocks without requiring a local UTXO set. `ChainstateManager::ValidateBlock` executes contextual checks and `ConnectBlock` in check-only mode against caller-supplied coins via a custom `CCoinsViewCache` implementation (`CoinsViewBlock`).

Problem: Applications using libbitcoinkernel cannot validate block transactions and scripts unless they maintain a full chainstate database on disk, preventing lightweight verifiers or external UTXO databases from validating blocks directly.

Evidence of importance:

  • PR description: 'This adds a kernel C header API endpoint for validating a block without having access to the full UTXO set... This allows validating the block\'s scripts and spends against user-provided UTXOs instead of using the chainstate\'s own internal UTXO set.'
  • Commit message: 'The spent coins are passed to the function with a callback, which is invoked for each output spent by the block (excluding outputs previously created by the block).'

Scope: Adds exposed C API functions (`btck_chainstate_manager_validate_block`, `btck_coin_create`, `btck_transaction_out_point_create`, `btck_transaction_is_coinbase`) along with kernel wrapper abstractions and unit tests.

Risk of a mistake: medium (validation, other). Flaws in the coin view cache or validation sequencing could lead kernel consumers to incorrectly validate or reject blocks, though standard bitcoind validation paths are unaffected.

Changed since the previous assessment: No substantial changes to the approach; includes helper methods for OutPoint comparisons and coinbase transaction queries.

Reviewability

Ready

Nothing known that would invalidate a review now.

Waiting on (from the thread read):

  • author: addressing contested objections on script cache poisoning and assertion crashes, and open comments on assumevalid 2026-09-25
  • reviewer: review of the rebased branch 2026-10-09

Agreement

Disputed

objection that needs a change, the objector pushed back after a reply (ajtowns, w0xlt)

Thread notes: The author rebased the branch to ed76d293770961547dab6875d434e99ab02b445b without addressing specific objections in the comment.

Objections, suggestions, and questions:

ReviewerTypeHarm, request, or questionStatusMust beAuthor repliedQuote
purpleKarrot (member)question, scopewhether the goal of this endeavor is to retire btck_block_spent_outputs_read from the kernelanswered · author clarified that retiring btck_block_spent_outputs_read is unrelatedansweredyes2026-05-01: 'The goal of this endeavor is to retire `btck_block_spent_outputs_read` from the kernel?'
Status from: 2026-05-01: sedited: 'No, I think that is unrelated.'
ismaelsadeeq (member)suggestion, approachpass spent coins directly instead of requiring the caller to construct and pass block undo datafixed · fix pushed · author agreed and updated PR to pass spent coins directlychangedyes2026-05-01: 'won't it be more straightforward to just pass the spent coins and add them to the coins cache'
Status from: 2026-07-14: sedited: 'Basically went with @ismaelsadeeq's suggestion of passing in two arrays encoding pairs of outpoints and coins.'
alexanderwiederin (member)suggestion, interfacepass a coin getter callback to the validate block function instead of arraysfixed · fix pushed · author adopted the callback approach in push 5c65fc94a3changedno2026-05-05: 'the callback passed to the validate method makes sense to me'
Status from: 2026-08-06: sedited: 'Switched to callback approach for fetching the coins'
purpleKarrot (member)objection, approachflattening coins into two arrays and converting them into an ephemeral map requires two full copies of the UTXO set and will not scale to productionfixed · fix pushed · author switched to callback approach for fetching coins in push 5c65fc94a3changedyes2026-07-21: 'While this approach may serve as a proof-of-concept, it will not scale to a production application.'
Status from: 2026-08-06: sedited: 'Switched to callback approach for fetching the coins'
ajtowns (member)objection, approacha caller-supplied lookup callback allows non-deterministic lookups across calls and fails to capture the resulting UTXO set changescontested · purpleKarrot questioned consistency concerns; ajtowns pushed back citing non-deterministic lookup breakagechangedno2026-08-06: 'I think we should control the container ourselves, rather than using a lookup(outpoint) -> coin callback that the caller provides so that we can be sure the lookups are consistent across calls.'
Status from: 2026-08-11: ajtowns: 'A `lookup(outpoint)` function can return `(randbool() ? coin1 : coin2)` to break our logic, or do something equivalent b'
optout21 (contributor)suggestion, maintenanceadd a direct (non-kernel) test for ValidateBlock in validation.cppopenansweredno2026-09-04: 'I think there should be a direct (non-kernel) test for the new `ValidateBlock` (in `validation.cpp`).'
nervana21 (contributor)objection, correctnessancestor blocks under assumevalid will skip script checks during validation instead of running full validationopenansweredno2026-09-09: 'When we call `ConnectBlock` on a main chain block that is an ancestor of the `assumevalid` block (and satisfies some other straightforward criteria), we inherit `assumevalid` logic and omit script che'
w0xlt (contributor)objection, correctnessValidateBlock caches successful script checks in m_script_execution_cache without committing to the spent coins, allowing caller-supplied forged coins to validate and later cause the node to connect an invalid blockcontested · author suggested clearing the cache; w0xlt pushed back that populated cache hits still occur, and proposed bypassing script cachechangedyes2026-09-24: 'ValidateBlock() can make the node connect an invalid block.'
Status from: 2026-09-25: w0xlt: 'the solution suggested above also needs refinement: it prevents new entries from being stored, but still allows hits fro'
w0xlt (contributor)objection, safetyValidateBlock can crash on an assertion failure in release builds when validating a transaction with a BIP68 relative lock if a caller-supplied coin has a confirmation height at or above the block heightcontested · author suggested documenting the height requirement; w0xlt pushed back that documenting still leaves an avoidable crashchangedyes2026-09-25: 'ValidateBlock() can crash on a coin with an impossible confirmation height.'
Status from: 2026-09-25: w0xlt: 'Just documenting the height requirement still leaves an avoidable crash.'

Support:

  • ismaelsadeeq (member) (Approach ACK, 2026-05-01) [read]: If we are going through this route, won't it be more straightforward to just pass the spent coins and add them to the coins cache
  • w0xlt (contributor) (Approach ACK, 2026-05-21): Approach ACK [not substantive]
  • ismaelsadeeq (member) (ACK, 2026-07-20) [both]: Code review ACK following testing against pr-35000 with unit tests passing
  • yuvicc (contributor) (Concept ACK, 2026-08-04): Concept ACK [not substantive]
  • ajtowns (member) (Concept ACK, 2026-08-06) [read]: ConceptACK, approach = nearly, but not quite?
  • nervana21 (contributor) (Concept ACK, 2026-08-25): Concept ACK [not substantive]
  • alexanderwiederin (member) (2026-08-26) [read]: The design holds up well in my opinion.

Participants:

  • ismaelsadeeq (member, support, 16 statements 2026-05-01 to 2026-07-26): Approach ACK and code review ACK; suggested passing spent coins directly instead of block undo data
  • purpleKarrot (member, objection, 6 statements 2026-05-01 to 2026-08-12): objected to passing two arrays requiring two full copies of UTXO set, preferring a lookup callback
  • alexanderwiederin (member, support, 4 statements 2026-05-05 to 2026-08-26): supported callback design and successfully tested rust-bitcoinkernel bindings against it
  • w0xlt (contributor, objection, 6 statements 2026-05-21 to 2026-09-25): Approach ACK initially, but objected to script execution cache poisoning and release assertion crashes on invalid coin confirmation heights
  • 17031996 (none, neutral, 1 statements 2026-07-14 to 2026-07-14): left an empty review comment
  • yuvicc (contributor, support, 1 statements 2026-08-04 to 2026-08-04): Concept ACK
  • ajtowns (member, objection, 2 statements 2026-08-06 to 2026-08-11): Concept ACK, but objected to caller-provided lookup callbacks over determinism concerns and requested an in-memory CCoinsView container
  • nervana21 (contributor, objection, 2 statements 2026-08-25 to 2026-09-09): Concept ACK, but noted that calling ConnectBlock inherits assumevalid and bypasses script checks for ancestor blocks
  • optout21 (contributor, neutral, 6 statements 2026-09-04 to 2026-09-26): left review comments and test suggestions without taking a stance on the approach

Files

119 lines under test/bench/ci.

  • src/test/kernel/test_kernel.cpp +119/-0
  • src/kernel/bitcoinkernel.cpp +76/-0
  • src/kernel/bitcoinkernel.h +76/-0
  • src/kernel/bitcoinkernel_wrapper.h +42/-0
  • src/validation.cpp +34/-0
  • src/validation.h +14/-0

Card

The short text the ranking pass compares across PRs.

Adds a libbitcoinkernel C API endpoint and C++ wrapper to validate a block without maintaining a complete UTXO set. Callers supply spent coins through a callback, which feeds a check-only ConnectBlock validation run via a custom coins view cache. This allows external applications to verify block transactions and scripts against arbitrary UTXO sources without a local chainstate database. It cannot detect BIP30 duplicates due to the absence of the complete historical UTXO set.

Data

dossier JSON · extract JSON · model ledger, generated 2026-10-11T01:20, confidence high, input hash 4d744dc52bb419ae
thread read through 2026-10-09 (55 statements), head ed76d29377
labels: Validation

Categories

Kernel (libbitcoinkernel)

P2 · new feature

The PR adds a major capability to libbitcoinkernel by enabling block validation against caller-supplied UTXOs without requiring an on-disk chainstate database. Expanding the library API to support block verification directly advances API completeness for external consumers.

Membership: Adds C API endpoints and C++ wrappers in src/kernel/bitcoinkernel.cpp, src/kernel/bitcoinkernel.h, and src/kernel/bitcoinkernel_wrapper.h, tested in src/test/kernel/test_kernel.cpp.

Factors: security/stability 1, bug 0, performance 0, user value 2, leverage 2

Validation

P3 · new feature (assessed alone as P2; Lowered to P3: primarily a libbitcoinkernel API addition for external callers; node-side validation behavior is unchanged, so it ranks below in-node validation work)

The PR introduces a new block validation entry point using ConnectBlock in check-only mode against custom UTXOs, altering how validation interacts with script caching and validity invariants. Combining substantive validation mechanics with kernel API bindings requires dedicated validation review and may need splitting.

Membership: Carries the Validation label and modifies src/validation.cpp and src/validation.h to implement ChainstateManager::ValidateBlock and custom coins view handling.

Factors: security/stability 1, bug 0, performance 0, user value 2, leverage 2