Skip to content

Commit

Permalink
merge bitcoin#14978: Factor out PSBT utilities from RPCs for use in G…
Browse files Browse the repository at this point in the history
…UI code; related refactoring
  • Loading branch information
kwvg committed Aug 9, 2021
1 parent 419bcd3 commit 5229347
Show file tree
Hide file tree
Showing 18 changed files with 1,016 additions and 751 deletions.
4 changes: 4 additions & 0 deletions src/Makefile.am
Expand Up @@ -227,6 +227,7 @@ BITCOIN_CORE_H = \
policy/policy.h \
pow.h \
protocol.h \
psbt.h \
random.h \
reverse_iterator.h \
reverselock.h \
Expand Down Expand Up @@ -291,6 +292,7 @@ BITCOIN_CORE_H = \
wallet/crypter.h \
wallet/db.h \
wallet/fees.h \
wallet/psbtwallet.h \
wallet/rpcwallet.h \
wallet/wallet.h \
wallet/walletdb.h \
Expand Down Expand Up @@ -431,6 +433,7 @@ libdash_wallet_a_SOURCES = \
wallet/db.cpp \
wallet/fees.cpp \
wallet/init.cpp \
wallet/psbtwallet.cpp \
wallet/rpcdump.cpp \
wallet/rpcwallet.cpp \
wallet/wallet.cpp \
Expand Down Expand Up @@ -577,6 +580,7 @@ libdash_common_a_SOURCES = \
netbase.cpp \
net_permissions.cpp \
policy/feerate.cpp \
psbt.cpp \
protocol.cpp \
saltedhasher.cpp \
scheduler.cpp \
Expand Down
6 changes: 5 additions & 1 deletion src/core_io.h
Expand Up @@ -28,9 +28,13 @@ std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDeco
[[nodiscard]] bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
uint256 ParseHashStr(const std::string&, const std::string& strName);
std::vector<unsigned char> ParseHexUV(const UniValue& v, const std::string& strName);
bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error);
int ParseSighashString(const UniValue& sighash);

//! Decode a base64ed PSBT into a PartiallySignedTransaction
[[nodiscard]] bool DecodeBase64PSBT(PartiallySignedTransaction& decoded_psbt, const std::string& base64_psbt, std::string& error);
//! Decode a raw (binary blob) PSBT into a PartiallySignedTransaction
[[nodiscard]] bool DecodeRawPSBT(PartiallySignedTransaction& decoded_psbt, const std::string& raw_psbt, std::string& error);

// core_write.cpp
UniValue ValueFromAmount(const CAmount& amount);
std::string FormatScript(const CScript& script);
Expand Down
17 changes: 14 additions & 3 deletions src/core_read.cpp
Expand Up @@ -4,6 +4,7 @@

#include <core_io.h>

#include <psbt.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
Expand Down Expand Up @@ -125,10 +126,20 @@ bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
return true;
}

bool DecodePSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
bool DecodeBase64PSBT(PartiallySignedTransaction& psbt, const std::string& base64_tx, std::string& error)
{
std::vector<unsigned char> tx_data = DecodeBase64(base64_tx.c_str());
CDataStream ss_data(tx_data, SER_NETWORK, PROTOCOL_VERSION);
bool invalid;
std::string tx_data = DecodeBase64(base64_tx, &invalid);
if (invalid) {
error = "invalid base64";
return false;
}
return DecodeRawPSBT(psbt, tx_data, error);
}

