Skip to content

Commit a24806c

Browse files
committed
Merge #19215: psbt: Include and allow both non_witness_utxo and 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/bitcoin#19215 (comment) and maybe refer to meshcollider: utACK 84d295e Tree-SHA512: ccc1fd3c16ac3859f5aca4fa489bd40f68be0b81bbdc4dd51188bbf28827a8642dc8b605a37318e5f16cf40f1c4910052dace2f27eca21bb58435f02a443e940
2 parents 7027c67 + 84d295e commit a24806c

File tree

8 files changed

+22
-63
lines changed

8 files changed

+22
-63
lines changed

src/psbt.cpp

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@ bool PartiallySignedTransaction::Merge(const PartiallySignedTransaction& psbt)
3535
return true;
3636
}
3737

38-
bool PartiallySignedTransaction::IsSane() const
39-
{
40-
for (PSBTInput input : inputs) {
41-
if (!input.IsSane()) return false;
42-
}
43-
return true;
44-
}
45-
4638
bool PartiallySignedTransaction::AddInput(const CTxIn& txin, PSBTInput& psbtin)
4739
{
4840
if (std::find(tx->vin.begin(), tx->vin.end(), txin) != tx->vin.end()) {
@@ -144,8 +136,8 @@ void PSBTInput::Merge(const PSBTInput& input)
144136
{
145137
if (!non_witness_utxo && input.non_witness_utxo) non_witness_utxo = input.non_witness_utxo;
146138
if (witness_utxo.IsNull() && !input.witness_utxo.IsNull()) {
139+
// TODO: For segwit v1, we will want to clear out the non-witness utxo when setting a witness one. For v0 and non-segwit, this is not safe
147140
witness_utxo = input.witness_utxo;
148-
non_witness_utxo = nullptr; // Clear out any non-witness utxo when we set a witness one.
149141
}
150142

151143
partial_sigs.insert(input.partial_sigs.begin(), input.partial_sigs.end());
@@ -158,18 +150,6 @@ void PSBTInput::Merge(const PSBTInput& input)
158150
if (final_script_witness.IsNull() && !input.final_script_witness.IsNull()) final_script_witness = input.final_script_witness;
159151
}
160152

161-
bool PSBTInput::IsSane() const
162-
{
163-
// Cannot have both witness and non-witness utxos
164-
if (!witness_utxo.IsNull() && non_witness_utxo) return false;
165-
166-
// If we have a witness_script or a scriptWitness, we must also have a witness utxo
167-
if (!witness_script.empty() && witness_utxo.IsNull()) return false;
168-
if (!final_script_witness.IsNull() && witness_utxo.IsNull()) return false;
169-
170-
return true;
171-
}
172-
173153
void PSBTOutput::FillSignatureData(SignatureData& sigdata) const
174154
{
175155
if (!redeem_script.empty()) {
@@ -261,11 +241,6 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
261241
bool require_witness_sig = false;
262242
CTxOut utxo;
263243

264-
// Verify input sanity, which checks that at most one of witness or non-witness utxos is provided.
265-
if (!input.IsSane()) {
266-
return false;
267-
}
268-
269244
if (input.non_witness_utxo) {
270245
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
271246
COutPoint prevout = tx.vin[index].prevout;
@@ -299,10 +274,11 @@ bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction&
299274
if (require_witness_sig && !sigdata.witness) return false;
300275
input.FromSignatureData(sigdata);
301276

302-
// If we have a witness signature, use the smaller witness UTXO.
277+
// If we have a witness signature, put a witness UTXO.
278+
// TODO: For segwit v1, we should remove the non_witness_utxo
303279
if (sigdata.witness) {
304280
input.witness_utxo = utxo;
305-
input.non_witness_utxo = nullptr;
281+
// input.non_witness_utxo = nullptr;
306282
}
307283

308284
// Fill in the missing info
@@ -356,10 +332,6 @@ TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector
356332
return TransactionError::PSBT_MISMATCH;
357333
}
358334
}
359-
if (!out.IsSane()) {
360-
return TransactionError::INVALID_PSBT;
361-
}
362-
363335
return TransactionError::OK;
364336
}
365337

src/psbt.h

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -62,18 +62,17 @@ struct PSBTInput
6262
void FillSignatureData(SignatureData& sigdata) const;
6363
void FromSignatureData(const SignatureData& sigdata);
6464
void Merge(const PSBTInput& input);
65-
bool IsSane() const;
6665
PSBTInput() {}
6766

6867
template <typename Stream>
6968
inline void Serialize(Stream& s) const {
7069
// Write the utxo
71-
// If there is a non-witness utxo, then don't add the witness one.
7270
if (non_witness_utxo) {
7371
SerializeToVector(s, PSBT_IN_NON_WITNESS_UTXO);
7472
OverrideStream<Stream> os(&s, s.GetType(), s.GetVersion() | SERIALIZE_TRANSACTION_NO_WITNESS);
7573
SerializeToVector(os, non_witness_utxo);
76-
} else if (!witness_utxo.IsNull()) {
74+
}
75+
if (!witness_utxo.IsNull()) {
7776
SerializeToVector(s, PSBT_IN_WITNESS_UTXO);
7877
SerializeToVector(s, witness_utxo);
7978
}
@@ -284,7 +283,6 @@ struct PSBTOutput
284283
void FillSignatureData(SignatureData& sigdata) const;
285284
void FromSignatureData(const SignatureData& sigdata);
286285
void Merge(const PSBTOutput& output);
287-
bool IsSane() const;
288286
PSBTOutput() {}
289287

290288
template <typename Stream>
@@ -401,7 +399,6 @@ struct PartiallySignedTransaction
401399
/** Merge psbt into this. The two psbts must have the same underlying CTransaction (i.e. the
402400
* same actual Bitcoin transaction.) Returns true if the merge succeeded, false otherwise. */
403401
NODISCARD bool Merge(const PartiallySignedTransaction& psbt);
404-
bool IsSane() const;
405402
bool AddInput(const CTxIn& txin, PSBTInput& psbtin);
406403
bool AddOutput(const CTxOut& txout, const PSBTOutput& psbtout);
407404
PartiallySignedTransaction() {}
@@ -551,10 +548,6 @@ struct PartiallySignedTransaction
551548
if (outputs.size() != tx->vout.size()) {
552549
throw std::ios_base::failure("Outputs provided does not match the number of outputs in transaction.");
553550
}
554-
// Sanity check
555-
if (!IsSane()) {
556-
throw std::ios_base::failure("PSBT is not sane.");
557-
}
558551
}
559552

560553
template <typename Stream>

src/rpc/rawtransaction.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
11041104
const PSBTInput& input = psbtx.inputs[i];
11051105
UniValue in(UniValue::VOBJ);
11061106
// UTXOs
1107+
bool have_a_utxo = false;
11071108
if (!input.witness_utxo.IsNull()) {
11081109
const CTxOut& txout = input.witness_utxo;
11091110

@@ -1121,7 +1122,9 @@ UniValue decodepsbt(const JSONRPCRequest& request)
11211122
ScriptToUniv(txout.scriptPubKey, o, true);
11221123
out.pushKV("scriptPubKey", o);
11231124
in.pushKV("witness_utxo", out);
1124-
} else if (input.non_witness_utxo) {
1125+
have_a_utxo = true;
1126+
}
1127+
if (input.non_witness_utxo) {
11251128
UniValue non_wit(UniValue::VOBJ);
11261129
TxToUniv(*input.non_witness_utxo, uint256(), non_wit, false);
11271130
in.pushKV("non_witness_utxo", non_wit);
@@ -1132,7 +1135,9 @@ UniValue decodepsbt(const JSONRPCRequest& request)
11321135
// Hack to just not show fee later
11331136
have_all_utxos = false;
11341137
}
1135-
} else {
1138+
have_a_utxo = true;
1139+
}
1140+
if (!have_a_utxo) {
11361141
have_all_utxos = false;
11371142
}
11381143

src/test/fuzz/psbt.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
3939
}
4040

