Skip to content

Commit

Permalink
Merge bitcoin#19215: psbt: Include and allow both non_witness_utxo an…
Browse files Browse the repository at this point in the history
…d witness_utxo for segwit inputs

84d295e tests: Check that segwit inputs in psbt have both UTXO types (Andrew Chow)
4600479 psbt: always put a non_witness_utxo and don't remove it (Andrew Chow)
5279d8b psbt: Allow both non_witness_utxo and witness_utxo (Andrew Chow)
72f6bec rpc: show both UTXOs in decodepsbt (Andrew Chow)

Pull request description:

  Due to recent changes to hardware wallets, the full previous transaction will need to be provided for segwit inputs. Since some software may be checking for the existence of a `witness_utxo` to determine whether to produce a segwit signature, we keep that field to ease the transition.

  Because all of the sanity checks implemented by the `IsSane` functions were related to having mixed segwit and non-segwit data in a PSBT, those functions are removed as those checks are no longer proper.

  Some tests are updated/removed to accommodate this and a simple test added to check that both UTXOs are being added to segwit inputs.

  As discussed in the wallet IRC meeting, our own signer will not require `non_witness_utxo` for segwit inputs.

ACKs for top commit:
  Sjors:
    utACK 84d295e (didn't retest compared to 836d6fc, but fortunately HWI's CI tracks our master branch, with a bunch of hardware wallet simulators)
  ryanofsky:
    Code review re-ACK 84d295e. No changes since last review, but now I understand the context better. I think it would good to improve the comments as suggested bitcoin#19215 (comment) and maybe refer to
  meshcollider:
    utACK 84d295e

Tree-SHA512: ccc1fd3c16ac3859f5aca4fa489bd40f68be0b81bbdc4dd51188bbf28827a8642dc8b605a37318e5f16cf40f1c4910052dace2f27eca21bb58435f02a443e940
  • Loading branch information
meshcollider authored and knst committed Jan 16, 2024
1 parent 2d0aff1 commit 043e5e0
Show file tree
Hide file tree
Showing 7 changed files with 7 additions and 42 deletions.
22 changes: 0 additions & 22 deletions src/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,6 @@ bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
return true;
}

bool PartiallySignedTransaction::IsSane() const
{
for (PSBTInput input : inputs) {
if (!input.IsSane()) return false;
}
return true;
}

bool PartiallySignedTransaction::AddInput(const CTxIn& txin, PSBTInput& psbtin)
{
if (std::find(tx->vin.begin(), tx->vin.end(), txin) != tx->vin.end()) {
Expand Down Expand Up @@ -142,11 +134,6 @@ void PSBTInput::Merge(const PSBTInput& input)
if (final_script_sig.empty() && !input.final_script_sig.empty()) final_script_sig = input.final_script_sig;
}

bool PSBTInput::IsSane() const
{
return true;
}

void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
{
if (!redeem_script.empty()) {
Expand Down Expand Up @@ -231,11 +218,6 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
// Get UTXO
CTxOut utxo;

// Verify input sanity, which checks that at most one of witness or non-witness utxos is provided.
if (!input.IsSane()) {
return false;
}

if (input.non_witness_utxo) {
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
COutPoint prevout = tx.vin[index].prevout;
Expand Down Expand Up @@ -308,10 +290,6 @@ TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector
return TransactionError::PSBT_MISMATCH;
}
}
if (!out.IsSane()) {
return TransactionError::INVALID_PSBT;
}

return TransactionError::OK;
}

Expand Down
7 changes: 0 additions & 7 deletions src/psbt.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ struct PSBTInput
void FillSignatureData(SignatureData& sigdata) const;
void FromSignatureData(const SignatureData& sigdata);
void Merge(const PSBTInput& input);
bool IsSane() const;
PSBTInput() {}

template <typename Stream>
Expand Down Expand Up @@ -229,7 +228,6 @@ struct PSBTOutput
void FillSignatureData(SignatureData& sigdata) const;
void FromSignatureData(const SignatureData& sigdata);
void Merge(const PSBTOutput& output);
bool IsSane() const;
PSBTOutput() {}

template <typename Stream>
Expand Down Expand Up @@ -330,7 +328,6 @@ struct PartiallySignedTransaction
/** Merge psbt into this. The two psbts must have the same underlying CTransaction (i.e. the
* same actual Bitcoin transaction.) Returns true if the merge succeeded, false otherwise. */
[[nodiscard]] bool Merge(const PartiallySignedTransaction& psbt);
bool IsSane() const;
bool AddInput(const CTxIn& txin, PSBTInput& psbtin);
bool AddOutput(const CTxOut& txout, const PSBTOutput& psbtout);
PartiallySignedTransaction() {}
Expand Down Expand Up @@ -478,10 +475,6 @@ struct PartiallySignedTransaction
if (outputs.size() != tx->vout.size()) {
throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction.");
}
// Sanity check
if (!IsSane()) {
throw std::ios_base::failure("PSBT is not sane.");
}
}

template <typename Stream>
Expand Down
5 changes: 4 additions & 1 deletion src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1264,6 +1264,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
const PSBTInput& input = psbtx.inputs[i];
UniValue in(UniValue::VOBJ);
// UTXOs
bool have_a_utxo = false;
if (input.non_witness_utxo) {
UniValue non_wit(UniValue::VOBJ);
TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
Expand All @@ -1275,7 +1276,9 @@ UniValue decodepsbt(const JSONRPCRequest& request)
// Hack to just not show fee later
have_all_utxos = false;
}
} else {
have_a_utxo = true;
}
if (!have_a_utxo) {
have_all_utxos = false;
}

Expand Down
2 changes: 0 additions & 2 deletions src/test/fuzz/psbt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ FUZZ_TARGET(psbt)
}

(void)psbt.IsNull();
(void)psbt.IsSane();

std::optional<CMutableTransaction> tx = psbt.tx;
if (tx) {
Expand All @@ -44,7 +43,6 @@ FUZZ_TARGET(psbt)
for (const PSBTInput& input : psbt.inputs) {
(void)PSBTInputSigned(input);
(void)input.IsNull();
(void)input.IsSane();
}

for (const PSBTOutput& output : psbt.outputs) {
Expand Down
5 changes: 0 additions & 5 deletions src/wallet/scriptpubkeyman.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -713,11 +713,6 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
continue;
}

// Verify input looks sane. This will check that we have at most one uxto.
if (!input.IsSane()) {
return TransactionError::INVALID_PSBT;
}

// Get the Sighash type
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
return TransactionError::SIGHASH_MISMATCH;
Expand Down
5 changes: 0 additions & 5 deletions src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3005,11 +3005,6 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
continue;
}

// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
if (!input.IsSane()) {
return TransactionError::INVALID_PSBT;
}

// If we have no utxo, grab it from the wallet.
if (!input.non_witness_utxo) {
const uint256& txhash = txin.prevout.hash;
Expand Down
3 changes: 3 additions & 0 deletions test/functional/rpc_psbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ def run_test(self):
# spend single key from node 1
rawtx = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():9.99})['psbt']
walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(rawtx)
# Make sure it has both types of UTXOs
decoded = self.nodes[1].decodepsbt(walletprocesspsbt_out['psbt'])
assert 'non_witness_utxo' in decoded['inputs'][0]
assert_equal(walletprocesspsbt_out['complete'], True)
self.nodes[1].sendrawtransaction(self.nodes[1].finalizepsbt(walletprocesspsbt_out['psbt'])['hex'])

Expand Down

0 comments on commit 043e5e0

Please sign in to comment.