Repository: bitcoin/bitcoin
File: src/kernel/bitcoinkernel.cpp (C++)

Code before:
void btck_transaction_output_destroy(btck_TransactionOutput* output)
{
    delete output;
}

btck_PrecomputedTransactionData* btck_precomputed_transaction_data_create(
    const btck_Transaction* tx_to,
    const btck_TransactionOutput** spent_outputs_, size_t spent_outputs_len)
{
    try {
        const CTransaction& tx{*btck_Transaction::get(tx_to)};
        auto txdata{btck_PrecomputedTransactionData::create(tx)};
        std::vector<CTxOut> spent_outputs;
        if (spent_outputs_ != nullptr && spent_outputs_len > 0) {
            assert(spent_outputs_len == tx.vin.size());
            spent_outputs.reserve(spent_outputs_len);
            for (size_t i = 0; i < spent_outputs_len; i++) {
                const CTxOut& tx_out{btck_TransactionOutput::get(spent_outputs_[i])};
                spent_outputs.push_back(tx_out);
            }
            btck_PrecomputedTransactionData::get(txdata).Init(tx, std::move(spent_outputs));
        }

        return txdata;
    } catch (...) {
        return nullptr;
    }
}

btck_PrecomputedTransactionData* btck_precomputed_transaction_data_copy(const btck_PrecomputedTransactionData* precomputed_txdata)
{
    return btck_PrecomputedTransactionData::copy(precomputed_txdata);
}

void btck_precomputed_transaction_data_destroy(btck_PrecomputedTransactionData* precomputed_txdata)
{
    delete precomputed_txdata;
}

int btck_script_pubkey_verify(const btck_ScriptPubkey* script_pubkey,
                              const int64_t amount,
                              const btck_Transaction* tx_to,
                              const btck_PrecomputedTransactionData* precomputed_txdata,
                              const unsigned int input_index,
                              const btck_ScriptVerificationFlags flags,
                              btck_ScriptVerifyStatus* status)
{
    // Assert that all specified flags are part of the interface before continuing
    assert((flags & ~btck_ScriptVerificationFlags_ALL) == 0);

    if (!is_valid_flag_combination(script_verify_flags::from_int(flags))) {

Review comment (bug):
Looks like we are triggering `PrecomputedTransactionData::Init()` twice; first when calling `btck_PrecomputedTransactionData::create(tx)` by triggering the `PrecomputedTransactionData` explicit ctor and second when calling it directly. This would cause some recalculations in case of taproot verification which can be avoided by:

<details>
<summary>diff</summary>

```diff
diff --git a/src/kernel/bitcoinkernel.cpp b/src/kernel/bitcoinkernel.cpp
index 13fce99709..5b5dd0aa19 100644
--- a/src/kernel/bitcoinkernel.cpp
+++ b/src/kernel/bitcoinkernel.cpp
@@ -614,16 +614,18 @@ btck_PrecomputedTransactionData* btck_precomputed_transaction_data_create(
 {
     try {
         const CTransaction& tx{*btck_Transaction::get(tx_to)};
-        auto txdata{btck_PrecomputedTransactionData::create(tx)};
-        std::vector<CTxOut> spent_outputs;
+        auto txdata{btck_PrecomputedTransactionData::create()};
         if (spent_outputs_ != nullptr && spent_outputs_len > 0) {
             assert(spent_outputs_len == tx.vin.size());
+            std::vector<CTxOut> spent_outputs;
             spent_outputs.reserve(spent_outputs_len);
             for (size_t i = 0; i < spent_outputs_len; i++) {
                 const CTxOut& tx_out{btck_TransactionOutput::get(spent_outputs_[i])};
                 spent_outputs.push_back(tx_out);
             }
             btck_PrecomputedTransactionData::get(txdata).Init(tx, std::move(spent_outputs));
+        } else {
+            btck_PrecomputedTransactionData::get(txdata).Init(tx, {});
         }
 
         return txdata;
```

</details> 

Code after:
void btck_transaction_output_destroy(btck_TransactionOutput* output)
{
    delete output;
}

btck_PrecomputedTransactionData* btck_precomputed_transaction_data_create(
    const btck_Transaction* tx_to,
    const btck_TransactionOutput** spent_outputs_, size_t spent_outputs_len)
{
    try {
        const CTransaction& tx{*btck_Transaction::get(tx_to)};
        auto txdata{btck_PrecomputedTransactionData::create()};
        if (spent_outputs_ != nullptr && spent_outputs_len > 0) {
            assert(spent_outputs_len == tx.vin.size());
            std::vector<CTxOut> spent_outputs;
            spent_outputs.reserve(spent_outputs_len);
            for (size_t i = 0; i < spent_outputs_len; i++) {
                const CTxOut& tx_out{btck_TransactionOutput::get(spent_outputs_[i])};
                spent_outputs.push_back(tx_out);
            }
            btck_PrecomputedTransactionData::get(txdata).Init(tx, std::move(spent_outputs));
        } else {
            btck_PrecomputedTransactionData::get(txdata).Init(tx, {});
        }

        return txdata;
    } catch (...) {
        return nullptr;
    }
}

btck_PrecomputedTransactionData* btck_precomputed_transaction_data_copy(const btck_PrecomputedTransactionData* precomputed_txdata)
{
    return btck_PrecomputedTransactionData::copy(precomputed_txdata);
}

void btck_precomputed_transaction_data_destroy(btck_PrecomputedTransactionData* precomputed_txdata)
{
    delete precomputed_txdata;
}

int btck_script_pubkey_verify(const btck_ScriptPubkey* script_pubkey,
                              const int64_t amount,
                              const btck_Transaction* tx_to,
                              const btck_PrecomputedTransactionData* precomputed_txdata,
                              const unsigned int input_index,
                              const btck_ScriptVerificationFlags flags,
                              btck_ScriptVerifyStatus* status)
{
    // Assert that all specified flags are part of the interface before continuing
    assert((flags & ~btck_ScriptVerificationFlags_ALL) == 0);