Skip to content

Commit fae9478

Browse files
author
MarcoFalke
committed
Merge #17524: psbt: handle unspendable psbts
773d457 Mark PSBTs spending unspendable outputs as invalid in analysis (Andrew Chow) 638e40c Have a PSBTAnalysis state that indicates invalid PSBT (Andrew Chow) Pull request description: When analyzing an unspendable PSBT, report that it is unspendable and exit analysis early. ACKs for top commit: Sjors: ACK 773d457 instagibbs: After some thought ACK bitcoin/bitcoin@773d457 Tree-SHA512: 99b0cb2fa1ea37593fc65a20effe881639d69ddeeecf5197bc87bc7f2220cbeb40f1d429d517e4d27f2e9fb563a00cd845d2b4b1ce05246a75a6cb56fb9b0ba5
2 parents 1189b6a + 773d457 commit fae9478

File tree

6 files changed

+29
-1
lines changed

6 files changed

+29
-1
lines changed

src/node/psbt.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <node/psbt.h>
88
#include <policy/policy.h>
99
#include <policy/settings.h>
10+
#include <tinyformat.h>
1011

1112
#include <numeric>
1213

@@ -39,6 +40,11 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx)
3940
calc_fee = false;
4041
}
4142

43+
if (!utxo.IsNull() && utxo.scriptPubKey.IsUnspendable()) {
44+
result.SetInvalid(strprintf("PSBT is not valid. Input %u spends unspendable output", i));
45+
return result;
46+
}
47+
4248
// Check if it is final
4349
if (!utxo.IsNull() && !PSBTInputSigned(input)) {
4450
input_analysis.is_final = false;

src/node/psbt.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ struct PSBTAnalysis {
3030
Optional<CAmount> fee; //!< Amount of fee being paid by the transaction
3131
std::vector<PSBTInputAnalysis> inputs; //!< More information about the individual inputs of the transaction
3232
PSBTRole next; //!< Which of the BIP 174 roles needs to handle the transaction next
33+
std::string error; //!< Error message
34+
35+
void SetInvalid(std::string err_msg)
36+
{
37+
estimated_vsize = nullopt;
38+
estimated_feerate = nullopt;
39+
fee = nullopt;
40+
inputs.clear();
41+
next = PSBTRole::CREATOR;
42+
error = err_msg;
43+
}
3344
};
3445

3546
/**

src/psbt.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector
348348

349349
std::string PSBTRoleName(PSBTRole role) {
350350
switch (role) {
351+
case PSBTRole::CREATOR: return "creator";
351352
case PSBTRole::UPDATER: return "updater";
352353
case PSBTRole::SIGNER: return "signer";
353354
case PSBTRole::FINALIZER: return "finalizer";

src/psbt.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,7 @@ struct PartiallySignedTransaction
560560
};
561561

562562
enum class PSBTRole {
563+
CREATOR,
563564
UPDATER,
564565
SIGNER,
565566
FINALIZER,

src/rpc/rawtransaction.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1674,6 +1674,7 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
16741674
" \"estimated_feerate\" : feerate (numeric, optional) Estimated feerate of the final signed transaction in " + CURRENCY_UNIT + "/kB. Shown only if all UTXO slots in the PSBT have been filled.\n"
16751675
" \"fee\" : fee (numeric, optional) The transaction fee paid. Shown only if all UTXO slots in the PSBT have been filled.\n"
16761676
" \"next\" : \"role\" (string) Role of the next person that this psbt needs to go to\n"
1677+
" \"error\" : \"error\" (string) Error message if there is one"
16771678
"}\n"
16781679
},
16791680
RPCExamples {
@@ -1726,7 +1727,7 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
17261727
}
17271728
inputs_result.push_back(input_univ);
17281729
}
1729-
result.pushKV("inputs", inputs_result);
1730+
if (!inputs_result.empty()) result.pushKV("inputs", inputs_result);
17301731

17311732
if (psbta.estimated_vsize != nullopt) {
17321733
result.pushKV("estimated_vsize", (int)*psbta.estimated_vsize);
@@ -1738,6 +1739,9 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
17381739
result.pushKV("fee", ValueFromAmount(*psbta.fee));
17391740
}
17401741
result.pushKV("next", PSBTRoleName(psbta.next));
1742+
if (!psbta.error.empty()) {
1743+
result.pushKV("error", psbta.error);
1744+
}
17411745

17421746
return result;
17431747
}

test/functional/rpc_psbt.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,5 +417,10 @@ def test_psbt_input_keys(psbt_input, keys):
417417
analyzed = self.nodes[0].analyzepsbt(signed)
418418
assert analyzed['inputs'][0]['has_utxo'] and analyzed['inputs'][0]['is_final'] and analyzed['next'] == 'extractor'
419419

420+
self.log.info("PSBT spending unspendable outputs should have error message and Creator as next")
421+
analysis = self.nodes[0].analyzepsbt('cHNidP8BAJoCAAAAAljoeiG1ba8MI76OcHBFbDNvfLqlyHV5JPVFiHuyq911AAAAAAD/////g40EJ9DsZQpoqka7CwmK6kQiwHGyyng1Kgd5WdB86h0BAAAAAP////8CcKrwCAAAAAAWAEHYXCtx0AYLCcmIauuBXlCZHdoSTQDh9QUAAAAAFv8/wADXYP/7//////8JxOh0LR2HAI8AAAAAAAEBIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHEAABAACAAAEBIADC6wsAAAAAF2oUt/X69ELjeX2nTof+fZ10l+OyAokDAQcJAwEHENkMak8AAAAA')
422+
assert_equal(analysis['next'], 'creator')
423+
assert_equal(analysis['error'], 'PSBT is not valid. Input 0 spends unspendable output')
424+
420425
if __name__ == '__main__':
421426
PSBTTest().main()

0 commit comments

Comments
 (0)