4141
(void)psbt.IsNull();
42-
(void)psbt.IsSane();
4342

4443
Optional<CMutableTransaction> tx = psbt.tx;
4544
if (tx) {
@@ -50,7 +49,6 @@ void test_one_input(const std::vector<uint8_t>& buffer)
5049
for (const PSBTInput& input : psbt.inputs) {
5150
(void)PSBTInputSigned(input);
5251
(void)input.IsNull();
53-
(void)input.IsSane();
5452
}
5553

5654
for (const PSBTOutput& output : psbt.outputs) {

src/wallet/scriptpubkeyman.cpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -597,11 +597,6 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
597597
continue;
598598
}
599599

600-
// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
601-
if (!input.IsSane()) {
602-
return TransactionError::INVALID_PSBT;
603-
}
604-
605600
// Get the Sighash type
606601
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
607602
return TransactionError::SIGHASH_MISMATCH;
@@ -2086,11 +2081,6 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
20862081
continue;
20872082
}
20882083

2089-
// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
2090-
if (!input.IsSane()) {
2091-
return TransactionError::INVALID_PSBT;
2092-
}
2093-
20942084
// Get the Sighash type
20952085
if (sign && input.sighash_type > 0 && input.sighash_type != sighash_type) {
20962086
return TransactionError::SIGHASH_MISMATCH;

src/wallet/test/psbt_wallet_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
6464
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
6565
ssTx << psbtx;
6666
std::string final_hex = HexStr(ssTx);
67-
BOOST_CHECK_EQUAL(final_hex, "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000");
67+
BOOST_CHECK_EQUAL(final_hex, "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001008a020000000158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e8876500000001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000");
6868

6969
// Mutate the transaction so that one of the inputs is invalid
7070
psbtx.tx->vin[0].prevout.n = 2;

src/wallet/wallet.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2507,13 +2507,8 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
25072507
continue;
25082508
}
25092509

