#35187 kernel: Block validation without a complete UTXO set
| Reviewability | Reviews | Agreement | Size | |||||||
|---|---|---|---|---|---|---|---|---|---|---|
| #35187 kernel: Block validation without a complete UTXO set | Ready | 0 (+1) | Disputed | 242 + 119 tests | ||||||
|
|
|
|
| ||||||
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:
| Reviewer | Type | Harm, request, or question | Status | Must be | Author replied | Quote |
|---|---|---|---|---|---|---|
| purpleKarrot (member) | question, scope | whether the goal of this endeavor is to retire btck_block_spent_outputs_read from the kernel | answered · author clarified that retiring btck_block_spent_outputs_read is unrelated | answered | yes | 2026-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, approach | pass spent coins directly instead of requiring the caller to construct and pass block undo data | fixed · fix pushed · author agreed and updated PR to pass spent coins directly | changed | yes | 2026-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, interface | pass a coin getter callback to the validate block function instead of arrays | fixed · fix pushed · author adopted the callback approach in push 5c65fc94a3 | changed | no | 2026-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, approach | flattening coins into two arrays and converting them into an ephemeral map requires two full copies of the UTXO set and will not scale to production | fixed · fix pushed · author switched to callback approach for fetching coins in push 5c65fc94a3 | changed | yes | 2026-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, approach | a caller-supplied lookup callback allows non-deterministic lookups across calls and fails to capture the resulting UTXO set changes | contested · purpleKarrot questioned consistency concerns; ajtowns pushed back citing non-deterministic lookup breakage | changed | no | 2026-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, maintenance | add a direct (non-kernel) test for ValidateBlock in validation.cpp | open | answered | no | 2026-09-04: 'I think there should be a direct (non-kernel) test for the new `ValidateBlock` (in `validation.cpp`).' |
| nervana21 (contributor) | objection, correctness | ancestor blocks under assumevalid will skip script checks during validation instead of running full validation | open | answered | no | 2026-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, correctness | ValidateBlock 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 block | contested · author suggested clearing the cache; w0xlt pushed back that populated cache hits still occur, and proposed bypassing script cache | changed | yes | 2026-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, safety | ValidateBlock 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 height | contested · author suggested documenting the height requirement; w0xlt pushed back that documenting still leaves an avoidable crash | changed | yes | 2026-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
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