#35641 kernel: Add script evaluation tracer
https://github.com/bitcoin/bitcoin/pull/35641 · · +617/-2 in 9 files, 1 commits · labels: Validation
Goal
- Allow external kernel consumers and debugger tools to observe intermediate script execution state
- Avoids the need for custom Bitcoin Core forks or reimplementations when inspecting or debugging scripts
Adds an optional script evaluation tracing mechanism to libbitcoinkernel gated behind the ENABLE_SCRIPT_TRACE CMake flag. When enabled, a registered callback receives snapshot frames at the start, step, and end of script evaluation in EvalScript, exposing stack items, opcodes, execution state, and script errors.
Problem: External applications and debuggers built on libbitcoinkernel lack visibility into internal script evaluation steps, making it difficult to write interactive script debuggers or trace interpreter execution.
Category: Kernel (libbitcoinkernel) (#10 of 18)
P3 · new feature
- P3 because external tooling and debuggers gain visibility into script execution
- It provides an optional compile-time utility rather than essential kernel validation infrastructure
Adds an optional script execution tracing API to the C kernel interface, allowing external tools like rust-bitcoinkernel to inspect script evaluation step by step. While valuable for developer tooling and script debugging, it is gated behind an optional build flag and does not block core node or validation library functionality.
Membership: Adds C API functions, headers, and CMake options to libbitcoinkernel under src/kernel/.
Factors: security/stability 0, bug 0, performance 0, user value 2, leverage 1
Reviewability: Paused: Waiting on author
- Author response is pending on a material assertion bug when serror is null
Reviewer w0xlt reported a reproducible assertion failure when EvalScript is called with a null serror pointer, which has been open for 15 days without an author reply.
Author status: silent since 2026-08-31 after addressing alexanderwiederin's comments
Open concerns:
- An assertion failure triggers when tracing is enabled and EvalScript is invoked with a null serror pointer (e.g. in CheckSignetBlockSolution).
- Oversized scripts are rejected before ScriptTraceScope is instantiated, preventing BEGIN and END trace frames from being emitted.
Resolved concerns:
- Opaque C API types and getters adopted instead of exposing internal struct layouts.
- Enum conversions between internal and kernel wrapper types made exhaustive.
- Thread safety documentation and callback unregistration cleanup in kernel test suite.
Agreement: Mild
- Strong concept and approach support across multiple contributors
- Concept approval noting it would benefit testing greatly (furszy, w0xlt)
- Approach approval (haanhvu)
- Verified by testing with Python bindings and a transaction visualizer (elmeriniemela)
- Unaddressed bug: assertion failure when serror is null (w0xlt)
Concept supported by multiple reviewers, but w0xlt found an unaddressed assertion bug when serror is null.
The PR has broad concept approval for exposing script tracing hooks in the kernel API, but w0xlt identified a real bug where internal callers passing null error pointers crash an assertion when tracing is enabled.
- furszy: 'Concept ACK... something that would benefit testing greatly'
- haanhvu: 'Approach ACK'
- w0xlt: 'The tracing code assumes serror is non-null whenever a callback is registered... This triggers the assertion'
Objections:
| Reviewer | Kind | Harm | Status | Blocking | Author replied | Quote |
|---|---|---|---|---|---|---|
| w0xlt | correctness | EvalScript triggers an assertion failure when tracing is enabled if called with a null serror pointer, such as in CheckSignetBlockSolution. | open | no | no | 2026-09-01: 'The tracing code assumes serror is non-null whenever a callback is registered, but EvalScript permits nullptr... This triggers the assertion assert(!m_callback || error); when tracing is enabled.' |
| alexanderwiederin | correctness | test suite fails when compiled with ENABLE_SCRIPT_TRACE=ON due to lingering callback pushing into destroyed vector in subsequent tests | resolved | no | yes | 2026-08-31: "Add ScriptTraceUnsetCallback() to avoid the next test case push into a destroyed vector. Test suite currently does not pass with the feature flag." Settled: 2026-08-31: "unsetting the callback after the trace tests are complete." |
| haanhvu | correctness | BOOST_CHECK_EQUAL on vector size allows test execution to continue and access out-of-bounds indices, causing undefined behavior | resolved | no | yes | 2026-07-29: "BOOST_CHECK_EQUAL(states.size(), 11) is non-fatal, but the test checks states[0] through states[10] after this. So if the size check fails, the test still continues and may access out-of-bound items, resulting in an undefined behavior instead of a clean test failure." Settled: 2026-08-28: "made the boost size check a hard requirement." |
Support:
- furszy: would benefit testing greatly
- theStack: Concept ACK [not substantive]
- alexanderwiederin: ACK 114e48e968a087f74c1ab611ac2a31a9266812e1 [not substantive]
- haanhvu: Approach ACK [not substantive]
- w0xlt: Strong Concept ACK [not substantive]
- elmeriniemela: tested with python bindings and visualizer, confirmed no issues found
Participants: furszy (support), w0xlt (objection), alexanderwiederin (objection), theStack (support), elmeriniemela (support), haanhvu (support), stringintech (neutral)
State derived from the lists: nonblocking objection open (w0xlt)
Review verdicts (DrahtBot): 0 (+1)
- Stale ACK: alexanderwiederin
- Approach ACK: haanhvu
- Concept ACK: furszy, w0xlt, theStack
Files
78 lines under test/bench/ci.
- src/kernel/bitcoinkernel.cpp +144/-1
- src/kernel/bitcoinkernel.h +134/-0
- src/script/trace.h +118/-0
- src/kernel/bitcoinkernel_wrapper.h +89/-0
- src/test/kernel/test_kernel.cpp +74/-0
- src/script/trace.cpp +40/-0
- src/script/interpreter.cpp +8/-1
- src/kernel/CMakeLists.txt +6/-0
- src/test/kernel/CMakeLists.txt +4/-0
Card
This PR introduces an optional script execution tracer in libbitcoinkernel, gated behind the ENABLE_SCRIPT_TRACE build option. It exposes a C callback interface providing snapshot frames for opcode execution, stack items, and script errors, enabling external tools like rust-bitcoinkernel to build interactive script debuggers. Multiple reviewers support the concept and API design, but review is currently paused waiting on the author to resolve a recently reported assertion crash when EvalScript receives null error pointers. It has no external dependencies.