aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/.clang-format5
-rw-r--r--src/Makefile.am1
-rw-r--r--src/Makefile.qt.include10
-rw-r--r--src/Makefile.test.include7
-rw-r--r--src/bitcoin-cli.cpp138
-rw-r--r--src/bloom.cpp4
-rw-r--r--src/core_write.cpp20
-rw-r--r--src/interfaces/wallet.cpp5
-rw-r--r--src/interfaces/wallet.h3
-rw-r--r--src/net_processing.cpp12
-rw-r--r--src/outputtype.cpp2
-rw-r--r--src/policy/feerate.cpp9
-rw-r--r--src/policy/feerate.h14
-rw-r--r--src/policy/fees.h7
-rw-r--r--src/policy/policy.cpp20
-rw-r--r--src/policy/policy.h2
-rw-r--r--src/psbt.cpp11
-rw-r--r--src/psbt.h3
-rw-r--r--src/pubkey.h3
-rw-r--r--src/qt/bitcoingui.cpp10
-rw-r--r--src/qt/bitcoingui.h5
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/forms/debugwindow.ui6
-rw-r--r--src/qt/forms/psbtoperationsdialog.ui148
-rw-r--r--src/qt/psbtoperationsdialog.cpp268
-rw-r--r--src/qt/psbtoperationsdialog.h54
-rw-r--r--src/qt/sendcoinsdialog.cpp2
-rw-r--r--src/qt/transactionrecord.cpp1
-rw-r--r--src/qt/transactiontablemodel.cpp4
-rw-r--r--src/qt/walletframe.cpp4
-rw-r--r--src/qt/walletframe.h2
-rw-r--r--src/qt/walletmodel.cpp9
-rw-r--r--src/qt/walletmodel.h3
-rw-r--r--src/qt/walletview.cpp97
-rw-r--r--src/qt/walletview.h2
-rw-r--r--src/rest.cpp8
-rw-r--r--src/rpc/blockchain.cpp6
-rw-r--r--src/rpc/client.cpp2
-rw-r--r--src/rpc/mining.cpp20
-rw-r--r--src/rpc/mining.h11
-rw-r--r--src/rpc/misc.cpp4
-rw-r--r--src/rpc/rawtransaction.cpp24
-rw-r--r--src/rpc/rawtransaction_util.cpp4
-rw-r--r--src/rpc/util.cpp4
-rw-r--r--src/scheduler.cpp42
-rw-r--r--src/scheduler.h95
-rw-r--r--src/script/descriptor.cpp18
-rw-r--r--src/script/sign.cpp60
-rw-r--r--src/script/signingprovider.cpp6
-rw-r--r--src/script/standard.cpp96
-rw-r--r--src/script/standard.h149
-rw-r--r--src/span.h56
-rw-r--r--src/test/coins_tests.cpp2
-rw-r--r--src/test/descriptor_tests.cpp2
-rw-r--r--src/test/fuzz/crypto.cpp124
-rw-r--r--src/test/fuzz/decode_tx.cpp2
-rw-r--r--src/test/fuzz/fuzz.cpp16
-rw-r--r--src/test/fuzz/key.cpp18
-rw-r--r--src/test/fuzz/script.cpp4
-rw-r--r--src/test/key_tests.cpp44
-rw-r--r--src/test/multisig_tests.cpp2
-rw-r--r--src/test/netbase_tests.cpp3
-rw-r--r--src/test/scheduler_tests.cpp6
-rw-r--r--src/test/script_standard_tests.cpp106
-rw-r--r--src/test/sighash_tests.cpp2
-rw-r--r--src/test/transaction_tests.cpp8
-rw-r--r--src/test/util/setup_common.cpp1
-rw-r--r--src/test/util/setup_common.h1
-rw-r--r--src/test/util/transaction_utils.h4
-rw-r--r--src/ui_interface.h2
-rw-r--r--src/util/error.cpp6
-rw-r--r--src/util/fees.cpp40
-rw-r--r--src/util/fees.h1
-rw-r--r--src/wallet/coincontrol.cpp2
-rw-r--r--src/wallet/coincontrol.h2
-rw-r--r--src/wallet/rpcdump.cpp26
-rw-r--r--src/wallet/rpcwallet.cpp233
-rw-r--r--src/wallet/scriptpubkeyman.cpp54
-rw-r--r--src/wallet/scriptpubkeyman.h6
-rw-r--r--src/wallet/test/ismine_tests.cpp6
-rw-r--r--src/wallet/test/psbt_wallet_tests.cpp2
-rw-r--r--src/wallet/test/wallet_crypto_tests.cpp4
-rw-r--r--src/wallet/wallet.cpp17
-rw-r--r--src/wallet/wallet.h3
-rw-r--r--src/wallet/walletdb.cpp5
85 files changed, 1595 insertions, 657 deletions
diff --git a/src/.clang-format b/src/.clang-format
index aae039dd77..a8f8565f80 100644
--- a/src/.clang-format
+++ b/src/.clang-format
@@ -1,9 +1,10 @@
Language: Cpp
AccessModifierOffset: -4
-AlignAfterOpenBracket: false
+AlignAfterOpenBracket: true
AlignEscapedNewlinesLeft: true
AlignTrailingComments: true
-AllowAllParametersOfDeclarationOnNextLine: false
+AllowAllArgumentsOnNextLine : true
+AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
diff --git a/src/Makefile.am b/src/Makefile.am
index a33ff8a461..632ed3e31f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -184,6 +184,7 @@ BITCOIN_CORE_H = \
reverse_iterator.h \
rpc/blockchain.h \
rpc/client.h \
+ rpc/mining.h \
rpc/protocol.h \
rpc/rawtransaction_util.h \
rpc/register.h \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 13bfea7646..e5c19e5afc 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -25,6 +25,7 @@ QT_FORMS_UI = \
qt/forms/openuridialog.ui \
qt/forms/optionsdialog.ui \
qt/forms/overviewpage.ui \
+ qt/forms/psbtoperationsdialog.ui \
qt/forms/receivecoinsdialog.ui \
qt/forms/receiverequestdialog.ui \
qt/forms/debugwindow.ui \
@@ -61,6 +62,7 @@ QT_MOC_CPP = \
qt/moc_overviewpage.cpp \
qt/moc_peertablemodel.cpp \
qt/moc_paymentserver.cpp \
+ qt/moc_psbtoperationsdialog.cpp \
qt/moc_qrimagewidget.cpp \
qt/moc_qvalidatedlineedit.cpp \
qt/moc_qvaluecombobox.cpp \
@@ -132,6 +134,7 @@ BITCOIN_QT_H = \
qt/paymentserver.h \
qt/peertablemodel.h \
qt/platformstyle.h \
+ qt/psbtoperationsdialog.h \
qt/qrimagewidget.h \
qt/qvalidatedlineedit.h \
qt/qvaluecombobox.h \
@@ -245,6 +248,7 @@ BITCOIN_QT_WALLET_CPP = \
qt/openuridialog.cpp \
qt/overviewpage.cpp \
qt/paymentserver.cpp \
+ qt/psbtoperationsdialog.cpp \
qt/qrimagewidget.cpp \
qt/receivecoinsdialog.cpp \
qt/receiverequestdialog.cpp \
@@ -272,8 +276,6 @@ if ENABLE_WALLET
BITCOIN_QT_CPP += $(BITCOIN_QT_WALLET_CPP)
endif # ENABLE_WALLET
-RES_IMAGES =
-
RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png)
BITCOIN_RC = qt/res/bitcoin-qt-res.rc
@@ -286,7 +288,7 @@ qt_libbitcoinqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS)
qt_libbitcoinqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \
- $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES)
+ $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(RES_ICONS) $(RES_MOVIES)
if TARGET_DARWIN
qt_libbitcoinqt_a_SOURCES += $(BITCOIN_MM)
endif
@@ -357,7 +359,7 @@ $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM)
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
@rm $(@D)/temp_$(<F)
-$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_ICONS) $(RES_IMAGES) $(RES_MOVIES)
+$(QT_QRC_CPP): $(QT_QRC) $(QT_FORMS_H) $(RES_ICONS) $(RES_MOVIES)
@test -f $(RCC)
$(AM_V_GEN) QT_SELECT=$(QT_SELECT) $(RCC) -name bitcoin $< | \
$(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index acdf9b97bd..9dc3078487 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -32,6 +32,7 @@ FUZZ_TARGETS = \
test/fuzz/checkqueue \
test/fuzz/coins_deserialize \
test/fuzz/coins_view \
+ test/fuzz/crypto \
test/fuzz/crypto_common \
test/fuzz/cuckoocache \
test/fuzz/decode_tx \
@@ -480,6 +481,12 @@ test_fuzz_coins_view_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_coins_view_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_coins_view_SOURCES = test/fuzz/coins_view.cpp
+test_fuzz_crypto_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_crypto_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_crypto_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_crypto_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_crypto_SOURCES = test/fuzz/crypto.cpp
+
test_fuzz_crypto_common_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_crypto_common_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_crypto_common_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 8d85789b4e..f5125f22db 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -11,6 +11,7 @@
#include <clientversion.h>
#include <optional.h>
#include <rpc/client.h>
+#include <rpc/mining.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <util/strencodings.h>
@@ -39,6 +40,9 @@ static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
static const bool DEFAULT_NAMED=false;
static const int CONTINUE_EXECUTION=-1;
+/** Default number of blocks to generate for RPC generatetoaddress. */
+static const std::string DEFAULT_NBLOCKS = "1";
+
static void SetupCliArgs()
{
SetupHelpOptions(gArgs);
@@ -50,6 +54,7 @@ static void SetupCliArgs()
gArgs.AddArg("-version", "Print version and exit", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-datadir=<dir>", "Specify data directory", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
+ gArgs.AddArg("-generate", strprintf("Generate blocks immediately, equivalent to RPC generatenewaddress followed by RPC generatetoaddress. Optional positional integer arguments are number of blocks to generate (default: %s) and maximum iterations to try (default: %s), equivalent to RPC generatetoaddress nblocks and maxtries arguments. Example: bitcoin-cli -generate 4 1000", DEFAULT_NBLOCKS, DEFAULT_MAX_TRIES), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
SetupChainParamsBaseOptions();
gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
@@ -286,6 +291,28 @@ public:
}
};
+/** Process RPC generatetoaddress request. */
+class GenerateToAddressRequestHandler : public BaseRequestHandler
+{
+public:
+ UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
+ {
+ address_str = args.at(1);
+ UniValue params{RPCConvertValues("generatetoaddress", args)};
+ return JSONRPCRequestObj("generatetoaddress", params, 1);
+ }
+
+ UniValue ProcessReply(const UniValue &reply) override
+ {
+ UniValue result(UniValue::VOBJ);
+ result.pushKV("address", address_str);
+ result.pushKV("blocks", reply.get_obj()["result"]);
+ return JSONRPCReplyObj(result, NullUniValue, 1);
+ }
+protected:
+ std::string address_str;
+};
+
/** Process default single requests */
class DefaultRequestHandler: public BaseRequestHandler {
public:
@@ -453,6 +480,34 @@ static UniValue ConnectAndCallRPC(BaseRequestHandler* rh, const std::string& str
return response;
}
+/** Parse UniValue result to update the message to print to std::cout. */
+static void ParseResult(const UniValue& result, std::string& strPrint)
+{
+ if (result.isNull()) return;
+ strPrint = result.isStr() ? result.get_str() : result.write(2);
+}
+
+/** Parse UniValue error to update the message to print to std::cerr and the code to return. */
+static void ParseError(const UniValue& error, std::string& strPrint, int& nRet)
+{
+ if (error.isObject()) {
+ const UniValue& err_code = find_value(error, "code");
+ const UniValue& err_msg = find_value(error, "message");
+ if (!err_code.isNull()) {
+ strPrint = "error code: " + err_code.getValStr() + "\n";
+ }
+ if (err_msg.isStr()) {
+ strPrint += ("error message:\n" + err_msg.get_str());
+ }
+ if (err_code.isNum() && err_code.get_int() == RPC_WALLET_NOT_SPECIFIED) {
+ strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
+ }
+ } else {
+ strPrint = "error: " + error.write();
+ }
+ nRet = abs(error["code"].get_int());
+}
+
/**
* GetWalletBalances calls listwallets; if more than one wallet is loaded, it then
* fetches mine.trusted balances for each loaded wallet and pushes them to `result`.
@@ -477,6 +532,34 @@ static void GetWalletBalances(UniValue& result)
result.pushKV("balances", balances);
}
+/**
+ * Call RPC getnewaddress.
+ * @returns getnewaddress response as a UniValue object.
+ */
+static UniValue GetNewAddress()
+{
+ Optional<std::string> wallet_name{};
+ if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
+ std::unique_ptr<BaseRequestHandler> rh{MakeUnique<DefaultRequestHandler>()};
+ return ConnectAndCallRPC(rh.get(), "getnewaddress", /* args=*/{}, wallet_name);
+}
+
+/**
+ * Check bounds and set up args for RPC generatetoaddress params: nblocks, address, maxtries.
+ * @param[in] address Reference to const string address to insert into the args.
+ * @param args Reference to vector of string args to modify.
+ */
+static void SetGenerateToAddressArgs(const std::string& address, std::vector<std::string>& args)
+{
+ if (args.size() > 2) throw std::runtime_error("too many arguments (maximum 2 for nblocks and maxtries)");
+ if (args.size() == 0) {
+ args.emplace_back(DEFAULT_NBLOCKS);
+ } else if (args.at(0) == "0") {
+ throw std::runtime_error("the first argument (number of blocks to generate, default: " + DEFAULT_NBLOCKS + ") must be an integer value greater than zero");
+ }
+ args.emplace(args.begin() + 1, address);
+}
+
static int CommandLineRPC(int argc, char *argv[])
{
std::string strPrint;
@@ -535,6 +618,15 @@ static int CommandLineRPC(int argc, char *argv[])
std::string method;
if (gArgs.IsArgSet("-getinfo")) {
rh.reset(new GetinfoRequestHandler());
+ } else if (gArgs.GetBoolArg("-generate", false)) {
+ const UniValue getnewaddress{GetNewAddress()};
+ const UniValue& error{find_value(getnewaddress, "error")};
+ if (error.isNull()) {
+ SetGenerateToAddressArgs(find_value(getnewaddress, "result").get_str(), args);
+ rh.reset(new GenerateToAddressRequestHandler());
+ } else {
+ ParseError(error, strPrint, nRet);
+ }
} else {
rh.reset(new DefaultRequestHandler());
if (args.size() < 1) {
@@ -543,40 +635,22 @@ static int CommandLineRPC(int argc, char *argv[])
method = args[0];
args.erase(args.begin()); // Remove trailing method name from arguments vector
}
- Optional<std::string> wallet_name{};
- if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
- const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
-
- // Parse reply
- UniValue result = find_value(reply, "result");
- const UniValue& error = find_value(reply, "error");
- if (!error.isNull()) {
- // Error
- strPrint = "error: " + error.write();
- nRet = abs(error["code"].get_int());
- if (error.isObject()) {
- const UniValue& errCode = find_value(error, "code");
- const UniValue& errMsg = find_value(error, "message");
- strPrint = errCode.isNull() ? "" : ("error code: " + errCode.getValStr() + "\n");
-
- if (errMsg.isStr()) {
- strPrint += ("error message:\n" + errMsg.get_str());
- }
- if (errCode.isNum() && errCode.get_int() == RPC_WALLET_NOT_SPECIFIED) {
- strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
+ if (nRet == 0) {
+ // Perform RPC call
+ Optional<std::string> wallet_name{};
+ if (gArgs.IsArgSet("-rpcwallet")) wallet_name = gArgs.GetArg("-rpcwallet", "");
+ const UniValue reply = ConnectAndCallRPC(rh.get(), method, args, wallet_name);
+
+ // Parse reply
+ UniValue result = find_value(reply, "result");
+ const UniValue& error = find_value(reply, "error");
+ if (error.isNull()) {
+ if (gArgs.IsArgSet("-getinfo") && !gArgs.IsArgSet("-rpcwallet")) {
+ GetWalletBalances(result); // fetch multiwallet balances and append to result
}
- }
- } else {
- if (gArgs.IsArgSet("-getinfo") && !gArgs.IsArgSet("-rpcwallet")) {
- GetWalletBalances(result); // fetch multiwallet balances and append to result
- }
- // Result
- if (result.isNull()) {
- strPrint = "";
- } else if (result.isStr()) {
- strPrint = result.get_str();
+ ParseResult(result, strPrint);
} else {
- strPrint = result.write(2);
+ ParseError(error, strPrint, nRet);
}
}
} catch (const std::exception& e) {
diff --git a/src/bloom.cpp b/src/bloom.cpp
index 54fcf487e4..d182f0728e 100644
--- a/src/bloom.cpp
+++ b/src/bloom.cpp
@@ -135,8 +135,8 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY)
{
std::vector<std::vector<unsigned char> > vSolutions;
- txnouttype type = Solver(txout.scriptPubKey, vSolutions);
- if (type == TX_PUBKEY || type == TX_MULTISIG) {
+ TxoutType type = Solver(txout.scriptPubKey, vSolutions);
+ if (type == TxoutType::PUBKEY || type == TxoutType::MULTISIG) {
insert(COutPoint(hash, i));
}
}
diff --git a/src/core_write.cpp b/src/core_write.cpp
index eb0cc35f06..69b62df901 100644
--- a/src/core_write.cpp
+++ b/src/core_write.cpp
@@ -131,20 +131,20 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags)
{
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | serializeFlags);
ssTx << tx;
- return HexStr(ssTx.begin(), ssTx.end());
+ return HexStr(ssTx);
}
void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
{
out.pushKV("asm", ScriptToAsmStr(script));
- out.pushKV("hex", HexStr(script.begin(), script.end()));
+ out.pushKV("hex", HexStr(script));
std::vector<std::vector<unsigned char>> solns;
- txnouttype type = Solver(script, solns);
+ TxoutType type = Solver(script, solns);
out.pushKV("type", GetTxnOutputType(type));
CTxDestination address;
- if (include_address && ExtractDestination(script, address) && type != TX_PUBKEY) {
+ if (include_address && ExtractDestination(script, address) && type != TxoutType::PUBKEY) {
out.pushKV("address", EncodeDestination(address));
}
}
@@ -152,15 +152,15 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
void ScriptPubKeyToUniv(const CScript& scriptPubKey,
UniValue& out, bool fIncludeHex)
{
- txnouttype type;
+ TxoutType type;
std::vector<CTxDestination> addresses;
int nRequired;
out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
if (fIncludeHex)
- out.pushKV("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ out.pushKV("hex", HexStr(scriptPubKey));
- if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TX_PUBKEY) {
+ if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TxoutType::PUBKEY) {
out.pushKV("type", GetTxnOutputType(type));
return;
}
@@ -190,19 +190,19 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
const CTxIn& txin = tx.vin[i];
UniValue in(UniValue::VOBJ);
if (tx.IsCoinBase())
- in.pushKV("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ in.pushKV("coinbase", HexStr(txin.scriptSig));
else {
in.pushKV("txid", txin.prevout.hash.GetHex());
in.pushKV("vout", (int64_t)txin.prevout.n);
UniValue o(UniValue::VOBJ);
o.pushKV("asm", ScriptToAsmStr(txin.scriptSig, true));
- o.pushKV("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ o.pushKV("hex", HexStr(txin.scriptSig));
in.pushKV("scriptSig", o);
}
if (!tx.vin[i].scriptWitness.IsNull()) {
UniValue txinwitness(UniValue::VARR);
for (const auto& item : tx.vin[i].scriptWitness.stack) {
- txinwitness.push_back(HexStr(item.begin(), item.end()));
+ txinwitness.push_back(HexStr(item));
}
in.pushKV("txinwitness", txinwitness);
}
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 397403d308..b65eb72b1c 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -335,9 +335,10 @@ public:
bool sign,
bool bip32derivs,
PartiallySignedTransaction& psbtx,
- bool& complete) override
+ bool& complete,
+ size_t* n_signed) override
{
- return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs);
+ return m_wallet->FillPSBT(psbtx, complete, sighash_type, sign, bip32derivs, n_signed);
}
WalletBalances getBalances() override
{
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index 67569a3e55..e2161521f6 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -197,7 +197,8 @@ public:
bool sign,
bool bip32derivs,
PartiallySignedTransaction& psbtx,
- bool& complete) = 0;
+ bool& complete,
+ size_t* n_signed) = 0;
//! Get balances.
virtual WalletBalances getBalances() = 0;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 02ad41ab00..80e58a6dba 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -189,7 +189,7 @@ namespace {
* We use this to avoid requesting transactions that have already been
* confirnmed.
*/
- RecursiveMutex g_cs_recent_confirmed_transactions;
+ Mutex g_cs_recent_confirmed_transactions;
std::unique_ptr<CRollingBloomFilter> g_recent_confirmed_transactions GUARDED_BY(g_cs_recent_confirmed_transactions);
/** Blocks that are in flight, and that are in the queue to be downloaded. */
@@ -2454,7 +2454,7 @@ void ProcessMessage(
if (vAddr.size() > 1000)
{
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 20, strprintf("message addr size() = %u", vAddr.size()));
+ Misbehaving(pfrom.GetId(), 20, strprintf("addr message size = %u", vAddr.size()));
return;
}
@@ -2530,7 +2530,7 @@ void ProcessMessage(
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 20, strprintf("message inv size() = %u", vInv.size()));
+ Misbehaving(pfrom.GetId(), 20, strprintf("inv message size = %u", vInv.size()));
return;
}
@@ -2596,7 +2596,7 @@ void ProcessMessage(
if (vInv.size() > MAX_INV_SZ)
{
LOCK(cs_main);
- Misbehaving(pfrom.GetId(), 20, strprintf("message getdata size() = %u", vInv.size()));
+ Misbehaving(pfrom.GetId(), 20, strprintf("getdata message size = %u", vInv.size()));
return;
}
@@ -4387,9 +4387,9 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// Message: feefilter
//
- // We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
if (pto->m_tx_relay != nullptr && pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !pto->HasPermission(PF_FORCERELAY)) {
+ !pto->HasPermission(PF_FORCERELAY) // peers with the forcerelay permission should not filter txs to us
+ ) {
CAmount currentFilter = m_mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
int64_t timeNow = GetTimeMicros();
static FeeFilterRounder g_filter_rounder{CFeeRate{DEFAULT_MIN_RELAY_TX_FEE}};
diff --git a/src/outputtype.cpp b/src/outputtype.cpp
index ea7a86d6d6..871474d56e 100644
--- a/src/outputtype.cpp
+++ b/src/outputtype.cpp
@@ -53,7 +53,7 @@ CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
case OutputType::P2SH_SEGWIT:
case OutputType::BECH32: {
if (!key.IsCompressed()) return PKHash(key);
- CTxDestination witdest = WitnessV0KeyHash(PKHash(key));
+ CTxDestination witdest = WitnessV0KeyHash(key);
CScript witprog = GetScriptForDestination(witdest);
if (type == OutputType::P2SH_SEGWIT) {
return ScriptHash(witprog);
diff --git a/src/policy/feerate.cpp b/src/policy/feerate.cpp
index 14be6192fe..a01e259731 100644
--- a/src/policy/feerate.cpp
+++ b/src/policy/feerate.cpp
@@ -7,8 +7,6 @@
#include <tinyformat.h>
-const std::string CURRENCY_UNIT = "BTC";
-
CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_)
{
assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max()));
@@ -37,7 +35,10 @@ CAmount CFeeRate::GetFee(size_t nBytes_) const
return nFee;
}
-std::string CFeeRate::ToString() const
+std::string CFeeRate::ToString(const FeeEstimateMode& fee_estimate_mode) const
{
- return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
+ switch (fee_estimate_mode) {
+ case FeeEstimateMode::SAT_B: return strprintf("%d.%03d %s/B", nSatoshisPerK / 1000, nSatoshisPerK % 1000, CURRENCY_ATOM);
+ default: return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
+ }
}
diff --git a/src/policy/feerate.h b/src/policy/feerate.h
index 61fa80c130..883940f73c 100644
--- a/src/policy/feerate.h
+++ b/src/policy/feerate.h
@@ -11,7 +11,17 @@
#include <string>
-extern const std::string CURRENCY_UNIT;
+const std::string CURRENCY_UNIT = "BTC"; // One formatted unit
+const std::string CURRENCY_ATOM = "sat"; // One indivisible minimum value unit
+
+/* Used to determine type of fee estimation requested */
+enum class FeeEstimateMode {
+ UNSET, //!< Use default settings based on other criteria
+ ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
+ CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
+ BTC_KB, //!< Use explicit BTC/kB fee given in coin control
+ SAT_B, //!< Use explicit sat/B fee given in coin control
+};
/**
* Fee rate in satoshis per kilobyte: CAmount / kB
@@ -46,7 +56,7 @@ public:
friend bool operator>=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK >= b.nSatoshisPerK; }
friend bool operator!=(const CFeeRate& a, const CFeeRate& b) { return a.nSatoshisPerK != b.nSatoshisPerK; }
CFeeRate& operator+=(const CFeeRate& a) { nSatoshisPerK += a.nSatoshisPerK; return *this; }
- std::string ToString() const;
+ std::string ToString(const FeeEstimateMode& fee_estimate_mode = FeeEstimateMode::BTC_KB) const;
SERIALIZE_METHODS(CFeeRate, obj) { READWRITE(obj.nSatoshisPerK); }
};
diff --git a/src/policy/fees.h b/src/policy/fees.h
index 6ee6e0d547..e445c1590d 100644
--- a/src/policy/fees.h
+++ b/src/policy/fees.h
@@ -45,13 +45,6 @@ enum class FeeReason {
REQUIRED,
};
-/* Used to determine type of fee estimation requested */
-enum class FeeEstimateMode {
- UNSET, //!< Use default settings based on other criteria
- ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
- CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
-};
-
/* Used to return detailed information about a feerate bucket */
struct EstimatorBucket
{
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index 07d51c0088..c56abaf6c9 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -50,14 +50,14 @@ bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn));
}
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
+bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
{
std::vector<std::vector<unsigned char> > vSolutions;
whichType = Solver(scriptPubKey, vSolutions);
- if (whichType == TX_NONSTANDARD) {
+ if (whichType == TxoutType::NONSTANDARD) {
return false;
- } else if (whichType == TX_MULTISIG) {
+ } else if (whichType == TxoutType::MULTISIG) {
unsigned char m = vSolutions.front()[0];
unsigned char n = vSolutions.back()[0];
// Support up to x-of-3 multisig txns as standard
@@ -65,7 +65,7 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
return false;
if (m < 1 || m > n)
return false;
- } else if (whichType == TX_NULL_DATA &&
+ } else if (whichType == TxoutType::NULL_DATA &&
(!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes)) {
return false;
}
@@ -110,16 +110,16 @@ bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeR
}
unsigned int nDataOut = 0;
- txnouttype whichType;
+ TxoutType whichType;
for (const CTxOut& txout : tx.vout) {
if (!::IsStandard(txout.scriptPubKey, whichType)) {
reason = "scriptpubkey";
return false;
}
- if (whichType == TX_NULL_DATA)
+ if (whichType == TxoutType::NULL_DATA)
nDataOut++;
- else if ((whichType == TX_MULTISIG) && (!permit_bare_multisig)) {
+ else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
reason = "bare-multisig";
return false;
} else if (IsDust(txout, dust_relay_fee)) {
@@ -163,10 +163,10 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out;
std::vector<std::vector<unsigned char> > vSolutions;
- txnouttype whichType = Solver(prev.scriptPubKey, vSolutions);
- if (whichType == TX_NONSTANDARD) {
+ TxoutType whichType = Solver(prev.scriptPubKey, vSolutions);
+ if (whichType == TxoutType::NONSTANDARD) {
return false;
- } else if (whichType == TX_SCRIPTHASH) {
+ } else if (whichType == TxoutType::SCRIPTHASH) {
std::vector<std::vector<unsigned char> > stack;
// convert the scriptSig into a stack, so we can inspect the redeemScript
if (!EvalScript(stack, tx.vin[i].scriptSig, SCRIPT_VERIFY_NONE, BaseSignatureChecker(), SigVersion::BASE))
diff --git a/src/policy/policy.h b/src/policy/policy.h
index 1561a41c5e..7f168ee20f 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -81,7 +81,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
-bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType);
+bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType);
/**
* Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
diff --git a/src/psbt.cpp b/src/psbt.cpp
index ef9781817a..10260740f0 100644
--- a/src/psbt.cpp
+++ b/src/psbt.cpp
@@ -214,6 +214,17 @@ bool PSBTInputSigned(const PSBTInput& input)
return !input.final_script_sig.empty() || !input.final_script_witness.IsNull();
}
+size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt) {
+ size_t count = 0;
+ for (const auto& input : psbt.inputs) {
+ if (!PSBTInputSigned(input)) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
void UpdatePSBTOutput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index)
{
const CTxOut& out = psbt.tx->vout.at(index);
diff --git a/src/psbt.h b/src/psbt.h
index 888e0fd119..0a8ea2ea0b 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -579,6 +579,9 @@ bool PSBTInputSigned(const PSBTInput& input);
/** Signs a PSBTInput, verifying that all provided data matches what is being signed. */
bool SignPSBTInput(const SigningProvider& provider, PartiallySignedTransaction& psbt, int index, int sighash = SIGHASH_ALL, SignatureData* out_sigdata = nullptr, bool use_dummy = false);
+/** Counts the unsigned inputs of a PSBT. */
+size_t CountPSBTUnsignedInputs(const PartiallySignedTransaction& psbt);
+
/** Updates a PSBTOutput with information from provider.
*
* This fills in the redeem_script, witness_script, and hd_keypaths where possible.
diff --git a/src/pubkey.h b/src/pubkey.h
index 261842b7f7..4c28af4a4d 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -142,6 +142,9 @@ public:
unsigned int len = ::ReadCompactSize(s);
if (len <= SIZE) {
s.read((char*)vch, len);
+ if (len != size()) {
+ Invalidate();
+ }
} else {
// invalid pubkey, skip available data
char dummy;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 1092cdd754..b4182e8524 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -321,8 +321,10 @@ void BitcoinGUI::createActions()
signMessageAction->setStatusTip(tr("Sign messages with your Bitcoin addresses to prove you own them"));
verifyMessageAction = new QAction(tr("&Verify message..."), this);
verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Bitcoin addresses"));
- m_load_psbt_action = new QAction(tr("Load PSBT..."), this);
+ m_load_psbt_action = new QAction(tr("&Load PSBT from file..."), this);
m_load_psbt_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction"));
+ m_load_psbt_clipboard_action = new QAction(tr("Load PSBT from clipboard..."), this);
+ m_load_psbt_clipboard_action->setStatusTip(tr("Load Partially Signed Bitcoin Transaction from clipboard"));
openRPCConsoleAction = new QAction(tr("Node window"), this);
openRPCConsoleAction->setStatusTip(tr("Open node debugging and diagnostic console"));
@@ -381,6 +383,7 @@ void BitcoinGUI::createActions()
connect(signMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(signMessageAction, &QAction::triggered, [this]{ gotoSignMessageTab(); });
connect(m_load_psbt_action, &QAction::triggered, [this]{ gotoLoadPSBT(); });
+ connect(m_load_psbt_clipboard_action, &QAction::triggered, [this]{ gotoLoadPSBT(true); });
connect(verifyMessageAction, &QAction::triggered, [this]{ showNormalIfMinimized(); });
connect(verifyMessageAction, &QAction::triggered, [this]{ gotoVerifyMessageTab(); });
connect(usedSendingAddressesAction, &QAction::triggered, walletFrame, &WalletFrame::usedSendingAddresses);
@@ -459,6 +462,7 @@ void BitcoinGUI::createMenuBar()
file->addAction(signMessageAction);
file->addAction(verifyMessageAction);
file->addAction(m_load_psbt_action);
+ file->addAction(m_load_psbt_clipboard_action);
file->addSeparator();
}
file->addAction(quitAction);
@@ -878,9 +882,9 @@ void BitcoinGUI::gotoVerifyMessageTab(QString addr)
{
if (walletFrame) walletFrame->gotoVerifyMessageTab(addr);
}
-void BitcoinGUI::gotoLoadPSBT()
+void BitcoinGUI::gotoLoadPSBT(bool from_clipboard)
{
- if (walletFrame) walletFrame->gotoLoadPSBT();
+ if (walletFrame) walletFrame->gotoLoadPSBT(from_clipboard);
}
#endif // ENABLE_WALLET
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index b009e279b6..697e83e772 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -139,6 +139,7 @@ private:
QAction* signMessageAction = nullptr;
QAction* verifyMessageAction = nullptr;
QAction* m_load_psbt_action = nullptr;
+ QAction* m_load_psbt_clipboard_action = nullptr;
QAction* aboutAction = nullptr;
QAction* receiveCoinsAction = nullptr;
QAction* receiveCoinsMenuAction = nullptr;
@@ -278,8 +279,8 @@ public Q_SLOTS:
void gotoSignMessageTab(QString addr = "");
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
- /** Show load Partially Signed Bitcoin Transaction dialog */
- void gotoLoadPSBT();
+ /** Load Partially Signed Bitcoin Transaction from file or clipboard */
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Show open dialog */
void openClicked();
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index f44a9f285a..7c72858501 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -456,7 +456,7 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
{
CPubKey pubkey;
PKHash *pkhash = boost::get<PKHash>(&address);
- if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, CKeyID(*pkhash), pubkey))
+ if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, ToKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui
index 8b70800838..1217ca3e2e 100644
--- a/src/qt/forms/debugwindow.ui
+++ b/src/qt/forms/debugwindow.ui
@@ -1078,12 +1078,6 @@
<height>426</height>
</rect>
</property>
- <property name="minimumSize">
- <size>
- <width>300</width>
- <height>0</height>
- </size>
- </property>
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,1">
<item row="0" column="0">
<widget class="QLabel" name="label_30">
diff --git a/src/qt/forms/psbtoperationsdialog.ui b/src/qt/forms/psbtoperationsdialog.ui
new file mode 100644
index 0000000000..c2e2f5035b
--- /dev/null
+++ b/src/qt/forms/psbtoperationsdialog.ui
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>PSBTOperationsDialog</class>
+ <widget class="QDialog" name="PSBTOperationsDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>585</width>
+ <height>327</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>12</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="bottomMargin">
+ <number>12</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="mainDialogLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="statusBar">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="autoFillBackground">
+ <bool>false</bool>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QTextEdit" name="transactionDescription">
+ <property name="undoRedoEnabled">
+ <bool>false</bool>
+ </property>
+ <property name="readOnly">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRowLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="signTransactionButton">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <weight>50</weight>
+ <bold>false</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Sign Tx</string>
+ </property>
+ <property name="autoDefault">
+ <bool>true</bool>
+ </property>
+ <property name="default">
+ <bool>false</bool>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="broadcastTransactionButton">
+ <property name="text">
+ <string>Broadcast Tx</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="copyToClipboardButton">
+ <property name="text">
+ <string>Copy to Clipboard</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="saveButton">
+ <property name="text">
+ <string>Save...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="closeButton">
+ <property name="text">
+ <string>Close</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp
new file mode 100644
index 0000000000..58167d4bb4
--- /dev/null
+++ b/src/qt/psbtoperationsdialog.cpp
@@ -0,0 +1,268 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <qt/psbtoperationsdialog.h>
+
+#include <core_io.h>
+#include <interfaces/node.h>
+#include <key_io.h>
+#include <node/psbt.h>
+#include <policy/policy.h>
+#include <qt/bitcoinunits.h>
+#include <qt/forms/ui_psbtoperationsdialog.h>
+#include <qt/guiutil.h>
+#include <qt/optionsmodel.h>
+#include <util/strencodings.h>
+
+#include <iostream>
+
+
+PSBTOperationsDialog::PSBTOperationsDialog(
+ QWidget* parent, WalletModel* wallet_model, ClientModel* client_model) : QDialog(parent),
+ m_ui(new Ui::PSBTOperationsDialog),
+ m_wallet_model(wallet_model),
+ m_client_model(client_model)
+{
+ m_ui->setupUi(this);
+ setWindowTitle("PSBT Operations");
+
+ connect(m_ui->signTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::signTransaction);
+ connect(m_ui->broadcastTransactionButton, &QPushButton::clicked, this, &PSBTOperationsDialog::broadcastTransaction);
+ connect(m_ui->copyToClipboardButton, &QPushButton::clicked, this, &PSBTOperationsDialog::copyToClipboard);
+ connect(m_ui->saveButton, &QPushButton::clicked, this, &PSBTOperationsDialog::saveTransaction);
+
+ connect(m_ui->closeButton, &QPushButton::clicked, this, &PSBTOperationsDialog::close);
+
+ m_ui->signTransactionButton->setEnabled(false);
+ m_ui->broadcastTransactionButton->setEnabled(false);
+}
+
+PSBTOperationsDialog::~PSBTOperationsDialog()
+{
+ delete m_ui;
+}
+
+void PSBTOperationsDialog::openWithPSBT(PartiallySignedTransaction psbtx)
+{
+ m_transaction_data = psbtx;
+
+ bool complete;
+ size_t n_could_sign;
+ FinalizePSBT(psbtx); // Make sure all existing signatures are fully combined before checking for completeness.
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_could_sign);
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to load transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
+ return;
+ }
+
+ m_ui->broadcastTransactionButton->setEnabled(complete);
+ m_ui->signTransactionButton->setEnabled(!complete && !m_wallet_model->wallet().privateKeysDisabled() && n_could_sign > 0);
+
+ updateTransactionDisplay();
+}
+
+void PSBTOperationsDialog::signTransaction()
+{
+ bool complete;
+ size_t n_signed;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, true /* sign */, true /* bip32derivs */, m_transaction_data, complete, &n_signed);
+
+ if (err != TransactionError::OK) {
+ showStatus(tr("Failed to sign transaction: %1")
+ .arg(QString::fromStdString(TransactionErrorString(err).translated)), StatusLevel::ERR);
+ return;
+ }
+
+ updateTransactionDisplay();
+
+ if (!complete && n_signed < 1) {
+ showStatus(tr("Could not sign any more inputs."), StatusLevel::WARN);
+ } else if (!complete) {
+ showStatus(tr("Signed %1 inputs, but more signatures are still required.").arg(n_signed),
+ StatusLevel::INFO);
+ } else {
+ showStatus(tr("Signed transaction successfully. Transaction is ready to broadcast."),
+ StatusLevel::INFO);
+ m_ui->broadcastTransactionButton->setEnabled(true);
+ }
+}
+
+void PSBTOperationsDialog::broadcastTransaction()
+{
+ CMutableTransaction mtx;
+ if (!FinalizeAndExtractPSBT(m_transaction_data, mtx)) {
+ // This is never expected to fail unless we were given a malformed PSBT
+ // (e.g. with an invalid signature.)
+ showStatus(tr("Unknown error processing transaction."), StatusLevel::ERR);
+ return;
+ }
+
+ CTransactionRef tx = MakeTransactionRef(mtx);
+ std::string err_string;
+ TransactionError error = BroadcastTransaction(
+ *m_client_model->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* await_callback */ false);
+
+ if (error == TransactionError::OK) {
+ showStatus(tr("Transaction broadcast successfully! Transaction ID: %1")
+ .arg(QString::fromStdString(tx->GetHash().GetHex())), StatusLevel::INFO);
+ } else {
+ showStatus(tr("Transaction broadcast failed: %1")
+ .arg(QString::fromStdString(TransactionErrorString(error).translated)), StatusLevel::ERR);
+ }
+}
+
+void PSBTOperationsDialog::copyToClipboard() {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << m_transaction_data;
+ GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
+ showStatus(tr("PSBT copied to clipboard."), StatusLevel::INFO);
+}
+
+void PSBTOperationsDialog::saveTransaction() {
+ CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
+ ssTx << m_transaction_data;
+
+ QString selected_filter;
+ QString filename_suggestion = "";
+ bool first = true;
+ for (const CTxOut& out : m_transaction_data.tx->vout) {
+ if (!first) {
+ filename_suggestion.append("-");
+ }
+ CTxDestination address;
+ ExtractDestination(out.scriptPubKey, address);
+ QString amount = BitcoinUnits::format(m_wallet_model->getOptionsModel()->getDisplayUnit(), out.nValue);
+ QString address_str = QString::fromStdString(EncodeDestination(address));
+ filename_suggestion.append(address_str + "-" + amount);
+ first = false;
+ }
+ filename_suggestion.append(".psbt");
+ QString filename = GUIUtil::getSaveFileName(this,
+ tr("Save Transaction Data"), filename_suggestion,
+ tr("Partially Signed Transaction (Binary) (*.psbt)"), &selected_filter);
+ if (filename.isEmpty()) {
+ return;
+ }
+ std::ofstream out(filename.toLocal8Bit().data());
+ out << ssTx.str();
+ out.close();
+ showStatus(tr("PSBT saved to disk."), StatusLevel::INFO);
+}
+
+void PSBTOperationsDialog::updateTransactionDisplay() {
+ m_ui->transactionDescription->setText(QString::fromStdString(renderTransaction(m_transaction_data)));
+ showTransactionStatus(m_transaction_data);
+}
+
+std::string PSBTOperationsDialog::renderTransaction(const PartiallySignedTransaction &psbtx)
+{
+ QString tx_description = "";
+ CAmount totalAmount = 0;
+ for (const CTxOut& out : psbtx.tx->vout) {
+ CTxDestination address;
+ ExtractDestination(out.scriptPubKey, address);
+ totalAmount += out.nValue;
+ tx_description.append(tr(" * Sends %1 to %2")
+ .arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, out.nValue))
+ .arg(QString::fromStdString(EncodeDestination(address))));
+ tx_description.append("<br>");
+ }
+
+ PSBTAnalysis analysis = AnalyzePSBT(psbtx);
+ tx_description.append(" * ");
+ if (!*analysis.fee) {
+ // This happens if the transaction is missing input UTXO information.
+ tx_description.append(tr("Unable to calculate transaction fee or total transaction amount."));
+ } else {
+ tx_description.append(tr("Pays transaction fee: "));
+ tx_description.append(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, *analysis.fee));
+
+ // add total amount in all subdivision units
+ tx_description.append("<hr />");
+ QStringList alternativeUnits;
+ for (const BitcoinUnits::Unit u : BitcoinUnits::availableUnits())
+ {
+ if(u != m_client_model->getOptionsModel()->getDisplayUnit()) {
+ alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount));
+ }
+ }
+ tx_description.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount"))
+ .arg(BitcoinUnits::formatHtmlWithUnit(m_client_model->getOptionsModel()->getDisplayUnit(), totalAmount)));
+ tx_description.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>")
+ .arg(alternativeUnits.join(" " + tr("or") + " ")));
+ }
+
+ size_t num_unsigned = CountPSBTUnsignedInputs(psbtx);
+ if (num_unsigned > 0) {
+ tx_description.append("<br><br>");
+ tx_description.append(tr("Transaction has %1 unsigned inputs.").arg(QString::number(num_unsigned)));
+ }
+
+ return tx_description.toStdString();
+}
+
+void PSBTOperationsDialog::showStatus(const QString &msg, StatusLevel level) {
+ m_ui->statusBar->setText(msg);
+ switch (level) {
+ case StatusLevel::INFO: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : lightgreen }");
+ break;
+ }
+ case StatusLevel::WARN: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : orange }");
+ break;
+ }
+ case StatusLevel::ERR: {
+ m_ui->statusBar->setStyleSheet("QLabel { background-color : red }");
+ break;
+ }
+ }
+ m_ui->statusBar->show();
+}
+
+size_t PSBTOperationsDialog::couldSignInputs(const PartiallySignedTransaction &psbtx) {
+ size_t n_signed;
+ bool complete;
+ TransactionError err = m_wallet_model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, false /* bip32derivs */, m_transaction_data, complete, &n_signed);
+
+ if (err != TransactionError::OK) {
+ return 0;
+ }
+ return n_signed;
+}
+
+void PSBTOperationsDialog::showTransactionStatus(const PartiallySignedTransaction &psbtx) {
+ PSBTAnalysis analysis = AnalyzePSBT(psbtx);
+ size_t n_could_sign = couldSignInputs(psbtx);
+
+ switch (analysis.next) {
+ case PSBTRole::UPDATER: {
+ showStatus(tr("Transaction is missing some information about inputs."), StatusLevel::WARN);
+ break;
+ }
+ case PSBTRole::SIGNER: {
+ QString need_sig_text = tr("Transaction still needs signature(s).");
+ StatusLevel level = StatusLevel::INFO;
+ if (m_wallet_model->wallet().privateKeysDisabled()) {
+ need_sig_text += " " + tr("(But this wallet cannot sign transactions.)");
+ level = StatusLevel::WARN;
+ } else if (n_could_sign < 1) {
+ need_sig_text += " " + tr("(But this wallet does not have the right keys.)"); // XXX wording
+ level = StatusLevel::WARN;
+ }
+ showStatus(need_sig_text, level);
+ break;
+ }
+ case PSBTRole::FINALIZER:
+ case PSBTRole::EXTRACTOR: {
+ showStatus(tr("Transaction is fully signed and ready for broadcast."), StatusLevel::INFO);
+ break;
+ }
+ default: {
+ showStatus(tr("Transaction status is unknown."), StatusLevel::ERR);
+ break;
+ }
+ }
+}
diff --git a/src/qt/psbtoperationsdialog.h b/src/qt/psbtoperationsdialog.h
new file mode 100644
index 0000000000..f37bdbe39a
--- /dev/null
+++ b/src/qt/psbtoperationsdialog.h
@@ -0,0 +1,54 @@
+// Copyright (c) 2011-2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_QT_PSBTOPERATIONSDIALOG_H
+#define BITCOIN_QT_PSBTOPERATIONSDIALOG_H
+
+#include <QDialog>
+
+#include <psbt.h>
+#include <qt/clientmodel.h>
+#include <qt/walletmodel.h>
+
+namespace Ui {
+class PSBTOperationsDialog;
+}
+
+/** Dialog showing transaction details. */
+class PSBTOperationsDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit PSBTOperationsDialog(QWidget* parent, WalletModel* walletModel, ClientModel* clientModel);
+ ~PSBTOperationsDialog();
+
+ void openWithPSBT(PartiallySignedTransaction psbtx);
+
+public Q_SLOTS:
+ void signTransaction();
+ void broadcastTransaction();
+ void copyToClipboard();
+ void saveTransaction();
+
+private:
+ Ui::PSBTOperationsDialog* m_ui;
+ PartiallySignedTransaction m_transaction_data;
+ WalletModel* m_wallet_model;
+ ClientModel* m_client_model;
+
+ enum class StatusLevel {
+ INFO,
+ WARN,
+ ERR
+ };
+
+ size_t couldSignInputs(const PartiallySignedTransaction &psbtx);
+ void updateTransactionDisplay();
+ std::string renderTransaction(const PartiallySignedTransaction &psbtx);
+ void showStatus(const QString &msg, StatusLevel level);
+ void showTransactionStatus(const PartiallySignedTransaction &psbtx);
+};
+
+#endif // BITCOIN_QT_PSBTOPERATIONSDIALOG_H
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 9e23fe78d8..0ac61f3adc 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -392,7 +392,7 @@ void SendCoinsDialog::on_sendButton_clicked()
CMutableTransaction mtx = CMutableTransaction{*(m_current_transaction->getWtx())};
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete);
+ const TransactionError err = model->wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
assert(!complete);
assert(err == TransactionError::OK);
// Serialize the PSBT
diff --git a/src/qt/transactionrecord.cpp b/src/qt/transactionrecord.cpp
index 52007ef350..632a18de5c 100644
--- a/src/qt/transactionrecord.cpp
+++ b/src/qt/transactionrecord.cpp
@@ -234,6 +234,7 @@ void TransactionRecord::updateStatus(const interfaces::WalletTxStatus& wtx, cons
bool TransactionRecord::statusUpdateNeeded(const uint256& block_hash) const
{
+ assert(!block_hash.IsNull());
return status.m_cur_block_hash != block_hash || status.needsUpdate;
}
diff --git a/src/qt/transactiontablemodel.cpp b/src/qt/transactiontablemodel.cpp
index 327a489488..22ba5187bb 100644
--- a/src/qt/transactiontablemodel.cpp
+++ b/src/qt/transactiontablemodel.cpp
@@ -192,7 +192,7 @@ public:
interfaces::WalletTxStatus wtx;
int numBlocks;
int64_t block_time;
- if (rec->statusUpdateNeeded(cur_block_hash) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
+ if (!cur_block_hash.IsNull() && rec->statusUpdateNeeded(cur_block_hash) && wallet.tryGetTxStatus(rec->hash, wtx, numBlocks, block_time)) {
rec->updateStatus(wtx, cur_block_hash, numBlocks, block_time);
}
return rec;
@@ -664,7 +664,7 @@ QVariant TransactionTableModel::headerData(int section, Qt::Orientation orientat
QModelIndex TransactionTableModel::index(int row, int column, const QModelIndex &parent) const
{
Q_UNUSED(parent);
- TransactionRecord* data = priv->index(walletModel->wallet(), walletModel->clientModel().getBestBlockHash(), row);
+ TransactionRecord* data = priv->index(walletModel->wallet(), walletModel->getLastBlockProcessed(), row);
if(data)
{
return createIndex(row, column, data);
diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp
index 5e68ee4f93..ec56f2755f 100644
--- a/src/qt/walletframe.cpp
+++ b/src/qt/walletframe.cpp
@@ -165,11 +165,11 @@ void WalletFrame::gotoVerifyMessageTab(QString addr)
walletView->gotoVerifyMessageTab(addr);
}
-void WalletFrame::gotoLoadPSBT()
+void WalletFrame::gotoLoadPSBT(bool from_clipboard)
{
WalletView *walletView = currentWalletView();
if (walletView) {
- walletView->gotoLoadPSBT();
+ walletView->gotoLoadPSBT(from_clipboard);
}
}
diff --git a/src/qt/walletframe.h b/src/qt/walletframe.h
index d90ade5005..2b5f263468 100644
--- a/src/qt/walletframe.h
+++ b/src/qt/walletframe.h
@@ -79,7 +79,7 @@ public Q_SLOTS:
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT();
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Encrypt the wallet */
void encryptWallet(bool status);
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 671b5e1ce6..1143969352 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -87,7 +87,7 @@ void WalletModel::pollBalanceChanged()
{
// Avoid recomputing wallet balances unless a TransactionChanged or
// BlockTip notification was received.
- if (!fForceCheckBalanceChanged && m_cached_last_update_tip == m_client_model->getBestBlockHash()) return;
+ if (!fForceCheckBalanceChanged && m_cached_last_update_tip == getLastBlockProcessed()) return;
// Try to get balances and return early if locks can't be acquired. This
// avoids the GUI from getting stuck on periodical polls if the core is
@@ -536,7 +536,7 @@ bool WalletModel::bumpFee(uint256 hash, uint256& new_hash)
if (create_psbt) {
PartiallySignedTransaction psbtx(mtx);
bool complete = false;
- const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete);
+ const TransactionError err = wallet().fillPSBT(SIGHASH_ALL, false /* sign */, true /* bip32derivs */, psbtx, complete, nullptr);
if (err != TransactionError::OK || complete) {
QMessageBox::critical(nullptr, tr("Fee bump error"), tr("Can't draft transaction."));
return false;
@@ -588,3 +588,8 @@ void WalletModel::refresh(bool pk_hash_only)
{
addressTableModel = new AddressTableModel(this, pk_hash_only);
}
+
+uint256 WalletModel::getLastBlockProcessed() const
+{
+ return m_client_model ? m_client_model->getBestBlockHash() : uint256{};
+}
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 38e8a14556..fd52db2da3 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -155,6 +155,9 @@ public:
AddressTableModel* getAddressTableModel() const { return addressTableModel; }
void refresh(bool pk_hash_only = false);
+
+ uint256 getLastBlockProcessed() const;
+
private:
std::unique_ptr<interfaces::Wallet> m_wallet;
std::unique_ptr<interfaces::Handler> m_handler_unload;
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 861d1c5f4a..cec9b0eeb8 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -4,13 +4,11 @@
#include <qt/walletview.h>
-#include <node/psbt.h>
-#include <node/transaction.h>
-#include <policy/policy.h>
#include <qt/addressbookpage.h>
#include <qt/askpassphrasedialog.h>
#include <qt/clientmodel.h>
#include <qt/guiutil.h>
+#include <qt/psbtoperationsdialog.h>
#include <qt/optionsmodel.h>
#include <qt/overviewpage.h>
#include <qt/platformstyle.h>
@@ -22,11 +20,14 @@
#include <qt/walletmodel.h>
#include <interfaces/node.h>
+#include <psbt.h>
#include <ui_interface.h>
#include <util/strencodings.h>
#include <QAction>
#include <QActionGroup>
+#include <QApplication>
+#include <QClipboard>
#include <QFileDialog>
#include <QHBoxLayout>
#include <QProgressDialog>
@@ -204,78 +205,42 @@ void WalletView::gotoVerifyMessageTab(QString addr)
signVerifyMessageDialog->setAddress_VM(addr);
}
-void WalletView::gotoLoadPSBT()
+void WalletView::gotoLoadPSBT(bool from_clipboard)
{
- QString filename = GUIUtil::getOpenFileName(this,
- tr("Load Transaction Data"), QString(),
- tr("Partially Signed Transaction (*.psbt)"), nullptr);
- if (filename.isEmpty()) return;
- if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
- Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
- return;
+ std::string data;
+
+ if (from_clipboard) {
+ std::string raw = QApplication::clipboard()->text().toStdString();
+ bool invalid;
+ data = DecodeBase64(raw, &invalid);
+ if (invalid) {
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT from clipboard (invalid base64)"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ } else {
+ QString filename = GUIUtil::getOpenFileName(this,
+ tr("Load Transaction Data"), QString(),
+ tr("Partially Signed Transaction (*.psbt)"), nullptr);
+ if (filename.isEmpty()) return;
+ if (GetFileSize(filename.toLocal8Bit().data(), MAX_FILE_SIZE_PSBT) == MAX_FILE_SIZE_PSBT) {
+ Q_EMIT message(tr("Error"), tr("PSBT file must be smaller than 100 MiB"), CClientUIInterface::MSG_ERROR);
+ return;
+ }
+ std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
+ data = std::string(std::istreambuf_iterator<char>{in}, {});
}
- std::ifstream in(filename.toLocal8Bit().data(), std::ios::binary);
- std::string data(std::istreambuf_iterator<char>{in}, {});
std::string error;
PartiallySignedTransaction psbtx;
if (!DecodeRawPSBT(psbtx, data, error)) {
- Q_EMIT message(tr("Error"), tr("Unable to decode PSBT file") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
+ Q_EMIT message(tr("Error"), tr("Unable to decode PSBT") + "\n" + QString::fromStdString(error), CClientUIInterface::MSG_ERROR);
return;
}
- CMutableTransaction mtx;
- bool complete = false;
- PSBTAnalysis analysis = AnalyzePSBT(psbtx);
- QMessageBox msgBox;
- msgBox.setText("PSBT");
- switch (analysis.next) {
- case PSBTRole::CREATOR:
- case PSBTRole::UPDATER:
- msgBox.setInformativeText("PSBT is incomplete. Copy to clipboard for manual inspection?");
- break;
- case PSBTRole::SIGNER:
- msgBox.setInformativeText("Transaction needs more signatures. Copy to clipboard?");
- break;
- case PSBTRole::FINALIZER:
- case PSBTRole::EXTRACTOR:
- complete = FinalizeAndExtractPSBT(psbtx, mtx);
- if (complete) {
- msgBox.setInformativeText(tr("Would you like to send this transaction?"));
- } else {
- // The analyzer missed something, e.g. if there are final_scriptSig/final_scriptWitness
- // but with invalid signatures.
- msgBox.setInformativeText(tr("There was an unexpected problem processing the PSBT. Copy to clipboard for manual inspection?"));
- }
- }
-
- msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
- switch (msgBox.exec()) {
- case QMessageBox::Yes: {
- if (complete) {
- std::string err_string;
- CTransactionRef tx = MakeTransactionRef(mtx);
-
- TransactionError result = BroadcastTransaction(*clientModel->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* wait_callback */ false);
- if (result == TransactionError::OK) {
- Q_EMIT message(tr("Success"), tr("Broadcasted transaction successfully."), CClientUIInterface::MSG_INFORMATION | CClientUIInterface::MODAL);
- } else {
- Q_EMIT message(tr("Error"), QString::fromStdString(err_string), CClientUIInterface::MSG_ERROR);
- }
- } else {
- // Serialize the PSBT
- CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
- ssTx << psbtx;
- GUIUtil::setClipboard(EncodeBase64(ssTx.str()).c_str());
- Q_EMIT message(tr("PSBT copied"), "Copied to clipboard", CClientUIInterface::MSG_INFORMATION);
- return;
- }
- }
- case QMessageBox::Cancel:
- break;
- default:
- assert(false);
- }
+ PSBTOperationsDialog* dlg = new PSBTOperationsDialog(this, walletModel, clientModel);
+ dlg->openWithPSBT(psbtx);
+ dlg->setAttribute(Qt::WA_DeleteOnClose);
+ dlg->exec();
}
bool WalletView::handlePaymentRequest(const SendCoinsRecipient& recipient)
diff --git a/src/qt/walletview.h b/src/qt/walletview.h
index fd09456baa..f186554758 100644
--- a/src/qt/walletview.h
+++ b/src/qt/walletview.h
@@ -84,7 +84,7 @@ public Q_SLOTS:
/** Show Sign/Verify Message dialog and switch to verify message tab */
void gotoVerifyMessageTab(QString addr = "");
/** Load Partially Signed Bitcoin Transaction */
- void gotoLoadPSBT();
+ void gotoLoadPSBT(bool from_clipboard = false);
/** Show incoming transaction notification for new transactions.
diff --git a/src/rest.cpp b/src/rest.cpp
index cde8b472d3..8cb594a03b 100644
--- a/src/rest.cpp
+++ b/src/rest.cpp
@@ -188,7 +188,7 @@ static bool rest_headers(const util::Ref& context,
ssHeader << pindex->GetBlockHeader();
}
- std::string strHex = HexStr(ssHeader.begin(), ssHeader.end()) + "\n";
+ std::string strHex = HexStr(ssHeader) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
@@ -253,7 +253,7 @@ static bool rest_block(HTTPRequest* req,
case RetFormat::HEX: {
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssBlock << block;
- std::string strHex = HexStr(ssBlock.begin(), ssBlock.end()) + "\n";
+ std::string strHex = HexStr(ssBlock) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
@@ -391,7 +391,7 @@ static bool rest_tx(const util::Ref& context, HTTPRequest* req, const std::strin
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssTx << tx;
- std::string strHex = HexStr(ssTx.begin(), ssTx.end()) + "\n";
+ std::string strHex = HexStr(ssTx) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
return true;
@@ -556,7 +556,7 @@ static bool rest_getutxos(const util::Ref& context, HTTPRequest* req, const std:
case RetFormat::HEX: {
CDataStream ssGetUTXOResponse(SER_NETWORK, PROTOCOL_VERSION);
ssGetUTXOResponse << ::ChainActive().Height() << ::ChainActive().Tip()->GetBlockHash() << bitmap << outs;
- std::string strHex = HexStr(ssGetUTXOResponse.begin(), ssGetUTXOResponse.end()) + "\n";
+ std::string strHex = HexStr(ssGetUTXOResponse) + "\n";
req->WriteHeader("Content-Type", "text/plain");
req->WriteReply(HTTP_OK, strHex);
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 8252af3ee6..64f8a5bb3b 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -779,7 +779,7 @@ static UniValue getblockheader(const JSONRPCRequest& request)
{
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION);
ssBlock << pblockindex->GetBlockHeader();
- std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
+ std::string strHex = HexStr(ssBlock);
return strHex;
}
@@ -905,7 +905,7 @@ static UniValue getblock(const JSONRPCRequest& request)
{
CDataStream ssBlock(SER_NETWORK, PROTOCOL_VERSION | RPCSerializationFlags());
ssBlock << block;
- std::string strHex = HexStr(ssBlock.begin(), ssBlock.end());
+ std::string strHex = HexStr(ssBlock);
return strHex;
}
@@ -2159,7 +2159,7 @@ UniValue scantxoutset(const JSONRPCRequest& request)
UniValue unspent(UniValue::VOBJ);
unspent.pushKV("txid", outpoint.hash.GetHex());
unspent.pushKV("vout", (int32_t)outpoint.n);
- unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey.begin(), txo.scriptPubKey.end()));
+ unspent.pushKV("scriptPubKey", HexStr(txo.scriptPubKey));
unspent.pushKV("desc", descriptors[txo.scriptPubKey]);
unspent.pushKV("amount", ValueFromAmount(txo.nValue));
unspent.pushKV("height", (int32_t)coin.nHeight);
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index 3045a74d7a..66ace7263a 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -217,7 +217,7 @@ UniValue ParseNonRFCJSONValue(const std::string& strVal)
UniValue jVal;
if (!jVal.read(std::string("[")+strVal+std::string("]")) ||
!jVal.isArray() || jVal.size()!=1)
- throw std::runtime_error(std::string("Error parsing JSON:")+strVal);
+ throw std::runtime_error(std::string("Error parsing JSON: ") + strVal);
return jVal[0];
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index d5fc3f57bb..fee6a893eb 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -17,6 +17,7 @@
#include <policy/fees.h>
#include <pow.h>
#include <rpc/blockchain.h>
+#include <rpc/mining.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <script/descriptor.h>
@@ -207,7 +208,7 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
{
{"num_blocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
{"descriptor", RPCArg::Type::STR, RPCArg::Optional::NO, "The descriptor to send the newly generated bitcoin to."},
- {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."},
+ {"maxtries", RPCArg::Type::NUM, /* default */ ToString(DEFAULT_MAX_TRIES), "How many iterations to try."},
},
RPCResult{
RPCResult::Type::ARR, "", "hashes of blocks generated",
@@ -221,7 +222,7 @@ static UniValue generatetodescriptor(const JSONRPCRequest& request)
.Check(request);
const int num_blocks{request.params[0].get_int()};
- const int64_t max_tries{request.params[2].isNull() ? 1000000 : request.params[2].get_int()};
+ const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
CScript coinbase_script;
std::string error;
@@ -242,7 +243,7 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
{
{"nblocks", RPCArg::Type::NUM, RPCArg::Optional::NO, "How many blocks are generated immediately."},
{"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The address to send the newly generated bitcoin to."},
- {"maxtries", RPCArg::Type::NUM, /* default */ "1000000", "How many iterations to try."},
+ {"maxtries", RPCArg::Type::NUM, /* default */ ToString(DEFAULT_MAX_TRIES), "How many iterations to try."},
},
RPCResult{
RPCResult::Type::ARR, "", "hashes of blocks generated",
@@ -257,11 +258,8 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
},
}.Check(request);
- int nGenerate = request.params[0].get_int();
- uint64_t nMaxTries = 1000000;
- if (!request.params[2].isNull()) {
- nMaxTries = request.params[2].get_int();
- }
+ const int num_blocks{request.params[0].get_int()};
+ const uint64_t max_tries{request.params[2].isNull() ? DEFAULT_MAX_TRIES : request.params[2].get_int()};
CTxDestination destination = DecodeDestination(request.params[1].get_str());
if (!IsValidDestination(destination)) {
@@ -273,7 +271,7 @@ static UniValue generatetoaddress(const JSONRPCRequest& request)
CScript coinbase_script = GetScriptForDestination(destination);
- return generateBlocks(chainman, mempool, coinbase_script, nGenerate, nMaxTries);
+ return generateBlocks(chainman, mempool, coinbase_script, num_blocks, max_tries);
}
static UniValue generateblock(const JSONRPCRequest& request)
@@ -371,7 +369,7 @@ static UniValue generateblock(const JSONRPCRequest& request)
}
uint256 block_hash;
- uint64_t max_tries{1000000};
+ uint64_t max_tries{DEFAULT_MAX_TRIES};
unsigned int extra_nonce{0};
if (!GenerateBlock(EnsureChainman(request.context), block, max_tries, extra_nonce, block_hash) || block_hash.IsNull()) {
@@ -875,7 +873,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
result.pushKV("height", (int64_t)(pindexPrev->nHeight+1));
if (!pblocktemplate->vchCoinbaseCommitment.empty()) {
- result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment.begin(), pblocktemplate->vchCoinbaseCommitment.end()));
+ result.pushKV("default_witness_commitment", HexStr(pblocktemplate->vchCoinbaseCommitment));
}
return result;
diff --git a/src/rpc/mining.h b/src/rpc/mining.h
new file mode 100644
index 0000000000..acc74e1dcc
--- /dev/null
+++ b/src/rpc/mining.h
@@ -0,0 +1,11 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_RPC_MINING_H
+#define BITCOIN_RPC_MINING_H
+
+/** Default max iterations to try in RPC generatetodescriptor, generatetoaddress, and generateblock. */
+static const uint64_t DEFAULT_MAX_TRIES{1000000};
+
+#endif // BITCOIN_RPC_MINING_H
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index ce98a7c937..53d38f4e11 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -63,7 +63,7 @@ static UniValue validateaddress(const JSONRPCRequest& request)
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
- ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
UniValue detail = DescribeAddress(dest);
ret.pushKVs(detail);
@@ -131,7 +131,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest));
- result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
+ result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
return result;
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index e14217c307..faec359d1c 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -305,7 +305,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request)
CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
CMerkleBlock mb(block, setTxids);
ssMB << mb;
- std::string strHex = HexStr(ssMB.begin(), ssMB.end());
+ std::string strHex = HexStr(ssMB);
return strHex;
}
@@ -511,12 +511,12 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request)
static std::string GetAllOutputTypes()
{
- std::string ret;
- for (int i = TX_NONSTANDARD; i <= TX_WITNESS_UNKNOWN; ++i) {
- if (i != TX_NONSTANDARD) ret += ", ";
- ret += GetTxnOutputType(static_cast<txnouttype>(i));
+ std::vector<std::string> ret;
+ using U = std::underlying_type<TxoutType>::type;
+ for (U i = (U)TxoutType::NONSTANDARD; i <= (U)TxoutType::WITNESS_UNKNOWN; ++i) {
+ ret.emplace_back(GetTxnOutputType(static_cast<TxoutType>(i)));
}
- return ret;
+ return Join(ret, ", ");
}
static UniValue decodescript(const JSONRPCRequest& request)
@@ -580,10 +580,10 @@ static UniValue decodescript(const JSONRPCRequest& request)
// is a witness program, don't return addresses for a segwit programs.
if (type.get_str() == "pubkey" || type.get_str() == "pubkeyhash" || type.get_str() == "multisig" || type.get_str() == "nonstandard") {
std::vector<std::vector<unsigned char>> solutions_data;
- txnouttype which_type = Solver(script, solutions_data);
+ TxoutType which_type = Solver(script, solutions_data);
// Uncompressed pubkeys cannot be used with segwit checksigs.
// If the script contains an uncompressed pubkey, skip encoding of a segwit program.
- if ((which_type == TX_PUBKEY) || (which_type == TX_MULTISIG)) {
+ if ((which_type == TxoutType::PUBKEY) || (which_type == TxoutType::MULTISIG)) {
for (const auto& solution : solutions_data) {
if ((solution.size() != 1) && !CPubKey(solution).IsCompressed()) {
return r;
@@ -592,10 +592,10 @@ static UniValue decodescript(const JSONRPCRequest& request)
}
UniValue sr(UniValue::VOBJ);
CScript segwitScr;
- if (which_type == TX_PUBKEY) {
+ if (which_type == TxoutType::PUBKEY) {
segwitScr = GetScriptForDestination(WitnessV0KeyHash(Hash160(solutions_data[0].begin(), solutions_data[0].end())));
- } else if (which_type == TX_PUBKEYHASH) {
- segwitScr = GetScriptForDestination(WitnessV0KeyHash(solutions_data[0]));
+ } else if (which_type == TxoutType::PUBKEYHASH) {
+ segwitScr = GetScriptForDestination(WitnessV0KeyHash(uint160{solutions_data[0]}));
} else {
// Scripts that are not fit for P2WPKH are encoded as P2WSH.
// Newer segwit program versions should be considered when then become available.
@@ -1186,7 +1186,7 @@ UniValue decodepsbt(const JSONRPCRequest& request)
if (!input.final_script_witness.IsNull()) {
UniValue txinwitness(UniValue::VARR);
for (const auto& item : input.final_script_witness.stack) {
- txinwitness.push_back(HexStr(item.begin(), item.end()));
+ txinwitness.push_back(HexStr(item));
}
in.pushKV("final_scriptwitness", txinwitness);
}
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index bd82773bf2..1031716b4a 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -138,10 +138,10 @@ static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::
entry.pushKV("vout", (uint64_t)txin.prevout.n);
UniValue witness(UniValue::VARR);
for (unsigned int i = 0; i < txin.scriptWitness.stack.size(); i++) {
- witness.push_back(HexStr(txin.scriptWitness.stack[i].begin(), txin.scriptWitness.stack[i].end()));
+ witness.push_back(HexStr(txin.scriptWitness.stack[i]));
}
entry.pushKV("witness", witness);
- entry.pushKV("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()));
+ entry.pushKV("scriptSig", HexStr(txin.scriptSig));
entry.pushKV("sequence", (uint64_t)txin.nSequence);
entry.pushKV("error", strMessage);
vErrorsRet.push_back(entry);
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index e7afef4cac..54ea352a72 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -224,7 +224,7 @@ public:
obj.pushKV("isscript", false);
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", 0);
- obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
+ obj.pushKV("witness_program", HexStr(id));
return obj;
}
@@ -234,7 +234,7 @@ public:
obj.pushKV("isscript", true);
obj.pushKV("iswitness", true);
obj.pushKV("witness_version", 0);
- obj.pushKV("witness_program", HexStr(id.begin(), id.end()));
+ obj.pushKV("witness_program", HexStr(id));
return obj;
}
diff --git a/src/scheduler.cpp b/src/scheduler.cpp
index c4bd47310b..7c361bf26f 100644
--- a/src/scheduler.cpp
+++ b/src/scheduler.cpp
@@ -30,9 +30,6 @@ void CScheduler::serviceQueue()
// is called.
while (!shouldStop()) {
try {
- if (!shouldStop() && taskQueue.empty()) {
- REVERSE_LOCK(lock);
- }
while (!shouldStop() && taskQueue.empty()) {
// Wait until there is something to do.
newTaskScheduled.wait(lock);
@@ -71,18 +68,6 @@ void CScheduler::serviceQueue()
newTaskScheduled.notify_one();
}
-void CScheduler::stop(bool drain)
-{
- {
- LOCK(newTaskMutex);
- if (drain)
- stopWhenEmpty = true;
- else
- stopRequested = true;
- }
- newTaskScheduled.notify_all();
-}
-
void CScheduler::schedule(CScheduler::Function f, std::chrono::system_clock::time_point t)
{
{
@@ -125,8 +110,8 @@ void CScheduler::scheduleEvery(CScheduler::Function f, std::chrono::milliseconds
scheduleFromNow([=] { Repeat(*this, f, delta); }, delta);
}
-size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point &first,
- std::chrono::system_clock::time_point &last) const
+size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point& first,
+ std::chrono::system_clock::time_point& last) const
{
LOCK(newTaskMutex);
size_t result = taskQueue.size();
@@ -137,13 +122,15 @@ size_t CScheduler::getQueueInfo(std::chrono::system_clock::time_point &first,
return result;
}
-bool CScheduler::AreThreadsServicingQueue() const {
+bool CScheduler::AreThreadsServicingQueue() const
+{
LOCK(newTaskMutex);
return nThreadsServicingQueue;
}
-void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
+void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue()
+{
{
LOCK(m_cs_callbacks_pending);
// Try to avoid scheduling too many copies here, but if we
@@ -155,8 +142,9 @@ void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this), std::chrono::system_clock::now());
}
-void SingleThreadedSchedulerClient::ProcessQueue() {
- std::function<void ()> callback;
+void SingleThreadedSchedulerClient::ProcessQueue()
+{
+ std::function<void()> callback;
{
LOCK(m_cs_callbacks_pending);
if (m_are_callbacks_running) return;
@@ -172,7 +160,8 @@ void SingleThreadedSchedulerClient::ProcessQueue() {
struct RAIICallbacksRunning {
SingleThreadedSchedulerClient* instance;
explicit RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {}
- ~RAIICallbacksRunning() {
+ ~RAIICallbacksRunning()
+ {
{
LOCK(instance->m_cs_callbacks_pending);
instance->m_are_callbacks_running = false;
@@ -184,7 +173,8 @@ void SingleThreadedSchedulerClient::ProcessQueue() {
callback();
}
-void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> func) {
+void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void()> func)
+{
assert(m_pscheduler);
{
@@ -194,7 +184,8 @@ void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void ()> fun
MaybeScheduleProcessQueue();
}
-void SingleThreadedSchedulerClient::EmptyQueue() {
+void SingleThreadedSchedulerClient::EmptyQueue()
+{
assert(!m_pscheduler->AreThreadsServicingQueue());
bool should_continue = true;
while (should_continue) {
@@ -204,7 +195,8 @@ void SingleThreadedSchedulerClient::EmptyQueue() {
}
}
-size_t SingleThreadedSchedulerClient::CallbacksPending() {
+size_t SingleThreadedSchedulerClient::CallbacksPending()
+{
LOCK(m_cs_callbacks_pending);
return m_callbacks_pending.size();
}
diff --git a/src/scheduler.h b/src/scheduler.h
index 1e64195484..d7fe00d1b4 100644
--- a/src/scheduler.h
+++ b/src/scheduler.h
@@ -5,11 +5,6 @@
#ifndef BITCOIN_SCHEDULER_H
#define BITCOIN_SCHEDULER_H
-//
-// NOTE:
-// boost::thread should be ported to std::thread
-// when we support C++11.
-//
#include <condition_variable>
#include <functional>
#include <list>
@@ -17,24 +12,23 @@
#include <sync.h>
-//
-// Simple class for background tasks that should be run
-// periodically or once "after a while"
-//
-// Usage:
-//
-// CScheduler* s = new CScheduler();
-// s->scheduleFromNow(doSomething, std::chrono::milliseconds{11}); // Assuming a: void doSomething() { }
-// s->scheduleFromNow([=] { this->func(argument); }, std::chrono::milliseconds{3});
-// boost::thread* t = new boost::thread(std::bind(CScheduler::serviceQueue, s));
-//
-// ... then at program shutdown, make sure to call stop() to clean up the thread(s) running serviceQueue:
-// s->stop();
-// t->join();
-// delete t;
-// delete s; // Must be done after thread is interrupted/joined.
-//
-
+/**
+ * Simple class for background tasks that should be run
+ * periodically or once "after a while"
+ *
+ * Usage:
+ *
+ * CScheduler* s = new CScheduler();
+ * s->scheduleFromNow(doSomething, std::chrono::milliseconds{11}); // Assuming a: void doSomething() { }
+ * s->scheduleFromNow([=] { this->func(argument); }, std::chrono::milliseconds{3});
+ * std::thread* t = new std::thread([&] { s->serviceQueue(); });
+ *
+ * ... then at program shutdown, make sure to call stop() to clean up the thread(s) running serviceQueue:
+ * s->stop();
+ * t->join();
+ * delete t;
+ * delete s; // Must be done after thread is interrupted/joined.
+ */
class CScheduler
{
public:
@@ -43,7 +37,7 @@ public:
typedef std::function<void()> Function;
- // Call func at/after time t
+ /** Call func at/after time t */
void schedule(Function f, std::chrono::system_clock::time_point t);
/** Call f once after the delta has passed */
@@ -67,23 +61,33 @@ public:
*/
void MockForward(std::chrono::seconds delta_seconds);
- // To keep things as simple as possible, there is no unschedule.
-
- // Services the queue 'forever'. Should be run in a thread,
- // and interrupted using boost::interrupt_thread
+ /**
+ * Services the queue 'forever'. Should be run in a thread,
+ * and interrupted using boost::interrupt_thread
+ */
void serviceQueue();
- // Tell any threads running serviceQueue to stop as soon as they're
- // done servicing whatever task they're currently servicing (drain=false)
- // or when there is no work left to be done (drain=true)
- void stop(bool drain=false);
+ /** Tell any threads running serviceQueue to stop as soon as the current task is done */
+ void stop()
+ {
+ WITH_LOCK(newTaskMutex, stopRequested = true);
+ newTaskScheduled.notify_all();
+ }
+ /** Tell any threads running serviceQueue to stop when there is no work left to be done */
+ void StopWhenDrained()
+ {
+ WITH_LOCK(newTaskMutex, stopWhenEmpty = true);
+ newTaskScheduled.notify_all();
+ }
- // Returns number of tasks waiting to be serviced,
- // and first and last task times
- size_t getQueueInfo(std::chrono::system_clock::time_point &first,
- std::chrono::system_clock::time_point &last) const;
+ /**
+ * Returns number of tasks waiting to be serviced,
+ * and first and last task times
+ */
+ size_t getQueueInfo(std::chrono::system_clock::time_point& first,
+ std::chrono::system_clock::time_point& last) const;
- // Returns true if there are threads actively running in serviceQueue()
+ /** Returns true if there are threads actively running in serviceQueue() */
bool AreThreadsServicingQueue() const;
private:
@@ -106,19 +110,20 @@ private:
* B() will be able to observe all of the effects of callback A() which executed
* before it.
*/
-class SingleThreadedSchedulerClient {
+class SingleThreadedSchedulerClient
+{
private:
- CScheduler *m_pscheduler;
+ CScheduler* m_pscheduler;
RecursiveMutex m_cs_callbacks_pending;
- std::list<std::function<void ()>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending);
+ std::list<std::function<void()>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending);
bool m_are_callbacks_running GUARDED_BY(m_cs_callbacks_pending) = false;
void MaybeScheduleProcessQueue();
void ProcessQueue();
public:
- explicit SingleThreadedSchedulerClient(CScheduler *pschedulerIn) : m_pscheduler(pschedulerIn) {}
+ explicit SingleThreadedSchedulerClient(CScheduler* pschedulerIn) : m_pscheduler(pschedulerIn) {}
/**
* Add a callback to be executed. Callbacks are executed serially
@@ -126,10 +131,12 @@ public:
* Practically, this means that callbacks can behave as if they are executed
* in order by a single thread.
*/
- void AddToProcessQueue(std::function<void ()> func);
+ void AddToProcessQueue(std::function<void()> func);
- // Processes all remaining queue members on the calling thread, blocking until queue is empty
- // Must be called after the CScheduler has no remaining processing threads!
+ /**
+ * Processes all remaining queue members on the calling thread, blocking until queue is empty
+ * Must be called after the CScheduler has no remaining processing threads!
+ */
void EmptyQueue();
size_t CallbacksPending();
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 7a5421ab6f..5fa128d62d 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -235,7 +235,7 @@ public:
}
bool IsRange() const override { return false; }
size_t GetSize() const override { return m_pubkey.size(); }
- std::string ToString() const override { return HexStr(m_pubkey.begin(), m_pubkey.end()); }
+ std::string ToString() const override { return HexStr(m_pubkey); }
bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
{
CKey key;
@@ -583,7 +583,7 @@ class RawDescriptor final : public DescriptorImpl
{
const CScript m_script;
protected:
- std::string ToStringExtra() const override { return HexStr(m_script.begin(), m_script.end()); }
+ std::string ToStringExtra() const override { return HexStr(m_script); }
std::vector<CScript> MakeScripts(const std::vector<CPubKey>&, const CScript*, FlatSigningProvider&) const override { return Vector(m_script); }
public:
RawDescriptor(CScript script) : DescriptorImpl({}, {}, "raw"), m_script(std::move(script)) {}
@@ -985,15 +985,15 @@ std::unique_ptr<PubkeyProvider> InferPubkey(const CPubKey& pubkey, ParseScriptCo
std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptContext ctx, const SigningProvider& provider)
{
std::vector<std::vector<unsigned char>> data;
- txnouttype txntype = Solver(script, data);
+ TxoutType txntype = Solver(script, data);
- if (txntype == TX_PUBKEY) {
+ if (txntype == TxoutType::PUBKEY) {
CPubKey pubkey(data[0].begin(), data[0].end());
if (pubkey.IsValid()) {
return MakeUnique<PKDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TX_PUBKEYHASH) {
+ if (txntype == TxoutType::PUBKEYHASH) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
@@ -1001,7 +1001,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return MakeUnique<PKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TX_WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
+ if (txntype == TxoutType::WITNESS_V0_KEYHASH && ctx != ParseScriptContext::P2WSH) {
uint160 hash(data[0]);
CKeyID keyid(hash);
CPubKey pubkey;
@@ -1009,7 +1009,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
return MakeUnique<WPKHDescriptor>(InferPubkey(pubkey, ctx, provider));
}
}
- if (txntype == TX_MULTISIG) {
+ if (txntype == TxoutType::MULTISIG) {
std::vector<std::unique_ptr<PubkeyProvider>> providers;
for (size_t i = 1; i + 1 < data.size(); ++i) {
CPubKey pubkey(data[i].begin(), data[i].end());
@@ -1017,7 +1017,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
}
return MakeUnique<MultisigDescriptor>((int)data[0][0], std::move(providers));
}
- if (txntype == TX_SCRIPTHASH && ctx == ParseScriptContext::TOP) {
+ if (txntype == TxoutType::SCRIPTHASH && ctx == ParseScriptContext::TOP) {
uint160 hash(data[0]);
CScriptID scriptid(hash);
CScript subscript;
@@ -1026,7 +1026,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
if (sub) return MakeUnique<SHDescriptor>(std::move(sub));
}
}
- if (txntype == TX_WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
+ if (txntype == TxoutType::WITNESS_V0_SCRIPTHASH && ctx != ParseScriptContext::P2WSH) {
CScriptID scriptid;
CRIPEMD160().Write(data[0].data(), data[0].size()).Finalize(scriptid.begin());
CScript subscript;
diff --git a/src/script/sign.cpp b/src/script/sign.cpp
index 1e00afcf89..f425215549 100644
--- a/src/script/sign.cpp
+++ b/src/script/sign.cpp
@@ -92,11 +92,11 @@ static bool CreateSig(const BaseSignatureCreator& creator, SignatureData& sigdat
/**
* Sign scriptPubKey using signature made with creator.
* Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed),
- * unless whichTypeRet is TX_SCRIPTHASH, in which case scriptSigRet is the redemption script.
+ * unless whichTypeRet is TxoutType::SCRIPTHASH, in which case scriptSigRet is the redemption script.
* Returns false if scriptPubKey could not be completely satisfied.
*/
static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator& creator, const CScript& scriptPubKey,
- std::vector<valtype>& ret, txnouttype& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
+ std::vector<valtype>& ret, TxoutType& whichTypeRet, SigVersion sigversion, SignatureData& sigdata)
{
CScript scriptRet;
uint160 h160;
@@ -108,15 +108,15 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
switch (whichTypeRet)
{
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
+ case TxoutType::NONSTANDARD:
+ case TxoutType::NULL_DATA:
+ case TxoutType::WITNESS_UNKNOWN:
return false;
- case TX_PUBKEY:
+ case TxoutType::PUBKEY:
if (!CreateSig(creator, sigdata, provider, sig, CPubKey(vSolutions[0]), scriptPubKey, sigversion)) return false;
ret.push_back(std::move(sig));
return true;
- case TX_PUBKEYHASH: {
+ case TxoutType::PUBKEYHASH: {
CKeyID keyID = CKeyID(uint160(vSolutions[0]));
CPubKey pubkey;
if (!GetPubKey(provider, sigdata, keyID, pubkey)) {
@@ -129,9 +129,9 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
ret.push_back(ToByteVector(pubkey));
return true;
}
- case TX_SCRIPTHASH:
+ case TxoutType::SCRIPTHASH:
h160 = uint160(vSolutions[0]);
- if (GetCScript(provider, sigdata, h160, scriptRet)) {
+ if (GetCScript(provider, sigdata, CScriptID{h160}, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -139,7 +139,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
sigdata.missing_redeem_script = h160;
return false;
- case TX_MULTISIG: {
+ case TxoutType::MULTISIG: {
size_t required = vSolutions.front()[0];
ret.push_back(valtype()); // workaround CHECKMULTISIG bug
for (size_t i = 1; i < vSolutions.size() - 1; ++i) {
@@ -159,13 +159,13 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
}
return ok;
}
- case TX_WITNESS_V0_KEYHASH:
+ case TxoutType::WITNESS_V0_KEYHASH:
ret.push_back(vSolutions[0]);
return true;
- case TX_WITNESS_V0_SCRIPTHASH:
+ case TxoutType::WITNESS_V0_SCRIPTHASH:
CRIPEMD160().Write(&vSolutions[0][0], vSolutions[0].size()).Finalize(h160.begin());
- if (GetCScript(provider, sigdata, h160, scriptRet)) {
+ if (GetCScript(provider, sigdata, CScriptID{h160}, scriptRet)) {
ret.push_back(std::vector<unsigned char>(scriptRet.begin(), scriptRet.end()));
return true;
}
@@ -198,44 +198,44 @@ bool ProduceSignature(const SigningProvider& provider, const BaseSignatureCreato
if (sigdata.complete) return true;
std::vector<valtype> result;
- txnouttype whichType;
+ TxoutType whichType;
bool solved = SignStep(provider, creator, fromPubKey, result, whichType, SigVersion::BASE, sigdata);
bool P2SH = false;
CScript subscript;
sigdata.scriptWitness.stack.clear();
- if (solved && whichType == TX_SCRIPTHASH)
+ if (solved && whichType == TxoutType::SCRIPTHASH)
{
// Solver returns the subscript that needs to be evaluated;
// the final scriptSig is the signatures from that
// and then the serialized subscript:
subscript = CScript(result[0].begin(), result[0].end());
sigdata.redeem_script = subscript;
- solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TX_SCRIPTHASH;
+ solved = solved && SignStep(provider, creator, subscript, result, whichType, SigVersion::BASE, sigdata) && whichType != TxoutType::SCRIPTHASH;
P2SH = true;
}
- if (solved && whichType == TX_WITNESS_V0_KEYHASH)
+ if (solved && whichType == TxoutType::WITNESS_V0_KEYHASH)
{
CScript witnessscript;
witnessscript << OP_DUP << OP_HASH160 << ToByteVector(result[0]) << OP_EQUALVERIFY << OP_CHECKSIG;
- txnouttype subType;
+ TxoutType subType;
solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata);
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
}
- else if (solved && whichType == TX_WITNESS_V0_SCRIPTHASH)
+ else if (solved && whichType == TxoutType::WITNESS_V0_SCRIPTHASH)
{
CScript witnessscript(result[0].begin(), result[0].end());
sigdata.witness_script = witnessscript;
- txnouttype subType;
- solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TX_SCRIPTHASH && subType != TX_WITNESS_V0_SCRIPTHASH && subType != TX_WITNESS_V0_KEYHASH;
+ TxoutType subType;
+ solved = solved && SignStep(provider, creator, witnessscript, result, subType, SigVersion::WITNESS_V0, sigdata) && subType != TxoutType::SCRIPTHASH && subType != TxoutType::WITNESS_V0_SCRIPTHASH && subType != TxoutType::WITNESS_V0_KEYHASH;
result.push_back(std::vector<unsigned char>(witnessscript.begin(), witnessscript.end()));
sigdata.scriptWitness.stack = result;
sigdata.witness = true;
result.clear();
- } else if (solved && whichType == TX_WITNESS_UNKNOWN) {
+ } else if (solved && whichType == TxoutType::WITNESS_UNKNOWN) {
sigdata.witness = true;
}
@@ -301,11 +301,11 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
// Get scripts
std::vector<std::vector<unsigned char>> solutions;
- txnouttype script_type = Solver(txout.scriptPubKey, solutions);
+ TxoutType script_type = Solver(txout.scriptPubKey, solutions);
SigVersion sigversion = SigVersion::BASE;
CScript next_script = txout.scriptPubKey;
- if (script_type == TX_SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
+ if (script_type == TxoutType::SCRIPTHASH && !stack.script.empty() && !stack.script.back().empty()) {
// Get the redeemScript
CScript redeem_script(stack.script.back().begin(), stack.script.back().end());
data.redeem_script = redeem_script;
@@ -315,7 +315,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
script_type = Solver(next_script, solutions);
stack.script.pop_back();
}
- if (script_type == TX_WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
+ if (script_type == TxoutType::WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
// Get the witnessScript
CScript witness_script(stack.witness.back().begin(), stack.witness.back().end());
data.witness_script = witness_script;
@@ -328,7 +328,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
stack.witness.clear();
sigversion = SigVersion::WITNESS_V0;
}
- if (script_type == TX_MULTISIG && !stack.script.empty()) {
+ if (script_type == TxoutType::MULTISIG && !stack.script.empty()) {
// Build a map of pubkey -> signature by matching sigs to pubkeys:
assert(solutions.size() > 1);
unsigned int num_pubkeys = solutions.size()-2;
@@ -454,13 +454,13 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
{
std::vector<valtype> solutions;
auto whichtype = Solver(script, solutions);
- if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
- if (whichtype == TX_SCRIPTHASH) {
+ if (whichtype == TxoutType::WITNESS_V0_SCRIPTHASH || whichtype == TxoutType::WITNESS_V0_KEYHASH || whichtype == TxoutType::WITNESS_UNKNOWN) return true;
+ if (whichtype == TxoutType::SCRIPTHASH) {
auto h160 = uint160(solutions[0]);
CScript subscript;
- if (provider.GetCScript(h160, subscript)) {
+ if (provider.GetCScript(CScriptID{h160}, subscript)) {
whichtype = Solver(subscript, solutions);
- if (whichtype == TX_WITNESS_V0_SCRIPTHASH || whichtype == TX_WITNESS_V0_KEYHASH || whichtype == TX_WITNESS_UNKNOWN) return true;
+ if (whichtype == TxoutType::WITNESS_V0_SCRIPTHASH || whichtype == TxoutType::WITNESS_V0_KEYHASH || whichtype == TxoutType::WITNESS_UNKNOWN) return true;
}
}
return false;
diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp
index 01757e2f65..2d8dc7d471 100644
--- a/src/script/signingprovider.cpp
+++ b/src/script/signingprovider.cpp
@@ -180,10 +180,10 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination&
// Only supports destinations which map to single public keys, i.e. P2PKH,
// P2WPKH, and P2SH-P2WPKH.
if (auto id = boost::get<PKHash>(&dest)) {
- return CKeyID(*id);
+ return ToKeyID(*id);
}
if (auto witness_id = boost::get<WitnessV0KeyHash>(&dest)) {
- return CKeyID(*witness_id);
+ return ToKeyID(*witness_id);
}
if (auto script_hash = boost::get<ScriptHash>(&dest)) {
CScript script;
@@ -191,7 +191,7 @@ CKeyID GetKeyForDestination(const SigningProvider& store, const CTxDestination&
CTxDestination inner_dest;
if (store.GetCScript(script_id, script) && ExtractDestination(script, inner_dest)) {
if (auto inner_witness_id = boost::get<WitnessV0KeyHash>(&inner_dest)) {
- return CKeyID(*inner_witness_id);
+ return ToKeyID(*inner_witness_id);
}
}
}
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index d199a84cee..39dd4ff39f 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -16,31 +16,47 @@ typedef std::vector<unsigned char> valtype;
bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER;
unsigned nMaxDatacarrierBytes = MAX_OP_RETURN_RELAY;
-CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+CScriptID::CScriptID(const CScript& in) : BaseHash(Hash160(in.begin(), in.end())) {}
+CScriptID::CScriptID(const ScriptHash& in) : BaseHash(static_cast<uint160>(in)) {}
-ScriptHash::ScriptHash(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
+ScriptHash::ScriptHash(const CScript& in) : BaseHash(Hash160(in.begin(), in.end())) {}
+ScriptHash::ScriptHash(const CScriptID& in) : BaseHash(static_cast<uint160>(in)) {}
-PKHash::PKHash(const CPubKey& pubkey) : uint160(pubkey.GetID()) {}
+PKHash::PKHash(const CPubKey& pubkey) : BaseHash(pubkey.GetID()) {}
+PKHash::PKHash(const CKeyID& pubkey_id) : BaseHash(pubkey_id) {}
+
+WitnessV0KeyHash::WitnessV0KeyHash(const CPubKey& pubkey) : BaseHash(pubkey.GetID()) {}
+WitnessV0KeyHash::WitnessV0KeyHash(const PKHash& pubkey_hash) : BaseHash(static_cast<uint160>(pubkey_hash)) {}
+
+CKeyID ToKeyID(const PKHash& key_hash)
+{
+ return CKeyID{static_cast<uint160>(key_hash)};
+}
+
+CKeyID ToKeyID(const WitnessV0KeyHash& key_hash)
+{
+ return CKeyID{static_cast<uint160>(key_hash)};
+}
WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
{
CSHA256().Write(in.data(), in.size()).Finalize(begin());
}
-std::string GetTxnOutputType(txnouttype t)
+std::string GetTxnOutputType(TxoutType t)
{
switch (t)
{
- case TX_NONSTANDARD: return "nonstandard";
- case TX_PUBKEY: return "pubkey";
- case TX_PUBKEYHASH: return "pubkeyhash";
- case TX_SCRIPTHASH: return "scripthash";
- case TX_MULTISIG: return "multisig";
- case TX_NULL_DATA: return "nulldata";
- case TX_WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
- case TX_WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
- case TX_WITNESS_UNKNOWN: return "witness_unknown";
- }
+ case TxoutType::NONSTANDARD: return "nonstandard";
+ case TxoutType::PUBKEY: return "pubkey";
+ case TxoutType::PUBKEYHASH: return "pubkeyhash";
+ case TxoutType::SCRIPTHASH: return "scripthash";
+ case TxoutType::MULTISIG: return "multisig";
+ case TxoutType::NULL_DATA: return "nulldata";
+ case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
+ case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
+ case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
+ } // no default case, so the compiler can warn about missing cases
assert(false);
}
@@ -90,7 +106,7 @@ static bool MatchMultisig(const CScript& script, unsigned int& required, std::ve
return (it + 1 == script.end());
}
-txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
+TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
{
vSolutionsRet.clear();
@@ -100,7 +116,7 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
{
std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
vSolutionsRet.push_back(hashBytes);
- return TX_SCRIPTHASH;
+ return TxoutType::SCRIPTHASH;
}
int witnessversion;
@@ -108,18 +124,18 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
vSolutionsRet.push_back(witnessprogram);
- return TX_WITNESS_V0_KEYHASH;
+ return TxoutType::WITNESS_V0_KEYHASH;
}
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
vSolutionsRet.push_back(witnessprogram);
- return TX_WITNESS_V0_SCRIPTHASH;
+ return TxoutType::WITNESS_V0_SCRIPTHASH;
}
if (witnessversion != 0) {
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
vSolutionsRet.push_back(std::move(witnessprogram));
- return TX_WITNESS_UNKNOWN;
+ return TxoutType::WITNESS_UNKNOWN;
}
- return TX_NONSTANDARD;
+ return TxoutType::NONSTANDARD;
}
// Provably prunable, data-carrying output
@@ -128,18 +144,18 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
// byte passes the IsPushOnly() test we don't care what exactly is in the
// script.
if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
- return TX_NULL_DATA;
+ return TxoutType::NULL_DATA;
}
std::vector<unsigned char> data;
if (MatchPayToPubkey(scriptPubKey, data)) {
vSolutionsRet.push_back(std::move(data));
- return TX_PUBKEY;
+ return TxoutType::PUBKEY;
}
if (MatchPayToPubkeyHash(scriptPubKey, data)) {
vSolutionsRet.push_back(std::move(data));
- return TX_PUBKEYHASH;
+ return TxoutType::PUBKEYHASH;
}
unsigned int required;
@@ -148,19 +164,19 @@ txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned
vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16
vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16
- return TX_MULTISIG;
+ return TxoutType::MULTISIG;
}
vSolutionsRet.clear();
- return TX_NONSTANDARD;
+ return TxoutType::NONSTANDARD;
}
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
std::vector<valtype> vSolutions;
- txnouttype whichType = Solver(scriptPubKey, vSolutions);
+ TxoutType whichType = Solver(scriptPubKey, vSolutions);
- if (whichType == TX_PUBKEY) {
+ if (whichType == TxoutType::PUBKEY) {
CPubKey pubKey(vSolutions[0]);
if (!pubKey.IsValid())
return false;
@@ -168,26 +184,26 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
addressRet = PKHash(pubKey);
return true;
}
- else if (whichType == TX_PUBKEYHASH)
+ else if (whichType == TxoutType::PUBKEYHASH)
{
addressRet = PKHash(uint160(vSolutions[0]));
return true;
}
- else if (whichType == TX_SCRIPTHASH)
+ else if (whichType == TxoutType::SCRIPTHASH)
{
addressRet = ScriptHash(uint160(vSolutions[0]));
return true;
- } else if (whichType == TX_WITNESS_V0_KEYHASH) {
+ } else if (whichType == TxoutType::WITNESS_V0_KEYHASH) {
WitnessV0KeyHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TX_WITNESS_V0_SCRIPTHASH) {
+ } else if (whichType == TxoutType::WITNESS_V0_SCRIPTHASH) {
WitnessV0ScriptHash hash;
std::copy(vSolutions[0].begin(), vSolutions[0].end(), hash.begin());
addressRet = hash;
return true;
- } else if (whichType == TX_WITNESS_UNKNOWN) {
+ } else if (whichType == TxoutType::WITNESS_UNKNOWN) {
WitnessUnknown unk;
unk.version = vSolutions[0][0];
std::copy(vSolutions[1].begin(), vSolutions[1].end(), unk.program);
@@ -199,19 +215,19 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
return false;
}
-bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
+bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
{
addressRet.clear();
std::vector<valtype> vSolutions;
typeRet = Solver(scriptPubKey, vSolutions);
- if (typeRet == TX_NONSTANDARD) {
+ if (typeRet == TxoutType::NONSTANDARD) {
return false;
- } else if (typeRet == TX_NULL_DATA) {
+ } else if (typeRet == TxoutType::NULL_DATA) {
// This is data, not addresses
return false;
}
- if (typeRet == TX_MULTISIG)
+ if (typeRet == TxoutType::MULTISIG)
{
nRequiredRet = vSolutions.front()[0];
for (unsigned int i = 1; i < vSolutions.size()-1; i++)
@@ -303,11 +319,11 @@ CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
CScript GetScriptForWitness(const CScript& redeemscript)
{
std::vector<std::vector<unsigned char> > vSolutions;
- txnouttype typ = Solver(redeemscript, vSolutions);
- if (typ == TX_PUBKEY) {
+ TxoutType typ = Solver(redeemscript, vSolutions);
+ if (typ == TxoutType::PUBKEY) {
return GetScriptForDestination(WitnessV0KeyHash(Hash160(vSolutions[0].begin(), vSolutions[0].end())));
- } else if (typ == TX_PUBKEYHASH) {
- return GetScriptForDestination(WitnessV0KeyHash(vSolutions[0]));
+ } else if (typ == TxoutType::PUBKEYHASH) {
+ return GetScriptForDestination(WitnessV0KeyHash(uint160{vSolutions[0]}));
}
return GetScriptForDestination(WitnessV0ScriptHash(redeemscript));
}
diff --git a/src/script/standard.h b/src/script/standard.h
index 2929425670..fd29353886 100644
--- a/src/script/standard.h
+++ b/src/script/standard.h
@@ -18,14 +18,77 @@ static const bool DEFAULT_ACCEPT_DATACARRIER = true;
class CKeyID;
class CScript;
+struct ScriptHash;
+
+template<typename HashType>
+class BaseHash
+{
+protected:
+ HashType m_hash;
+
+public:
+ BaseHash() : m_hash() {}
+ BaseHash(const HashType& in) : m_hash(in) {}
+
+ unsigned char* begin()
+ {
+ return m_hash.begin();
+ }
+
+ const unsigned char* begin() const
+ {
+ return m_hash.begin();
+ }
+
+ unsigned char* end()
+ {
+ return m_hash.end();
+ }
+
+ const unsigned char* end() const
+ {
+ return m_hash.end();
+ }
+
+ operator std::vector<unsigned char>() const
+ {
+ return std::vector<unsigned char>{m_hash.begin(), m_hash.end()};
+ }
+
+ std::string ToString() const
+ {
+ return m_hash.ToString();
+ }
+
+ bool operator==(const BaseHash<HashType>& other) const noexcept
+ {
+ return m_hash == other.m_hash;
+ }
+
+ bool operator!=(const BaseHash<HashType>& other) const noexcept
+ {
+ return !(m_hash == other.m_hash);
+ }
+
+ bool operator<(const BaseHash<HashType>& other) const noexcept
+ {
+ return m_hash < other.m_hash;
+ }
+
+ size_t size() const
+ {
+ return m_hash.size();
+ }
+};
/** A reference to a CScript: the Hash160 of its serialization (see script.h) */
-class CScriptID : public uint160
+class CScriptID : public BaseHash<uint160>
{
public:
- CScriptID() : uint160() {}
+ CScriptID() : BaseHash() {}
explicit CScriptID(const CScript& in);
- CScriptID(const uint160& in) : uint160(in) {}
+ explicit CScriptID(const uint160& in) : BaseHash(in) {}
+ explicit CScriptID(const ScriptHash& in);
};
/**
@@ -36,11 +99,11 @@ static const unsigned int MAX_OP_RETURN_RELAY = 83;
/**
* A data carrying output is an unspendable output containing data. The script
- * type is designated as TX_NULL_DATA.
+ * type is designated as TxoutType::NULL_DATA.
*/
extern bool fAcceptDatacarrier;
-/** Maximum size of TX_NULL_DATA scripts that this node considers standard. */
+/** Maximum size of TxoutType::NULL_DATA scripts that this node considers standard. */
extern unsigned nMaxDatacarrierBytes;
/**
@@ -53,18 +116,17 @@ extern unsigned nMaxDatacarrierBytes;
*/
static const unsigned int MANDATORY_SCRIPT_VERIFY_FLAGS = SCRIPT_VERIFY_P2SH;
-enum txnouttype
-{
- TX_NONSTANDARD,
+enum class TxoutType {
+ NONSTANDARD,
// 'standard' transaction types:
- TX_PUBKEY,
- TX_PUBKEYHASH,
- TX_SCRIPTHASH,
- TX_MULTISIG,
- TX_NULL_DATA, //!< unspendable OP_RETURN script that carries data
- TX_WITNESS_V0_SCRIPTHASH,
- TX_WITNESS_V0_KEYHASH,
- TX_WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
+ PUBKEY,
+ PUBKEYHASH,
+ SCRIPTHASH,
+ MULTISIG,
+ NULL_DATA, //!< unspendable OP_RETURN script that carries data
+ WITNESS_V0_SCRIPTHASH,
+ WITNESS_V0_KEYHASH,
+ WITNESS_UNKNOWN, //!< Only for Witness versions not already defined above
};
class CNoDestination {
@@ -73,41 +135,44 @@ public:
friend bool operator<(const CNoDestination &a, const CNoDestination &b) { return true; }
};
-struct PKHash : public uint160
+struct PKHash : public BaseHash<uint160>
{
- PKHash() : uint160() {}
- explicit PKHash(const uint160& hash) : uint160(hash) {}
+ PKHash() : BaseHash() {}
+ explicit PKHash(const uint160& hash) : BaseHash(hash) {}
explicit PKHash(const CPubKey& pubkey);
- using uint160::uint160;
+ explicit PKHash(const CKeyID& pubkey_id);
};
+CKeyID ToKeyID(const PKHash& key_hash);
struct WitnessV0KeyHash;
-struct ScriptHash : public uint160
+struct ScriptHash : public BaseHash<uint160>
{
- ScriptHash() : uint160() {}
+ ScriptHash() : BaseHash() {}
// These don't do what you'd expect.
// Use ScriptHash(GetScriptForDestination(...)) instead.
explicit ScriptHash(const WitnessV0KeyHash& hash) = delete;
explicit ScriptHash(const PKHash& hash) = delete;
- explicit ScriptHash(const uint160& hash) : uint160(hash) {}
+
+ explicit ScriptHash(const uint160& hash) : BaseHash(hash) {}
explicit ScriptHash(const CScript& script);
- using uint160::uint160;
+ explicit ScriptHash(const CScriptID& script);
};
-struct WitnessV0ScriptHash : public uint256
+struct WitnessV0ScriptHash : public BaseHash<uint256>
{
- WitnessV0ScriptHash() : uint256() {}
- explicit WitnessV0ScriptHash(const uint256& hash) : uint256(hash) {}
+ WitnessV0ScriptHash() : BaseHash() {}
+ explicit WitnessV0ScriptHash(const uint256& hash) : BaseHash(hash) {}
explicit WitnessV0ScriptHash(const CScript& script);
- using uint256::uint256;
};
-struct WitnessV0KeyHash : public uint160
+struct WitnessV0KeyHash : public BaseHash<uint160>
{
- WitnessV0KeyHash() : uint160() {}
- explicit WitnessV0KeyHash(const uint160& hash) : uint160(hash) {}
- using uint160::uint160;
+ WitnessV0KeyHash() : BaseHash() {}
+ explicit WitnessV0KeyHash(const uint160& hash) : BaseHash(hash) {}
+ explicit WitnessV0KeyHash(const CPubKey& pubkey);
+ explicit WitnessV0KeyHash(const PKHash& pubkey_hash);
};
+CKeyID ToKeyID(const WitnessV0KeyHash& key_hash);
//! CTxDestination subtype to encode any future Witness version
struct WitnessUnknown
@@ -134,11 +199,11 @@ struct WitnessUnknown
/**
* A txout script template with a specific destination. It is either:
* * CNoDestination: no destination set
- * * PKHash: TX_PUBKEYHASH destination (P2PKH)
- * * ScriptHash: TX_SCRIPTHASH destination (P2SH)
- * * WitnessV0ScriptHash: TX_WITNESS_V0_SCRIPTHASH destination (P2WSH)
- * * WitnessV0KeyHash: TX_WITNESS_V0_KEYHASH destination (P2WPKH)
- * * WitnessUnknown: TX_WITNESS_UNKNOWN destination (P2W???)
+ * * PKHash: TxoutType::PUBKEYHASH destination (P2PKH)
+ * * ScriptHash: TxoutType::SCRIPTHASH destination (P2SH)
+ * * WitnessV0ScriptHash: TxoutType::WITNESS_V0_SCRIPTHASH destination (P2WSH)
+ * * WitnessV0KeyHash: TxoutType::WITNESS_V0_KEYHASH destination (P2WPKH)
+ * * WitnessUnknown: TxoutType::WITNESS_UNKNOWN destination (P2W???)
* A CTxDestination is the internal data type encoded in a bitcoin address
*/
typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash, WitnessV0KeyHash, WitnessUnknown> CTxDestination;
@@ -146,8 +211,8 @@ typedef boost::variant<CNoDestination, PKHash, ScriptHash, WitnessV0ScriptHash,
/** Check whether a CTxDestination is a CNoDestination. */
bool IsValidDestination(const CTxDestination& dest);
-/** Get the name of a txnouttype as a C string, or nullptr if unknown. */
-std::string GetTxnOutputType(txnouttype t);
+/** Get the name of a TxoutType as a string */
+std::string GetTxnOutputType(TxoutType t);
/**
* Parse a scriptPubKey and identify script type for standard scripts. If
@@ -157,9 +222,9 @@ std::string GetTxnOutputType(txnouttype t);
*
* @param[in] scriptPubKey Script to parse
* @param[out] vSolutionsRet Vector of parsed pubkeys and hashes
- * @return The script type. TX_NONSTANDARD represents a failed solve.
+ * @return The script type. TxoutType::NONSTANDARD represents a failed solve.
*/
-txnouttype Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet);
+TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet);
/**
* Parse a standard scriptPubKey for the destination address. Assigns result to
@@ -180,7 +245,7 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
* encodable as an address) with key identifiers (of keys involved in a
* CScript), and its use should be phased out.
*/
-bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
+bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);
/**
* Generate a Bitcoin scriptPubKey for the given CTxDestination. Returns a P2PKH
diff --git a/src/span.h b/src/span.h
index 4931507719..841f1eadf7 100644
--- a/src/span.h
+++ b/src/span.h
@@ -21,6 +21,62 @@
/** A Span is an object that can refer to a contiguous sequence of objects.
*
* It implements a subset of C++20's std::span.
+ *
+ * Things to be aware of when writing code that deals with Spans:
+ *
+ * - Similar to references themselves, Spans are subject to reference lifetime
+ * issues. The user is responsible for making sure the objects pointed to by
+ * a Span live as long as the Span is used. For example:
+ *
+ * std::vector<int> vec{1,2,3,4};
+ * Span<int> sp(vec);
+ * vec.push_back(5);
+ * printf("%i\n", sp.front()); // UB!
+ *
+ * may exhibit undefined behavior, as increasing the size of a vector may
+ * invalidate references.
+ *
+ * - One particular pitfall is that Spans can be constructed from temporaries,
+ * but this is unsafe when the Span is stored in a variable, outliving the
+ * temporary. For example, this will compile, but exhibits undefined behavior:
+ *
+ * Span<const int> sp(std::vector<int>{1, 2, 3});
+ * printf("%i\n", sp.front()); // UB!
+ *
+ * The lifetime of the vector ends when the statement it is created in ends.
+ * Thus the Span is left with a dangling reference, and using it is undefined.
+ *
+ * - Due to Span's automatic creation from range-like objects (arrays, and data
+ * types that expose a data() and size() member function), functions that
+ * accept a Span as input parameter can be called with any compatible
+ * range-like object. For example, this works:
+*
+ * void Foo(Span<const int> arg);
+ *
+ * Foo(std::vector<int>{1, 2, 3}); // Works
+ *
+ * This is very useful in cases where a function truly does not care about the
+ * container, and only about having exactly a range of elements. However it
+ * may also be surprising to see automatic conversions in this case.
+ *
+ * When a function accepts a Span with a mutable element type, it will not
+ * accept temporaries; only variables or other references. For example:
+ *
+ * void FooMut(Span<int> arg);
+ *
+ * FooMut(std::vector<int>{1, 2, 3}); // Does not compile
+ * std::vector<int> baz{1, 2, 3};
+ * FooMut(baz); // Works
+ *
+ * This is similar to how functions that take (non-const) lvalue references
+ * as input cannot accept temporaries. This does not work either:
+ *
+ * void FooVec(std::vector<int>& arg);
+ * FooVec(std::vector<int>{1, 2, 3}); // Does not compile
+ *
+ * The idea is that if a function accepts a mutable reference, a meaningful
+ * result will be present in that variable after the call. Passing a temporary
+ * is useless in that context.
*/
template<typename C>
class Span
diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp
index 60196c36a5..173ec5e3d9 100644
--- a/src/test/coins_tests.cpp
+++ b/src/test/coins_tests.cpp
@@ -538,7 +538,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
CDataStream tmp(SER_DISK, CLIENT_VERSION);
uint64_t x = 3000000000ULL;
tmp << VARINT(x);
- BOOST_CHECK_EQUAL(HexStr(tmp.begin(), tmp.end()), "8a95c0bb00");
+ BOOST_CHECK_EQUAL(HexStr(tmp), "8a95c0bb00");
CDataStream ss5(ParseHex("00008a95c0bb00"), SER_DISK, CLIENT_VERSION);
try {
Coin cc5;
diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp
index 5f9a78ceb2..5d7065dafb 100644
--- a/src/test/descriptor_tests.cpp
+++ b/src/test/descriptor_tests.cpp
@@ -216,7 +216,7 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st
// For each of the produced scripts, verify solvability, and when possible, try to sign a transaction spending it.
for (size_t n = 0; n < spks.size(); ++n) {
- BOOST_CHECK_EQUAL(ref[n], HexStr(spks[n].begin(), spks[n].end()));
+ BOOST_CHECK_EQUAL(ref[n], HexStr(spks[n]));
BOOST_CHECK_EQUAL(IsSolvable(Merge(key_provider, script_provider), spks[n]), (flags & UNSOLVABLE) == 0);
if (flags & SIGNABLE) {
diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp
new file mode 100644
index 0000000000..595cdf9abb
--- /dev/null
+++ b/src/test/fuzz/crypto.cpp
@@ -0,0 +1,124 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <crypto/hmac_sha256.h>
+#include <crypto/hmac_sha512.h>
+#include <crypto/ripemd160.h>
+#include <crypto/sha1.h>
+#include <crypto/sha256.h>
+#include <crypto/sha512.h>
+#include <hash.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
+ std::vector<uint8_t> data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (data.empty()) {
+ data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+
+ CHash160 hash160;
+ CHash256 hash256;
+ CHMAC_SHA256 hmac_sha256{data.data(), data.size()};
+ CHMAC_SHA512 hmac_sha512{data.data(), data.size()};
+ CRIPEMD160 ripemd160;
+ CSHA1 sha1;
+ CSHA256 sha256;
+ CSHA512 sha512;
+ CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral<uint64_t>(), fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
+
+ while (fuzzed_data_provider.ConsumeBool()) {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 2)) {
+ case 0: {
+ if (fuzzed_data_provider.ConsumeBool()) {
+ data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+ if (data.empty()) {
+ data.resize(fuzzed_data_provider.ConsumeIntegralInRange<size_t>(1, 4096), fuzzed_data_provider.ConsumeIntegral<uint8_t>());
+ }
+ }
+
+ (void)hash160.Write(data.data(), data.size());
+ (void)hash256.Write(data.data(), data.size());
+ (void)hmac_sha256.Write(data.data(), data.size());
+ (void)hmac_sha512.Write(data.data(), data.size());
+ (void)ripemd160.Write(data.data(), data.size());
+ (void)sha1.Write(data.data(), data.size());
+ (void)sha256.Write(data.data(), data.size());
+ (void)sha512.Write(data.data(), data.size());
+ (void)sip_hasher.Write(data.data(), data.size());
+
+ (void)Hash(data.begin(), data.end());
+ (void)Hash160(data);
+ (void)Hash160(data.begin(), data.end());
+ (void)sha512.Size();
+ break;
+ }
+ case 1: {
+ (void)hash160.Reset();
+ (void)hash256.Reset();
+ (void)ripemd160.Reset();
+ (void)sha1.Reset();
+ (void)sha256.Reset();
+ (void)sha512.Reset();
+ break;
+ }
+ case 2: {
+ switch (fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 8)) {
+ case 0: {
+ data.resize(CHash160::OUTPUT_SIZE);
+ hash160.Finalize(data.data());
+ break;
+ }
+ case 1: {
+ data.resize(CHash256::OUTPUT_SIZE);
+ hash256.Finalize(data.data());
+ break;
+ }
+ case 2: {
+ data.resize(CHMAC_SHA256::OUTPUT_SIZE);
+ hmac_sha256.Finalize(data.data());
+ break;
+ }
+ case 3: {
+ data.resize(CHMAC_SHA512::OUTPUT_SIZE);
+ hmac_sha512.Finalize(data.data());
+ break;
+ }
+ case 4: {
+ data.resize(CRIPEMD160::OUTPUT_SIZE);
+ ripemd160.Finalize(data.data());
+ break;
+ }
+ case 5: {
+ data.resize(CSHA1::OUTPUT_SIZE);
+ sha1.Finalize(data.data());
+ break;
+ }
+ case 6: {
+ data.resize(CSHA256::OUTPUT_SIZE);
+ sha256.Finalize(data.data());
+ break;
+ }
+ case 7: {
+ data.resize(CSHA512::OUTPUT_SIZE);
+ sha512.Finalize(data.data());
+ break;
+ }
+ case 8: {
+ data.resize(1);
+ data[0] = sip_hasher.Finalize() % 256;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/src/test/fuzz/decode_tx.cpp b/src/test/fuzz/decode_tx.cpp
index 09c4ff05df..0d89d4228a 100644
--- a/src/test/fuzz/decode_tx.cpp
+++ b/src/test/fuzz/decode_tx.cpp
@@ -14,7 +14,7 @@
void test_one_input(const std::vector<uint8_t>& buffer)
{
- const std::string tx_hex = HexStr(std::string{buffer.begin(), buffer.end()});
+ const std::string tx_hex = HexStr(buffer);
CMutableTransaction mtx;
const bool result_none = DecodeHexTx(mtx, tx_hex, false, false);
const bool result_try_witness = DecodeHexTx(mtx, tx_hex, false, true);
diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp
index 82e1d55c0b..1e1807d734 100644
--- a/src/test/fuzz/fuzz.cpp
+++ b/src/test/fuzz/fuzz.cpp
@@ -12,7 +12,16 @@
const std::function<void(const std::string&)> G_TEST_LOG_FUN{};
-#if defined(__AFL_COMPILER)
+// Decide if main(...) should be provided:
+// * AFL needs main(...) regardless of platform.
+// * macOS handles __attribute__((weak)) main(...) poorly when linking
+// against libFuzzer. See https://github.com/bitcoin/bitcoin/pull/18008
+// for details.
+#if defined(__AFL_COMPILER) || !defined(MAC_OSX)
+#define PROVIDE_MAIN_FUNCTION
+#endif
+
+#if defined(PROVIDE_MAIN_FUNCTION)
static bool read_stdin(std::vector<uint8_t>& data)
{
uint8_t buffer[1024];
@@ -44,9 +53,8 @@ extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv)
return 0;
}
-// Generally, the fuzzer will provide main(), except for AFL
-#if defined(__AFL_COMPILER)
-int main(int argc, char** argv)
+#if defined(PROVIDE_MAIN_FUNCTION)
+__attribute__((weak)) int main(int argc, char** argv)
{
initialize();
#ifdef __AFL_INIT
diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp
index 1919a5f881..c746374c61 100644
--- a/src/test/fuzz/key.cpp
+++ b/src/test/fuzz/key.cpp
@@ -108,7 +108,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
assert(pubkey.IsCompressed());
assert(pubkey.IsValid());
assert(pubkey.IsFullyValid());
- assert(HexToPubKey(HexStr(pubkey.begin(), pubkey.end())) == pubkey);
+ assert(HexToPubKey(HexStr(pubkey)) == pubkey);
assert(GetAllDestinationsForKey(pubkey).size() == 3);
}
@@ -157,25 +157,25 @@ void test_one_input(const std::vector<uint8_t>& buffer)
assert(ok_add_key_pubkey);
assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
- txnouttype which_type_tx_pubkey;
+ TxoutType which_type_tx_pubkey;
const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, which_type_tx_pubkey);
assert(is_standard_tx_pubkey);
- assert(which_type_tx_pubkey == txnouttype::TX_PUBKEY);
+ assert(which_type_tx_pubkey == TxoutType::PUBKEY);
- txnouttype which_type_tx_multisig;
+ TxoutType which_type_tx_multisig;
const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, which_type_tx_multisig);
assert(is_standard_tx_multisig);
- assert(which_type_tx_multisig == txnouttype::TX_MULTISIG);
+ assert(which_type_tx_multisig == TxoutType::MULTISIG);
std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
- const txnouttype outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
- assert(outtype_tx_pubkey == txnouttype::TX_PUBKEY);
+ const TxoutType outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
+ assert(outtype_tx_pubkey == TxoutType::PUBKEY);
assert(v_solutions_ret_tx_pubkey.size() == 1);
assert(v_solutions_ret_tx_pubkey[0].size() == 33);
std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
- const txnouttype outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
- assert(outtype_tx_multisig == txnouttype::TX_MULTISIG);
+ const TxoutType outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
+ assert(outtype_tx_multisig == TxoutType::MULTISIG);
assert(v_solutions_ret_tx_multisig.size() == 3);
assert(v_solutions_ret_tx_multisig[0].size() == 1);
assert(v_solutions_ret_tx_multisig[1].size() == 33);
diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp
index 933cf9049d..cad548178d 100644
--- a/src/test/fuzz/script.cpp
+++ b/src/test/fuzz/script.cpp
@@ -58,7 +58,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
CTxDestination address;
(void)ExtractDestination(script, address);
- txnouttype type_ret;
+ TxoutType type_ret;
std::vector<CTxDestination> addresses;
int required_ret;
(void)ExtractDestinations(script, type_ret, addresses, required_ret);
@@ -72,7 +72,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)IsSolvable(signing_provider, script);
- txnouttype which_type;
+ TxoutType which_type;
(void)IsStandard(script, which_type);
(void)RecursiveDynamicUsage(script);
diff --git a/src/test/key_tests.cpp b/src/test/key_tests.cpp
index cf2bd03698..fd35537c77 100644
--- a/src/test/key_tests.cpp
+++ b/src/test/key_tests.cpp
@@ -5,6 +5,7 @@
#include <key.h>
#include <key_io.h>
+#include <streams.h>
#include <test/util/setup_common.h>
#include <uint256.h>
#include <util/strencodings.h>
@@ -220,4 +221,47 @@ BOOST_AUTO_TEST_CASE(key_key_negation)
BOOST_CHECK(key.GetPubKey().data()[0] == 0x03);
}
+static CPubKey UnserializePubkey(const std::vector<uint8_t>& data)
+{
+ CDataStream stream{SER_NETWORK, INIT_PROTO_VERSION};
+ stream << data;
+ CPubKey pubkey;
+ stream >> pubkey;
+ return pubkey;
+}
+
+static unsigned int GetLen(unsigned char chHeader)
+{
+ if (chHeader == 2 || chHeader == 3)
+ return CPubKey::COMPRESSED_SIZE;
+ if (chHeader == 4 || chHeader == 6 || chHeader == 7)
+ return CPubKey::SIZE;
+ return 0;
+}
+
+static void CmpSerializationPubkey(const CPubKey& pubkey)
+{
+ CDataStream stream{SER_NETWORK, INIT_PROTO_VERSION};
+ stream << pubkey;
+ CPubKey pubkey2;
+ stream >> pubkey2;
+ BOOST_CHECK(pubkey == pubkey2);
+}
+
+BOOST_AUTO_TEST_CASE(pubkey_unserialize)
+{
+ for (uint8_t i = 2; i <= 7; ++i) {
+ CPubKey key = UnserializePubkey({0x02});
+ BOOST_CHECK(!key.IsValid());
+ CmpSerializationPubkey(key);
+ key = UnserializePubkey(std::vector<uint8_t>(GetLen(i), i));
+ CmpSerializationPubkey(key);
+ if (i == 5) {
+ BOOST_CHECK(!key.IsValid());
+ } else {
+ BOOST_CHECK(key.IsValid());
+ }
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp
index dd2890c134..e14d2dd72d 100644
--- a/src/test/multisig_tests.cpp
+++ b/src/test/multisig_tests.cpp
@@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
for (int i = 0; i < 4; i++)
key[i].MakeNewKey(true);
- txnouttype whichType;
+ TxoutType whichType;
CScript a_and_b;
a_and_b << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index d0ec401f9d..0fbf257f0e 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -160,6 +160,9 @@ BOOST_AUTO_TEST_CASE(subnet_test)
BOOST_CHECK(ResolveSubNet("1.2.2.20/26").Match(ResolveIP("1.2.2.63")));
// All-Matching IPv6 Matches arbitrary IPv4 and IPv6
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
+ // But not `::` or `0.0.0.0` because they are considered invalid addresses
+ BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("::")));
+ BOOST_CHECK(!ResolveSubNet("::/0").Match(ResolveIP("0.0.0.0")));
BOOST_CHECK(ResolveSubNet("::/0").Match(ResolveIP("1.2.3.4")));
// All-Matching IPv4 does not Match IPv6
BOOST_CHECK(!ResolveSubNet("0.0.0.0/0").Match(ResolveIP("1:2:3:4:5:6:7:1234")));
diff --git a/src/test/scheduler_tests.cpp b/src/test/scheduler_tests.cpp
index fcee6a9b9d..2e5a7549b7 100644
--- a/src/test/scheduler_tests.cpp
+++ b/src/test/scheduler_tests.cpp
@@ -89,7 +89,7 @@ BOOST_AUTO_TEST_CASE(manythreads)
}
// Drain the task queue then exit threads
- microTasks.stop(true);
+ microTasks.StopWhenDrained();
microThreads.join_all(); // ... wait until all the threads are done
int counterSum = 0;
@@ -155,7 +155,7 @@ BOOST_AUTO_TEST_CASE(singlethreadedscheduler_ordered)
}
// finish up
- scheduler.stop(true);
+ scheduler.StopWhenDrained();
threads.join_all();
BOOST_CHECK_EQUAL(counter1, 100);
@@ -186,7 +186,7 @@ BOOST_AUTO_TEST_CASE(mockforward)
scheduler.MockForward(std::chrono::minutes{5});
// ensure scheduler has chance to process all tasks queued for before 1 ms from now.
- scheduler.scheduleFromNow([&scheduler] { scheduler.stop(false); }, std::chrono::milliseconds{1});
+ scheduler.scheduleFromNow([&scheduler] { scheduler.stop(); }, std::chrono::milliseconds{1});
scheduler_thread.join();
// check that the queue only has one job remaining
diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp
index b185d3b4ac..77d748241b 100644
--- a/src/test/script_standard_tests.cpp
+++ b/src/test/script_standard_tests.cpp
@@ -31,35 +31,35 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
CScript s;
std::vector<std::vector<unsigned char> > solutions;
- // TX_PUBKEY
+ // TxoutType::PUBKEY
s.clear();
s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_PUBKEY);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::PUBKEY);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0]));
- // TX_PUBKEYHASH
+ // TxoutType::PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_PUBKEYHASH);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::PUBKEYHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID()));
- // TX_SCRIPTHASH
+ // TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::SCRIPTHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(CScriptID(redeemScript)));
- // TX_MULTISIG
+ // TxoutType::MULTISIG
s.clear();
s << OP_1 <<
ToByteVector(pubkeys[0]) <<
ToByteVector(pubkeys[1]) <<
OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_MULTISIG);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::MULTISIG);
BOOST_CHECK_EQUAL(solutions.size(), 4U);
BOOST_CHECK(solutions[0] == std::vector<unsigned char>({1}));
BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0]));
@@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
ToByteVector(pubkeys[1]) <<
ToByteVector(pubkeys[2]) <<
OP_3 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_MULTISIG);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::MULTISIG);
BOOST_CHECK_EQUAL(solutions.size(), 5U);
BOOST_CHECK(solutions[0] == std::vector<unsigned char>({2}));
BOOST_CHECK(solutions[1] == ToByteVector(pubkeys[0]));
@@ -80,37 +80,37 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_success)
BOOST_CHECK(solutions[3] == ToByteVector(pubkeys[2]));
BOOST_CHECK(solutions[4] == std::vector<unsigned char>({3}));
- // TX_NULL_DATA
+ // TxoutType::NULL_DATA
s.clear();
s << OP_RETURN <<
std::vector<unsigned char>({0}) <<
std::vector<unsigned char>({75}) <<
std::vector<unsigned char>({255});
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NULL_DATA);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NULL_DATA);
BOOST_CHECK_EQUAL(solutions.size(), 0U);
- // TX_WITNESS_V0_KEYHASH
+ // TxoutType::WITNESS_V0_KEYHASH
s.clear();
s << OP_0 << ToByteVector(pubkeys[0].GetID());
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_WITNESS_V0_KEYHASH);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V0_KEYHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(pubkeys[0].GetID()));
- // TX_WITNESS_V0_SCRIPTHASH
+ // TxoutType::WITNESS_V0_SCRIPTHASH
uint256 scriptHash;
CSHA256().Write(&redeemScript[0], redeemScript.size())
.Finalize(scriptHash.begin());
s.clear();
s << OP_0 << ToByteVector(scriptHash);
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_WITNESS_V0_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::WITNESS_V0_SCRIPTHASH);
BOOST_CHECK_EQUAL(solutions.size(), 1U);
BOOST_CHECK(solutions[0] == ToByteVector(scriptHash));
- // TX_NONSTANDARD
+ // TxoutType::NONSTANDARD
s.clear();
s << OP_9 << OP_ADD << OP_11 << OP_EQUAL;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
}
BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
@@ -123,50 +123,50 @@ BOOST_AUTO_TEST_CASE(script_standard_Solver_failure)
CScript s;
std::vector<std::vector<unsigned char> > solutions;
- // TX_PUBKEY with incorrectly sized pubkey
+ // TxoutType::PUBKEY with incorrectly sized pubkey
s.clear();
s << std::vector<unsigned char>(30, 0x01) << OP_CHECKSIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_PUBKEYHASH with incorrectly sized key hash
+ // TxoutType::PUBKEYHASH with incorrectly sized key hash
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey) << OP_EQUALVERIFY << OP_CHECKSIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_SCRIPTHASH with incorrectly sized script hash
+ // TxoutType::SCRIPTHASH with incorrectly sized script hash
s.clear();
s << OP_HASH160 << std::vector<unsigned char>(21, 0x01) << OP_EQUAL;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_MULTISIG 0/2
+ // TxoutType::MULTISIG 0/2
s.clear();
s << OP_0 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_MULTISIG 2/1
+ // TxoutType::MULTISIG 2/1
s.clear();
s << OP_2 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_MULTISIG n = 2 with 1 pubkey
+ // TxoutType::MULTISIG n = 2 with 1 pubkey
s.clear();
s << OP_1 << ToByteVector(pubkey) << OP_2 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_MULTISIG n = 1 with 0 pubkeys
+ // TxoutType::MULTISIG n = 1 with 0 pubkeys
s.clear();
s << OP_1 << OP_1 << OP_CHECKMULTISIG;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_NULL_DATA with other opcodes
+ // TxoutType::NULL_DATA with other opcodes
s.clear();
s << OP_RETURN << std::vector<unsigned char>({75}) << OP_ADD;
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
- // TX_WITNESS with incorrect program size
+ // TxoutType::WITNESS_UNKNOWN with incorrect program size
s.clear();
s << OP_0 << std::vector<unsigned char>(19, 0x01);
- BOOST_CHECK_EQUAL(Solver(s, solutions), TX_NONSTANDARD);
+ BOOST_CHECK_EQUAL(Solver(s, solutions), TxoutType::NONSTANDARD);
}
BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
@@ -179,21 +179,21 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
CScript s;
CTxDestination address;
- // TX_PUBKEY
+ // TxoutType::PUBKEY
s.clear();
s << ToByteVector(pubkey) << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(boost::get<PKHash>(&address) &&
*boost::get<PKHash>(&address) == PKHash(pubkey));
- // TX_PUBKEYHASH
+ // TxoutType::PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(boost::get<PKHash>(&address) &&
*boost::get<PKHash>(&address) == PKHash(pubkey));
- // TX_SCRIPTHASH
+ // TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
@@ -201,17 +201,17 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(boost::get<ScriptHash>(&address) &&
*boost::get<ScriptHash>(&address) == ScriptHash(redeemScript));
- // TX_MULTISIG
+ // TxoutType::MULTISIG
s.clear();
s << OP_1 << ToByteVector(pubkey) << OP_1 << OP_CHECKMULTISIG;
BOOST_CHECK(!ExtractDestination(s, address));
- // TX_NULL_DATA
+ // TxoutType::NULL_DATA
s.clear();
s << OP_RETURN << std::vector<unsigned char>({75});
BOOST_CHECK(!ExtractDestination(s, address));
- // TX_WITNESS_V0_KEYHASH
+ // TxoutType::WITNESS_V0_KEYHASH
s.clear();
s << OP_0 << ToByteVector(pubkey.GetID());
BOOST_CHECK(ExtractDestination(s, address));
@@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
CHash160().Write(pubkey.begin(), pubkey.size()).Finalize(keyhash.begin());
BOOST_CHECK(boost::get<WitnessV0KeyHash>(&address) && *boost::get<WitnessV0KeyHash>(&address) == keyhash);
- // TX_WITNESS_V0_SCRIPTHASH
+ // TxoutType::WITNESS_V0_SCRIPTHASH
s.clear();
WitnessV0ScriptHash scripthash;
CSHA256().Write(redeemScript.data(), redeemScript.size()).Finalize(scripthash.begin());
@@ -227,7 +227,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(ExtractDestination(s, address));
BOOST_CHECK(boost::get<WitnessV0ScriptHash>(&address) && *boost::get<WitnessV0ScriptHash>(&address) == scripthash);
- // TX_WITNESS with unknown version
+ // TxoutType::WITNESS_UNKNOWN with unknown version
s.clear();
s << OP_1 << ToByteVector(pubkey);
BOOST_CHECK(ExtractDestination(s, address));
@@ -248,49 +248,49 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
}
CScript s;
- txnouttype whichType;
+ TxoutType whichType;
std::vector<CTxDestination> addresses;
int nRequired;
- // TX_PUBKEY
+ // TxoutType::PUBKEY
s.clear();
s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TX_PUBKEY);
+ BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
*boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
- // TX_PUBKEYHASH
+ // TxoutType::PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TX_PUBKEYHASH);
+ BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
*boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
- // TX_SCRIPTHASH
+ // TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TX_SCRIPTHASH);
+ BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<ScriptHash>(&addresses[0]) &&
*boost::get<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript));
- // TX_MULTISIG
+ // TxoutType::MULTISIG
s.clear();
s << OP_2 <<
ToByteVector(pubkeys[0]) <<
ToByteVector(pubkeys[1]) <<
OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
- BOOST_CHECK_EQUAL(whichType, TX_MULTISIG);
+ BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG);
BOOST_CHECK_EQUAL(addresses.size(), 2U);
BOOST_CHECK_EQUAL(nRequired, 2);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
@@ -298,7 +298,7 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
BOOST_CHECK(boost::get<PKHash>(&addresses[1]) &&
*boost::get<PKHash>(&addresses[1]) == PKHash(pubkeys[1]));
- // TX_NULL_DATA
+ // TxoutType::NULL_DATA
s.clear();
s << OP_RETURN << std::vector<unsigned char>({75});
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
diff --git a/src/test/sighash_tests.cpp b/src/test/sighash_tests.cpp
index 5ca136ea6e..c0bb92258b 100644
--- a/src/test/sighash_tests.cpp
+++ b/src/test/sighash_tests.cpp
@@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(sighash_test)
ss << txTo;
std::cout << "\t[\"" ;
- std::cout << HexStr(ss.begin(), ss.end()) << "\", \"";
+ std::cout << HexStr(ss) << "\", \"";
std::cout << HexStr(scriptCode) << "\", ";
std::cout << nIn << ", ";
std::cout << nHashType << ", \"";
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index ddbc68f8e2..4bf6e734ce 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -716,12 +716,12 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptpubkey");
- // MAX_OP_RETURN_RELAY-byte TX_NULL_DATA (standard)
+ // MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
- // MAX_OP_RETURN_RELAY+1-byte TX_NULL_DATA (non-standard)
+ // MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800");
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size());
reason.clear();
@@ -745,12 +745,12 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
BOOST_CHECK_EQUAL(reason, "scriptpubkey");
- // TX_NULL_DATA w/o PUSHDATA
+ // TxoutType::NULL_DATA w/o PUSHDATA
t.vout.resize(1);
t.vout[0].scriptPubKey = CScript() << OP_RETURN;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
- // Only one TX_NULL_DATA permitted in all cases
+ // Only one TxoutType::NULL_DATA permitted in all cases
t.vout.resize(2);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38");
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 3b7a7c8d12..709d357b8a 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -19,6 +19,7 @@
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
+#include <scheduler.h>
#include <script/sigcache.h>
#include <streams.h>
#include <txdb.h>
diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h
index d5cda8a95b..e480782c12 100644
--- a/src/test/util/setup_common.h
+++ b/src/test/util/setup_common.h
@@ -11,7 +11,6 @@
#include <node/context.h>
#include <pubkey.h>
#include <random.h>
-#include <scheduler.h>
#include <txmempool.h>
#include <util/string.h>
diff --git a/src/test/util/transaction_utils.h b/src/test/util/transaction_utils.h
index 1beddd334b..6f2faeec6c 100644
--- a/src/test/util/transaction_utils.h
+++ b/src/test/util/transaction_utils.h
@@ -22,8 +22,8 @@ CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey, int n
CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CScriptWitness& scriptWitness, const CTransaction& txCredit);
// Helper: create two dummy transactions, each with two outputs.
-// The first has nValues[0] and nValues[1] outputs paid to a TX_PUBKEY,
-// the second nValues[2] and nValues[3] outputs paid to a TX_PUBKEYHASH.
+// The first has nValues[0] and nValues[1] outputs paid to a TxoutType::PUBKEY,
+// the second nValues[2] and nValues[3] outputs paid to a TxoutType::PUBKEYHASH.
std::vector<CMutableTransaction> SetupDummyInputs(FillableSigningProvider& keystoreRet, CCoinsViewCache& coinsRet, const std::array<CAmount,4>& nValues);
#endif // BITCOIN_TEST_UTIL_TRANSACTION_UTILS_H
diff --git a/src/ui_interface.h b/src/ui_interface.h
index 356d30eaf6..b7895e373f 100644
--- a/src/ui_interface.h
+++ b/src/ui_interface.h
@@ -122,7 +122,7 @@ void InitWarning(const bilingual_str& str);
/** Show error message **/
bool InitError(const bilingual_str& str);
-constexpr auto AbortError = InitError;
+inline bool AbortError(const bilingual_str& str) { return InitError(str); }
extern CClientUIInterface uiInterface;
diff --git a/src/util/error.cpp b/src/util/error.cpp
index c4d9ffd037..3e29083712 100644
--- a/src/util/error.cpp
+++ b/src/util/error.cpp
@@ -14,7 +14,7 @@ bilingual_str TransactionErrorString(const TransactionError err)
case TransactionError::OK:
return Untranslated("No error");
case TransactionError::MISSING_INPUTS:
- return Untranslated("Missing inputs");
+ return Untranslated("Inputs missing or spent");
case TransactionError::ALREADY_IN_CHAIN:
return Untranslated("Transaction already in block chain");
case TransactionError::P2P_DISABLED:
@@ -24,11 +24,11 @@ bilingual_str TransactionErrorString(const TransactionError err)
case TransactionError::MEMPOOL_ERROR:
return Untranslated("AcceptToMemoryPool failed");
case TransactionError::INVALID_PSBT:
- return Untranslated("PSBT is not sane");
+ 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 existing value");
+ return Untranslated("Specified sighash value does not match value stored in PSBT");
case TransactionError::MAX_FEE_EXCEEDED:
return Untranslated("Fee exceeds maximum configured by -maxtxfee");
// no default case, so the compiler can warn about missing cases
diff --git a/src/util/fees.cpp b/src/util/fees.cpp
index b335bfa666..6208a20a97 100644
--- a/src/util/fees.cpp
+++ b/src/util/fees.cpp
@@ -6,11 +6,16 @@
#include <util/fees.h>
#include <policy/fees.h>
+#include <util/strencodings.h>
+#include <util/string.h>
#include <map>
#include <string>
+#include <vector>
+#include <utility>
-std::string StringForFeeReason(FeeReason reason) {
+std::string StringForFeeReason(FeeReason reason)
+{
static const std::map<FeeReason, std::string> fee_reason_strings = {
{FeeReason::NONE, "None"},
{FeeReason::HALF_ESTIMATE, "Half Target 60% Threshold"},
@@ -29,16 +34,31 @@ std::string StringForFeeReason(FeeReason reason) {
return reason_string->second;
}
-bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
- static const std::map<std::string, FeeEstimateMode> fee_modes = {
- {"UNSET", FeeEstimateMode::UNSET},
- {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
- {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
+const std::vector<std::pair<std::string, FeeEstimateMode>>& FeeModeMap()
+{
+ static const std::vector<std::pair<std::string, FeeEstimateMode>> FEE_MODES = {
+ {"unset", FeeEstimateMode::UNSET},
+ {"economical", FeeEstimateMode::ECONOMICAL},
+ {"conservative", FeeEstimateMode::CONSERVATIVE},
+ {(CURRENCY_UNIT + "/kB"), FeeEstimateMode::BTC_KB},
+ {(CURRENCY_ATOM + "/B"), FeeEstimateMode::SAT_B},
};
- auto mode = fee_modes.find(mode_string);
+ return FEE_MODES;
+}
- if (mode == fee_modes.end()) return false;
+std::string FeeModes(const std::string& delimiter)
+{
+ return Join(FeeModeMap(), delimiter, [&](const std::pair<std::string, FeeEstimateMode>& i) { return i.first; });
+}
- fee_estimate_mode = mode->second;
- return true;
+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode)
+{
+ auto searchkey = ToUpper(mode_string);
+ for (const auto& pair : FeeModeMap()) {
+ if (ToUpper(pair.first) == searchkey) {
+ fee_estimate_mode = pair.second;
+ return true;
+ }
+ }
+ return false;
}
diff --git a/src/util/fees.h b/src/util/fees.h
index a930c8935a..d52046a44c 100644
--- a/src/util/fees.h
+++ b/src/util/fees.h
@@ -12,5 +12,6 @@ enum class FeeReason;
bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode);
std::string StringForFeeReason(FeeReason reason);
+std::string FeeModes(const std::string& delimiter);
#endif // BITCOIN_UTIL_FEES_H
diff --git a/src/wallet/coincontrol.cpp b/src/wallet/coincontrol.cpp
index c83e598825..720877ead0 100644
--- a/src/wallet/coincontrol.cpp
+++ b/src/wallet/coincontrol.cpp
@@ -10,6 +10,7 @@ void CCoinControl::SetNull()
{
destChange = CNoDestination();
m_change_type.reset();
+ m_add_inputs = true;
fAllowOtherInputs = false;
fAllowWatchOnly = false;
m_avoid_partial_spends = gArgs.GetBoolArg("-avoidpartialspends", DEFAULT_AVOIDPARTIALSPENDS);
@@ -23,4 +24,3 @@ void CCoinControl::SetNull()
m_min_depth = DEFAULT_MIN_DEPTH;
m_max_depth = DEFAULT_MAX_DEPTH;
}
-
diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h
index 2893d0ab3d..c499b0ff25 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -26,6 +26,8 @@ public:
CTxDestination destChange;
//! Override the default change type if set, ignored if destChange is set
Optional<OutputType> m_change_type;
+ //! If false, only selected inputs are used
+ bool m_add_inputs;
//! If false, allows unselected inputs, but requires all selected inputs be used
bool fAllowOtherInputs;
//! Includes watch only addresses which are solvable
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index d9fe741a6e..c9ea6c2ad9 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -297,7 +297,7 @@ UniValue importaddress(const JSONRPCRequest& request)
pwallet->ImportScripts(scripts, 0 /* timestamp */);
if (fP2SH) {
- scripts.insert(GetScriptForDestination(ScriptHash(CScriptID(redeem_script))));
+ scripts.insert(GetScriptForDestination(ScriptHash(redeem_script)));
}
pwallet->ImportScriptPubKeys(strLabel, scripts, false /* have_solving_data */, true /* apply_label */, 1 /* timestamp */);
@@ -817,7 +817,7 @@ UniValue dumpwallet(const JSONRPCRequest& request)
create_time = FormatISO8601DateTime(it->second.nCreateTime);
}
if(spk_man.GetCScript(scriptid, script)) {
- file << strprintf("%s %s script=1", HexStr(script.begin(), script.end()), create_time);
+ file << strprintf("%s %s script=1", HexStr(script), create_time);
file << strprintf(" # addr=%s\n", address);
}
}
@@ -856,20 +856,20 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
{
// Use Solver to obtain script type and parsed pubkeys or hashes:
std::vector<std::vector<unsigned char>> solverdata;
- txnouttype script_type = Solver(script, solverdata);
+ TxoutType script_type = Solver(script, solverdata);
switch (script_type) {
- case TX_PUBKEY: {
+ case TxoutType::PUBKEY: {
CPubKey pubkey(solverdata[0].begin(), solverdata[0].end());
import_data.used_keys.emplace(pubkey.GetID(), false);
return "";
}
- case TX_PUBKEYHASH: {
+ case TxoutType::PUBKEYHASH: {
CKeyID id = CKeyID(uint160(solverdata[0]));
import_data.used_keys[id] = true;
return "";
}
- case TX_SCRIPTHASH: {
+ case TxoutType::SCRIPTHASH: {
if (script_ctx == ScriptContext::P2SH) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside another P2SH");
if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2SH inside a P2WSH");
CHECK_NONFATAL(script_ctx == ScriptContext::TOP);
@@ -880,14 +880,14 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
import_data.import_scripts.emplace(*subscript);
return RecurseImportData(*subscript, import_data, ScriptContext::P2SH);
}
- case TX_MULTISIG: {
+ case TxoutType::MULTISIG: {
for (size_t i = 1; i + 1< solverdata.size(); ++i) {
CPubKey pubkey(solverdata[i].begin(), solverdata[i].end());
import_data.used_keys.emplace(pubkey.GetID(), false);
}
return "";
}
- case TX_WITNESS_V0_SCRIPTHASH: {
+ case TxoutType::WITNESS_V0_SCRIPTHASH: {
if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WSH inside another P2WSH");
uint256 fullid(solverdata[0]);
CScriptID id;
@@ -901,7 +901,7 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
import_data.import_scripts.emplace(*subscript);
return RecurseImportData(*subscript, import_data, ScriptContext::WITNESS_V0);
}
- case TX_WITNESS_V0_KEYHASH: {
+ case TxoutType::WITNESS_V0_KEYHASH: {
if (script_ctx == ScriptContext::WITNESS_V0) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Trying to nest P2WPKH inside P2WSH");
CKeyID id = CKeyID(uint160(solverdata[0]));
import_data.used_keys[id] = true;
@@ -910,10 +910,10 @@ static std::string RecurseImportData(const CScript& script, ImportData& import_d
}
return "";
}
- case TX_NULL_DATA:
+ case TxoutType::NULL_DATA:
return "unspendable script";
- case TX_NONSTANDARD:
- case TX_WITNESS_UNKNOWN:
+ case TxoutType::NONSTANDARD:
+ case TxoutType::WITNESS_UNKNOWN:
default:
return "unrecognized script";
}
@@ -1193,7 +1193,7 @@ static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, con
// Check whether we have any work to do
for (const CScript& script : script_pub_keys) {
if (pwallet->IsMine(script) & ISMINE_SPENDABLE) {
- throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script.begin(), script.end()) + "\")");
+ throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script (\"" + HexStr(script) + "\")");
}
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index c3a64cf46a..00927a2e55 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -45,6 +45,8 @@ using interfaces::FoundBlock;
static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
static const std::string HELP_REQUIRING_PASSPHRASE{"\nRequires wallet passphrase to be set with walletpassphrase call if wallet is encrypted.\n"};
+static const uint32_t WALLET_BTC_KB_TO_SAT_B = COIN / 1000; // 1 sat / B = 0.00001 BTC / kB
+
static inline bool GetAvoidReuseFlag(const CWallet* const pwallet, const UniValue& param) {
bool can_avoid_reuse = pwallet->IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE);
bool avoid_reuse = param.isNull() ? can_avoid_reuse : param.get_bool();
@@ -191,6 +193,42 @@ static std::string LabelFromValue(const UniValue& value)
return label;
}
+/**
+ * Update coin control with fee estimation based on the given parameters
+ *
+ * @param[in] pwallet Wallet pointer
+ * @param[in,out] cc Coin control which is to be updated
+ * @param[in] estimate_mode String value (e.g. "ECONOMICAL")
+ * @param[in] estimate_param Parameter (blocks to confirm, explicit fee rate, etc)
+ * @throws a JSONRPCError if estimate_mode is unknown, or if estimate_param is missing when required
+ */
+static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const UniValue& estimate_mode, const UniValue& estimate_param)
+{
+ if (!estimate_mode.isNull()) {
+ if (!FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
+ }
+ }
+
+ if (cc.m_fee_mode == FeeEstimateMode::BTC_KB || cc.m_fee_mode == FeeEstimateMode::SAT_B) {
+ if (estimate_param.isNull()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Selected estimate_mode requires a fee rate");
+ }
+
+ CAmount fee_rate = AmountFromValue(estimate_param);
+ if (cc.m_fee_mode == FeeEstimateMode::SAT_B) {
+ fee_rate /= WALLET_BTC_KB_TO_SAT_B;
+ }
+
+ cc.m_feerate = CFeeRate(fee_rate);
+
+ // default RBF to true for explicit fee rate modes
+ if (cc.m_signal_bip125_rbf == boost::none) cc.m_signal_bip125_rbf = true;
+ } else if (!estimate_param.isNull()) {
+ cc.m_confirm_target = ParseConfirmTarget(estimate_param, pwallet->chain().estimateMaxBlocks());
+ }
+}
+
static UniValue getnewaddress(const JSONRPCRequest& request)
{
RPCHelpMan{"getnewaddress",
@@ -369,11 +407,9 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
{"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
" The recipient will receive less bitcoins than you enter in the amount field."},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
- " \"UNSET\"\n"
- " \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\""},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
{"avoid_reuse", RPCArg::Type::BOOL, /* default */ "true", "(only available if avoid_reuse wallet flag is set) Avoid spending from dirty addresses; addresses are considered\n"
" dirty if they have previously been used in a transaction."},
},
@@ -384,6 +420,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1")
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"seans outpost\"")
+ HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" true")
+ + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 0.00002 " + (CURRENCY_UNIT + "/kB"))
+ + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 2 " + (CURRENCY_ATOM + "/B"))
+ HelpExampleRpc("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\"")
},
}.Check(request);
@@ -425,20 +463,12 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
- if (!request.params[6].isNull()) {
- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
- }
-
- if (!request.params[7].isNull()) {
- if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
- }
- }
-
coin_control.m_avoid_address_reuse = GetAvoidReuseFlag(pwallet, request.params[8]);
// We also enable partial spend avoidance if reuse avoidance is set.
coin_control.m_avoid_partial_spends |= coin_control.m_avoid_address_reuse;
+ SetFeeEstimateMode(pwallet, coin_control, request.params[7], request.params[6]);
+
EnsureWalletIsUnlocked(pwallet);
CTransactionRef tx = SendMoney(pwallet, dest, nAmount, fSubtractFeeFromAmount, coin_control, std::move(mapValue));
@@ -780,11 +810,9 @@ static UniValue sendmany(const JSONRPCRequest& request)
},
},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
- " \"UNSET\"\n"
- " \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\""},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
RPCResult{
RPCResult::Type::STR_HEX, "txid", "The transaction id for the send. Only 1 transaction is created regardless of\n"
@@ -830,15 +858,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
}
- if (!request.params[6].isNull()) {
- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
- }
-
- if (!request.params[7].isNull()) {
- if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
- }
- }
+ SetFeeEstimateMode(pwallet, coin_control, request.params[7], request.params[6]);
std::set<CTxDestination> destinations;
std::vector<CRecipient> vecSend;
@@ -964,7 +984,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
UniValue result(UniValue::VOBJ);
result.pushKV("address", EncodeDestination(dest));
- result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
+ result.pushKV("redeemScript", HexStr(inner));
result.pushKV("descriptor", descriptor->ToString());
return result;
}
@@ -2870,7 +2890,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
CScript redeemScript;
if (provider->GetCScript(hash, redeemScript)) {
- entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
+ entry.pushKV("redeemScript", HexStr(redeemScript));
// Now check if the redeemScript is actually a P2WSH script
CTxDestination witness_destination;
if (redeemScript.IsPayToWitnessScriptHash()) {
@@ -2882,7 +2902,7 @@ static UniValue listunspent(const JSONRPCRequest& request)
CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
CScript witnessScript;
if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ entry.pushKV("witnessScript", HexStr(witnessScript));
}
}
}
@@ -2892,13 +2912,13 @@ static UniValue listunspent(const JSONRPCRequest& request)
CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
CScript witnessScript;
if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ entry.pushKV("witnessScript", HexStr(witnessScript));
}
}
}
}
- entry.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ entry.pushKV("scriptPubKey", HexStr(scriptPubKey));
entry.pushKV("amount", ValueFromAmount(out.tx->tx->vout[out.i].nValue));
entry.pushKV("confirmations", out.nDepth);
entry.pushKV("spendable", out.fSpendable);
@@ -2918,13 +2938,12 @@ static UniValue listunspent(const JSONRPCRequest& request)
return results;
}
-void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options)
+void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& fee_out, int& change_position, UniValue options, CCoinControl& coinControl)
{
// Make sure the results are valid at least up to the most recent block
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
- CCoinControl coinControl;
change_position = -1;
bool lockUnspents = false;
UniValue subtractFeeFromOutputs;
@@ -2939,6 +2958,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
RPCTypeCheckArgument(options, UniValue::VOBJ);
RPCTypeCheckObj(options,
{
+ {"add_inputs", UniValueType(UniValue::VBOOL)},
{"changeAddress", UniValueType(UniValue::VSTR)},
{"changePosition", UniValueType(UniValue::VNUM)},
{"change_type", UniValueType(UniValue::VSTR)},
@@ -2952,6 +2972,10 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
},
true, true);
+ if (options.exists("add_inputs") ) {
+ coinControl.m_add_inputs = options["add_inputs"].get_bool();
+ }
+
if (options.exists("changeAddress")) {
CTxDestination dest = DecodeDestination(options["changeAddress"].get_str());
@@ -2982,6 +3006,12 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("feeRate"))
{
+ if (options.exists("conf_target")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
+ }
+ if (options.exists("estimate_mode")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
+ }
coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
coinControl.fOverrideFeeRate = true;
}
@@ -2992,20 +3022,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("replaceable")) {
coinControl.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
- if (options.exists("conf_target")) {
- if (options.exists("feeRate")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
- }
- coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"], pwallet->chain().estimateMaxBlocks());
- }
- if (options.exists("estimate_mode")) {
- if (options.exists("feeRate")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
- }
- if (!FeeModeFromString(options["estimate_mode"].get_str(), coinControl.m_fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
- }
- }
+ SetFeeEstimateMode(pwallet, coinControl, options["estimate_mode"], options["conf_target"]);
}
} else {
// if options is null and not a bool
@@ -3039,8 +3056,8 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
static UniValue fundrawtransaction(const JSONRPCRequest& request)
{
RPCHelpMan{"fundrawtransaction",
- "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
- "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
+ "\nIf the transaction has no inputs, they will be automatically selected to meet its out value.\n"
+ "It will add at most one change output to the outputs.\n"
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
"The inputs added will not be signed, use signrawtransactionwithkey\n"
@@ -3054,6 +3071,7 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}",
{
+ {"add_inputs", RPCArg::Type::BOOL, /* default */ "true", "For a transaction with existing inputs, automatically include more if they are not enough."},
{"changeAddress", RPCArg::Type::STR, /* default */ "pool address", "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
{"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
@@ -3072,11 +3090,9 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
},
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees"},
- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
- " \"UNSET\"\n"
- " \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\""},
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/kB or " + CURRENCY_ATOM + "/B estimate modes)"},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
"options"},
{"iswitness", RPCArg::Type::BOOL, /* default */ "depends on heuristic tests", "Whether the transaction hex is a serialized witness transaction.\n"
@@ -3123,7 +3139,10 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request)
CAmount fee;
int change_position;
- FundTransaction(pwallet, tx, fee, change_position, request.params[1]);
+ CCoinControl coin_control;
+ // Automatically select (additional) coins. Can be overriden by options.add_inputs.
+ coin_control.m_add_inputs = true;
+ FundTransaction(pwallet, tx, fee, change_position, request.params[1], coin_control);
UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(tx)));
@@ -3241,8 +3260,8 @@ static UniValue bumpfee(const JSONRPCRequest& request)
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
- {"confTarget", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
- {"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'confTarget'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + " per kB\n"
+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
+ {"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'conf_target'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + " per kB\n"
" Specify a fee rate instead of relying on the built-in fee estimator.\n"
"Must be at least 0.0001 " + CURRENCY_UNIT + " per kB higher than the current transaction fee rate.\n"},
{"replaceable", RPCArg::Type::BOOL, /* default */ "true", "Whether the new transaction should still be\n"
@@ -3252,10 +3271,8 @@ static UniValue bumpfee(const JSONRPCRequest& request)
" so the new transaction will not be explicitly bip-125 replaceable (though it may\n"
" still be replaceable in practice, for example if it has unconfirmed ancestors which\n"
" are replaceable)."},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
- " \"UNSET\"\n"
- " \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\""},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
"options"},
},
@@ -3294,15 +3311,24 @@ static UniValue bumpfee(const JSONRPCRequest& request)
RPCTypeCheckObj(options,
{
{"confTarget", UniValueType(UniValue::VNUM)},
+ {"conf_target", UniValueType(UniValue::VNUM)},
{"fee_rate", UniValueType(UniValue::VNUM)},
{"replaceable", UniValueType(UniValue::VBOOL)},
{"estimate_mode", UniValueType(UniValue::VSTR)},
},
true, true);
- if (options.exists("confTarget") && options.exists("fee_rate")) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget can't be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
- } else if (options.exists("confTarget")) { // TODO: alias this to conf_target
- coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"], pwallet->chain().estimateMaxBlocks());
+
+ if (options.exists("confTarget") && options.exists("conf_target")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and conf_target options should not both be set. Use conf_target (confTarget is deprecated).");
+ }
+
+ auto conf_target = options.exists("confTarget") ? options["confTarget"] : options["conf_target"];
+
+ if (!conf_target.isNull()) {
+ if (options.exists("fee_rate")) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "conf_target can't be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
+ }
+ coin_control.m_confirm_target = ParseConfirmTarget(conf_target, pwallet->chain().estimateMaxBlocks());
} else if (options.exists("fee_rate")) {
CFeeRate fee_rate(AmountFromValue(options["fee_rate"]));
if (fee_rate <= CFeeRate(0)) {
@@ -3314,11 +3340,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (options.exists("replaceable")) {
coin_control.m_signal_bip125_rbf = options["replaceable"].get_bool();
}
- if (options.exists("estimate_mode")) {
- if (!FeeModeFromString(options["estimate_mode"].get_str(), coin_control.m_fee_mode)) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
- }
- }
+ SetFeeEstimateMode(pwallet, coin_control, options["estimate_mode"], conf_target);
}
// Make sure the results are valid at least up to the most recent block
@@ -3481,9 +3503,9 @@ public:
{
// Always present: script type and redeemscript
std::vector<std::vector<unsigned char>> solutions_data;
- txnouttype which_type = Solver(subscript, solutions_data);
+ TxoutType which_type = Solver(subscript, solutions_data);
obj.pushKV("script", GetTxnOutputType(which_type));
- obj.pushKV("hex", HexStr(subscript.begin(), subscript.end()));
+ obj.pushKV("hex", HexStr(subscript));
CTxDestination embedded;
if (ExtractDestination(subscript, embedded)) {
@@ -3494,18 +3516,18 @@ public:
UniValue wallet_detail = boost::apply_visitor(*this, embedded);
subobj.pushKVs(wallet_detail);
subobj.pushKV("address", EncodeDestination(embedded));
- subobj.pushKV("scriptPubKey", HexStr(subscript.begin(), subscript.end()));
+ subobj.pushKV("scriptPubKey", HexStr(subscript));
// Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
obj.pushKV("embedded", std::move(subobj));
- } else if (which_type == TX_MULTISIG) {
+ } else if (which_type == TxoutType::MULTISIG) {
// Also report some information on multisig scripts (which do not have a corresponding address).
// TODO: abstract out the common functionality between this logic and ExtractDestinations.
obj.pushKV("sigsrequired", solutions_data[0][0]);
UniValue pubkeys(UniValue::VARR);
for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
- pubkeys.push_back(HexStr(key.begin(), key.end()));
+ pubkeys.push_back(HexStr(key));
}
obj.pushKV("pubkeys", std::move(pubkeys));
}
@@ -3517,7 +3539,7 @@ public:
UniValue operator()(const PKHash& pkhash) const
{
- CKeyID keyID(pkhash);
+ CKeyID keyID{ToKeyID(pkhash)};
UniValue obj(UniValue::VOBJ);
CPubKey vchPubKey;
if (provider && provider->GetPubKey(keyID, vchPubKey)) {
@@ -3542,7 +3564,7 @@ public:
{
UniValue obj(UniValue::VOBJ);
CPubKey pubkey;
- if (provider && provider->GetPubKey(CKeyID(id), pubkey)) {
+ if (provider && provider->GetPubKey(ToKeyID(id), pubkey)) {
obj.pushKV("pubkey", HexStr(pubkey));
}
return obj;
@@ -3623,12 +3645,10 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
{RPCResult::Type::STR_HEX, "pubkey", /* optional */ true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
{RPCResult::Type::OBJ, "embedded", /* optional */ true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
{
- {RPCResult::Type::ELISION, "", "Includes all\n"
- " getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n"
- "hdseedid) and relation to the wallet (ismine, iswatchonly)."},
+ {RPCResult::Type::ELISION, "", "Includes all getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath, hdseedid)\n"
+ "and relation to the wallet (ismine, iswatchonly)."},
}},
{RPCResult::Type::BOOL, "iscompressed", /* optional */ true, "If the pubkey is compressed."},
- {RPCResult::Type::STR, "label", "DEPRECATED. The label associated with the address. Defaults to \"\". Replaced by the labels array below."},
{RPCResult::Type::NUM_TIME, "timestamp", /* optional */ true, "The creation time of the key, if available, expressed in " + UNIX_EPOCH_TIME + "."},
{RPCResult::Type::STR, "hdkeypath", /* optional */ true, "The HD keypath, if the key is HD and available."},
{RPCResult::Type::STR_HEX, "hdseedid", /* optional */ true, "The Hash160 of the HD seed."},
@@ -3636,12 +3656,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
{RPCResult::Type::ARR, "labels", "Array of labels associated with the address. Currently limited to one label but returned\n"
"as an array to keep the API stable if multiple labels are enabled in the future.",
{
- {RPCResult::Type::STR, "label name", "The label name. Defaults to \"\"."},
- {RPCResult::Type::OBJ, "", "label data, DEPRECATED, will be removed in 0.21. To re-enable, launch bitcoind with `-deprecatedrpc=labelspurpose`",
- {
- {RPCResult::Type::STR, "name", "The label name. Defaults to \"\"."},
- {RPCResult::Type::STR, "purpose", "The purpose of the associated address (send or receive)."},
- }},
+ {RPCResult::Type::STR, "label name", "Label name (defaults to \"\")."},
}},
}
},
@@ -3668,7 +3683,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
ret.pushKV("address", currentAddress);
CScript scriptPubKey = GetScriptForDestination(dest);
- ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
+ ret.pushKV("scriptPubKey", HexStr(scriptPubKey));
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
@@ -3687,14 +3702,6 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
- // DEPRECATED: Return label field if existing. Currently only one label can
- // be associated with an address, so the label should be equivalent to the
- // value of the name key/value pair in the labels array below.
- const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
- if (pwallet->chain().rpcEnableDeprecated("label") && address_book_entry) {
- ret.pushKV("label", address_book_entry->GetLabel());
- }
-
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
@@ -3715,14 +3722,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// stable if we allow multiple labels to be associated with an address in
// the future.
UniValue labels(UniValue::VARR);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
if (address_book_entry) {
- // DEPRECATED: The previous behavior of returning an array containing a
- // JSON object of `name` and `purpose` key/value pairs is deprecated.
- if (pwallet->chain().rpcEnableDeprecated("labelspurpose")) {
- labels.push_back(AddressBookDataToJSON(*address_book_entry, true));
- } else {
- labels.push_back(address_book_entry->GetLabel());
- }
+ labels.push_back(address_book_entry->GetLabel());
}
ret.pushKV("labels", std::move(labels));
@@ -3976,16 +3978,16 @@ UniValue walletprocesspsbt(const JSONRPCRequest& request)
UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{
RPCHelpMan{"walletcreatefundedpsbt",
- "\nCreates and funds a transaction in the Partially Signed Transaction format. Inputs will be added if supplied inputs are not enough\n"
+ "\nCreates and funds a transaction in the Partially Signed Transaction format.\n"
"Implements the Creator and Updater roles.\n",
{
- {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs",
+ {"inputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "The inputs. Leave empty to add inputs automatically. See add_inputs option.",
{
{"", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED, "",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
{"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The output number"},
- {"sequence", RPCArg::Type::NUM, RPCArg::Optional::NO, "The sequence number"},
+ {"sequence", RPCArg::Type::NUM, /* default */ "depends on the value of the 'locktime' and 'options.replaceable' arguments", "The sequence number"},
},
},
},
@@ -4010,6 +4012,7 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"locktime", RPCArg::Type::NUM, /* default */ "0", "Raw locktime. Non-0 value also locktime-activates inputs"},
{"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
{
+ {"add_inputs", RPCArg::Type::BOOL, /* default */ "false", "If inputs are specified, automatically include more if they are not enough."},
{"changeAddress", RPCArg::Type::STR_HEX, /* default */ "pool address", "The bitcoin address to receive the change"},
{"changePosition", RPCArg::Type::NUM, /* default */ "random", "The index of the change output"},
{"change_type", RPCArg::Type::STR, /* default */ "set by -changetype", "The output type to use. Only valid if changeAddress is not specified. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\"."},
@@ -4027,10 +4030,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
{"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees"},
{"conf_target", RPCArg::Type::NUM, /* default */ "fall back to wallet's confirmation target (txconfirmtarget)", "Confirmation target (in blocks)"},
- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
- " \"UNSET\"\n"
- " \"ECONOMICAL\"\n"
- " \"CONSERVATIVE\""},
+ {"estimate_mode", RPCArg::Type::STR, /* default */ "unset", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
+ " \"" + FeeModes("\"\n\"") + "\""},
},
"options"},
{"bip32derivs", RPCArg::Type::BOOL, /* default */ "true", "Include BIP 32 derivation paths for public keys if we know them"},
@@ -4071,7 +4072,11 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
rbf = replaceable_arg.isTrue();
}
CMutableTransaction rawTx = ConstructTransaction(request.params[0], request.params[1], request.params[2], rbf);
- FundTransaction(pwallet, rawTx, fee, change_position, request.params[3]);
+ CCoinControl coin_control;
+ // Automatically select coins, unless at least one is manually selected. Can
+ // be overriden by options.add_inputs.
+ coin_control.m_add_inputs = rawTx.vin.size() == 0;
+ FundTransaction(pwallet, rawTx, fee, change_position, request.params[3], coin_control);
// Make a blank psbt
PartiallySignedTransaction psbtx(rawTx);
diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp
index 8a2a798644..d7c50a9d2a 100644
--- a/src/wallet/scriptpubkeyman.cpp
+++ b/src/wallet/scriptpubkeyman.cpp
@@ -88,16 +88,16 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
IsMineResult ret = IsMineResult::NO;
std::vector<valtype> vSolutions;
- txnouttype whichType = Solver(scriptPubKey, vSolutions);
+ TxoutType whichType = Solver(scriptPubKey, vSolutions);
CKeyID keyID;
switch (whichType)
{
- case TX_NONSTANDARD:
- case TX_NULL_DATA:
- case TX_WITNESS_UNKNOWN:
+ case TxoutType::NONSTANDARD:
+ case TxoutType::NULL_DATA:
+ case TxoutType::WITNESS_UNKNOWN:
break;
- case TX_PUBKEY:
+ case TxoutType::PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID();
if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) {
return IsMineResult::INVALID;
@@ -106,7 +106,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
ret = std::max(ret, IsMineResult::SPENDABLE);
}
break;
- case TX_WITNESS_V0_KEYHASH:
+ case TxoutType::WITNESS_V0_KEYHASH:
{
if (sigversion == IsMineSigVersion::WITNESS_V0) {
// P2WPKH inside P2WSH is invalid.
@@ -121,7 +121,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(PKHash(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0));
break;
}
- case TX_PUBKEYHASH:
+ case TxoutType::PUBKEYHASH:
keyID = CKeyID(uint160(vSolutions[0]));
if (!PermitsUncompressed(sigversion)) {
CPubKey pubkey;
@@ -133,7 +133,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
ret = std::max(ret, IsMineResult::SPENDABLE);
}
break;
- case TX_SCRIPTHASH:
+ case TxoutType::SCRIPTHASH:
{
if (sigversion != IsMineSigVersion::TOP) {
// P2SH inside P2WSH or P2SH is invalid.
@@ -146,7 +146,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
}
break;
}
- case TX_WITNESS_V0_SCRIPTHASH:
+ case TxoutType::WITNESS_V0_SCRIPTHASH:
{
if (sigversion == IsMineSigVersion::WITNESS_V0) {
// P2WSH inside P2WSH is invalid.
@@ -165,7 +165,7 @@ IsMineResult IsMineInner(const LegacyScriptPubKeyMan& keystore, const CScript& s
break;
}
- case TX_MULTISIG:
+ case TxoutType::MULTISIG:
{
// Never treat bare multisig outputs as ours (they can still be made watchonly-though)
if (sigversion == IsMineSigVersion::TOP) {
@@ -573,9 +573,8 @@ bool LegacyScriptPubKeyMan::SignTransaction(CMutableTransaction& tx, const std::
SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const
{
- CKeyID key_id(pkhash);
CKey key;
- if (!GetKey(key_id, key)) {
+ if (!GetKey(ToKeyID(pkhash), key)) {
return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
@@ -585,8 +584,11 @@ SigningResult LegacyScriptPubKeyMan::SignMessage(const std::string& message, con
return SigningResult::SIGNING_FAILED;
}
-TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
+TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
+ if (n_signed) {
+ *n_signed = 0;
+ }
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
@@ -617,6 +619,14 @@ TransactionError LegacyScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psb
SignatureData sigdata;
input.FillSignatureData(sigdata);
SignPSBTInput(HidingSigningProvider(this, !sign, !bip32derivs), psbtx, i, sighash_type);
+
+ bool signed_one = PSBTInputSigned(input);
+ if (n_signed && (signed_one || !sign)) {
+ // If sign is false, we assume that we _could_ sign if we get here. This
+ // will never have false negatives; it is hard to tell under what i
+ // circumstances it could have false positives.
+ (*n_signed)++;
+ }
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
@@ -826,7 +836,7 @@ bool LegacyScriptPubKeyMan::HaveWatchOnly() const
static bool ExtractPubKey(const CScript &dest, CPubKey& pubKeyOut)
{
std::vector<std::vector<unsigned char>> solutions;
- return Solver(dest, solutions) == TX_PUBKEY &&
+ return Solver(dest, solutions) == TxoutType::PUBKEY &&
(pubKeyOut = CPubKey(solutions[0])).IsFullyValid();
}
@@ -2052,9 +2062,8 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
- CKeyID key_id(pkhash);
CKey key;
- if (!keys->GetKey(key_id, key)) {
+ if (!keys->GetKey(ToKeyID(pkhash), key)) {
return SigningResult::PRIVATE_KEY_NOT_AVAILABLE;
}
@@ -2064,8 +2073,11 @@ SigningResult DescriptorScriptPubKeyMan::SignMessage(const std::string& message,
return SigningResult::OK;
}
-TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs) const
+TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction& psbtx, int sighash_type, bool sign, bool bip32derivs, int* n_signed) const
{
+ if (n_signed) {
+ *n_signed = 0;
+ }
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
const CTxIn& txin = psbtx.tx->vin[i];
PSBTInput& input = psbtx.inputs.at(i);
@@ -2117,6 +2129,14 @@ TransactionError DescriptorScriptPubKeyMan::FillPSBT(PartiallySignedTransaction&
}
SignPSBTInput(HidingSigningProvider(keys.get(), !sign, !bip32derivs), psbtx, i, sighash_type);
+
+ bool signed_one = PSBTInputSigned(input);
+ if (n_signed && (signed_one || !sign)) {
+ // If sign is false, we assume that we _could_ sign if we get here. This
+ // will never have false negatives; it is hard to tell under what i
+ // circumstances it could have false positives.
+ (*n_signed)++;
+ }
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
diff --git a/src/wallet/scriptpubkeyman.h b/src/wallet/scriptpubkeyman.h
index d62d30f339..9fa2a68284 100644
--- a/src/wallet/scriptpubkeyman.h
+++ b/src/wallet/scriptpubkeyman.h
@@ -234,7 +234,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, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const { return TransactionError::INVALID_PSBT; }
+ virtual TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const { return TransactionError::INVALID_PSBT; }
virtual uint256 GetID() const { return uint256(); }
@@ -393,7 +393,7 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
@@ -596,7 +596,7 @@ public:
bool SignTransaction(CMutableTransaction& tx, const std::map<COutPoint, Coin>& coins, int sighash, std::map<int, std::string>& input_errors) const override;
SigningResult SignMessage(const std::string& message, const PKHash& pkhash, std::string& str_sig) const override;
- TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false) const override;
+ TransactionError FillPSBT(PartiallySignedTransaction& psbt, int sighash_type = 1 /* SIGHASH_ALL */, bool sign = true, bool bip32derivs = false, int* n_signed = nullptr) const override;
uint256 GetID() const override;
diff --git a/src/wallet/test/ismine_tests.cpp b/src/wallet/test/ismine_tests.cpp
index cdb0522920..e416f16044 100644
--- a/src/wallet/test/ismine_tests.cpp
+++ b/src/wallet/test/ismine_tests.cpp
@@ -167,7 +167,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
keystore.SetupLegacyScriptPubKeyMan();
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
- CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0])));
+ CScript witnessscript = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
scriptPubKey = GetScriptForDestination(WitnessV0ScriptHash(witnessscript));
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(witnessscript));
@@ -202,7 +202,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(keys[0]));
- scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(pubkeys[0])));
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(pubkeys[0]));
// Keystore implicitly has key and P2SH redeemScript
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddCScript(scriptPubKey));
@@ -217,7 +217,7 @@ BOOST_AUTO_TEST_CASE(ismine_standard)
LOCK(keystore.GetLegacyScriptPubKeyMan()->cs_KeyStore);
BOOST_CHECK(keystore.GetLegacyScriptPubKeyMan()->AddKey(uncompressedKey));
- scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(PKHash(uncompressedPubkey)));
+ scriptPubKey = GetScriptForDestination(WitnessV0KeyHash(uncompressedPubkey));
// Keystore has key, but no P2SH redeemScript
result = keystore.GetLegacyScriptPubKeyMan()->IsMine(scriptPubKey);
diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp
index b4c65a8665..3f85a48ff3 100644
--- a/src/wallet/test/psbt_wallet_tests.cpp
+++ b/src/wallet/test/psbt_wallet_tests.cpp
@@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test)
// Get the final tx
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << psbtx;
- std::string final_hex = HexStr(ssTx.begin(), ssTx.end());
+ std::string final_hex = HexStr(ssTx);
BOOST_CHECK_EQUAL(final_hex, "70736274ff01009a020000000258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd750000000000ffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d0100000000ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f00000000000100bb0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f6187650000000104475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752ae2206029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f10d90c6a4f000000800000008000000080220602dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d710d90c6a4f0000008000000080010000800001012000c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88701042200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903010547522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae2206023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7310d90c6a4f000000800000008003000080220603089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc10d90c6a4f00000080000000800200008000220203a9a4c37f5996d3aa25dbac6b570af0650394492942460b354753ed9eeca5877110d90c6a4f000000800000008004000080002202027f6399757d2eff55a136ad02c684b1838b6556e5f1b6b34282a94b6b5005109610d90c6a4f00000080000000800500008000");
// Mutate the transaction so that one of the inputs is invalid
diff --git a/src/wallet/test/wallet_crypto_tests.cpp b/src/wallet/test/wallet_crypto_tests.cpp
index 97f8c94fa6..10ddfa22ef 100644
--- a/src/wallet/test/wallet_crypto_tests.cpp
+++ b/src/wallet/test/wallet_crypto_tests.cpp
@@ -24,10 +24,10 @@ static void TestPassphraseSingle(const std::vector<unsigned char>& vchSalt, cons
if(!correctKey.empty())
BOOST_CHECK_MESSAGE(memcmp(crypt.vchKey.data(), correctKey.data(), crypt.vchKey.size()) == 0, \
- HexStr(crypt.vchKey.begin(), crypt.vchKey.end()) + std::string(" != ") + HexStr(correctKey.begin(), correctKey.end()));
+ HexStr(crypt.vchKey) + std::string(" != ") + HexStr(correctKey));
if(!correctIV.empty())
BOOST_CHECK_MESSAGE(memcmp(crypt.vchIV.data(), correctIV.data(), crypt.vchIV.size()) == 0,
- HexStr(crypt.vchIV.begin(), crypt.vchIV.end()) + std::string(" != ") + HexStr(correctIV.begin(), correctIV.end()));
+ HexStr(crypt.vchIV) + std::string(" != ") + HexStr(correctIV));
}
static void TestPassphrase(const std::vector<unsigned char>& vchSalt, const SecureString& passphrase, uint32_t rounds,
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 4037e23b69..57eec9baf9 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2160,6 +2160,11 @@ void CWallet::AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe, const
}
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
+ // Only consider selected coins if add_inputs is false
+ if (coinControl && !coinControl->m_add_inputs && !coinControl->IsSelected(COutPoint(entry.first, i))) {
+ continue;
+ }
+
if (wtx.tx->vout[i].nValue < nMinimumAmount || wtx.tx->vout[i].nValue > nMaximumAmount)
continue;
@@ -2471,8 +2476,11 @@ 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) const
+TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& complete, int sighash_type, bool sign, bool bip32derivs, size_t * n_signed) const
{
+ if (n_signed) {
+ *n_signed = 0;
+ }
LOCK(cs_wallet);
// Get all of the previous transactions
for (unsigned int i = 0; i < psbtx.tx->vin.size(); ++i) {
@@ -2503,10 +2511,15 @@ TransactionError CWallet::FillPSBT(PartiallySignedTransaction& psbtx, bool& comp
// Fill in information from ScriptPubKeyMans
for (ScriptPubKeyMan* spk_man : GetAllScriptPubKeyMans()) {
- TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs);
+ int n_signed_this_spkm = 0;
+ TransactionError res = spk_man->FillPSBT(psbtx, sighash_type, sign, bip32derivs, &n_signed_this_spkm);
if (res != TransactionError::OK) {
return res;
}
+
+ if (n_signed) {
+ (*n_signed) += n_signed_this_spkm;
+ }
}
// Complete if every input is now signed
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index cf000b0b70..9931671fb4 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -964,7 +964,8 @@ public:
bool& complete,
int sighash_type = 1 /* SIGHASH_ALL */,
bool sign = true,
- bool bip32derivs = true) const;
+ bool bip32derivs = true,
+ size_t* n_signed = nullptr) const;
/**
* Create a new transaction paying the recipients with a set of coins
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index 6f38398076..603887ee58 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -439,10 +439,11 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
// Extract some CHDChain info from this metadata if it has any
if (keyMeta.nVersion >= CKeyMetadata::VERSION_WITH_HDDATA && !keyMeta.hd_seed_id.IsNull() && keyMeta.hdKeypath.size() > 0) {
// Get the path from the key origin or from the path string
- // Not applicable when path is "s" as that indicates a seed
+ // Not applicable when path is "s" or "m" as those indicate a seed
+ // See https://github.com/bitcoin/bitcoin/pull/12924
bool internal = false;
uint32_t index = 0;
- if (keyMeta.hdKeypath != "s") {
+ if (keyMeta.hdKeypath != "s" && keyMeta.hdKeypath != "m") {
std::vector<uint32_t> path;
if (keyMeta.has_key_origin) {
// We have a key origin, so pull it from its path vector