diff --git a/src/common/messages.cpp b/src/common/messages.cpp index 12e32cae806..700f4f03cd6 100644 --- a/src/common/messages.cpp +++ b/src/common/messages.cpp @@ -116,6 +116,8 @@ bilingual_str PSBTErrorString(PSBTError err) return Untranslated("Signer does not support PSBT"); case PSBTError::INCOMPLETE: return Untranslated("Input needs additional signatures or other data"); + case PSBTError::INVALID_TX: + return Untranslated("The transaction cannot be valid"); case PSBTError::OK: return Untranslated("No errors"); } // no default case, so the compiler can warn about missing cases diff --git a/src/common/types.h b/src/common/types.h index c366a847562..b9ebca15e84 100644 --- a/src/common/types.h +++ b/src/common/types.h @@ -23,6 +23,7 @@ enum class PSBTError { EXTERNAL_SIGNER_FAILED, UNSUPPORTED, INCOMPLETE, + INVALID_TX, OK, }; /** diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp index 3827290902d..5b78001c6d6 100644 --- a/src/node/psbt.cpp +++ b/src/node/psbt.cpp @@ -24,7 +24,8 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) result.inputs.resize(psbtx.inputs.size()); - const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx); + // PrecomputePSBTData calls GetUnsignedTx() which we checked already works + const PrecomputedTransactionData txdata = *PrecomputePSBTData(psbtx); for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { PSBTInput& input = psbtx.inputs[i]; diff --git a/src/psbt.cpp b/src/psbt.cpp index 2c67af233eb..d7320682b27 100644 --- a/src/psbt.cpp +++ b/src/psbt.cpp @@ -429,7 +429,7 @@ void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransactio psbt_out.FromSignatureData(sigdata); } -PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt) +std::optional PrecomputePSBTData(const PartiallySignedTransaction& psbt) { const CMutableTransaction& tx = *psbt.tx; bool have_all_spent_outputs = true; @@ -602,7 +602,11 @@ bool FinalizePSBT(PartiallySignedTransaction& psbtx) // PartiallySignedTransaction did not understand them), this will combine them into a final // script. bool complete = true; - const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx); + std::optional txdata_res = PrecomputePSBTData(psbtx); + if (!txdata_res) { + return false; + } + const PrecomputedTransactionData& txdata = *txdata_res; for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { PSBTInput& input = psbtx.inputs.at(i); complete &= (SignPSBTInput(DUMMY_SIGNING_PROVIDER, psbtx, i, &txdata, {.sighash_type = input.sighash_type, .finalize = true}, /*out_sigdata=*/nullptr) == PSBTError::OK); diff --git a/src/psbt.h b/src/psbt.h index 6e7e0cb925a..6fe7a8b2fc6 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -1333,7 +1333,7 @@ enum class PSBTRole { std::string PSBTRoleName(PSBTRole role); /** Compute a PrecomputedTransactionData object from a psbt. */ -PrecomputedTransactionData PrecomputePSBTData(const PartiallySignedTransaction& psbt); +std::optional PrecomputePSBTData(const PartiallySignedTransaction& psbt); /** Checks whether a PSBTInput is already signed by checking for non-null finalized fields. */ bool PSBTInputSigned(const PSBTInput& input); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 1ed94fac545..f6d323fc65d 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -180,7 +180,11 @@ PartiallySignedTransaction ProcessPSBT(const std::string& psbt_string, const std } } - const PrecomputedTransactionData& txdata = PrecomputePSBTData(psbtx); + std::optional txdata_res = PrecomputePSBTData(psbtx); + if (!txdata_res) { + throw JSONRPCPSBTError(common::PSBTError::INVALID_TX); + } + const PrecomputedTransactionData& txdata = *txdata_res; for (unsigned int i = 0; i < psbtx.inputs.size(); ++i) { if (PSBTInputSigned(psbtx.inputs.at(i))) { diff --git a/src/wallet/test/fuzz/scriptpubkeyman.cpp b/src/wallet/test/fuzz/scriptpubkeyman.cpp index 5733bc90e6f..620c1be6ef3 100644 --- a/src/wallet/test/fuzz/scriptpubkeyman.cpp +++ b/src/wallet/test/fuzz/scriptpubkeyman.cpp @@ -184,7 +184,11 @@ FUZZ_TARGET(scriptpubkeyman, .init = initialize_spkm) return; } auto psbt{*opt_psbt}; - const PrecomputedTransactionData txdata{PrecomputePSBTData(psbt)}; + std::optional txdata_res = PrecomputePSBTData(psbt); + if (!txdata_res) { + return; + } + const PrecomputedTransactionData& txdata = *txdata_res; common::PSBTFillOptions options{ .sign = fuzzed_data_provider.ConsumeBool(), .sighash_type = fuzzed_data_provider.ConsumeIntegralInRange(0, 151), diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 7473f80f4d0..e569ea6ef03 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2245,7 +2245,11 @@ std::optional CWallet::FillPSBT(PartiallySignedTransaction& psbtx, co } } - const PrecomputedTransactionData txdata = PrecomputePSBTData(psbtx); + std::optional txdata_res = PrecomputePSBTData(psbtx); + if (!txdata_res) { + return PSBTError::INVALID_TX; + } + const PrecomputedTransactionData& txdata = *txdata_res; // Fill in information from ScriptPubKeyMans for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {