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 1f85f6911d
commit 7515c35f22
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; 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; // Figure out if any non_witness_utxos should be dropped
// Only drop non_witness_utxos if sighash_type != SIGHASH_ANYONECANPAY std::vector<unsigned int> to_drop;
if ((*sighash_type & 0x80) != SIGHASH_ANYONECANPAY) { for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) {
// Figure out if any non_witness_utxos should be dropped const auto& input = psbtx.inputs.at(i);
std::vector<unsigned int> to_drop; int wit_ver;
for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { std::vector<unsigned char> wit_prog;
const auto& input = psbtx.inputs.at(i); if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) {
int wit_ver; // There's a non-segwit input, so we cannot drop any non_witness_utxos
std::vector<unsigned char> wit_prog; to_drop.clear();
if (input.witness_utxo.IsNull() || !input.witness_utxo.scriptPubKey.IsWitnessProgram(wit_ver, wit_prog)) { break;
// There's a non-segwit input or Segwit v0, so we cannot drop any witness_utxos }
to_drop.clear(); if (wit_ver == 0) {
break; // Segwit v0, so we cannot drop any non_witness_utxos
} to_drop.clear();
if (wit_ver == 0) { break;
// Segwit v0, so we cannot drop any non_witness_utxos }
to_drop.clear(); // non_witness_utxos cannot be dropped if the sighash type includes SIGHASH_ANYONECANPAY
break; // 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.non_witness_utxo) { if (input.sighash_type != std::nullopt && (*input.sighash_type & 0x80) == SIGHASH_ANYONECANPAY) {
to_drop.push_back(i); to_drop.clear();
} break;
} }
// Drop the non_witness_utxos that we can drop if (input.non_witness_utxo) {
for (unsigned int i : to_drop) { to_drop.push_back(i);
psbtx.inputs.at(i).non_witness_utxo = nullptr;
} }
} }
// 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) bool FinalizePSBT(PartiallySignedTransaction& psbtx)

View file

@ -1407,7 +1407,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); [[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. */ /** 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. */ /** Counts the unsigned inputs of a PSBT. */
size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& 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); UpdatePSBTOutput(provider, psbtx, i);
} }
RemoveUnnecessaryTransactions(psbtx, /*sighash_type=*/std::nullopt); RemoveUnnecessaryTransactions(psbtx);
return psbtx; return psbtx;
} }

View file

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