mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-13 11:35:20 +01:00
common: Add PSBTError enum
Add separate PSBTError enum instead of reusing TransactionError enum for PSBT operations, and drop unused error codes. The error codes returned by PSBT operations and transaction broadcast functions mostly do not overlap, so using an unified enum makes it harder to call any of these functions and know which errors actually need to be handled. Define PSBTError in the common library because PSBT functionality is implemented in the common library and used by both the node (for rawtransaction RPCs) and the wallet.
This commit is contained in:
parent
0d44c44ae3
commit
02e62c6c9a
25 changed files with 156 additions and 97 deletions
|
@ -137,6 +137,7 @@ BITCOIN_CORE_H = \
|
|||
common/bloom.h \
|
||||
common/init.h \
|
||||
common/run_command.h \
|
||||
common/types.h \
|
||||
common/url.h \
|
||||
compat/assumptions.h \
|
||||
compat/byteswap.h \
|
||||
|
|
26
src/common/types.h
Normal file
26
src/common/types.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) 2010-2021 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
//! @file common/types.h is a home for simple enum and struct type definitions
|
||||
//! that can be used internally by functions in the libbitcoin_common library,
|
||||
//! but also used externally by node, wallet, and GUI code.
|
||||
//!
|
||||
//! This file is intended to define only simple types that do not have external
|
||||
//! dependencies. More complicated types should be defined in dedicated header
|
||||
//! files.
|
||||
|
||||
#ifndef BITCOIN_COMMON_TYPES_H
|
||||
#define BITCOIN_COMMON_TYPES_H
|
||||
|
||||
namespace common {
|
||||
enum class PSBTError {
|
||||
MISSING_INPUTS,
|
||||
SIGHASH_MISMATCH,
|
||||
EXTERNAL_SIGNER_NOT_FOUND,
|
||||
EXTERNAL_SIGNER_FAILED,
|
||||
UNSUPPORTED,
|
||||
};
|
||||
} // namespace common
|
||||
|
||||
#endif // BITCOIN_COMMON_TYPES_H
|
|
@ -30,9 +30,11 @@ class CFeeRate;
|
|||
class CKey;
|
||||
enum class FeeReason;
|
||||
enum class OutputType;
|
||||
enum class TransactionError;
|
||||
struct PartiallySignedTransaction;
|
||||
struct bilingual_str;
|
||||
namespace common {
|
||||
enum class PSBTError;
|
||||
} // namespace common
|
||||
namespace wallet {
|
||||
class CCoinControl;
|
||||
class CWallet;
|
||||
|
@ -202,7 +204,7 @@ public:
|
|||
int& num_blocks) = 0;
|
||||
|
||||
//! Fill PSBT.
|
||||
virtual TransactionError fillPSBT(int sighash_type,
|
||||
virtual std::optional<common::PSBTError> fillPSBT(int sighash_type,
|
||||
bool sign,
|
||||
bool bip32derivs,
|
||||
size_t* n_signed,
|
||||
|
|
|
@ -17,16 +17,10 @@ enum class TransactionError {
|
|||
OK, //!< No error
|
||||
MISSING_INPUTS,
|
||||
ALREADY_IN_CHAIN,
|
||||
P2P_DISABLED,
|
||||
MEMPOOL_REJECTED,
|
||||
MEMPOOL_ERROR,
|
||||
INVALID_PSBT,
|
||||
PSBT_MISMATCH,
|
||||
SIGHASH_MISMATCH,
|
||||
MAX_FEE_EXCEEDED,
|
||||
MAX_BURN_EXCEEDED,
|
||||
EXTERNAL_SIGNER_NOT_FOUND,
|
||||
EXTERNAL_SIGNER_FAILED,
|
||||
INVALID_PACKAGE,
|
||||
};
|
||||
|
||||
|
|
|
@ -508,17 +508,17 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
|
|||
return true;
|
||||
}
|
||||
|
||||
TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs)
|
||||
bool CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs)
|
||||
{
|
||||
out = psbtxs[0]; // Copy the first one
|
||||
|
||||
// Merge
|
||||
for (auto it = std::next(psbtxs.begin()); it != psbtxs.end(); ++it) {
|
||||
if (!out.Merge(*it)) {
|
||||
return TransactionError::PSBT_MISMATCH;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return TransactionError::OK;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PSBTRoleName(PSBTRole role) {
|
||||
|
|
|
@ -1263,9 +1263,9 @@ bool FinalizeAndExtractPSBT(PartiallySignedTransaction& psbtx, CMutableTransacti
|
|||
*
|
||||
* @param[out] out the combined PSBT, if successful
|
||||
* @param[in] psbtxs the PSBTs to combine
|
||||
* @return error (OK if we successfully combined the transactions, other error if they were not compatible)
|
||||
* @return True if we successfully combined the transactions, false if they were not compatible
|
||||
*/
|
||||
[[nodiscard]] TransactionError CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs);
|
||||
[[nodiscard]] bool CombinePSBTs(PartiallySignedTransaction& out, const std::vector<PartiallySignedTransaction>& psbtxs);
|
||||
|
||||
//! Decode a base64ed PSBT into a PartiallySignedTransaction
|
||||
[[nodiscard]] bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <qt/forms/ui_psbtoperationsdialog.h>
|
||||
#include <qt/guiutil.h>
|
||||
#include <qt/optionsmodel.h>
|
||||
#include <util/error.h>
|
||||
#include <util/fs.h>
|
||||
#include <util/strencodings.h>
|
||||
|
||||
|
@ -55,10 +56,10 @@ void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
|
|||
bool complete = FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
|
||||
if (m_wallet_model) {
|
||||
size_t n_could_sign;
|
||||
TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, &n_could_sign, m_transaction_data, complete);
|
||||
if (err != TransactionError::OK) {
|
||||
const auto err{m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, &n_could_sign, m_transaction_data, complete)};
|
||||
if (err) {
|
||||
showStatus(tr("Failed to load transaction: %1")
|
||||
.arg(QString::fromStdString(TransactionErrorString(err).translated)),
|
||||
.arg(QString::fromStdString(PSBTErrorString(*err).translated)),
|
||||
StatusLevel::ERR);
|
||||
return;
|
||||
}
|
||||
|
@ -79,11 +80,11 @@ void PSBTOperationsDialog::signTransaction()
|
|||
|
||||
WalletModel::UnlockContext ctx(m_wallet_model->requestUnlock());
|
||||
|
||||
TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/true, /*bip32derivs=*/true, &n_signed, m_transaction_data, complete);
|
||||
const auto err{m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/true, /*bip32derivs=*/true, &n_signed, m_transaction_data, complete)};
|
||||
|
||||
if (err != TransactionError::OK) {
|
||||
if (err) {
|
||||
showStatus(tr("Failed to sign transaction: %1")
|
||||
.arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
|
||||
.arg(QString::fromStdString(PSBTErrorString(*err).translated)), StatusLevel::ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -247,9 +248,9 @@ size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &p
|
|||
|
||||
size_t n_signed;
|
||||
bool complete;
|
||||
TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/false, &n_signed, m_transaction_data, complete);
|
||||
const auto err{m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/false, &n_signed, m_transaction_data, complete)};
|
||||
|
||||
if (err != TransactionError::OK) {
|
||||
if (err) {
|
||||
return 0;
|
||||
}
|
||||
return n_signed;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <QSettings>
|
||||
#include <QTextDocument>
|
||||
|
||||
using common::PSBTError;
|
||||
using wallet::CCoinControl;
|
||||
using wallet::DEFAULT_PAY_TX_FEE;
|
||||
|
||||
|
@ -442,26 +443,26 @@ void SendCoinsDialog::presentPSBT(PartiallySignedTransaction& psbtx)
|
|||
}
|
||||
|
||||
bool SendCoinsDialog::signWithExternalSigner(PartiallySignedTransaction& psbtx, CMutableTransaction& mtx, bool& complete) {
|
||||
TransactionError err;
|
||||
std::optional<PSBTError> err;
|
||||
try {
|
||||
err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/true, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
|
||||
} catch (const std::runtime_error& e) {
|
||||
QMessageBox::critical(nullptr, tr("Sign failed"), e.what());
|
||||
return false;
|
||||
}
|
||||
if (err == TransactionError::EXTERNAL_SIGNER_NOT_FOUND) {
|
||||
if (err == PSBTError::EXTERNAL_SIGNER_NOT_FOUND) {
|
||||
//: "External signer" means using devices such as hardware wallets.
|
||||
const QString msg = tr("External signer not found");
|
||||
QMessageBox::critical(nullptr, msg, msg);
|
||||
return false;
|
||||
}
|
||||
if (err == TransactionError::EXTERNAL_SIGNER_FAILED) {
|
||||
if (err == PSBTError::EXTERNAL_SIGNER_FAILED) {
|
||||
//: "External signer" means using devices such as hardware wallets.
|
||||
const QString msg = tr("External signer failure");
|
||||
QMessageBox::critical(nullptr, msg, msg);
|
||||
return false;
|
||||
}
|
||||
if (err != TransactionError::OK) {
|
||||
if (err) {
|
||||
tfm::format(std::cerr, "Failed to sign PSBT");
|
||||
processSendCoinsReturn(WalletModel::TransactionCreationFailed);
|
||||
return false;
|
||||
|
@ -501,9 +502,9 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
|||
PartiallySignedTransaction psbtx(mtx);
|
||||
bool complete = false;
|
||||
// Fill without signing
|
||||
TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
|
||||
const auto err{model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete)};
|
||||
assert(!complete);
|
||||
assert(err == TransactionError::OK);
|
||||
assert(!err);
|
||||
|
||||
// Copy PSBT to clipboard and offer to save
|
||||
presentPSBT(psbtx);
|
||||
|
@ -517,9 +518,9 @@ void SendCoinsDialog::sendButtonClicked([[maybe_unused]] bool checked)
|
|||
bool complete = false;
|
||||
// Always fill without signing first. This prevents an external signer
|
||||
// from being called prematurely and is not expensive.
|
||||
TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete);
|
||||
const auto err{model->wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, /*n_signed=*/nullptr, psbtx, complete)};
|
||||
assert(!complete);
|
||||
assert(err == TransactionError::OK);
|
||||
assert(!err);
|
||||
send_failure = !signWithExternalSigner(psbtx, mtx, complete);
|
||||
// Don't broadcast when user rejects it on the device or there's a failure:
|
||||
broadcast = complete && !send_failure;
|
||||
|
|
|
@ -534,8 +534,8 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
|
|||
// "Create Unsigned" clicked
|
||||
PartiallySignedTransaction psbtx(mtx);
|
||||
bool complete = false;
|
||||
const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, nullptr, psbtx, complete);
|
||||
if (err != TransactionError::OK || complete) {
|
||||
const auto err{wallet().fillPSBT(SIGHASH_ALL, /*sign=*/false, /*bip32derivs=*/true, nullptr, psbtx, complete)};
|
||||
if (err || complete) {
|
||||
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1485,9 +1485,8 @@ static RPCHelpMan combinepsbt()
|
|||
}
|
||||
|
||||
PartiallySignedTransaction merged_psbt;
|
||||
const TransactionError error = CombinePSBTs(merged_psbt, psbtxs);
|
||||
if (error != TransactionError::OK) {
|
||||
throw JSONRPCTransactionError(error);
|
||||
if (!CombinePSBTs(merged_psbt, psbtxs)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "PSBTs not compatible (different transactions)");
|
||||
}
|
||||
|
||||
DataStream ssTx{};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <clientversion.h>
|
||||
#include <core_io.h>
|
||||
#include <common/args.h>
|
||||
#include <common/types.h>
|
||||
#include <consensus/amount.h>
|
||||
#include <script/interpreter.h>
|
||||
#include <key_io.h>
|
||||
|
@ -30,6 +31,8 @@
|
|||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
using common::PSBTError;
|
||||
|
||||
const std::string UNIX_EPOCH_TIME = "UNIX epoch time";
|
||||
const std::string EXAMPLE_ADDRESS[2] = {"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", "bc1q02ad21edsxd23d32dfgqqsz4vv4nmtfzuklhy3"};
|
||||
|
||||
|
@ -364,6 +367,18 @@ unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
|
|||
return unsigned_target;
|
||||
}
|
||||
|
||||
RPCErrorCode RPCErrorFromPSBTError(PSBTError err)
|
||||
{
|
||||
switch (err) {
|
||||
case PSBTError::UNSUPPORTED:
|
||||
return RPC_INVALID_PARAMETER;
|
||||
case PSBTError::SIGHASH_MISMATCH:
|
||||
return RPC_DESERIALIZATION_ERROR;
|
||||
default: break;
|
||||
}
|
||||
return RPC_TRANSACTION_ERROR;
|
||||
}
|
||||
|
||||
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
|
||||
{
|
||||
switch (terr) {
|
||||
|
@ -371,18 +386,16 @@ RPCErrorCode RPCErrorFromTransactionError(TransactionError terr)
|
|||
return RPC_TRANSACTION_REJECTED;
|
||||
case TransactionError::ALREADY_IN_CHAIN:
|
||||
return RPC_TRANSACTION_ALREADY_IN_CHAIN;
|
||||
case TransactionError::P2P_DISABLED:
|
||||
return RPC_CLIENT_P2P_DISABLED;
|
||||
case TransactionError::INVALID_PSBT:
|
||||
case TransactionError::PSBT_MISMATCH:
|
||||
return RPC_INVALID_PARAMETER;
|
||||
case TransactionError::SIGHASH_MISMATCH:
|
||||
return RPC_DESERIALIZATION_ERROR;
|
||||
default: break;
|
||||
}
|
||||
return RPC_TRANSACTION_ERROR;
|
||||
}
|
||||
|
||||
UniValue JSONRPCPSBTError(PSBTError err)
|
||||
{
|
||||
return JSONRPCError(RPCErrorFromPSBTError(err), PSBTErrorString(err).original);
|
||||
}
|
||||
|
||||
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string)
|
||||
{
|
||||
if (err_string.length() > 0) {
|
||||
|
|
|
@ -37,6 +37,9 @@ enum class OutputType;
|
|||
enum class TransactionError;
|
||||
struct FlatSigningProvider;
|
||||
struct bilingual_str;
|
||||
namespace common {
|
||||
enum class PSBTError;
|
||||
} // namespace common
|
||||
|
||||
static constexpr bool DEFAULT_RPC_DOC_CHECK{
|
||||
#ifdef RPC_DOC_CHECK
|
||||
|
@ -128,6 +131,7 @@ int ParseSighashString(const UniValue& sighash);
|
|||
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target);
|
||||
|
||||
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
|
||||
UniValue JSONRPCPSBTError(common::PSBTError err);
|
||||
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");
|
||||
|
||||
//! Parse a JSON range specified as int64, or [int64, int64]
|
||||
|
|
|
@ -18,15 +18,10 @@
|
|||
|
||||
namespace {
|
||||
constexpr TransactionError ALL_TRANSACTION_ERROR[] = {
|
||||
TransactionError::OK,
|
||||
TransactionError::MISSING_INPUTS,
|
||||
TransactionError::ALREADY_IN_CHAIN,
|
||||
TransactionError::P2P_DISABLED,
|
||||
TransactionError::MEMPOOL_REJECTED,
|
||||
TransactionError::MEMPOOL_ERROR,
|
||||
TransactionError::INVALID_PSBT,
|
||||
TransactionError::PSBT_MISMATCH,
|
||||
TransactionError::SIGHASH_MISMATCH,
|
||||
TransactionError::MAX_FEE_EXCEEDED,
|
||||
};
|
||||
}; // namespace
|
||||
|
|
|
@ -4,12 +4,33 @@
|
|||
|
||||
#include <util/error.h>
|
||||
|
||||
#include <common/types.h>
|
||||
#include <tinyformat.h>
|
||||
#include <util/translation.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
|
||||
using common::PSBTError;
|
||||
|
||||
bilingual_str PSBTErrorString(PSBTError err)
|
||||
{
|
||||
switch (err) {
|
||||
case PSBTError::MISSING_INPUTS:
|
||||
return Untranslated("Inputs missing or spent");
|
||||
case PSBTError::SIGHASH_MISMATCH:
|
||||
return Untranslated("Specified sighash value does not match value stored in PSBT");
|
||||
case PSBTError::EXTERNAL_SIGNER_NOT_FOUND:
|
||||
return Untranslated("External signer not found");
|
||||
case PSBTError::EXTERNAL_SIGNER_FAILED:
|
||||
return Untranslated("External signer failed to sign");
|
||||
case PSBTError::UNSUPPORTED:
|
||||
return Untranslated("Signer does not support PSBT");
|
||||
// no default case, so the compiler can warn about missing cases
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
bilingual_str TransactionErrorString(const TransactionError err)
|
||||
{
|
||||
switch (err) {
|
||||
|
@ -19,26 +40,14 @@ bilingual_str TransactionErrorString(const TransactionError err)
|
|||
return Untranslated("Inputs missing or spent");
|
||||
case TransactionError::ALREADY_IN_CHAIN:
|
||||
return Untranslated("Transaction already in block chain");
|
||||
case TransactionError::P2P_DISABLED:
|
||||
return Untranslated("Peer-to-peer functionality missing or disabled");
|
||||
case TransactionError::MEMPOOL_REJECTED:
|
||||
return Untranslated("Transaction rejected by mempool");
|
||||
case TransactionError::MEMPOOL_ERROR:
|
||||
return Untranslated("Mempool internal error");
|
||||
case TransactionError::INVALID_PSBT:
|
||||
return Untranslated("PSBT is not well-formed");
|
||||
case TransactionError::PSBT_MISMATCH:
|
||||
return Untranslated("PSBTs not compatible (different transactions)");
|
||||
case TransactionError::SIGHASH_MISMATCH:
|
||||
return Untranslated("Specified sighash value does not match value stored in PSBT");
|
||||
case TransactionError::MAX_FEE_EXCEEDED:
|
||||
return Untranslated("Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)");
|
||||
case TransactionError::MAX_BURN_EXCEEDED:
|
||||
return Untranslated("Unspendable output exceeds maximum configured by user (maxburnamount)");
|
||||
case TransactionError::EXTERNAL_SIGNER_NOT_FOUND:
|
||||
return Untranslated("External signer not found");
|
||||
case TransactionError::EXTERNAL_SIGNER_FAILED:
|
||||
return Untranslated("External signer failed to sign");
|
||||
case TransactionError::INVALID_PACKAGE:
|
||||
return Untranslated("Transaction rejected due to invalid package");
|
||||
// no default case, so the compiler can warn about missing cases
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
#include <string>
|
||||
|
||||
struct bilingual_str;
|
||||
namespace common {
|
||||
enum class PSBTError;
|
||||
} // namespace common
|
||||
|
||||
bilingual_str PSBTErrorString(common::PSBTError err);
|
||||
|
||||
bilingual_str TransactionErrorString(const TransactionError error);
|
||||
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using common::PSBTError;
|
||||
|
||||
namespace wallet {
|
||||
bool ExternalSignerScriptPubKeyMan::SetupDescriptor(WalletBatch& batch, std::unique_ptr<Descriptor> desc)
|
||||
{
|
||||
|
@ -76,7 +78,7 @@ util::Result<void> ExternalSignerScriptPubKeyMan::DisplayAddress(const CTxDestin
|
|||
}
|
||||
|
||||
// If sign is true, transaction must previously have been filled
|
||||
TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
std::optional<PSBTError> ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
{
|
||||
if (!sign) {
|
||||
return DescriptorScriptPubKeyMan::FillPSBT(psbt, txdata, sighash_type, false, bip32derivs, n_signed, finalize);
|
||||
|
@ -88,14 +90,14 @@ TransactionError ExternalSignerScriptPubKeyMan::FillPSBT(PartiallySignedTransact
|
|||
// TODO: for multisig wallets, we should only care if all _our_ inputs are signed
|
||||
complete &= PSBTInputSigned(input);
|
||||
}
|
||||
if (complete) return TransactionError::OK;
|
||||
if (complete) return {};
|
||||
|
||||
std::string strFailReason;
|
||||
if(!GetExternalSigner().SignTransaction(psbt, strFailReason)) {
|
||||
tfm::format(std::cerr, "Failed to sign: %s\n", strFailReason);
|
||||
return TransactionError::EXTERNAL_SIGNER_FAILED;
|
||||
return PSBTError::EXTERNAL_SIGNER_FAILED;
|
||||
}
|
||||
if (finalize) FinalizePSBT(psbt); // This won't work in a multisig setup
|
||||
return TransactionError::OK;
|
||||
return {};
|
||||
}
|
||||
} // namespace wallet
|
||||
|
|
|
@ -35,7 +35,7 @@ class ExternalSignerScriptPubKeyMan : public DescriptorScriptPubKeyMan
|
|||
*/
|
||||
util::Result<void> DisplayAddress(const CTxDestination& dest, const ExternalSigner& signer) const;
|
||||
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
std::optional<common::PSBTError> FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
};
|
||||
} // namespace wallet
|
||||
#endif // BITCOIN_WALLET_EXTERNAL_SIGNER_SCRIPTPUBKEYMAN_H
|
||||
|
|
|
@ -343,8 +343,8 @@ bool SignTransaction(CWallet& wallet, CMutableTransaction& mtx) {
|
|||
// so external signers are not asked to sign more than once.
|
||||
bool complete;
|
||||
wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
|
||||
const TransactionError err = wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true /* sign */, false /* bip32derivs */);
|
||||
if (err != TransactionError::OK) return false;
|
||||
auto err{wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true /* sign */, false /* bip32derivs */)};
|
||||
if (err) return false;
|
||||
complete = FinalizeAndExtractPSBT(psbtx, mtx);
|
||||
return complete;
|
||||
} else {
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using common::PSBTError;
|
||||
using interfaces::Chain;
|
||||
using interfaces::FoundBlock;
|
||||
using interfaces::Handler;
|
||||
|
@ -389,7 +390,7 @@ public:
|
|||
}
|
||||
return {};
|
||||
}
|
||||
TransactionError fillPSBT(int sighash_type,
|
||||
std::optional<PSBTError> fillPSBT(int sighash_type,
|
||||
bool sign,
|
||||
bool bip32derivs,
|
||||
size_t* n_signed,
|
||||
|
|
|
@ -97,9 +97,9 @@ static UniValue FinishTransaction(const std::shared_ptr<CWallet> pwallet, const
|
|||
// so external signers are not asked to sign more than once.
|
||||
bool complete;
|
||||
pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, /*sign=*/false, /*bip32derivs=*/true);
|
||||
const TransactionError err{pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, /*sign=*/true, /*bip32derivs=*/false)};
|
||||
if (err != TransactionError::OK) {
|
||||
throw JSONRPCTransactionError(err);
|
||||
const auto err{pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, /*sign=*/true, /*bip32derivs=*/false)};
|
||||
if (err) {
|
||||
throw JSONRPCPSBTError(*err);
|
||||
}
|
||||
|
||||
CMutableTransaction mtx;
|
||||
|
@ -1153,8 +1153,8 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
|
|||
} else {
|
||||
PartiallySignedTransaction psbtx(mtx);
|
||||
bool complete = false;
|
||||
const TransactionError err = pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, /*sign=*/false, /*bip32derivs=*/true);
|
||||
CHECK_NONFATAL(err == TransactionError::OK);
|
||||
const auto err{pwallet->FillPSBT(psbtx, complete, SIGHASH_DEFAULT, /*sign=*/false, /*bip32derivs=*/true)};
|
||||
CHECK_NONFATAL(!err);
|
||||
CHECK_NONFATAL(!complete);
|
||||
DataStream ssTx{};
|
||||
ssTx << psbtx;
|
||||
|
@ -1602,9 +1602,9 @@ RPCHelpMan walletprocesspsbt()
|
|||
|
||||
if (sign) EnsureWalletIsUnlocked(*pwallet);
|
||||
|
||||
const TransactionError err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs, nullptr, finalize)};
|
||||
if (err != TransactionError::OK) {
|
||||
throw JSONRPCTransactionError(err);
|
||||
const auto err{wallet.FillPSBT(psbtx, complete, nHashType, sign, bip32derivs, nullptr, finalize)};
|
||||
if (err) {
|
||||
throw JSONRPCPSBTError(*err);
|
||||
}
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
|
@ -1736,9 +1736,9 @@ RPCHelpMan walletcreatefundedpsbt()
|
|||
// Fill transaction with out data but don't sign
|
||||
bool bip32derivs = request.params[4].isNull() ? true : request.params[4].get_bool();
|
||||
bool complete = true;
|
||||
const TransactionError err{wallet.FillPSBT(psbtx, complete, 1, /*sign=*/false, /*bip32derivs=*/bip32derivs)};
|
||||
if (err != TransactionError::OK) {
|
||||
throw JSONRPCTransactionError(err);
|
||||
const auto err{wallet.FillPSBT(psbtx, complete, 1, /*sign=*/false, /*bip32derivs=*/bip32derivs)};
|
||||
if (err) {
|
||||
throw JSONRPCPSBTError(*err);
|
||||
}
|
||||
|
||||
// Serialize the PSBT
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
|
||||
#include <optional>
|
||||
|
||||
using common::PSBTError;
|
||||
|
||||
namespace wallet {
|
||||
//! Value for the first BIP 32 hardened derivation. Can be used as a bit mask and as a value. See BIP 32 for more details.
|
||||
const uint32_t BIP32_HARDENED_KEY_LIMIT = 0x80000000;
|
||||
|
@ -627,7 +629,7 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
|
|||
return SigningResult::SIGNING_FAILED;
|
||||
}
|
||||
|
||||
TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
std::optional<PSBTError> LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
{
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
|
@ -642,13 +644,13 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
|
|||
|
||||
// Get the Sighash type
|
||||
if (sign && input.sighash_type != std::nullopt && *input.sighash_type != sighash_type) {
|
||||
return TransactionError::SIGHASH_MISMATCH;
|
||||
return PSBTError::SIGHASH_MISMATCH;
|
||||
}
|
||||
|
||||
// Check non_witness_utxo has specified prevout
|
||||
if (input.non_witness_utxo) {
|
||||
if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
|
||||
return TransactionError::MISSING_INPUTS;
|
||||
return PSBTError::MISSING_INPUTS;
|
||||
}
|
||||
} else if (input.witness_utxo.IsNull()) {
|
||||
// There's no UTXO so we can just skip this now
|
||||
|
@ -670,7 +672,7 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
|
|||
UpdatePSBTOutput(HidingSigningProvider(this, true, !bip32derivs), psbtx, i);
|
||||
}
|
||||
|
||||
return TransactionError::OK;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<CKeyMetadata> LegacyScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const
|
||||
|
@ -2485,7 +2487,7 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
|
|||
return SigningResult::OK;
|
||||
}
|
||||
|
||||
TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
std::optional<PSBTError> DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, const PrecomputedTransactionData& txdata, int sighash_type, bool sign, bool bip32derivs, int* n_signed, bool finalize) const
|
||||
{
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
|
@ -2500,7 +2502,7 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
|
|||
|
||||
// Get the Sighash type
|
||||
if (sign && input.sighash_type != std::nullopt && *input.sighash_type != sighash_type) {
|
||||
return TransactionError::SIGHASH_MISMATCH;
|
||||
return PSBTError::SIGHASH_MISMATCH;
|
||||
}
|
||||
|
||||
// Get the scriptPubKey to know which SigningProvider to use
|
||||
|
@ -2509,7 +2511,7 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
|
|||
script = input.witness_utxo.scriptPubKey;
|
||||
} else if (input.non_witness_utxo) {
|
||||
if (txin.prevout.n >= input.non_witness_utxo->vout.size()) {
|
||||
return TransactionError::MISSING_INPUTS;
|
||||
return PSBTError::MISSING_INPUTS;
|
||||
}
|
||||
script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
|
||||
} else {
|
||||
|
@ -2580,7 +2582,7 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
|
|||
UpdatePSBTOutput(HidingSigningProvider(keys.get(), /*hide_secret=*/true, /*hide_origin=*/!bip32derivs), psbtx, i);
|
||||
}
|
||||
|
||||
return TransactionError::OK;
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unique_ptr<CKeyMetadata> DescriptorScriptPubKeyMan::GetMetadata(const CTxDestination& dest) const
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
#include <addresstype.h>
|
||||
#include <common/signmessage.h>
|
||||
#include <common/types.h>
|
||||
#include <logging.h>
|
||||
#include <psbt.h>
|
||||
#include <script/descriptor.h>
|
||||
|
@ -243,7 +244,7 @@ public:
|
|||
/** Sign a message with the given script */
|
||||
virtual SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const { return SigningResult::SIGNING_FAILED; };
|
||||
/** Adds script and derivation path information to a PSBT, and optionally signs it. */
|
||||
virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const { return TransactionError::INVALID_PSBT; }
|
||||
virtual std::optional<common::PSBTError> FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const { return common::PSBTError::UNSUPPORTED; }
|
||||
|
||||
virtual uint256 GetID() const { return uint256(); }
|
||||
|
||||
|
@ -421,7 +422,7 @@ public:
|
|||
|
||||
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
|
||||
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
std::optional<common::PSBTError> FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
|
||||
uint256 GetID() const override;
|
||||
|
||||
|
@ -651,7 +652,7 @@ public:
|
|||
|
||||
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, bilingual_str>& input_errors) const override;
|
||||
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
std::optional<common::PSBTError> FillPSBT(PartiallySignedTransaction& psbt, const PrecomputedTransactionData& txdata, int sighash_type = SIGHASH_DEFAULT, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr, bool finalize = true) const override;
|
||||
|
||||
uint256 GetID() const override;
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
|
|||
|
||||
// Fill transaction with our data
|
||||
bool complete = true;
|
||||
BOOST_REQUIRE_EQUAL(TransactionError::OK, m_wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, false, true));
|
||||
BOOST_REQUIRE(!m_wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, false, true));
|
||||
|
||||
// Get the final tx
|
||||
DataStream ssTx{};
|
||||
|
@ -73,7 +73,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
|
|||
|
||||
// Try to sign the mutated input
|
||||
SignatureData sigdata;
|
||||
BOOST_CHECK(m_wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true, true) != TransactionError::OK);
|
||||
BOOST_CHECK(m_wallet.FillPSBT(psbtx, complete, SIGHASH_ALL, true, true));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(parse_hd_keypath)
|
||||
|
|
|
@ -81,6 +81,7 @@
|
|||
|
||||
struct KeyOriginInfo;
|
||||
|
||||
using common::PSBTError;
|
||||
using interfaces::FoundBlock;
|
||||
|
||||
namespace wallet {
|
||||
|
@ -2167,7 +2168,7 @@ bool CWallet::SignTransaction(CMutableTransaction& tx, const std::map<COutPoint,
|
|||
return false;
|
||||
}
|
||||
|
||||
TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed, bool finalize) const
|
||||
std::optional<PSBTError> CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed, bool finalize) const
|
||||
{
|
||||
if (n_signed) {
|
||||
*n_signed = 0;
|
||||
|
@ -2200,9 +2201,9 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
// Fill in information from ScriptPubKeyMans
|
||||
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
|
||||
int n_signed_this_spkm = 0;
|
||||
TransactionError res = spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm, finalize);
|
||||
if (res != TransactionError::OK) {
|
||||
return res;
|
||||
const auto error{spk_man->FillPSBT(psbtx, txdata, sighash_type, sign, bip32derivs, &n_signed_this_spkm, finalize)};
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
if (n_signed) {
|
||||
|
@ -2218,7 +2219,7 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
|
|||
complete &= PSBTInputSigned(input);
|
||||
}
|
||||
|
||||
return TransactionError::OK;
|
||||
return {};
|
||||
}
|
||||
|
||||
SigningResult CWallet::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
|
||||
|
|
|
@ -58,7 +58,9 @@ class Coin;
|
|||
class SigningProvider;
|
||||
enum class MemPoolRemovalReason;
|
||||
enum class SigningResult;
|
||||
enum class TransactionError;
|
||||
namespace common {
|
||||
enum class PSBTError;
|
||||
} // namespace common
|
||||
namespace interfaces {
|
||||
class Wallet;
|
||||
}
|
||||
|
@ -659,7 +661,7 @@ public:
|
|||
* @param[in] finalize whether to create the final scriptSig or scriptWitness if possible
|
||||
* return error
|
||||
*/
|
||||
TransactionError FillPSBT(PartiallySignedTransaction& psbtx,
|
||||
std::optional<common::PSBTError> FillPSBT(PartiallySignedTransaction& psbtx,
|
||||
bool& complete,
|
||||
int sighash_type = SIGHASH_DEFAULT,
|
||||
bool sign = true,
|
||||
|
|
Loading…
Add table
Reference in a new issue