bool DecodeRawPSBT(PartiallySignedTransaction& psbt, const std::string& tx_data, std::string& error)
{
CDataStream ss_data(tx_data.data(), tx_data.data() + tx_data.size(), SER_NETWORK, PROTOCOL_VERSION);
try {
ss_data >> psbt;
if (!ss_data.empty()) {
Expand Down
60 changes: 48 additions & 12 deletions src/node/transaction.cpp
Expand Up @@ -5,7 +5,6 @@

#include <consensus/validation.h>
#include <net.h>
#include <rpc/server.h>
#include <txmempool.h>
#include <validation.h>
#include <validationinterface.h>
Expand All @@ -14,9 +13,38 @@

#include <future>

uint256 BroadcastTransaction(const CTransactionRef tx, const bool allowhighfees) {
const char* TransactionErrorString(const TransactionError err)
{
switch (err) {
case TransactionError::OK:
return "No error";
case TransactionError::MISSING_INPUTS:
return "Missing inputs";
case TransactionError::ALREADY_IN_CHAIN:
return "Transaction already in block chain";
case TransactionError::P2P_DISABLED:
return "Peer-to-peer functionality missing or disabled";
case TransactionError::MEMPOOL_REJECTED:
return "Transaction rejected by AcceptToMemoryPool";
case TransactionError::MEMPOOL_ERROR:
return "AcceptToMemoryPool failed";
case TransactionError::INVALID_PSBT:
return "PSBT is not sane";
case TransactionError::PSBT_MISMATCH:
return "PSBTs not compatible (different transactions)";
case TransactionError::SIGHASH_MISMATCH:
return "Specified sighash value does not match existing value";

case TransactionError::UNKNOWN_ERROR:
default: break;
}
return "Unknown error";
}

bool BroadcastTransaction(const CTransactionRef tx, uint256& hashTx, TransactionError& error, std::string& err_string, const bool allowhighfees, const bool bypass_limits)
{
std::promise<void> promise;
const uint256& hashTx = tx->GetHash();
hashTx = tx->GetHash();

CAmount nMaxRawTxFee = maxTxFee;
if (allowhighfees)
Expand All @@ -36,14 +64,19 @@ uint256 BroadcastTransaction(const CTransactionRef tx, const bool allowhighfees)
CValidationState state;
bool fMissingInputs;
if (!AcceptToMemoryPool(mempool, state, std::move(tx), &fMissingInputs,
false /* plTxnReplaced */, false /* bypass_limits */, nMaxRawTxFee)) {
bypass_limits, nMaxRawTxFee)) {
if (state.IsInvalid()) {
throw JSONRPCError(RPC_TRANSACTION_REJECTED, FormatStateMessage(state));
err_string = FormatStateMessage(state);
error = TransactionError::MEMPOOL_REJECTED;
return false;
} else {
if (fMissingInputs) {
throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs");
error = TransactionError::MISSING_INPUTS;
return false;
}
throw JSONRPCError(RPC_TRANSACTION_ERROR, FormatStateMessage(state));
err_string = FormatStateMessage(state);
error = TransactionError::MEMPOOL_ERROR;
return false;
}
} else {
// If wallet is enabled, ensure that the wallet has been made aware
Expand All @@ -56,7 +89,8 @@ uint256 BroadcastTransaction(const CTransactionRef tx, const bool allowhighfees)
});
}
} else if (fHaveChain) {
throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain");
error = TransactionError::ALREADY_IN_CHAIN;
return false;
} else {
// Make sure we don't block forever if re-sending
// a transaction already in mempool.
Expand All @@ -67,10 +101,12 @@ uint256 BroadcastTransaction(const CTransactionRef tx, const bool allowhighfees)

promise.get_future().wait();

if(!g_connman)
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
if(!g_connman) {
error = TransactionError::P2P_DISABLED;
return false;
}

g_connman->RelayTransaction(*tx);

return hashTx;
}
return true;
}
33 changes: 31 additions & 2 deletions src/node/transaction.h
Expand Up @@ -8,7 +8,36 @@
#include <primitives/transaction.h>
#include <uint256.h>

/** Broadcast a transaction */
uint256 BroadcastTransaction(CTransactionRef tx, bool allowhighfees = false);
enum class TransactionError {
OK = 0,
UNKNOWN_ERROR,

MISSING_INPUTS,
ALREADY_IN_CHAIN,
P2P_DISABLED,
MEMPOOL_REJECTED,
MEMPOOL_ERROR,
INVALID_PSBT,
PSBT_MISMATCH,
SIGHASH_MISMATCH,

ERROR_COUNT
};

#define TRANSACTION_ERR_LAST TransactionError::ERROR_COUNT

const char* TransactionErrorString(const TransactionError error);

/**
* Broadcast a transaction
*
* @param[in] tx the transaction to broadcast
* @param[out] &txid the txid of the transaction, if successfully broadcast
* @param[out] &error reference to UniValue to fill with error info on failure
* @param[out] &err_string reference to std::string to fill with error string if available
* @param[in] allowhighfees whether to allow fees exceeding maxTxFee
* return true on success, false on error (and fills in `error`)
*/
bool BroadcastTransaction(const CTransactionRef tx, uint256& txid, TransactionError& error, std::string& err_string, const bool allowhighfees = false, const bool bypass_limits = false);

#endif // BITCOIN_NODE_TRANSACTION_H

0 comments on commit 5229347

Please sign in to comment.