Files
bitcoin/src/test/kernel
merge-script eb0594e23f Merge bitcoin/bitcoin#33891: kernel: Expose reusable PrecomputedTransactionData in script validation
44e006d438 [kernel] Expose reusable PrecomputedTransactionData in script valid (Josh Doman)

Pull request description:

  This PR exposes a reusable `PrecomputedTransactionData` object in script validation using libkernel.

  Currently, libkernel computes `PrecomputedTransactionData` each time `btck_script_pubkey_verify` is called, exposing clients to quadratic hashing when validating a transaction with multiple inputs. By externalizing `PrecomputedTransactionData` and making it reusable, libkernel can eliminate this attack vector.

  I discussed this problem in [this issue](https://github.com/TheCharlatan/rust-bitcoinkernel/issues/46). The design of this PR is inspired by @sedited's comments.

  The PR introduces three new APIs for managing the `btck_PrecomputedTransactionData` object:
  ```c
  /**
   * @brief Create precomputed transaction data for script verification.
   *
   * @param[in] tx_to             Non-null.
   * @param[in] spent_outputs     Nullable for non-taproot verification. Points to an array of
   *                              outputs spent by the transaction.
   * @param[in] spent_outputs_len Length of the spent_outputs array.
   * @return                      The precomputed data, or null on error.
   */
  btck_PrecomputedTransactionData* btck_precomputed_transaction_data_create(
      const btck_Transaction* tx_to,
      const btck_TransactionOutput** spent_outputs, size_t spent_outputs_len) BITCOINKERNEL_ARG_NONNULL(1);

  /**
   * @brief Copy precomputed transaction data.
   *
   * @param[in] precomputed_txdata  Non-null.
   * @return                      The copied precomputed transaction data.
   */
  btck_PrecomputedTransactionData* btck_precomputed_transaction_data_copy(
      const btck_PrecomputedTransactionData* precomputed_txdata) BITCOINKERNEL_ARG_NONNULL(1);

  /**
   * Destroy the precomputed transaction data.
   */
  void btck_precomputed_transaction_data_destroy(btck_PrecomputedTransactionData* precomputed_txdata);
  ```

  The PR also modifies `btck_script_pubkey_verify` so that it accepts `precomputed_txdata` instead of `spent_outputs`:
  ```c
  /**
   * @brief Verify if the input at input_index of tx_to spends the script pubkey
   * under the constraints specified by flags. If the
   * `btck_ScriptVerificationFlags_WITNESS` flag is set in the flags bitfield, the
   * amount parameter is used. If the taproot flag is set, the precomputed data
   * must contain the spent outputs.
   *
   * @param[in] script_pubkey      Non-null, script pubkey to be spent.
   * @param[in] amount             Amount of the script pubkey's associated output. May be zero if
   *                               the witness flag is not set.
   * @param[in] tx_to              Non-null, transaction spending the script_pubkey.
   * @param[in] precomputed_txdata Nullable if the taproot flag is not set. Otherwise, precomputed data
   *                               for tx_to with the spent outputs must be provided.
   * @param[in] input_index        Index of the input in tx_to spending the script_pubkey.
   * @param[in] flags              Bitfield of btck_ScriptVerificationFlags controlling validation constraints.
   * @param[out] status            Nullable, will be set to an error code if the operation fails, or OK otherwise.
   * @return                       1 if the script is valid, 0 otherwise.
   */
  int btck_script_pubkey_verify(
      const btck_ScriptPubkey* script_pubkey,
      int64_t amount,
      const btck_Transaction* tx_to,
      const btck_PrecomputedTransactionData* precomputed_txdata,
      unsigned int input_index,
      btck_ScriptVerificationFlags flags,
      btck_ScriptVerifyStatus* status) BITCOINKERNEL_ARG_NONNULL(1, 3);
  ```

  As before, an error is thrown if the taproot flag is set and `spent_outputs` is not provided in `precomputed_txdata` (or `precomputed_txdata` is null). For simple single-input non-taproot verification, `precomputed_txdata` may be null, and the kernel will construct the precomputed data on-the-fly.

  Both the C++ wrapper and the test suite are updated with the new API. Tests cover both `precomputed_txdata` reuse and nullability.

  Appreciate feedback on this concept / approach!

ACKs for top commit:
  sedited:
    Re-ACK 44e006d438
  stringintech:
    ACK 44e006d

Tree-SHA512: 1ed435173e6ff4ec82bc603194cf182c685cb79f167439a442b9b179a32f6c189c358f04d4cb56d153fab04e3424a11b73c31680e42b87b8a6efcc3ccefc366c
2025-12-27 16:20:43 +00:00
..