2510-
// Verify input looks sane. This will check that we have at most one uxto, witness or non-witness.
2511-
if (!input.IsSane()) {
2512-
return TransactionError::INVALID_PSBT;
2513-
}
2514-
25152510
// If we have no utxo, grab it from the wallet.
2516-
if (!input.non_witness_utxo && input.witness_utxo.IsNull()) {
2511+
if (!input.non_witness_utxo) {
25172512
const uint256& txhash = txin.prevout.hash;
25182513
const auto it = mapWallet.find(txhash);
25192514
if (it != mapWallet.end()) {

test/functional/rpc_psbt.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def set_test_params(self):
3838
def skip_test_if_missing_module(self):
3939
self.skip_if_no_wallet()
4040

41+
# TODO: Re-enable this test with segwit v1
4142
def test_utxo_conversion(self):
4243
mining_node = self.nodes[2]
4344
offline_node = self.nodes[0]
@@ -156,6 +157,10 @@ def run_test(self):
156157
# spend single key from node 1
157158
rawtx = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99})['psbt']
158159
walletprocesspsbt_out = self.nodes[1].walletprocesspsbt(rawtx)
160+
# Make sure it has both types of UTXOs
161+
decoded = self.nodes[1].decodepsbt(walletprocesspsbt_out['psbt'])
162+
assert 'non_witness_utxo' in decoded['inputs'][0]
163+
assert 'witness_utxo' in decoded['inputs'][0]
159164
assert_equal(walletprocesspsbt_out['complete'], True)
160165
self.nodes[1].sendrawtransaction(self.nodes[1].finalizepsbt(walletprocesspsbt_out['psbt'])['hex'])
161166

@@ -352,7 +357,8 @@ def run_test(self):
352357
for i, signer in enumerate(signers):
353358
self.nodes[2].unloadwallet("wallet{}".format(i))
354359

355-
self.test_utxo_conversion()
360+
# TODO: Re-enable this for segwit v1
361+
# self.test_utxo_conversion()
356362

357363
# Test that psbts with p2pkh outputs are created properly
358364
p2pkh = self.nodes[0].getnewaddress(address_type='legacy')

0 commit comments

Comments
 (0)