psbt: use sighash type field to determine whether to remove non-witness utxos

Since the sighash type field is written for atypical sighash types, we
can look at that field to figure out whether the psbt contains
unnecessary transactions.
This commit is contained in:
Ava Chow 2025-01-21 18:02:15 -05:00
parent 47bec7af45
commit f8b71807fd
4 changed files with 33 additions and 29 deletions

View File

@ -487,37 +487,41 @@ PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransact
return sig_complete ? PSBTError::OK : PSBTError::INCOMPLETE;
}
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, std::optional<int> sighash_type)
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx)
{
if (!sighash_type) sighash_type = SIGHASH_DEFAULT;
// 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;
}
if (input.non_witness_utxo) {
to_drop.push_back(i);
}
// 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, so we cannot drop any non_witness_utxos
to_drop.clear();
break;
}
if (wit_ver == 0) {
// Segwit v0, so we cannot drop any non_witness_utxos
to_drop.clear();
break;
}
// non_witness_utxos cannot be dropped if the sighash type includes SIGHASH_ANYONECANPAY
// Since callers should have called SignPSBTInput which updates the sighash type in the PSBT, we only
// need to look at that field. If it is not present, then we can assume SIGHASH_DEFAULT or SIGHASH_ALL.
if (input.sighash_type != std::nullopt && (*input.sighash_type & 0x80) == SIGHASH_ANYONECANPAY) {
to_drop.clear();
break;
}
// Drop the non_witness_utxos that we can drop
for (unsigned int i : to_drop) {
psbtx.inputs.at(i).non_witness_utxo = nullptr;
if (input.non_witness_utxo) {
to_drop.push_back(i);
}
}
// Drop the non_witness_utxos that we can drop
for (unsigned int i : to_drop) {
psbtx.inputs.at(i).non_witness_utxo = nullptr;
}
}
bool FinalizePSBT(PartiallySignedTransaction& psbtx)

View File

@ -1247,7 +1247,7 @@ bool PSBTInputSignedAndVerified(const PartiallySignedTransaction psbt, unsigned
[[nodiscard]] PSBTError SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, const PrecomputedTransactionData* txdata, std::optional<int> sighash = std::nullopt, SignatureData* out_sigdata = nullptr, bool finalize = true);
/** Reduces the size of the PSBT by dropping unnecessary `non_witness_utxos` (i.e. complete previous transactions) from a psbt when all inputs are segwit v1. */
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx, std::optional<int> sighash_type);
void RemoveUnnecessaryTransactions(PartiallySignedTransaction& psbtx);
/** Counts the unsigned inputs of a PSBT. */
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);

View File

@ -244,7 +244,7 @@ PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std
UpdatePSBTOutput(provider, psbtx, i);
}
RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/std::nullopt);
RemoveUnnecessaryTransactions(psbtx);
return psbtx;
}

View File

@ -2240,7 +2240,7 @@ std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bo
}
}
RemoveUnnecessaryTransactions(psbtx, sighash_type);
RemoveUnnecessaryTransactions(psbtx);
// Complete if every input is now signed
complete = true;