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

Code before:
    } else {
        MutableTransactionSignatureCreator creator(tx, index, utxo.nValue, txdata, sighash);
        sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
    }
    // Verify that a witness signature was produced in case one was required.
    if (require_witness_sig && !sigdata.witness) return false;

    // If we are not finalizing, set sigdata.complete to false to not set the scriptWitness
    if (!finalize && sigdata.complete) sigdata.complete = false;

    input.FromSignatureData(sigdata);

    // If we have a witness signature, put a witness UTXO.
    if (sigdata.witness) {
        input.witness_utxo = utxo;
        // We can remove the non_witness_utxo if and only if there are no non-segwit or segwit v0
        // inputs in this transaction. Since this requires inspecting the entire transaction, this
        // is something for the caller to deal with (i.e. FillPSBT).
    }

    // Fill in the missing info
    if (out_sigdata) {
        out_sigdata->missing_pubkeys = sigdata.missing_pubkeys;
        out_sigdata->missing_sigs = sigdata.missing_sigs;
        out_sigdata->missing_redeem_script = sigdata.missing_redeem_script;
        out_sigdata->missing_witness_script = sigdata.missing_witness_script;
    }

    return sig_complete;
}

void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, const int& sighash_type)
{
    // Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY
    if ((sighash_type & 0x80) != SIGHASH_ANYONECANPAY) {
        // Figure out if any non_witness_utxos should be dropped
        std::vector<unsigned int> to_drop;
        for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
            const auto& input = psbtx.inputs.at(i);
            int wit_ver;
            std::vector<unsigned char> wit_prog;
            if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) {
                // There's a non-segwit input or Segwit v0, so we cannot drop any witness_utxos
                to_drop.clear();
                break;
            }
            if (wit_ver == 0) {
                // Segwit v0, so we cannot drop any non_witness_utxos
                to_drop.clear();
                break;
            }

Review comment (suggestion):
I gather from the interpreter code that these 2 checks are testing for the fixed length of 64 in case of Schnorr signatures if sighash is absent, which is ok but it'd be decent to have a constant for `64` to make it explicit to the reader.
https://github.com/bitcoin/bitcoin/blob/de90b47ea09826d52043f4ce40c734847187c075/src/script/interpreter.cpp#L1673-L1698

Maybe an enum `SCHNORR_SIG_LENGTH`?

```cpp
enum SCHNORR_SIG_LENGTH: size_t {
 WITHOUT_HASHTYPE = 64,
 WITH_HASHTYPE = 65,
}
```

Code after:
    // If both the field and the parameter are provided, they must match
    // If only the parameter is provided, use it and add it to the PSBT if it is other than SIGHASH_DEFAULT
    // for all input types, and not SIGHASH_ALL for non-taproot input types.
    // If neither are provided, use SIGHASH_DEFAULT if it is taproot, and SIGHASH_ALL for everything else.
    if (!sighash) sighash = utxo.scriptPubKey.IsPayToTaproot() ? SIGHASH_DEFAULT : SIGHASH_ALL;
    Assert(sighash.has_value());
    // For user safety, the desired sighash must be provided if the PSBT wants something other than the default set in the previous line.
    if (input.sighash_type && input.sighash_type != sighash) {
        return PSBTError::SIGHASH_MISMATCH;
    }
    // Set the PSBT sighash field when sighash is not DEFAULT or ALL
    // DEFAULT is allowed for non-taproot inputs since DEFAULT may be passed for them (e.g. the psbt being signed also has taproot inputs)
    // Note that signing already aliases DEFAULT to ALL for non-taproot inputs.
    if (utxo.scriptPubKey.IsPayToTaproot() ? sighash != SIGHASH_DEFAULT :
                                            (sighash != SIGHASH_DEFAULT && sighash != SIGHASH_ALL)) {
        input.sighash_type = sighash;
    }

    // Check all existing signatures use the sighash type
    if (sighash == SIGHASH_DEFAULT) {
        if (!input.m_tap_key_sig.empty() && input.m_tap_key_sig.size() != 64) {
            return PSBTError::SIGHASH_MISMATCH;
        }
        for (const auto& [_, sig] : input.m_tap_script_sigs) {
            if (sig.size() != 64) return PSBTError::SIGHASH_MISMATCH;
        }
    } else {
        if (!input.m_tap_key_sig.empty() && (input.m_tap_key_sig.size() != 65 || input.m_tap_key_sig.back() != *sighash)) {
            return PSBTError::SIGHASH_MISMATCH;
        }
        for (const auto& [_, sig] : input.m_tap_script_sigs) {
            if (sig.size() != 65 || sig.back() != *sighash) return PSBTError::SIGHASH_MISMATCH;
        }
        for (const auto& [_, sig] : input.partial_sigs) {
            if (sig.second.back() != *sighash) return PSBTError::SIGHASH_MISMATCH;
        }
    }

    sigdata.witness = false;
    bool sig_complete;
    if (txdata == nullptr) {
        sig_complete = ProduceSignature(provider, DUMMY_SIGNATURE_CREATOR, utxo.scriptPubKey, sigdata);
    } else {
        MutableTransactionSignatureCreator creator(tx, index, utxo.nValue, txdata, *sighash);
        sig_complete = ProduceSignature(provider, creator, utxo.scriptPubKey, sigdata);
    }
    // Verify that a witness signature was produced in case one was required.
    if (require_witness_sig && !sigdata.witness) return PSBTError::INCOMPLETE;

    // If we are not finalizing, set sigdata.complete to false to not set the scriptWitness
    if (!finalize && sigdata.complete) sigdata.complete = false;