#35662 script: prevent stale sighash caches across transactions

full analysis

https://github.com/bitcoin/bitcoin/pull/35662 · l0rinc · +82/-74 in 17 files, 4 commits · labels: Consensus

Goal

  • Prevent stale sighash cache state from leaking across transactions or remaining uninitialized
  • Avoid unnecessary memory allocation during assumevalid block connection

This pull request removes the default constructor and public `Init()` method of `PrecomputedTransactionData`, ensuring that sighash cache objects are constructed directly for a specific transaction. Validation call sites that require deferred precomputation now wrap the object in `std::optional`, and `ConnectBlock` skips allocating `txsdata` storage entirely when script verification is skipped under assumevalid.

Problem: Previously, `PrecomputedTransactionData` could be default-constructed and populated later with `Init()`, exposing a public reuse path where stale BIP143 and Taproot sighash cache state could be carried across transactions or left partially uninitialized.

Category: Validation (#36 of 48)

P3 · cleanup

  • P3 because it hardens sighash cache lifetime against misuse and invalid reuse across transactions
  • Saves an unnecessary vector allocation during assumevalid sync

Hardens the validation and script interpreter interface against misuse by guaranteeing that PrecomputedTransactionData is bound to a single transaction at construction time. It also avoids an unneeded vector allocation in ConnectBlock during assumevalid sync.

Membership: Modifies script interpreter precomputed transaction data structures and validation script check workflows in src/validation.cpp.

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

Reviewability: Ready

  • Ready for review; author addressed feedback with a rework and CI passes

The author addressed all reviewer feedback in a rework, CI passes, and no further changes are pending.

Author status: Active; rebased and addressed review feedback.

Resolved concerns:

  • sedited objected to resetting flags inside Init() and suggested requiring initialization at construction instead. The author reworked the PR to eliminate default construction and public Init().

Agreement: Neutral

  • Resolved objection against re-initialization by adopting construction-time initialization (sedited)
  • Awaiting follow-up review after the rework

Initial objection from sedited resolved by implementing their suggested architectural change; awaiting follow-up review.

sedited initially leaned NACK on re-initializing through Init() and suggested construction-time initialization. The author adopted that suggestion and reworked the PR, but no formal ACKs have been posted yet.

  • sedited: 'I think rather than messing with Init, we should seriously reconsider whether this data can be initialized at construction.'
  • l0rinc: reworked PR to remove default construction and public Init(), following sedited's suggestion.

Review verdicts (DrahtBot): 0

Files

74 lines under test/bench/ci.

  • src/test/sighash_tests.cpp +30/-3
  • src/validation.cpp +15/-14
  • src/psbt.cpp +5/-9
  • src/test/txvalidationcache_tests.cpp +8/-6
  • src/kernel/bitcoinkernel.cpp +2/-7
  • src/script/interpreter.h +4/-5
  • src/script/interpreter.cpp +4/-4
  • src/script/sign.cpp +2/-5
  • src/test/fuzz/script_assets_test_minimizer.cpp +2/-4
  • src/test/script_assets_tests.cpp +2/-4
  • src/bench/verify_script.cpp +1/-2
  • src/signet.cpp +1/-2
  • src/test/descriptor_tests.cpp +1/-2
  • src/test/fuzz/script_flags.cpp +1/-2
  • src/test/fuzz/script_sigcache.cpp +2/-1
  • src/test/script_tests.cpp +1/-2
  • src/wallet/feebumper.cpp +1/-2

Card

This PR removes default construction and public Init() from PrecomputedTransactionData, enforcing that each instance is constructed explicitly for a single transaction. This closes an API reuse footgun where cached BIP143 and Taproot sighash data could theoretically persist across re-initializations. Validation now manages deferred precomputations via std::optional and skips allocating transaction data vectors during assumevalid. The changes incorporate reviewer suggestions from sedited and are ready for review.

Data

dossier JSON · extract JSON · model openrouter/google/gemini-3.8-flash, generated 2026-09-17T15:54, confidence high, input hash 263060b1340aa57a