aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am12
-rw-r--r--src/Makefile.bench.include7
-rw-r--r--src/Makefile.qt.include2
-rw-r--r--src/Makefile.qttest.include14
-rw-r--r--src/Makefile.test.include23
-rw-r--r--src/Makefile.test_util.include31
-rw-r--r--src/init.cpp12
-rw-r--r--src/interfaces/wallet.cpp29
-rw-r--r--src/interfaces/wallet.h12
-rw-r--r--src/key.cpp20
-rw-r--r--src/key.h10
-rw-r--r--src/miner.cpp2
-rw-r--r--src/node/context.h7
-rw-r--r--src/psbt.h2
-rw-r--r--src/pubkey.cpp18
-rw-r--r--src/pubkey.h22
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/forms/receivecoinsdialog.ui4
-rw-r--r--src/qt/guiutil.cpp2
-rw-r--r--src/qt/openuridialog.cpp2
-rw-r--r--src/qt/paymentserver.h6
-rw-r--r--src/qt/receiverequestdialog.cpp1
-rw-r--r--src/qt/receiverequestdialog.h4
-rw-r--r--src/qt/recentrequeststablemodel.cpp1
-rw-r--r--src/qt/recentrequeststablemodel.h4
-rw-r--r--src/qt/sendcoinsdialog.cpp76
-rw-r--r--src/qt/sendcoinsdialog.h3
-rw-r--r--src/qt/sendcoinsentry.cpp1
-rw-r--r--src/qt/sendcoinsentry.h6
-rw-r--r--src/qt/sendcoinsrecipient.h74
-rw-r--r--src/qt/signverifymessagedialog.cpp2
-rw-r--r--src/qt/test/rpcnestedtests.cpp1
-rw-r--r--src/qt/transactiondesc.cpp5
-rw-r--r--src/qt/walletcontroller.cpp7
-rw-r--r--src/qt/walletcontroller.h4
-rw-r--r--src/qt/walletmodel.cpp2
-rw-r--r--src/qt/walletmodel.h58
-rw-r--r--src/qt/walletmodeltransaction.h3
-rw-r--r--src/random.cpp47
-rw-r--r--src/random.h1
-rw-r--r--src/randomenv.cpp1
-rw-r--r--src/rpc/blockchain.cpp10
-rw-r--r--src/rpc/blockchain.h2
-rw-r--r--src/rpc/mining.cpp5
-rw-r--r--src/rpc/protocol.h3
-rw-r--r--src/rpc/rawtransaction.cpp4
-rw-r--r--src/rpc/rawtransaction_util.cpp8
-rw-r--r--src/rpc/rawtransaction_util.h4
-rw-r--r--src/script/descriptor.cpp4
-rw-r--r--src/script/interpreter.cpp8
-rw-r--r--src/script/sign.h2
-rw-r--r--src/script/standard.cpp8
-rw-r--r--src/support/lockedpool.cpp8
-rw-r--r--src/test/miner_tests.cpp98
-rw-r--r--src/test/txvalidation_tests.cpp6
-rw-r--r--src/test/txvalidationcache_tests.cpp26
-rw-r--r--src/test/util/setup_common.cpp4
-rw-r--r--src/test/validation_block_tests.cpp12
-rw-r--r--src/txmempool.cpp8
-rw-r--r--src/validation.cpp3
-rw-r--r--src/validation.h1
-rw-r--r--src/validationinterface.cpp31
-rw-r--r--src/validationinterface.h9
-rw-r--r--src/wallet/init.cpp2
-rw-r--r--src/wallet/psbtwallet.cpp27
-rw-r--r--src/wallet/rpcwallet.cpp94
-rw-r--r--src/wallet/test/coinselector_tests.cpp52
-rw-r--r--src/wallet/wallet.cpp116
-rw-r--r--src/wallet/wallet.h23
69 files changed, 651 insertions, 467 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index cbe5479956..27c87688b4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -19,7 +19,7 @@ else
LIBUNIVALUE = $(UNIVALUE_LIBS)
endif
-BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS) $(CRYPTO_CFLAGS)
+BITCOIN_INCLUDES=-I$(builddir) $(BDB_CPPFLAGS) $(BOOST_CPPFLAGS) $(LEVELDB_CPPFLAGS)
BITCOIN_INCLUDES += -I$(srcdir)/secp256k1/include
BITCOIN_INCLUDES += $(UNIVALUE_CFLAGS)
@@ -571,7 +571,7 @@ bitcoind_LDADD = \
$(LIBMEMENV) \
$(LIBSECP256K1)
-bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
+bitcoind_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(ZMQ_LIBS)
# bitcoin-cli binary #
bitcoin_cli_SOURCES = bitcoin-cli.cpp
@@ -589,7 +589,7 @@ bitcoin_cli_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO)
-bitcoin_cli_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS) $(EVENT_LIBS)
+bitcoin_cli_LDADD += $(BOOST_LIBS) $(EVENT_LIBS)
#
# bitcoin-tx binary #
@@ -610,7 +610,7 @@ bitcoin_tx_LDADD = \
$(LIBBITCOIN_CRYPTO) \
$(LIBSECP256K1)
-bitcoin_tx_LDADD += $(BOOST_LIBS) $(CRYPTO_LIBS)
+bitcoin_tx_LDADD += $(BOOST_LIBS)
#
# bitcoin-wallet binary #
@@ -637,7 +637,7 @@ bitcoin_wallet_LDADD = \
$(LIBSECP256K1) \
$(LIBUNIVALUE)
-bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS)
+bitcoin_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS)
#
# bitcoinconsensus library #
@@ -717,6 +717,8 @@ if EMBEDDED_LEVELDB
include Makefile.leveldb.include
endif
+include Makefile.test_util.include
+
if ENABLE_TESTS
include Makefile.test.include
endif
diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include
index c9e4fcc4bc..9e70db116b 100644
--- a/src/Makefile.bench.include
+++ b/src/Makefile.bench.include
@@ -40,10 +40,6 @@ bench_bench_bitcoin_SOURCES = \
bench/lockedpool.cpp \
bench/poly1305.cpp \
bench/prevector.cpp \
- test/util/transaction_utils.h \
- test/util/transaction_utils.cpp \
- test/util/setup_common.h \
- test/util/setup_common.cpp \
test/util.h \
test/util.cpp
@@ -59,6 +55,7 @@ bench_bench_bitcoin_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
+ $(LIBTEST_UTIL) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
@@ -76,7 +73,7 @@ bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
bench_bench_bitcoin_SOURCES += bench/wallet_balance.cpp
endif
-bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
+bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 13b1470b58..cf09eee2cb 100644
--- a/src/Makefile.qt.include
+++ b/src/Makefile.qt.include
@@ -136,6 +136,7 @@ BITCOIN_QT_H = \
qt/rpcconsole.h \
qt/sendcoinsdialog.h \
qt/sendcoinsentry.h \
+ qt/sendcoinsrecipient.h \
qt/signverifymessagedialog.h \
qt/splashscreen.h \
qt/trafficgraphwidget.h \
@@ -314,7 +315,6 @@ endif
qt_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) \
$(BOOST_LIBS) $(QT_LIBS) $(QT_DBUS_LIBS) $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
-qt_bitcoin_qt_LDADD += $(CRYPTO_LIBS)
qt_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_bitcoin_qt_LIBTOOLFLAGS = $(AM_LIBTOOLFLAGS) --tag CXX
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 562b393b22..8c47fabad9 100644
--- a/src/Makefile.qttest.include
+++ b/src/Makefile.qttest.include
@@ -26,12 +26,6 @@ TEST_QT_H = \
qt/test/util.h \
qt/test/wallettests.h
-TEST_BITCOIN_CPP = \
- test/util/setup_common.cpp
-
-TEST_BITCOIN_H = \
- test/util/setup_common.h
-
qt_test_test_bitcoin_qt_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BITCOIN_QT_INCLUDES) \
$(QT_INCLUDES) $(QT_TEST_INCLUDES)
@@ -42,9 +36,7 @@ qt_test_test_bitcoin_qt_SOURCES = \
qt/test/test_main.cpp \
qt/test/uritests.cpp \
qt/test/util.cpp \
- $(TEST_QT_H) \
- $(TEST_BITCOIN_CPP) \
- $(TEST_BITCOIN_H)
+ $(TEST_QT_H)
if ENABLE_WALLET
qt_test_test_bitcoin_qt_SOURCES += \
qt/test/addressbooktests.cpp \
@@ -54,7 +46,7 @@ endif # ENABLE_WALLET
nodist_qt_test_test_bitcoin_qt_SOURCES = $(TEST_QT_MOC_CPP)
-qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER)
+qt_test_test_bitcoin_qt_LDADD = $(LIBBITCOINQT) $(LIBBITCOIN_SERVER) $(LIBTEST_UTIL)
if ENABLE_WALLET
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_UTIL) $(LIBBITCOIN_WALLET)
endif
@@ -63,7 +55,7 @@ qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_ZMQ) $(ZMQ_LIBS)
endif
qt_test_test_bitcoin_qt_LDADD += $(LIBBITCOIN_CLI) $(LIBBITCOIN_COMMON) $(LIBBITCOIN_UTIL) $(LIBBITCOIN_CONSENSUS) $(LIBBITCOIN_CRYPTO) $(LIBUNIVALUE) $(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(QT_DBUS_LIBS) $(QT_TEST_LIBS) $(QT_LIBS) \
- $(QR_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
+ $(QR_LIBS) $(BDB_LIBS) $(MINIUPNPC_LIBS) $(LIBSECP256K1) \
$(EVENT_PTHREADS_LIBS) $(EVENT_LIBS)
qt_test_test_bitcoin_qt_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(QT_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
qt_test_test_bitcoin_qt_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index c5353c6554..b4ca01b1fe 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -58,31 +58,19 @@ RAW_TEST_FILES =
GENERATED_TEST_FILES = $(JSON_TEST_FILES:.json=.json.h) $(RAW_TEST_FILES:.raw=.raw.h)
BITCOIN_TEST_SUITE = \
- test/util/blockfilter.cpp \
- test/util/blockfilter.h \
- test/util/logging.cpp \
- test/util/logging.h \
- test/util/transaction_utils.cpp \
- test/util/transaction_utils.h \
test/main.cpp \
- test/util/setup_common.h \
- test/util/setup_common.cpp \
- test/util/str.h \
- test/util/str.cpp
+ $(TEST_UTIL_H)
FUZZ_SUITE = \
test/fuzz/fuzz.cpp \
test/fuzz/fuzz.h \
- test/fuzz/FuzzedDataProvider.h \
- test/util/setup_common.cpp \
- test/util/setup_common.h \
- test/util/str.cpp \
- test/util/str.h
+ test/fuzz/FuzzedDataProvider.h
FUZZ_SUITE_LD_COMMON = \
$(LIBBITCOIN_SERVER) \
$(LIBBITCOIN_COMMON) \
$(LIBBITCOIN_UTIL) \
+ $(LIBTEST_UTIL) \
$(LIBBITCOIN_CONSENSUS) \
$(LIBBITCOIN_CRYPTO) \
$(LIBUNIVALUE) \
@@ -92,7 +80,6 @@ FUZZ_SUITE_LD_COMMON = \
$(LIBMEMENV) \
$(LIBSECP256K1) \
$(EVENT_LIBS) \
- $(CRYPTO_LIBS) \
$(EVENT_PTHREADS_LIBS)
# test_bitcoin binary #
@@ -199,7 +186,7 @@ endif
test_test_bitcoin_SOURCES = $(BITCOIN_TEST_SUITE) $(BITCOIN_TESTS) $(JSON_TEST_FILES) $(RAW_TEST_FILES)
test_test_bitcoin_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(TESTDEFS) $(EVENT_CFLAGS)
-test_test_bitcoin_LDADD =
+test_test_bitcoin_LDADD = $(LIBTEST_UTIL)
if ENABLE_WALLET
test_test_bitcoin_LDADD += $(LIBBITCOIN_WALLET)
endif
@@ -208,7 +195,7 @@ test_test_bitcoin_LDADD += $(LIBBITCOIN_SERVER) $(LIBBITCOIN_CLI) $(LIBBITCOIN_C
$(LIBLEVELDB) $(LIBLEVELDB_SSE42) $(LIBMEMENV) $(BOOST_LIBS) $(BOOST_UNIT_TEST_FRAMEWORK_LIB) $(LIBSECP256K1) $(EVENT_LIBS) $(EVENT_PTHREADS_LIBS)
test_test_bitcoin_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
-test_test_bitcoin_LDADD += $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
+test_test_bitcoin_LDADD += $(BDB_LIBS) $(MINIUPNPC_LIBS) $(RAPIDCHECK_LIBS)
test_test_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) -static
if ENABLE_ZMQ
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
new file mode 100644
index 0000000000..cf55d141b0
--- /dev/null
+++ b/src/Makefile.test_util.include
@@ -0,0 +1,31 @@
+# Copyright (c) 2013-2019 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+LIBTEST_UTIL=libtest_util.a
+
+EXTRA_LIBRARIES += \
+ $(LIBTEST_UTIL)
+
+TEST_UTIL_H = \
+ test/util/blockfilter.h \
+ test/util/logging.h \
+ test/util/setup_common.h \
+ test/util/str.h \
+ test/util/transaction_utils.h
+
+libtest_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(MINIUPNPC_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS)
+libtest_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+libtest_util_a_SOURCES = \
+ test/util/blockfilter.cpp \
+ test/util/logging.cpp \
+ test/util/setup_common.cpp \
+ test/util/str.cpp \
+ test/util/transaction_utils.cpp \
+ $(TEST_UTIL_H)
+
+LIBTEST_UTIL += $(LIBBITCOIN_SERVER)
+LIBTEST_UTIL += $(LIBBITCOIN_COMMON)
+LIBTEST_UTIL += $(LIBBITCOIN_UTIL)
+LIBTEST_UTIL += $(LIBBITCOIN_CRYPTO_BASE)
+
diff --git a/src/init.cpp b/src/init.cpp
index 2abdf7dbc4..e7dda59590 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -281,9 +281,9 @@ void Shutdown(NodeContext& node)
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
- GetMainSignals().UnregisterWithMempoolSignals(mempool);
globalVerifyHandle.reset();
ECC_Stop();
+ if (node.mempool) node.mempool = nullptr;
LogPrintf("%s: done\n", __func__);
}
@@ -563,9 +563,7 @@ std::string LicenseInfo()
"\n" +
"\n" +
_("This is experimental software.").translated + "\n" +
- strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s").translated, "COPYING", "<https://opensource.org/licenses/MIT>") + "\n" +
- "\n" +
- strprintf(_("This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit %s and cryptographic software written by Eric Young and UPnP software written by Thomas Bernard.").translated, "<https://www.openssl.org>") +
+ strprintf(_("Distributed under the MIT software license, see the accompanying file %s or %s").translated, "COPYING", "<https://opensource.org/licenses/MIT>") +
"\n";
}
@@ -1264,7 +1262,6 @@ bool AppInitMain(NodeContext& node)
}, 60000);
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
- GetMainSignals().RegisterWithMempoolSignals(mempool);
// Create client interfaces for wallets that are supposed to be loaded
// according to -wallet and -disablewallet options. This only constructs
@@ -1637,6 +1634,11 @@ bool AppInitMain(NodeContext& node)
return false;
}
+ // Now that the chain state is loaded, make mempool generally available in the node context. For example the
+ // connection manager, wallet, or RPC threads, which are all started after this, may use it from the node context.
+ assert(!node.mempool);
+ node.mempool = &::mempool;
+
fs::path est_path = GetDataDir() / FEE_ESTIMATES_FILENAME;
CAutoFile est_filein(fsbridge::fopen(est_path, "rb"), SER_DISK, CLIENT_VERSION);
// Allowed to fail as this file IS missing on first startup.
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 701a748e55..94c6e8c7b7 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -18,8 +18,9 @@
#include <wallet/feebumper.h>
#include <wallet/fees.h>
#include <wallet/ismine.h>
-#include <wallet/rpcwallet.h>
#include <wallet/load.h>
+#include <wallet/psbtwallet.h>
+#include <wallet/rpcwallet.h>
#include <wallet/wallet.h>
#include <memory>
@@ -116,8 +117,22 @@ public:
std::string error;
return m_wallet->GetNewDestination(type, label, dest, error);
}
- bool getPubKey(const CKeyID& address, CPubKey& pub_key) override { return m_wallet->GetLegacyScriptPubKeyMan()->GetPubKey(address, pub_key); }
- bool getPrivKey(const CKeyID& address, CKey& key) override { return m_wallet->GetLegacyScriptPubKeyMan()->GetKey(address, key); }
+ bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) override
+ {
+ const SigningProvider* provider = m_wallet->GetSigningProvider(script);
+ if (provider) {
+ return provider->GetPubKey(address, pub_key);
+ }
+ return false;
+ }
+ bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) override
+ {
+ const SigningProvider* provider = m_wallet->GetSigningProvider(script);
+ if (provider) {
+ return provider->GetKey(address, key);
+ }
+ return false;
+ }
bool isSpendable(const CTxDestination& dest) override { return m_wallet->IsMine(dest) & ISMINE_SPENDABLE; }
bool haveWatchOnly() override
{
@@ -343,6 +358,14 @@ public:
}
return {};
}
+ TransactionError fillPSBT(PartiallySignedTransaction& psbtx,
+ bool& complete,
+ int sighash_type = 1 /* SIGHASH_ALL */,
+ bool sign = true,
+ bool bip32derivs = false) override
+ {
+ return FillPSBT(m_wallet.get(), psbtx, complete, sighash_type, sign, bip32derivs);
+ }
WalletBalances getBalances() override
{
const auto bal = m_wallet->GetBalance();
diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h
index a96b93b4c3..8d2b8a2eca 100644
--- a/src/interfaces/wallet.h
+++ b/src/interfaces/wallet.h
@@ -14,6 +14,7 @@
#include <functional>
#include <map>
#include <memory>
+#include <psbt.h>
#include <stdint.h>
#include <string>
#include <tuple>
@@ -81,10 +82,10 @@ public:
virtual bool getNewDestination(const OutputType type, const std::string label, CTxDestination& dest) = 0;
//! Get public key.
- virtual bool getPubKey(const CKeyID& address, CPubKey& pub_key) = 0;
+ virtual bool getPubKey(const CScript& script, const CKeyID& address, CPubKey& pub_key) = 0;
//! Get private key.
- virtual bool getPrivKey(const CKeyID& address, CKey& key) = 0;
+ virtual bool getPrivKey(const CScript& script, const CKeyID& address, CKey& key) = 0;
//! Return whether wallet has private key.
virtual bool isSpendable(const CTxDestination& dest) = 0;
@@ -194,6 +195,13 @@ public:
bool& in_mempool,
int& num_blocks) = 0;
+ //! Fill PSBT.
+ virtual TransactionError fillPSBT(PartiallySignedTransaction& psbtx,
+ bool& complete,
+ int sighash_type = 1 /* SIGHASH_ALL */,
+ bool sign = true,
+ bool bip32derivs = false) = 0;
+
//! Get balances.
virtual WalletBalances getBalances() = 0;
diff --git a/src/key.cpp b/src/key.cpp
index 3ba21753a2..10b6668aae 100644
--- a/src/key.cpp
+++ b/src/key.cpp
@@ -83,13 +83,13 @@ static int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *ou
* <http://www.secg.org/sec1-v2.pdf>. The optional parameters and publicKey fields are
* included.
*
- * privkey must point to an output buffer of length at least CKey::PRIVATE_KEY_SIZE bytes.
+ * privkey must point to an output buffer of length at least CKey::SIZE bytes.
* privkeylen must initially be set to the size of the privkey buffer. Upon return it
* will be set to the number of bytes used in the buffer.
* key32 must point to a 32-byte raw private key.
*/
static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, bool compressed) {
- assert(*privkeylen >= CKey::PRIVATE_KEY_SIZE);
+ assert(*privkeylen >= CKey::SIZE);
secp256k1_pubkey pubkey;
size_t pubkeylen = 0;
if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) {
@@ -115,11 +115,11 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
- pubkeylen = CPubKey::COMPRESSED_PUBLIC_KEY_SIZE;
+ pubkeylen = CPubKey::COMPRESSED_SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
- assert(*privkeylen == CKey::COMPRESSED_PRIVATE_KEY_SIZE);
+ assert(*privkeylen == CKey::COMPRESSED_SIZE);
} else {
static const unsigned char begin[] = {
0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20
@@ -141,11 +141,11 @@ static int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *pr
memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin);
memcpy(ptr, key32, 32); ptr += 32;
memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle);
- pubkeylen = CPubKey::PUBLIC_KEY_SIZE;
+ pubkeylen = CPubKey::SIZE;
secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
ptr += pubkeylen;
*privkeylen = ptr - privkey;
- assert(*privkeylen == CKey::PRIVATE_KEY_SIZE);
+ assert(*privkeylen == CKey::SIZE);
}
return 1;
}
@@ -173,8 +173,8 @@ CPrivKey CKey::GetPrivKey() const {
CPrivKey privkey;
int ret;
size_t privkeylen;
- privkey.resize(PRIVATE_KEY_SIZE);
- privkeylen = PRIVATE_KEY_SIZE;
+ privkey.resize(SIZE);
+ privkeylen = SIZE;
ret = ec_privkey_export_der(secp256k1_context_sign, privkey.data(), &privkeylen, begin(), fCompressed);
assert(ret);
privkey.resize(privkeylen);
@@ -184,7 +184,7 @@ CPrivKey CKey::GetPrivKey() const {
CPubKey CKey::GetPubKey() const {
assert(fValid);
secp256k1_pubkey pubkey;
- size_t clen = CPubKey::PUBLIC_KEY_SIZE;
+ size_t clen = CPubKey::SIZE;
CPubKey result;
int ret = secp256k1_ec_pubkey_create(secp256k1_context_sign, &pubkey, begin());
assert(ret);
@@ -276,7 +276,7 @@ bool CKey::Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const
std::vector<unsigned char, secure_allocator<unsigned char>> vout(64);
if ((nChild >> 31) == 0) {
CPubKey pubkey = GetPubKey();
- assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE);
+ assert(pubkey.size() == CPubKey::COMPRESSED_SIZE);
BIP32Hash(cc, nChild, *pubkey.begin(), pubkey.begin()+1, vout.data());
} else {
assert(size() == 32);
diff --git a/src/key.h b/src/key.h
index 6a86b0411d..1d401a0c8a 100644
--- a/src/key.h
+++ b/src/key.h
@@ -19,7 +19,7 @@
/**
* secure_allocator is defined in allocators.h
* CPrivKey is a serialized private key, with all parameters included
- * (PRIVATE_KEY_SIZE bytes)
+ * (SIZE bytes)
*/
typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey;
@@ -30,15 +30,15 @@ public:
/**
* secp256k1:
*/
- static const unsigned int PRIVATE_KEY_SIZE = 279;
- static const unsigned int COMPRESSED_PRIVATE_KEY_SIZE = 214;
+ static const unsigned int SIZE = 279;
+ static const unsigned int COMPRESSED_SIZE = 214;
/**
* see www.keylength.com
* script supports up to 75 for single byte push
*/
static_assert(
- PRIVATE_KEY_SIZE >= COMPRESSED_PRIVATE_KEY_SIZE,
- "COMPRESSED_PRIVATE_KEY_SIZE is larger than PRIVATE_KEY_SIZE");
+ SIZE >= COMPRESSED_SIZE,
+ "COMPRESSED_SIZE is larger than SIZE");
private:
//! Whether this private key is valid. We check for correctness when modifying the key
diff --git a/src/miner.cpp b/src/miner.cpp
index 1c9174ee07..2f6feb9e02 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -435,7 +435,7 @@ void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned
++nExtraNonce;
unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
CMutableTransaction txCoinbase(*pblock->vtx[0]);
- txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
+ txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce));
assert(txCoinbase.vin[0].scriptSig.size() <= 100);
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
diff --git a/src/node/context.h b/src/node/context.h
index 2b124af4db..dab5b5d048 100644
--- a/src/node/context.h
+++ b/src/node/context.h
@@ -10,6 +10,7 @@
class BanMan;
class CConnman;
+class CTxMemPool;
class PeerLogicValidation;
namespace interfaces {
class Chain;
@@ -22,13 +23,13 @@ class ChainClient;
//! This is used by init, rpc, and test code to pass object references around
//! without needing to declare the same variables and parameters repeatedly, or
//! to use globals. More variables could be added to this struct (particularly
-//! references to validation and mempool objects) to eliminate use of globals
+//! references to validation objects) to eliminate use of globals
//! and make code more modular and testable. The struct isn't intended to have
//! any member functions. It should just be a collection of references that can
//! be used without pulling in unwanted dependencies or functionality.
-struct NodeContext
-{
+struct NodeContext {
std::unique_ptr<CConnman> connman;
+ CTxMemPool* mempool{nullptr}; // Currently a raw pointer because the memory is not managed by this struct
std::unique_ptr<PeerLogicValidation> peer_logic;
std::unique_ptr<BanMan> banman;
std::unique_ptr<interfaces::Chain> chain;
diff --git a/src/psbt.h b/src/psbt.h
index 6a5c468058..bcff66f7a1 100644
--- a/src/psbt.h
+++ b/src/psbt.h
@@ -171,7 +171,7 @@ struct PSBTInput
case PSBT_IN_PARTIAL_SIG:
{
// Make sure that the key is the size of pubkey + 1
- if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) {
throw std::ios_base::failure("Size of key was not the expected size for the type partial signature pubkey");
}
// Read in the pubkey from key
diff --git a/src/pubkey.cpp b/src/pubkey.cpp
index d38df716bd..10953adc35 100644
--- a/src/pubkey.cpp
+++ b/src/pubkey.cpp
@@ -196,8 +196,8 @@ bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector<unsigned cha
if (!secp256k1_ecdsa_recover(secp256k1_context_verify, &pubkey, &sig, hash.begin())) {
return false;
}
- unsigned char pub[PUBLIC_KEY_SIZE];
- size_t publen = PUBLIC_KEY_SIZE;
+ unsigned char pub[SIZE];
+ size_t publen = SIZE;
secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, fComp ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED);
Set(pub, pub + publen);
return true;
@@ -217,8 +217,8 @@ bool CPubKey::Decompress() {
if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, vch, size())) {
return false;
}
- unsigned char pub[PUBLIC_KEY_SIZE];
- size_t publen = PUBLIC_KEY_SIZE;
+ unsigned char pub[SIZE];
+ size_t publen = SIZE;
secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_UNCOMPRESSED);
Set(pub, pub + publen);
return true;
@@ -227,7 +227,7 @@ bool CPubKey::Decompress() {
bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const {
assert(IsValid());
assert((nChild >> 31) == 0);
- assert(size() == COMPRESSED_PUBLIC_KEY_SIZE);
+ assert(size() == COMPRESSED_SIZE);
unsigned char out[64];
BIP32Hash(cc, nChild, *begin(), begin()+1, out);
memcpy(ccChild.begin(), out+32, 32);
@@ -238,8 +238,8 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, ChainCode &ccChild, unsigned int nChi
if (!secp256k1_ec_pubkey_tweak_add(secp256k1_context_verify, &pubkey, out)) {
return false;
}
- unsigned char pub[COMPRESSED_PUBLIC_KEY_SIZE];
- size_t publen = COMPRESSED_PUBLIC_KEY_SIZE;
+ unsigned char pub[COMPRESSED_SIZE];
+ size_t publen = COMPRESSED_SIZE;
secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub, &publen, &pubkey, SECP256K1_EC_COMPRESSED);
pubkeyChild.Set(pub, pub + publen);
return true;
@@ -251,8 +251,8 @@ void CExtPubKey::Encode(unsigned char code[BIP32_EXTKEY_SIZE]) const {
code[5] = (nChild >> 24) & 0xFF; code[6] = (nChild >> 16) & 0xFF;
code[7] = (nChild >> 8) & 0xFF; code[8] = (nChild >> 0) & 0xFF;
memcpy(code+9, chaincode.begin(), 32);
- assert(pubkey.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE);
- memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_PUBLIC_KEY_SIZE);
+ assert(pubkey.size() == CPubKey::COMPRESSED_SIZE);
+ memcpy(code+41, pubkey.begin(), CPubKey::COMPRESSED_SIZE);
}
void CExtPubKey::Decode(const unsigned char code[BIP32_EXTKEY_SIZE]) {
diff --git a/src/pubkey.h b/src/pubkey.h
index fd815a871b..76f743da66 100644
--- a/src/pubkey.h
+++ b/src/pubkey.h
@@ -33,17 +33,17 @@ public:
/**
* secp256k1:
*/
- static constexpr unsigned int PUBLIC_KEY_SIZE = 65;
- static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33;
- static constexpr unsigned int SIGNATURE_SIZE = 72;
- static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65;
+ static constexpr unsigned int SIZE = 65;
+ static constexpr unsigned int COMPRESSED_SIZE = 33;
+ static constexpr unsigned int SIGNATURE_SIZE = 72;
+ static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65;
/**
* see www.keylength.com
* script supports up to 75 for single byte push
*/
static_assert(
- PUBLIC_KEY_SIZE >= COMPRESSED_PUBLIC_KEY_SIZE,
- "COMPRESSED_PUBLIC_KEY_SIZE is larger than PUBLIC_KEY_SIZE");
+ SIZE >= COMPRESSED_SIZE,
+ "COMPRESSED_SIZE is larger than SIZE");
private:
@@ -51,15 +51,15 @@ private:
* Just store the serialized data.
* Its length can very cheaply be computed from the first byte.
*/
- unsigned char vch[PUBLIC_KEY_SIZE];
+ unsigned char vch[SIZE];
//! Compute the length of a pubkey with a given first byte.
unsigned int static GetLen(unsigned char chHeader)
{
if (chHeader == 2 || chHeader == 3)
- return COMPRESSED_PUBLIC_KEY_SIZE;
+ return COMPRESSED_SIZE;
if (chHeader == 4 || chHeader == 6 || chHeader == 7)
- return PUBLIC_KEY_SIZE;
+ return SIZE;
return 0;
}
@@ -140,7 +140,7 @@ public:
void Unserialize(Stream& s)
{
unsigned int len = ::ReadCompactSize(s);
- if (len <= PUBLIC_KEY_SIZE) {
+ if (len <= SIZE) {
s.read((char*)vch, len);
} else {
// invalid pubkey, skip available data
@@ -179,7 +179,7 @@ public:
//! Check whether this is a compressed public key.
bool IsCompressed() const
{
- return size() == COMPRESSED_PUBLIC_KEY_SIZE;
+ return size() == COMPRESSED_SIZE;
}
/**
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index f928f1ca2a..3302dde4ed 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -468,7 +468,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
{
CPubKey pubkey;
PKHash *pkhash = boost::get<PKHash>(&address);
- if (pkhash && model->wallet().getPubKey(CKeyID(*pkhash), pubkey))
+ if (pkhash && model->wallet().getPubKey(out.txout.scriptPubKey, CKeyID(*pkhash), pubkey))
{
nBytesInputs += (pubkey.IsCompressed() ? 148 : 180);
}
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 0214356eaa..64f52fcf45 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -63,7 +63,7 @@
<item row="4" column="2">
<widget class="QLineEdit" name="reqLabel">
<property name="toolTip">
- <string>An optional label to associate with the new receiving address.</string>
+ <string>An optional label to associate with the new receiving address (used by you to identify an invoice). It is also attached to the payment request.</string>
</property>
</widget>
</item>
@@ -93,7 +93,7 @@
<item row="6" column="2">
<widget class="QLineEdit" name="reqMessage">
<property name="toolTip">
- <string>An optional message to attach to the payment request, which will be displayed when the request is opened. Note: The message will not be sent with the payment over the Bitcoin network.</string>
+ <string>An optional message that is attached to the payment request and may be displayed to the sender.</string>
</property>
</widget>
</item>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index c4e0321f28..31e62ab63d 100644
--- a/src/qt/guiutil.cpp
+++ b/src/qt/guiutil.cpp
@@ -7,7 +7,7 @@
#include <qt/bitcoinaddressvalidator.h>
#include <qt/bitcoinunits.h>
#include <qt/qvalidatedlineedit.h>
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <base58.h>
#include <chainparams.h>
diff --git a/src/qt/openuridialog.cpp b/src/qt/openuridialog.cpp
index 199804f84d..6a0143ac7e 100644
--- a/src/qt/openuridialog.cpp
+++ b/src/qt/openuridialog.cpp
@@ -6,7 +6,7 @@
#include <qt/forms/ui_openuridialog.h>
#include <qt/guiutil.h>
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QUrl>
diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h
index 8b2533508d..6d4ce4a7e4 100644
--- a/src/qt/paymentserver.h
+++ b/src/qt/paymentserver.h
@@ -36,13 +36,17 @@
#include <config/bitcoin-config.h>
#endif
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QObject>
#include <QString>
class OptionsModel;
+namespace interfaces {
+class Node;
+} // namespace interfaces
+
QT_BEGIN_NAMESPACE
class QApplication;
class QByteArray;
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp
index e492502002..2674c9b953 100644
--- a/src/qt/receiverequestdialog.cpp
+++ b/src/qt/receiverequestdialog.cpp
@@ -8,6 +8,7 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <qt/walletmodel.h>
#include <QClipboard>
#include <QPixmap>
diff --git a/src/qt/receiverequestdialog.h b/src/qt/receiverequestdialog.h
index a6e1a2af16..1bb43ce6d1 100644
--- a/src/qt/receiverequestdialog.h
+++ b/src/qt/receiverequestdialog.h
@@ -5,10 +5,12 @@
#ifndef BITCOIN_QT_RECEIVEREQUESTDIALOG_H
#define BITCOIN_QT_RECEIVEREQUESTDIALOG_H
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QDialog>
+class WalletModel;
+
namespace Ui {
class ReceiveRequestDialog;
}
diff --git a/src/qt/recentrequeststablemodel.cpp b/src/qt/recentrequeststablemodel.cpp
index 1611ec823c..6d933f46d6 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -7,6 +7,7 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <qt/walletmodel.h>
#include <clientversion.h>
#include <streams.h>
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index 130b709d46..30578b7a03 100644
--- a/src/qt/recentrequeststablemodel.h
+++ b/src/qt/recentrequeststablemodel.h
@@ -5,12 +5,14 @@
#ifndef BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H
#define BITCOIN_QT_RECENTREQUESTSTABLEMODEL_H
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QAbstractTableModel>
#include <QStringList>
#include <QDateTime>
+class WalletModel;
+
class RecentRequestEntry
{
public:
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 8edcca684d..c3d00fa38e 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -21,11 +21,12 @@
#include <chainparams.h>
#include <interfaces/node.h>
#include <key_io.h>
-#include <wallet/coincontrol.h>
-#include <ui_interface.h>
-#include <txmempool.h>
#include <policy/fees.h>
+#include <txmempool.h>
+#include <ui_interface.h>
+#include <wallet/coincontrol.h>
#include <wallet/fees.h>
+#include <wallet/psbtwallet.h>
#include <QFontMetrics>
#include <QScrollBar>
@@ -186,6 +187,11 @@ void SendCoinsDialog::setModel(WalletModel *_model)
// set default rbf checkbox state
ui->optInRBF->setCheckState(Qt::Checked);
+ if (model->privateKeysDisabled()) {
+ ui->sendButton->setText(tr("Cr&eate Unsigned"));
+ ui->sendButton->setToolTip(tr("Creates a Partially Signed Bitcoin Transaction (PSBT) for use with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
+ }
+
// set the smartfee-sliders default value (wallets default conf.target or last stored value)
QSettings settings;
if (settings.value("nSmartFeeSliderPosition").toInt() != 0) {
@@ -305,9 +311,19 @@ void SendCoinsDialog::on_sendButton_clicked()
formatted.append(recipientElement);
}
- QString questionString = tr("Are you sure you want to send?");
+ QString questionString;
+ if (model->privateKeysDisabled()) {
+ questionString.append(tr("Do you want to draft this transaction?"));
+ } else {
+ questionString.append(tr("Are you sure you want to send?"));
+ }
+
questionString.append("<br /><span style='font-size:10pt;'>");
- questionString.append(tr("Please, review your transaction."));
+ if (model->privateKeysDisabled()) {
+ questionString.append(tr("Please, review your transaction proposal. This will produce a Partially Signed Bitcoin Transaction (PSBT) which you can copy and then sign with e.g. an offline %1 wallet, or a PSBT-compatible hardware wallet.").arg(PACKAGE_NAME));
+ } else {
+ questionString.append(tr("Please, review your transaction."));
+ }
questionString.append("</span>%1");
if(txFee > 0)
@@ -358,8 +374,9 @@ void SendCoinsDialog::on_sendButton_clicked()
} else {
questionString = questionString.arg("<br /><br />" + formatted.at(0));
}
-
- SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, this);
+ const QString confirmation = model->privateKeysDisabled() ? tr("Confirm transaction proposal") : tr("Confirm send coins");
+ const QString confirmButtonText = model->privateKeysDisabled() ? tr("Copy PSBT to clipboard") : tr("Send");
+ SendConfirmationDialog confirmationDialog(confirmation, questionString, informative_text, detailed_text, SEND_CONFIRM_DELAY, confirmButtonText, this);
confirmationDialog.exec();
QMessageBox::StandardButton retval = static_cast<QMessageBox::StandardButton>(confirmationDialog.result());
@@ -369,17 +386,35 @@ void SendCoinsDialog::on_sendButton_clicked()
return;
}
- // now send the prepared transaction
- WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
- // process sendStatus and on error generate message shown to user
- processSendCoinsReturn(sendStatus);
+ bool send_failure = false;
+ if (model->privateKeysDisabled()) {
+ CMutableTransaction mtx = CMutableTransaction{*(currentTransaction.getWtx())};
+ PartiallySignedTransaction psbtx(mtx);
+ bool complete = false;
+ const TransactionError err = model->wallet().fillPSBT(psbtx, complete, SIGHASH_ALL, false /* sign */, true /* bip32derivs */);
+ assert(!complete);
+ assert(err == TransactionError::OK);
+ // 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);
+ } else {
+ // now send the prepared transaction
+ WalletModel::SendCoinsReturn sendStatus = model->sendCoins(currentTransaction);
+ // process sendStatus and on error generate message shown to user
+ processSendCoinsReturn(sendStatus);
- if (sendStatus.status == WalletModel::OK)
- {
+ if (sendStatus.status == WalletModel::OK) {
+ Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
+ } else {
+ send_failure = true;
+ }
+ }
+ if (!send_failure) {
accept();
CoinControlDialog::coinControl()->UnSelectAll();
coinControlUpdateLabels();
- Q_EMIT coinsSent(currentTransaction.getWtx()->GetHash());
}
fNewRecipientAllowed = true;
}
@@ -611,6 +646,9 @@ void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry)
coin_control = *CoinControlDialog::coinControl();
}
+ // Include watch-only for wallets without private key
+ coin_control.fAllowWatchOnly = model->privateKeysDisabled();
+
// Calculate available amount to send.
CAmount amount = model->wallet().getAvailableBalance(coin_control);
for (int i = 0; i < ui->entries->count(); ++i) {
@@ -663,6 +701,8 @@ void SendCoinsDialog::updateCoinControlState(CCoinControl& ctrl)
// Either custom fee will be used or if not selected, the confirmation target from dropdown box
ctrl.m_confirm_target = getConfTargetForIndex(ui->confTargetSelector->currentIndex());
ctrl.m_signal_bip125_rbf = ui->optInRBF->isChecked();
+ // Include watch-only for wallets without private key
+ ctrl.fAllowWatchOnly = model->privateKeysDisabled();
}
void SendCoinsDialog::updateSmartFeeLabel()
@@ -870,8 +910,8 @@ void SendCoinsDialog::coinControlUpdateLabels()
}
}
-SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, QWidget* parent)
- : QMessageBox(parent), secDelay(_secDelay)
+SendConfirmationDialog::SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text, const QString& detailed_text, int _secDelay, const QString& _confirmButtonText, QWidget* parent)
+ : QMessageBox(parent), secDelay(_secDelay), confirmButtonText(_confirmButtonText)
{
setIcon(QMessageBox::Question);
setWindowTitle(title); // On macOS, the window title is ignored (as required by the macOS Guidelines).
@@ -908,11 +948,11 @@ void SendConfirmationDialog::updateYesButton()
if(secDelay > 0)
{
yesButton->setEnabled(false);
- yesButton->setText(tr("Send") + " (" + QString::number(secDelay) + ")");
+ yesButton->setText(confirmButtonText + " (" + QString::number(secDelay) + ")");
}
else
{
yesButton->setEnabled(true);
- yesButton->setText(tr("Send"));
+ yesButton->setText(confirmButtonText);
}
}
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index c6c1816877..ccd8494613 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -108,7 +108,7 @@ class SendConfirmationDialog : public QMessageBox
Q_OBJECT
public:
- SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, QWidget* parent = nullptr);
+ SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "Send", QWidget* parent = nullptr);
int exec();
private Q_SLOTS:
@@ -119,6 +119,7 @@ private:
QAbstractButton *yesButton;
QTimer countDownTimer;
int secDelay;
+ QString confirmButtonText;
};
#endif // BITCOIN_QT_SENDCOINSDIALOG_H
diff --git a/src/qt/sendcoinsentry.cpp b/src/qt/sendcoinsentry.cpp
index be417655b4..ad37e09114 100644
--- a/src/qt/sendcoinsentry.cpp
+++ b/src/qt/sendcoinsentry.cpp
@@ -14,6 +14,7 @@
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
#include <qt/platformstyle.h>
+#include <qt/walletmodel.h>
#include <QApplication>
#include <QClipboard>
diff --git a/src/qt/sendcoinsentry.h b/src/qt/sendcoinsentry.h
index 42e2217130..aa69d30f99 100644
--- a/src/qt/sendcoinsentry.h
+++ b/src/qt/sendcoinsentry.h
@@ -5,13 +5,17 @@
#ifndef BITCOIN_QT_SENDCOINSENTRY_H
#define BITCOIN_QT_SENDCOINSENTRY_H
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <QStackedWidget>
class WalletModel;
class PlatformStyle;
+namespace interfaces {
+class Node;
+} // namespace interfaces
+
namespace Ui {
class SendCoinsEntry;
}
diff --git a/src/qt/sendcoinsrecipient.h b/src/qt/sendcoinsrecipient.h
new file mode 100644
index 0000000000..12279fab64
--- /dev/null
+++ b/src/qt/sendcoinsrecipient.h
@@ -0,0 +1,74 @@
+// Copyright (c) 2011-2019 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_SENDCOINSRECIPIENT_H
+#define BITCOIN_QT_SENDCOINSRECIPIENT_H
+
+#if defined(HAVE_CONFIG_H)
+#include <config/bitcoin-config.h>
+#endif
+
+#include <amount.h>
+#include <serialize.h>
+
+#include <string>
+
+#include <QString>
+
+class SendCoinsRecipient
+{
+public:
+ explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { }
+ explicit SendCoinsRecipient(const QString &addr, const QString &_label, const CAmount& _amount, const QString &_message):
+ address(addr), label(_label), amount(_amount), message(_message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
+
+ // If from an unauthenticated payment request, this is used for storing
+ // the addresses, e.g. address-A<br />address-B<br />address-C.
+ // Info: As we don't need to process addresses in here when using
+ // payment requests, we can abuse it for displaying an address list.
+ // Todo: This is a hack, should be replaced with a cleaner solution!
+ QString address;
+ QString label;
+ CAmount amount;
+ // If from a payment request, this is used for storing the memo
+ QString message;
+ // Keep the payment request around as a serialized string to ensure
+ // load/store is lossless.
+ std::string sPaymentRequest;
+ // Empty if no authentication or invalid signature/cert/etc.
+ QString authenticatedMerchant;
+
+ bool fSubtractFeeFromAmount; // memory only
+
+ static const int CURRENT_VERSION = 1;
+ int nVersion;
+
+ ADD_SERIALIZE_METHODS;
+
+ template <typename Stream, typename Operation>
+ inline void SerializationOp(Stream& s, Operation ser_action) {
+ std::string sAddress = address.toStdString();
+ std::string sLabel = label.toStdString();
+ std::string sMessage = message.toStdString();
+ std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
+
+ READWRITE(this->nVersion);
+ READWRITE(sAddress);
+ READWRITE(sLabel);
+ READWRITE(amount);
+ READWRITE(sMessage);
+ READWRITE(sPaymentRequest);
+ READWRITE(sAuthenticatedMerchant);
+
+ if (ser_action.ForRead())
+ {
+ address = QString::fromStdString(sAddress);
+ label = QString::fromStdString(sLabel);
+ message = QString::fromStdString(sMessage);
+ authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
+ }
+ }
+};
+
+#endif // BITCOIN_QT_SENDCOINSRECIPIENT_H
diff --git a/src/qt/signverifymessagedialog.cpp b/src/qt/signverifymessagedialog.cpp
index 1d0e1323bc..9d250bcb83 100644
--- a/src/qt/signverifymessagedialog.cpp
+++ b/src/qt/signverifymessagedialog.cpp
@@ -136,7 +136,7 @@ void SignVerifyMessageDialog::on_signMessageButton_SM_clicked()
}
CKey key;
- if (!model->wallet().getPrivKey(CKeyID(*pkhash), key))
+ if (!model->wallet().getPrivKey(GetScriptForDestination(destination), CKeyID(*pkhash), key))
{
ui->statusLabel_SM->setStyleSheet("QLabel { color: red; }");
ui->statusLabel_SM->setText(tr("Private key for the entered address is not available."));
diff --git a/src/qt/test/rpcnestedtests.cpp b/src/qt/test/rpcnestedtests.cpp
index 971d9f4a7c..374fc9b59b 100644
--- a/src/qt/test/rpcnestedtests.cpp
+++ b/src/qt/test/rpcnestedtests.cpp
@@ -32,7 +32,6 @@ void RPCNestedTests::rpcNestedTests()
// do some test setup
// could be moved to a more generic place when we add more tests on QT level
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
- //mempool.setSanityCheck(1.0);
TestingSetup test;
diff --git a/src/qt/transactiondesc.cpp b/src/qt/transactiondesc.cpp
index 318b0756c7..9a93798aef 100644
--- a/src/qt/transactiondesc.cpp
+++ b/src/qt/transactiondesc.cpp
@@ -15,11 +15,12 @@
#include <consensus/consensus.h>
#include <interfaces/node.h>
+#include <interfaces/wallet.h>
#include <key_io.h>
-#include <validation.h>
+#include <policy/policy.h>
#include <script/script.h>
#include <util/system.h>
-#include <policy/policy.h>
+#include <validation.h>
#include <wallet/ismine.h>
#include <stdint.h>
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 4c253f8ddd..7413a1f09e 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -2,17 +2,18 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <qt/walletcontroller.h>
+
#include <qt/askpassphrasedialog.h>
#include <qt/createwalletdialog.h>
#include <qt/guiconstants.h>
#include <qt/guiutil.h>
-#include <qt/walletcontroller.h>
-
-#include <wallet/wallet.h>
+#include <qt/walletmodel.h>
#include <interfaces/handler.h>
#include <interfaces/node.h>
#include <util/string.h>
+#include <wallet/wallet.h>
#include <algorithm>
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index e50dd5c7eb..956245775e 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -5,7 +5,7 @@
#ifndef BITCOIN_QT_WALLETCONTROLLER_H
#define BITCOIN_QT_WALLETCONTROLLER_H
-#include <qt/walletmodel.h>
+#include <qt/sendcoinsrecipient.h>
#include <support/allocators/secure.h>
#include <sync.h>
@@ -23,10 +23,12 @@
class OptionsModel;
class PlatformStyle;
+class WalletModel;
namespace interfaces {
class Handler;
class Node;
+class Wallet;
} // namespace interfaces
class AskPassphraseDialog;
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index 33801d3907..fb92e29f21 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -183,7 +183,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
std::string strFailReason;
auto& newTx = transaction.getWtx();
- newTx = m_wallet->createTransaction(vecSend, coinControl, true /* sign */, nChangePosRet, nFeeRequired, strFailReason);
+ newTx = m_wallet->createTransaction(vecSend, coinControl, !privateKeysDisabled() /* sign */, nChangePosRet, nFeeRequired, strFailReason);
transaction.setTransactionFee(nFeeRequired);
if (fSubtractFeeFromAmount && newTx)
transaction.reassignAmounts(nChangePosRet);
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index a873519a34..8087356f5e 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -9,9 +9,7 @@
#include <config/bitcoin-config.h>
#endif
-#include <amount.h>
#include <key.h>
-#include <serialize.h>
#include <script/standard.h>
#include <qt/walletmodeltransaction.h>
@@ -29,6 +27,7 @@ class AddressTableModel;
class OptionsModel;
class PlatformStyle;
class RecentRequestsTableModel;
+class SendCoinsRecipient;
class TransactionTableModel;
class WalletModelTransaction;
@@ -47,61 +46,6 @@ QT_BEGIN_NAMESPACE
class QTimer;
QT_END_NAMESPACE
-class SendCoinsRecipient
-{
-public:
- explicit SendCoinsRecipient() : amount(0), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) { }
- explicit SendCoinsRecipient(const QString &addr, const QString &_label, const CAmount& _amount, const QString &_message):
- address(addr), label(_label), amount(_amount), message(_message), fSubtractFeeFromAmount(false), nVersion(SendCoinsRecipient::CURRENT_VERSION) {}
-
- // If from an unauthenticated payment request, this is used for storing
- // the addresses, e.g. address-A<br />address-B<br />address-C.
- // Info: As we don't need to process addresses in here when using
- // payment requests, we can abuse it for displaying an address list.
- // Todo: This is a hack, should be replaced with a cleaner solution!
- QString address;
- QString label;
- CAmount amount;
- // If from a payment request, this is used for storing the memo
- QString message;
- // Keep the payment request around as a serialized string to ensure
- // load/store is lossless.
- std::string sPaymentRequest;
- // Empty if no authentication or invalid signature/cert/etc.
- QString authenticatedMerchant;
-
- bool fSubtractFeeFromAmount; // memory only
-
- static const int CURRENT_VERSION = 1;
- int nVersion;
-
- ADD_SERIALIZE_METHODS;
-
- template <typename Stream, typename Operation>
- inline void SerializationOp(Stream& s, Operation ser_action) {
- std::string sAddress = address.toStdString();
- std::string sLabel = label.toStdString();
- std::string sMessage = message.toStdString();
- std::string sAuthenticatedMerchant = authenticatedMerchant.toStdString();
-
- READWRITE(this->nVersion);
- READWRITE(sAddress);
- READWRITE(sLabel);
- READWRITE(amount);
- READWRITE(sMessage);
- READWRITE(sPaymentRequest);
- READWRITE(sAuthenticatedMerchant);
-
- if (ser_action.ForRead())
- {
- address = QString::fromStdString(sAddress);
- label = QString::fromStdString(sLabel);
- message = QString::fromStdString(sMessage);
- authenticatedMerchant = QString::fromStdString(sAuthenticatedMerchant);
- }
- }
-};
-
/** Interface to Bitcoin wallet from Qt view code. */
class WalletModel : public QObject
{
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index 242ba13897..9e5d285e8c 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -5,7 +5,8 @@
#ifndef BITCOIN_QT_WALLETMODELTRANSACTION_H
#define BITCOIN_QT_WALLETMODELTRANSACTION_H
-#include <qt/walletmodel.h>
+#include <primitives/transaction.h>
+#include <qt/sendcoinsrecipient.h>
#include <amount.h>
diff --git a/src/random.cpp b/src/random.cpp
index 3e6398f7b4..47d76d8700 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -43,10 +43,6 @@
#include <sys/sysctl.h>
#endif
-#include <openssl/err.h>
-#include <openssl/rand.h>
-#include <openssl/conf.h>
-
[[noreturn]] static void RandFailure()
{
LogPrintf("Failed to read randomness, aborting\n");
@@ -347,8 +343,6 @@ void GetOSRand(unsigned char *ent32)
#endif
}
-void LockingCallbackOpenSSL(int mode, int i, const char* file, int line);
-
namespace {
class RNGState {
@@ -364,31 +358,15 @@ class RNGState {
unsigned char m_state[32] GUARDED_BY(m_mutex) = {0};
uint64_t m_counter GUARDED_BY(m_mutex) = 0;
bool m_strongly_seeded GUARDED_BY(m_mutex) = false;
- std::unique_ptr<Mutex[]> m_mutex_openssl;
public:
RNGState() noexcept
{
InitHardwareRand();
-
- // Init OpenSSL library multithreading support
- m_mutex_openssl.reset(new Mutex[CRYPTO_num_locks()]);
- CRYPTO_set_locking_callback(LockingCallbackOpenSSL);
-
- // OpenSSL can optionally load a config file which lists optional loadable modules and engines.
- // We don't use them so we don't require the config. However some of our libs may call functions
- // which attempt to load the config file, possibly resulting in an exit() or crash if it is missing
- // or corrupt. Explicitly tell OpenSSL not to try to load the file. The result for our libs will be
- // that the config appears to have been loaded and there are no modules/engines available.
- OPENSSL_no_config();
}
~RNGState()
{
- // Securely erase the memory used by the OpenSSL PRNG
- RAND_cleanup();
- // Shutdown OpenSSL library multithreading support
- CRYPTO_set_locking_callback(nullptr);
}
/** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher.
@@ -424,8 +402,6 @@ public:
memory_cleanse(buf, 64);
return ret;
}
-
- Mutex& GetOpenSSLMutex(int i) { return m_mutex_openssl[i]; }
};
RNGState& GetRNGState() noexcept
@@ -437,17 +413,6 @@ RNGState& GetRNGState() noexcept
}
}
-void LockingCallbackOpenSSL(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS
-{
- RNGState& rng = GetRNGState();
-
- if (mode & CRYPTO_LOCK) {
- rng.GetOpenSSLMutex(i).lock();
- } else {
- rng.GetOpenSSLMutex(i).unlock();
- }
-}
-
/* A note on the use of noexcept in the seeding functions below:
*
* None of the RNG code should ever throw any exception, with the sole exception
@@ -495,10 +460,6 @@ static void SeedSlow(CSHA512& hasher) noexcept
GetOSRand(buffer);
hasher.Write(buffer, sizeof(buffer));
- // OpenSSL RNG (for now)
- RAND_bytes(buffer, sizeof(buffer));
- hasher.Write(buffer, sizeof(buffer));
-
// High-precision timestamp.
//
// Note that we also commit to a timestamp in the Fast seeder, so we indirectly commit to a
@@ -586,14 +547,6 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedStartup(startup_hasher, rng);
rng.MixExtract(out, num, std::move(startup_hasher), true);
}
-
- // For anything but the 'fast' level, feed the resulting RNG output (after an additional hashing step) back into OpenSSL.
- if (level != RNGLevel::FAST) {
- unsigned char buf[64];
- CSHA512().Write(out, num).Finalize(buf);
- RAND_add(buf, sizeof(buf), num);
- memory_cleanse(buf, 64);
- }
}
void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); }
diff --git a/src/random.h b/src/random.h
index 7768f9d3c5..2d8ab085e3 100644
--- a/src/random.h
+++ b/src/random.h
@@ -35,7 +35,6 @@
* that fast seeding includes, but additionally:
* - OS entropy (/dev/urandom, getrandom(), ...). The application will terminate if
* this entropy source fails.
- * - Bytes from OpenSSL's RNG (which itself may be seeded from various sources)
* - Another high-precision timestamp (indirectly committing to a benchmark of all the
* previous sources).
* These entropy sources are slower, but designed to make sure the RNG state contains
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
index 603c88eaab..ec42ddabc3 100644
--- a/src/randomenv.cpp
+++ b/src/randomenv.cpp
@@ -70,7 +70,6 @@ namespace {
void RandAddSeedPerfmon(CSHA512& hasher)
{
#ifdef WIN32
- // Don't need this on Linux, OpenSSL automatically uses /dev/urandom
// Seed with the entire set of perfmon data
// This can take up to 2 seconds, so only do it every 10 minutes
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index 2f4b4412f5..946152d9aa 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -15,6 +15,7 @@
#include <hash.h>
#include <index/blockfilterindex.h>
#include <node/coinstats.h>
+#include <node/context.h>
#include <node/utxo_snapshot.h>
#include <policy/feerate.h>
#include <policy/policy.h>
@@ -53,6 +54,15 @@ static Mutex cs_blockchange;
static std::condition_variable cond_blockchange;
static CUpdatedBlock latestblock;
+CTxMemPool& EnsureMemPool()
+{
+ CHECK_NONFATAL(g_rpc_node);
+ if (!g_rpc_node->mempool) {
+ throw JSONRPCError(RPC_CLIENT_MEMPOOL_DISABLED, "Mempool disabled or instance not found");
+ }
+ return *g_rpc_node->mempool;
+}
+
/* Calculate the difficulty for a given block index.
*/
double GetDifficulty(const CBlockIndex* blockindex)
diff --git a/src/rpc/blockchain.h b/src/rpc/blockchain.h
index 8a1264f824..ccb3e39722 100644
--- a/src/rpc/blockchain.h
+++ b/src/rpc/blockchain.h
@@ -52,4 +52,6 @@ void CalculatePercentilesByWeight(CAmount result[NUM_GETBLOCKSTATS_PERCENTILES],
//! direct way to pass in state to RPC methods without globals.
extern NodeContext* g_rpc_node;
+CTxMemPool& EnsureMemPool();
+
#endif
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index ab22155651..85f9f1e8b7 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -375,9 +375,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
" }\n"
" ,...\n"
" ],\n"
- " \"coinbaseaux\" : { (json object) data that should be included in the coinbase's scriptSig content\n"
- " \"flags\" : \"xx\" (string) key name is to be ignored, and value included in scriptSig\n"
- " },\n"
+ " \"coinbaseaux\" : { ... }, (json object) data that should be included in the coinbase's scriptSig content\n"
" \"coinbasevalue\" : n, (numeric) maximum allowable input to coinbase transaction, including the generation award and transaction fees (in satoshis)\n"
" \"coinbasetxn\" : { ... }, (json object) information for coinbase transaction\n"
" \"target\" : \"xxxx\", (string) The hash target\n"
@@ -607,7 +605,6 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
}
UniValue aux(UniValue::VOBJ);
- aux.pushKV("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end()));
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
diff --git a/src/rpc/protocol.h b/src/rpc/protocol.h
index ef6537e4ec..ca779497b9 100644
--- a/src/rpc/protocol.h
+++ b/src/rpc/protocol.h
@@ -63,6 +63,9 @@ enum RPCErrorCode
RPC_CLIENT_INVALID_IP_OR_SUBNET = -30, //!< Invalid IP/Subnet
RPC_CLIENT_P2P_DISABLED = -31, //!< No valid connection manager instance found
+ //! Chain errors
+ RPC_CLIENT_MEMPOOL_DISABLED = -33, //!< No mempool instance found
+
//! Wallet errors
RPC_WALLET_ERROR = -4, //!< Unspecified problem with wallet (key not found etc.)
RPC_WALLET_INSUFFICIENT_FUNDS = -6, //!< Not enough funds in wallet or account
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 983f251d6b..6f24caee21 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -763,7 +763,9 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request)
// Parse the prevtxs array
ParsePrevouts(request.params[2], &keystore, coins);
- return SignTransaction(mtx, &keystore, coins, request.params[3]);
+ UniValue result(UniValue::VOBJ);
+ SignTransaction(mtx, &keystore, coins, request.params[3], result);
+ return result;
}
static UniValue sendrawtransaction(const JSONRPCRequest& request)
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index fe98fff4bb..40334883c5 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -268,7 +268,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
}
}
-UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType)
+void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result)
{
int nHashType = ParseSighashString(hashType);
@@ -319,12 +319,12 @@ UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keysto
}
bool fComplete = vErrors.empty();
- UniValue result(UniValue::VOBJ);
result.pushKV("hex", EncodeHexTx(CTransaction(mtx)));
result.pushKV("complete", fComplete);
if (!vErrors.empty()) {
+ if (result.exists("errors")) {
+ vErrors.push_backV(result["errors"].getValues());
+ }
result.pushKV("errors", vErrors);
}
-
- return result;
}
diff --git a/src/rpc/rawtransaction_util.h b/src/rpc/rawtransaction_util.h
index 1936998ff3..0b7712b83c 100644
--- a/src/rpc/rawtransaction_util.h
+++ b/src/rpc/rawtransaction_util.h
@@ -21,9 +21,9 @@ class SigningProvider;
* @param keystore Temporary keystore containing signing keys
* @param coins Map of unspent outputs
* @param hashType The signature hash type
- * @returns JSON object with details of signed transaction
+ * @param result JSON object where signed transaction results accumulate
*/
-UniValue SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType);
+void SignTransaction(CMutableTransaction& mtx, const SigningProvider* keystore, const std::map<COutPoint, Coin>& coins, const UniValue& hashType, UniValue& result);
/**
* Parse a prevtxs UniValue array and get the map of coins from it
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 13cdd6c61a..32b388b7fa 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -815,8 +815,8 @@ std::unique_ptr<DescriptorImpl> ParseScript(Span<const char>& sp, ParseScriptCon
}
}
if (ctx == ParseScriptContext::P2SH) {
- if (script_size + 3 > 520) {
- error = strprintf("P2SH script is too large, %d bytes is larger than 520 bytes", script_size + 3);
+ if (script_size + 3 > MAX_SCRIPT_ELEMENT_SIZE) {
+ error = strprintf("P2SH script is too large, %d bytes is larger than %d bytes", script_size + 3, MAX_SCRIPT_ELEMENT_SIZE);
return nullptr;
}
}
diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp
index 20fae2eebf..ad833bc025 100644
--- a/src/script/interpreter.cpp
+++ b/src/script/interpreter.cpp
@@ -61,17 +61,17 @@ static inline void popstack(std::vector<valtype>& stack)
}
bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
- if (vchPubKey.size() < CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
+ if (vchPubKey.size() < CPubKey::COMPRESSED_SIZE) {
// Non-canonical public key: too short
return false;
}
if (vchPubKey[0] == 0x04) {
- if (vchPubKey.size() != CPubKey::PUBLIC_KEY_SIZE) {
+ if (vchPubKey.size() != CPubKey::SIZE) {
// Non-canonical public key: invalid length for uncompressed key
return false;
}
} else if (vchPubKey[0] == 0x02 || vchPubKey[0] == 0x03) {
- if (vchPubKey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
+ if (vchPubKey.size() != CPubKey::COMPRESSED_SIZE) {
// Non-canonical public key: invalid length for compressed key
return false;
}
@@ -83,7 +83,7 @@ bool static IsCompressedOrUncompressedPubKey(const valtype &vchPubKey) {
}
bool static IsCompressedPubKey(const valtype &vchPubKey) {
- if (vchPubKey.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE) {
+ if (vchPubKey.size() != CPubKey::COMPRESSED_SIZE) {
// Non-canonical public key: invalid length for compressed key
return false;
}
diff --git a/src/script/sign.h b/src/script/sign.h
index 9d0a5b4d70..4c2403f83f 100644
--- a/src/script/sign.h
+++ b/src/script/sign.h
@@ -101,7 +101,7 @@ template<typename Stream>
void DeserializeHDKeypaths(Stream& s, const std::vector<unsigned char>& key, std::map<CPubKey, KeyOriginInfo>& hd_keypaths)
{
// Make sure that the key is the size of pubkey + 1
- if (key.size() != CPubKey::PUBLIC_KEY_SIZE + 1 && key.size() != CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1) {
+ if (key.size() != CPubKey::SIZE + 1 && key.size() != CPubKey::COMPRESSED_SIZE + 1) {
throw std::ios_base::failure("Size of key was not the expected size for the type BIP32 keypath");
}
// Read in the pubkey from key
diff --git a/src/script/standard.cpp b/src/script/standard.cpp
index fc6898f444..144bdcff98 100644
--- a/src/script/standard.cpp
+++ b/src/script/standard.cpp
@@ -44,12 +44,12 @@ const char* GetTxnOutputType(txnouttype t)
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
{
- if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
- pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1);
+ if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {
+ pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);
return CPubKey::ValidSize(pubkey);
}
- if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) {
- pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1);
+ if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {
+ pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);
return CPubKey::ValidSize(pubkey);
}
return false;
diff --git a/src/support/lockedpool.cpp b/src/support/lockedpool.cpp
index 5c2050e4a2..85e3351e72 100644
--- a/src/support/lockedpool.cpp
+++ b/src/support/lockedpool.cpp
@@ -23,6 +23,10 @@
#endif
#include <algorithm>
+#ifdef ARENA_DEBUG
+#include <iomanip>
+#include <iostream>
+#endif
LockedPoolManager* LockedPoolManager::_instance = nullptr;
std::once_flag LockedPoolManager::init_flag;
@@ -137,7 +141,7 @@ Arena::Stats Arena::stats() const
}
#ifdef ARENA_DEBUG
-static void printchunk(char* base, size_t sz, bool used) {
+static void printchunk(void* base, size_t sz, bool used) {
std::cout <<
"0x" << std::hex << std::setw(16) << std::setfill('0') << base <<
" 0x" << std::hex << std::setw(16) << std::setfill('0') << sz <<
@@ -149,7 +153,7 @@ void Arena::walk() const
printchunk(chunk.first, chunk.second, true);
std::cout << std::endl;
for (const auto& chunk: chunks_free)
- printchunk(chunk.first, chunk.second, false);
+ printchunk(chunk.first, chunk.second->first, false);
std::cout << std::endl;
}
#endif
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 6ed7350ea2..d79a598bf1 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -23,7 +23,17 @@
#include <boost/test/unit_test.hpp>
-BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup)
+namespace miner_tests {
+struct MinerTestingSetup : public TestingSetup {
+ void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs);
+ bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
+ {
+ return CheckSequenceLocks(*m_node.mempool, tx, flags);
+ }
+};
+} // namespace miner_tests
+
+BOOST_FIXTURE_TEST_SUITE(miner_tests, MinerTestingSetup)
// BOOST_CHECK_EXCEPTION predicates to check the specific validation error
class HasReason {
@@ -89,16 +99,10 @@ static CBlockIndex CreateBlockIndex(int nHeight) EXCLUSIVE_LOCKS_REQUIRED(cs_mai
return index;
}
-static bool TestSequenceLocks(const CTransaction &tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
-{
- LOCK(::mempool.cs);
- return CheckSequenceLocks(::mempool, tx, flags);
-}
-
// Test suite for ancestor feerate transaction selection.
// Implemented as an additional function, rather than a separate test case,
// to allow reusing the blockchain created in CreateNewBlock_validity.
-static void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst) EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::mempool.cs)
+void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector<CTransactionRef>& txFirst)
{
// Test the ancestor feerate transaction selection.
TestMemPoolEntryHelper entry;
@@ -114,19 +118,19 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vout[0].nValue = 5000000000LL - 1000;
// This tx has a low fee: 1000 satoshis
uint256 hashParentTx = tx.GetHash(); // save this txid for later use
- mempool.addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(1000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
// This tx has a medium fee: 10000 satoshis
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = 5000000000LL - 10000;
uint256 hashMediumFeeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(10000).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
// This tx has a high fee, but depends on the first transaction
tx.vin[0].prevout.hash = hashParentTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 50k satoshi fee
uint256 hashHighFeeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
std::unique_ptr<CBlockTemplate> pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx);
@@ -137,7 +141,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vin[0].prevout.hash = hashHighFeeTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000; // 0 fee
uint256 hashFreeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(0).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(0).FromTx(tx));
size_t freeTxSize = ::GetSerializeSize(tx, PROTOCOL_VERSION);
// Calculate a fee on child transaction that will put the package just
@@ -147,7 +151,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vin[0].prevout.hash = hashFreeTx;
tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse;
uint256 hashLowFeeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(feeToUse).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx));
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected
for (size_t i=0; i<pblocktemplate->block.vtx.size(); ++i) {
@@ -158,10 +162,10 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
// Test that packages above the min relay fee do get included, even if one
// of the transactions is below the min relay fee
// Remove the low fee transaction and replace with a higher fee transaction
- mempool.removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
+ m_node.mempool->removeRecursive(CTransaction(tx), MemPoolRemovalReason::REPLACED);
tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee
hashLowFeeTx = tx.GetHash();
- mempool.addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx));
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx);
BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx);
@@ -174,7 +178,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
tx.vout[0].nValue = 5000000000LL - 100000000;
tx.vout[1].nValue = 100000000; // 1BTC output
uint256 hashFreeTx2 = tx.GetHash();
- mempool.addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(0).SpendsCoinbase(true).FromTx(tx));
// This tx can't be mined by itself
tx.vin[0].prevout.hash = hashFreeTx2;
@@ -182,7 +186,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
feeToUse = blockMinFeeRate.GetFee(freeTxSize);
tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse;
uint256 hashLowFeeTx2 = tx.GetHash();
- mempool.addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
// Verify that this tx isn't selected.
@@ -195,7 +199,7 @@ static void TestPackageSelection(const CChainParams& chainparams, const CScript&
// as well.
tx.vin[0].prevout.n = 1;
tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee
- mempool.addUnchecked(entry.Fee(10000).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx));
pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2);
}
@@ -252,7 +256,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
}
LOCK(cs_main);
- LOCK(::mempool.cs);
+ LOCK(m_node.mempool->cs);
// Just to make sure we can still make simple blocks
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
@@ -276,12 +280,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
// If we don't set the # of sig ops in the CTxMemPoolEntry, template creation fails
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops"));
- mempool.clear();
+ m_node.mempool->clear();
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vout[0].nValue = BLOCKSUBSIDY;
@@ -291,11 +295,11 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
// If we do set the # of sig ops in the CTxMemPoolEntry, template creation passes
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOpsCost(80).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- mempool.clear();
+ m_node.mempool->clear();
// block size > limit
tx.vin[0].scriptSig = CScript();
@@ -311,24 +315,24 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].nValue -= LOWFEE;
hash = tx.GetHash();
bool spendsCoinbase = i == 0; // only first tx spends coinbase
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx));
tx.vin[0].prevout.hash = hash;
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- mempool.clear();
+ m_node.mempool->clear();
- // orphan in mempool, template creation fails
+ // orphan in *m_node.mempool, template creation fails
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
- mempool.clear();
+ m_node.mempool->clear();
// child with higher feerate than parent
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vin[0].prevout.hash = txFirst[1]->GetHash();
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin.resize(2);
tx.vin[1].scriptSig = CScript() << OP_1;
@@ -336,34 +340,34 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[1].prevout.n = 0;
tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- mempool.clear();
+ m_node.mempool->clear();
- // coinbase in mempool, template creation fails
+ // coinbase in *m_node.mempool, template creation fails
tx.vin.resize(1);
tx.vin[0].prevout.SetNull();
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
tx.vout[0].nValue = 0;
hash = tx.GetHash();
// give it a fee so it'll get mined
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw bad-cb-multiple
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple"));
- mempool.clear();
+ m_node.mempool->clear();
- // double spend txn pair in mempool, template creation fails
+ // double spend txn pair in *m_node.mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY-HIGHFEE;
tx.vout[0].scriptPubKey = CScript() << OP_1;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vout[0].scriptPubKey = CScript() << OP_2;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
- mempool.clear();
+ m_node.mempool->clear();
// subsidy changing
int nHeight = ::ChainActive().Height();
@@ -392,7 +396,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey));
- // invalid p2sh txn in mempool, template creation fails
+ // invalid p2sh txn in *m_node.mempool, template creation fails
tx.vin[0].prevout.hash = txFirst[0]->GetHash();
tx.vin[0].prevout.n = 0;
tx.vin[0].scriptSig = CScript() << OP_1;
@@ -400,15 +404,15 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(ScriptHash(script));
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout.hash = hash;
tx.vin[0].scriptSig = CScript() << std::vector<unsigned char>(script.begin(), script.end());
tx.vout[0].nValue -= LOWFEE;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw block-validation-failed
BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed"));
- mempool.clear();
+ m_node.mempool->clear();
// Delete the dummy blocks again.
while (::ChainActive().Tip()->nHeight > nHeight) {
@@ -439,7 +443,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vout[0].scriptPubKey = CScript() << OP_1;
tx.nLockTime = 0;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, &prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block
@@ -449,7 +453,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | (((::ChainActive().Tip()->GetMedianTimePast()+1-::ChainActive()[1]->GetMedianTimePast()) >> CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) + 1); // txFirst[1] is the 3rd block
prevheights[0] = baseheight + 2;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail
@@ -465,7 +469,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights[0] = baseheight + 3;
tx.nLockTime = ::ChainActive().Tip()->nHeight + 1;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block
@@ -476,7 +480,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
prevheights.resize(1);
prevheights[0] = baseheight + 4;
hash = tx.GetHash();
- mempool.addUnchecked(entry.Time(GetTime()).FromTx(tx));
+ m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx));
BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass
BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later
@@ -513,7 +517,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
::ChainActive().Tip()->nHeight--;
SetMockTime(0);
- mempool.clear();
+ m_node.mempool->clear();
TestPackageSelection(chainparams, scriptPubKey, txFirst);
diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp
index 245c03d774..cace75f093 100644
--- a/src/test/txvalidation_tests.cpp
+++ b/src/test/txvalidation_tests.cpp
@@ -34,17 +34,17 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup)
LOCK(cs_main);
- unsigned int initialPoolSize = mempool.size();
+ unsigned int initialPoolSize = m_node.mempool->size();
BOOST_CHECK_EQUAL(
false,
- AcceptToMemoryPool(mempool, state, MakeTransactionRef(coinbaseTx),
+ AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx),
nullptr /* plTxnReplaced */,
true /* bypass_limits */,
0 /* nAbsurdFee */));
// Check that the transaction hasn't been added to mempool.
- BOOST_CHECK_EQUAL(mempool.size(), initialPoolSize);
+ BOOST_CHECK_EQUAL(m_node.mempool->size(), initialPoolSize);
// Check that the validation state reflects the unsuccessful attempt.
BOOST_CHECK(state.IsInvalid());
diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp
index a5bc15bb9f..67f45c4ed4 100644
--- a/src/test/txvalidationcache_tests.cpp
+++ b/src/test/txvalidationcache_tests.cpp
@@ -17,16 +17,6 @@ bool CheckInputs(const CTransaction& tx, TxValidationState &state, const CCoinsV
BOOST_AUTO_TEST_SUITE(tx_validationcache_tests)
-static bool
-ToMemPool(const CMutableTransaction& tx)
-{
- LOCK(cs_main);
-
- TxValidationState state;
- return AcceptToMemoryPool(mempool, state, MakeTransactionRef(tx),
- nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
-}
-
BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
{
// Make sure skipping validation of transactions that were
@@ -35,6 +25,14 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
CScript scriptPubKey = CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG;
+ const auto ToMemPool = [this](const CMutableTransaction& tx) {
+ LOCK(cs_main);
+
+ TxValidationState state;
+ return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx),
+ nullptr /* plTxnReplaced */, true /* bypass_limits */, 0 /* nAbsurdFee */);
+ };
+
// Create a double-spend of mature coinbase txn:
std::vector<CMutableTransaction> spends;
spends.resize(2);
@@ -72,7 +70,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
LOCK(cs_main);
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
}
- mempool.clear();
+ m_node.mempool->clear();
// Test 3: ... and should be rejected if spend2 is in the memory pool
BOOST_CHECK(ToMemPool(spends[1]));
@@ -81,9 +79,9 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
LOCK(cs_main);
BOOST_CHECK(::ChainActive().Tip()->GetBlockHash() != block.GetHash());
}
- mempool.clear();
+ m_node.mempool->clear();
- // Final sanity test: first spend in mempool, second in block, that's OK:
+ // Final sanity test: first spend in *m_node.mempool, second in block, that's OK:
std::vector<CMutableTransaction> oneSpend;
oneSpend.push_back(spends[0]);
BOOST_CHECK(ToMemPool(spends[1]));
@@ -94,7 +92,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup)
}
// spends[1] should have been removed from the mempool when the
// block with spends[0] is accepted:
- BOOST_CHECK_EQUAL(mempool.size(), 0U);
+ BOOST_CHECK_EQUAL(m_node.mempool->size(), 0U);
}
// Run CheckInputs (using CoinsTip()) on the given transaction, for all script
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index 0c6ecdf69d..86c355fdcd 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -107,7 +107,6 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
threadGroup.create_thread(std::bind(&CScheduler::serviceQueue, &scheduler));
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
- mempool.setSanityCheck(1.0);
pblocktree.reset(new CBlockTreeDB(1 << 20, true));
g_chainstate = MakeUnique<CChainState>();
::ChainstateActive().InitCoinsDB(
@@ -131,6 +130,8 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
}
g_parallel_script_checks = true;
+ m_node.mempool = &::mempool;
+ m_node.mempool->setSanityCheck(1.0);
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
}
@@ -144,6 +145,7 @@ TestingSetup::~TestingSetup()
g_rpc_node = nullptr;
m_node.connman.reset();
m_node.banman.reset();
+ m_node.mempool = nullptr;
UnloadBlockIndex();
g_chainstate.reset();
pblocktree.reset();
diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp
index b7cf82906a..1fa48b325c 100644
--- a/src/test/validation_block_tests.cpp
+++ b/src/test/validation_block_tests.cpp
@@ -279,7 +279,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
std::list<CTransactionRef> plTxnReplaced;
for (const auto& tx : txs) {
BOOST_REQUIRE(AcceptToMemoryPool(
- ::mempool,
+ *m_node.mempool,
state,
tx,
&plTxnReplaced,
@@ -290,8 +290,8 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// Check that all txs are in the pool
{
- LOCK(::mempool.cs);
- BOOST_CHECK_EQUAL(::mempool.mapTx.size(), txs.size());
+ LOCK(m_node.mempool->cs);
+ BOOST_CHECK_EQUAL(m_node.mempool->mapTx.size(), txs.size());
}
// Run a thread that simulates an RPC caller that is polling while
@@ -301,8 +301,8 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// the transactions invalidated by the reorg, or none of them, and
// not some intermediate amount.
while (true) {
- LOCK(::mempool.cs);
- if (::mempool.mapTx.size() == 0) {
+ LOCK(m_node.mempool->cs);
+ if (m_node.mempool->mapTx.size() == 0) {
// We are done with the reorg
break;
}
@@ -311,7 +311,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg)
// be atomic. So the caller assumes that the returned mempool
// is consistent. That is, it has all txs that were there
// before the reorg.
- assert(::mempool.mapTx.size() == txs.size());
+ assert(m_node.mempool->mapTx.size() == txs.size());
continue;
}
LOCK(cs_main);
diff --git a/src/txmempool.cpp b/src/txmempool.cpp
index 08f935c24f..3c967a04c0 100644
--- a/src/txmempool.cpp
+++ b/src/txmempool.cpp
@@ -17,6 +17,7 @@
#include <util/system.h>
#include <util/moneystr.h>
#include <util/time.h>
+#include <validationinterface.h>
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
int64_t _nTime, unsigned int _entryHeight,
@@ -403,7 +404,12 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces
void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason)
{
- NotifyEntryRemoved(it->GetSharedTx(), reason);
+ CTransactionRef ptx = it->GetSharedTx();
+ NotifyEntryRemoved(ptx, reason);
+ if (reason != MemPoolRemovalReason::BLOCK && reason != MemPoolRemovalReason::CONFLICT) {
+ GetMainSignals().TransactionRemovedFromMempool(ptx);
+ }
+
const uint256 hash = it->GetTx().GetHash();
for (const CTxIn& txin : it->GetTx().vin)
mapNextTx.erase(txin.prevout);
diff --git a/src/validation.cpp b/src/validation.cpp
index e744110371..8f5d333170 100644
--- a/src/validation.cpp
+++ b/src/validation.cpp
@@ -129,9 +129,6 @@ CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CBlockPolicyEstimator feeEstimator;
CTxMemPool mempool(&feeEstimator);
-/** Constant stuff for coinbase transactions we create: */
-CScript COINBASE_FLAGS;
-
// Internal stuff
namespace {
CBlockIndex* pindexBestInvalid = nullptr;
diff --git a/src/validation.h b/src/validation.h
index 31721cfbf7..d87113c41f 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -137,7 +137,6 @@ struct BlockHasher
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
};
-extern CScript COINBASE_FLAGS;
extern CCriticalSection cs_main;
extern CBlockPolicyEstimator feeEstimator;
extern CTxMemPool mempool;
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index 663308bae9..6c0f4d5edd 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -7,9 +7,9 @@
#include <primitives/block.h>
#include <scheduler.h>
-#include <txmempool.h>
#include <future>
+#include <unordered_map>
#include <utility>
#include <boost/signals2/signal.hpp>
@@ -46,11 +46,6 @@ struct MainSignalsInstance {
static CMainSignals g_signals;
-// This map has to a separate global instead of a member of MainSignalsInstance,
-// because RegisterWithMempoolSignals is currently called before RegisterBackgroundSignalScheduler,
-// so MainSignalsInstance hasn't been created yet.
-static std::unordered_map<CTxMemPool*, boost::signals2::scoped_connection> g_connNotifyEntryRemoved;
-
void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) {
assert(!m_internals);
m_internals.reset(new MainSignalsInstance(&scheduler));
@@ -71,17 +66,6 @@ size_t CMainSignals::CallbacksPending() {
return m_internals->m_schedulerClient.CallbacksPending();
}
-void CMainSignals::RegisterWithMempoolSignals(CTxMemPool& pool) {
- g_connNotifyEntryRemoved.emplace(std::piecewise_construct,
- std::forward_as_tuple(&pool),
- std::forward_as_tuple(pool.NotifyEntryRemoved.connect(std::bind(&CMainSignals::MempoolEntryRemoved, this, std::placeholders::_1, std::placeholders::_2)))
- );
-}
-
-void CMainSignals::UnregisterWithMempoolSignals(CTxMemPool& pool) {
- g_connNotifyEntryRemoved.erase(&pool);
-}
-
CMainSignals& GetMainSignals()
{
return g_signals;
@@ -126,13 +110,6 @@ void SyncWithValidationInterfaceQueue() {
promise.get_future().wait();
}
-void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason reason) {
- if (reason != MemPoolRemovalReason::BLOCK && reason != MemPoolRemovalReason::CONFLICT) {
- m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] {
- m_internals->TransactionRemovedFromMempool(ptx);
- });
- }
-}
void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
// Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
@@ -150,6 +127,12 @@ void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
});
}
+void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
+ m_internals->m_schedulerClient.AddToProcessQueue([ptx, this] {
+ m_internals->TransactionRemovedFromMempool(ptx);
+ });
+}
+
void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>>& pvtxConflicted) {
m_internals->m_schedulerClient.AddToProcessQueue([pblock, pindex, pvtxConflicted, this] {
m_internals->BlockConnected(pblock, pindex, *pvtxConflicted);
diff --git a/src/validationinterface.h b/src/validationinterface.h
index 6a8059a6a0..63aad8661f 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -21,8 +21,6 @@ class CConnman;
class CValidationInterface;
class uint256;
class CScheduler;
-class CTxMemPool;
-enum class MemPoolRemovalReason;
// These functions dispatch to one or all registered wallets
@@ -158,8 +156,6 @@ private:
friend void ::UnregisterAllValidationInterfaces();
friend void ::CallFunctionInValidationInterfaceQueue(std::function<void ()> func);
- void MempoolEntryRemoved(CTransactionRef tx, MemPoolRemovalReason reason);
-
public:
/** Register a CScheduler to give callbacks which should run in the background (may only be called once) */
void RegisterBackgroundSignalScheduler(CScheduler& scheduler);
@@ -170,13 +166,10 @@ public:
size_t CallbacksPending();
- /** Register with mempool to call TransactionRemovedFromMempool callbacks */
- void RegisterWithMempoolSignals(CTxMemPool& pool);
- /** Unregister with mempool */
- void UnregisterWithMempoolSignals(CTxMemPool& pool);
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
void TransactionAddedToMempool(const CTransactionRef &);
+ void TransactionRemovedFromMempool(const CTransactionRef &);
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::shared_ptr<const std::vector<CTransactionRef>> &);
void BlockDisconnected(const std::shared_ptr<const CBlock> &, const CBlockIndex* pindex);
void ChainStateFlushed(const CBlockLocator &);
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index a8b3df1f2e..8b09dc9ad0 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -58,7 +58,7 @@ void WalletInit::AddWalletOptions() const
gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
- gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
+ gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::WALLET);
#if HAVE_SYSTEM
gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", ArgsManager::ALLOW_ANY, OptionsCategory::WALLET);
#endif
diff --git a/src/wallet/psbtwallet.cpp b/src/wallet/psbtwallet.cpp
index aa13cacca4..96c1ad8d3f 100644
--- a/src/wallet/psbtwallet.cpp
+++ b/src/wallet/psbtwallet.cpp
@@ -39,12 +39,35 @@ TransactionError FillPSBT(const CWallet* pwallet, PartiallySignedTransaction& ps
return TransactionError::SIGHASH_MISMATCH;
}
- complete &= SignPSBTInput(HidingSigningProvider(pwallet->GetSigningProvider(), !sign, !bip32derivs), psbtx, i, sighash_type);
+ // Get the scriptPubKey to know which SigningProvider to use
+ CScript script;
+ if (!input.witness_utxo.IsNull()) {
+ script = input.witness_utxo.scriptPubKey;
+ } else if (input.non_witness_utxo) {
+ script = input.non_witness_utxo->vout[txin.prevout.n].scriptPubKey;
+ } else {
+ // There's no UTXO so we can just skip this now
+ complete = false;
+ continue;
+ }
+ SignatureData sigdata;
+ input.FillSignatureData(sigdata);
+ const SigningProvider* provider = pwallet->GetSigningProvider(script, sigdata);
+ if (!provider) {
+ complete = false;
+ continue;
+ }
+
+ complete &= SignPSBTInput(HidingSigningProvider(provider, !sign, !bip32derivs), psbtx, i, sighash_type);
}
// Fill in the bip32 keypaths and redeemscripts for the outputs so that hardware wallets can identify change
for (unsigned int i = 0; i < psbtx.tx->vout.size(); ++i) {
- UpdatePSBTOutput(HidingSigningProvider(pwallet->GetSigningProvider(), true, !bip32derivs), psbtx, i);
+ const CTxOut& out = psbtx.tx->vout.at(i);
+ const SigningProvider* provider = pwallet->GetSigningProvider(out.scriptPubKey);
+ if (provider) {
+ UpdatePSBTOutput(HidingSigningProvider(provider, true, !bip32derivs), psbtx, i);
+ }
}
return TransactionError::OK;
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 00742aed7a..3563b05c71 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -560,7 +560,11 @@ static UniValue signmessage(const JSONRPCRequest& request)
throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key");
}
- const SigningProvider* provider = pwallet->GetSigningProvider();
+ CScript script_pub_key = GetScriptForDestination(*pkhash);
+ const SigningProvider* provider = pwallet->GetSigningProvider(script_pub_key);
+ if (!provider) {
+ throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available");
+ }
CKey key;
CKeyID keyID(*pkhash);
@@ -2940,34 +2944,36 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("label", i->second.name);
}
- const SigningProvider* provider = pwallet->GetSigningProvider();
- if (scriptPubKey.IsPayToScriptHash()) {
- const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
- CScript redeemScript;
- if (provider->GetCScript(hash, redeemScript)) {
- entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
- // Now check if the redeemScript is actually a P2WSH script
- CTxDestination witness_destination;
- if (redeemScript.IsPayToWitnessScriptHash()) {
- bool extracted = ExtractDestination(redeemScript, witness_destination);
- CHECK_NONFATAL(extracted);
- // Also return the witness script
- const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination);
- CScriptID id;
- CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
- CScript witnessScript;
- if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey);
+ if (provider) {
+ if (scriptPubKey.IsPayToScriptHash()) {
+ const CScriptID& hash = CScriptID(boost::get<ScriptHash>(address));
+ CScript redeemScript;
+ if (provider->GetCScript(hash, redeemScript)) {
+ entry.pushKV("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()));
+ // Now check if the redeemScript is actually a P2WSH script
+ CTxDestination witness_destination;
+ if (redeemScript.IsPayToWitnessScriptHash()) {
+ bool extracted = ExtractDestination(redeemScript, witness_destination);
+ CHECK_NONFATAL(extracted);
+ // Also return the witness script
+ const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(witness_destination);
+ CScriptID id;
+ CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
+ CScript witnessScript;
+ if (provider->GetCScript(id, witnessScript)) {
+ entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ }
}
}
- }
- } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
- const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address);
- CScriptID id;
- CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
- CScript witnessScript;
- if (provider->GetCScript(id, witnessScript)) {
- entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ } else if (scriptPubKey.IsPayToWitnessScriptHash()) {
+ const WitnessV0ScriptHash& whash = boost::get<WitnessV0ScriptHash>(address);
+ CScriptID id;
+ CRIPEMD160().Write(whash.begin(), whash.size()).Finalize(id.begin());
+ CScript witnessScript;
+ if (provider->GetCScript(id, witnessScript)) {
+ entry.pushKV("witnessScript", HexStr(witnessScript.begin(), witnessScript.end()));
+ }
}
}
}
@@ -2978,8 +2984,11 @@ static UniValue listunspent(const JSONRPCRequest& request)
entry.pushKV("spendable", out.fSpendable);
entry.pushKV("solvable", out.fSolvable);
if (out.fSolvable) {
- auto descriptor = InferDescriptor(scriptPubKey, *pwallet->GetLegacyScriptPubKeyMan());
- entry.pushKV("desc", descriptor->ToString());
+ const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey);
+ if (provider) {
+ auto descriptor = InferDescriptor(scriptPubKey, *provider);
+ entry.pushKV("desc", descriptor->ToString());
+ }
}
if (avoid_reuse) entry.pushKV("reused", reused);
entry.pushKV("safe", out.fSafe);
@@ -3288,7 +3297,23 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request)
// Parse the prevtxs array
ParsePrevouts(request.params[1], nullptr, coins);
- return SignTransaction(mtx, &*pwallet->GetLegacyScriptPubKeyMan(), coins, request.params[2]);
+ std::set<const SigningProvider*> providers;
+ for (const std::pair<COutPoint, Coin> coin_pair : coins) {
+ const SigningProvider* provider = pwallet->GetSigningProvider(coin_pair.second.out.scriptPubKey);
+ if (provider) {
+ providers.insert(std::move(provider));
+ }
+ }
+ if (providers.size() == 0) {
+ // When there are no available providers, use DUMMY_SIGNING_PROVIDER so we can check if the tx is complete
+ providers.insert(&DUMMY_SIGNING_PROVIDER);
+ }
+
+ UniValue result(UniValue::VOBJ);
+ for (const SigningProvider* provider : providers) {
+ SignTransaction(mtx, provider, coins, request.params[2], result);
+ }
+ return result;
}
static UniValue bumpfee(const JSONRPCRequest& request)
@@ -3653,9 +3678,10 @@ static UniValue DescribeWalletAddress(CWallet* pwallet, const CTxDestination& de
{
UniValue ret(UniValue::VOBJ);
UniValue detail = DescribeAddress(dest);
+ CScript script = GetScriptForDestination(dest);
const SigningProvider* provider = nullptr;
if (pwallet) {
- provider = pwallet->GetSigningProvider();
+ provider = pwallet->GetSigningProvider(script);
}
ret.pushKVs(detail);
ret.pushKVs(boost::apply_visitor(DescribeWalletAddressVisitor(provider), dest));
@@ -3747,11 +3773,11 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
CScript scriptPubKey = GetScriptForDestination(dest);
ret.pushKV("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()));
- const SigningProvider* provider = pwallet->GetSigningProvider();
+ const SigningProvider* provider = pwallet->GetSigningProvider(scriptPubKey);
isminetype mine = pwallet->IsMine(dest);
ret.pushKV("ismine", bool(mine & ISMINE_SPENDABLE));
- bool solvable = IsSolvable(*provider, scriptPubKey);
+ bool solvable = provider && IsSolvable(*provider, scriptPubKey);
ret.pushKV("solvable", solvable);
if (solvable) {
ret.pushKV("desc", InferDescriptor(scriptPubKey, *provider)->ToString());
@@ -3764,7 +3790,7 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
}
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
- ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan();
+ ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
if (spk_man) {
if (const CKeyMetadata* meta = spk_man->GetMetadata(dest)) {
ret.pushKV("timestamp", meta->nCreateTime);
diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp
index bc068f1499..7e9f88f6b7 100644
--- a/src/wallet/test/coinselector_tests.cpp
+++ b/src/wallet/test/coinselector_tests.cpp
@@ -55,7 +55,7 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set)
set.emplace(MakeTransactionRef(tx), nInput);
}
-static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
+static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
{
balance += nValue;
static int nextLockTime = 0;
@@ -63,21 +63,31 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
tx.vout.resize(nInput + 1);
tx.vout[nInput].nValue = nValue;
+ if (spendable) {
+ CTxDestination dest;
+ std::string error;
+ assert(wallet.GetNewDestination(OutputType::BECH32, "", dest, error));
+ tx.vout[nInput].scriptPubKey = GetScriptForDestination(dest);
+ }
if (fIsFromMe) {
// IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(),
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
tx.vin.resize(1);
}
- std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&testWallet, MakeTransactionRef(std::move(tx)));
+ std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx)));
if (fIsFromMe)
{
wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1);
}
COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */);
vCoins.push_back(output);
- testWallet.AddToWallet(*wtx.get());
+ wallet.AddToWallet(*wtx.get());
wtxn.emplace_back(std::move(wtx));
}
+static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false)
+{
+ add_coin(testWallet, nValue, nAge, fIsFromMe, nInput, spendable);
+}
static void empty_wallet(void)
{
@@ -252,17 +262,33 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
vCoins.at(0).nInputBytes = 40; // Make sure that it has a negative effective value. The next check should assert if this somehow got through. Otherwise it will fail
BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
- // Make sure that we aren't using BnB when there are preset inputs
+ // Test fees subtracted from output:
+ empty_wallet();
+ add_coin(1 * CENT);
+ vCoins.at(0).nInputBytes = 40;
+ BOOST_CHECK(!testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
+ coin_selection_params_bnb.m_subtract_fee_outputs = true;
+ BOOST_CHECK(testWallet.SelectCoinsMinConf( 1 * CENT, filter_standard, GroupCoins(vCoins), setCoinsRet, nValueRet, coin_selection_params_bnb, bnb_used));
+ BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
+
+ // Make sure that can use BnB when there are preset inputs
empty_wallet();
- add_coin(5 * CENT);
- add_coin(3 * CENT);
- add_coin(2 * CENT);
- CCoinControl coin_control;
- coin_control.fAllowOtherInputs = true;
- coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
- BOOST_CHECK(testWallet.SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used));
- BOOST_CHECK(!bnb_used);
- BOOST_CHECK(!coin_selection_params_bnb.use_bnb);
+ {
+ std::unique_ptr<CWallet> wallet = MakeUnique<CWallet>(m_chain.get(), WalletLocation(), WalletDatabase::CreateMock());
+ bool firstRun;
+ wallet->LoadWallet(firstRun);
+ LOCK(wallet->cs_wallet);
+ add_coin(*wallet, 5 * CENT, 6 * 24, false, 0, true);
+ add_coin(*wallet, 3 * CENT, 6 * 24, false, 0, true);
+ add_coin(*wallet, 2 * CENT, 6 * 24, false, 0, true);
+ CCoinControl coin_control;
+ coin_control.fAllowOtherInputs = true;
+ coin_control.Select(COutPoint(vCoins.at(0).tx->GetHash(), vCoins.at(0).i));
+ coin_selection_params_bnb.effective_fee = CFeeRate(0);
+ BOOST_CHECK(wallet->SelectCoins(vCoins, 10 * CENT, setCoinsRet, nValueRet, coin_control, coin_selection_params_bnb, bnb_used));
+ BOOST_CHECK(bnb_used);
+ BOOST_CHECK(coin_selection_params_bnb.use_bnb);
+ }
}
BOOST_AUTO_TEST_CASE(knapsack_solver_test)
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 2f2931cef1..b1e1385ca3 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1363,7 +1363,11 @@ bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, bool use_max_sig
const CScript& scriptPubKey = txout.scriptPubKey;
SignatureData sigdata;
- const SigningProvider* provider = GetSigningProvider();
+ const SigningProvider* provider = GetSigningProvider(scriptPubKey);
+ if (!provider) {
+ // We don't know about this scriptpbuKey;
+ return false;
+ }
if (!ProduceSignature(*provider, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigdata)) {
return false;
@@ -1444,11 +1448,9 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri
int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
- // Look up the inputs. We should have already checked that this transaction
- // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our
- // wallet, with a valid index into the vout array, and the ability to sign.
for (const CTxIn& input : tx.vin) {
const auto mi = wallet->mapWallet.find(input.prevout.hash);
+ // Can not estimate size without knowing the input details
if (mi == wallet->mapWallet.end()) {
return -1;
}
@@ -1463,8 +1465,6 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall
{
CMutableTransaction txNew(tx);
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
- // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE)
- // implies that we can sign for every input.
return -1;
}
return GetVirtualTransactionSize(CTransaction(txNew));
@@ -2125,7 +2125,7 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
continue;
}
- const SigningProvider* provider = GetSigningProvider();
+ const SigningProvider* provider = GetSigningProvider(wtx.tx->vout[i].scriptPubKey);
bool solvable = provider ? IsSolvable(*provider, wtx.tx->vout[i].scriptPubKey) : false;
bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable));
@@ -2160,7 +2160,7 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch
for (const COutput& coin : availableCoins) {
CTxDestination address;
- if (coin.fSpendable &&
+ if ((coin.fSpendable || (IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS) && coin.fSolvable)) &&
ExtractDestination(FindNonChangeParentOutput(*coin.tx->tx, coin.i).scriptPubKey, address)) {
result[address].emplace_back(std::move(coin));
}
@@ -2168,12 +2168,16 @@ std::map<CTxDestination, std::vector<COutput>> CWallet::ListCoins(interfaces::Ch
std::vector<COutPoint> lockedCoins;
ListLockedCoins(lockedCoins);
+ // Include watch-only for wallets without private keys
+ const bool include_watch_only = IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS);
+ const isminetype is_mine_filter = include_watch_only ? ISMINE_WATCH_ONLY : ISMINE_SPENDABLE;
for (const COutPoint& output : lockedCoins) {
auto it = mapWallet.find(output.hash);
if (it != mapWallet.end()) {
int depth = it->second.GetDepthInMainChain();
if (depth >= 0 && output.n < it->second.tx->vout.size() &&
- IsMine(it->second.tx->vout[output.n]) == ISMINE_SPENDABLE) {
+ IsMine(it->second.tx->vout[output.n]) == is_mine_filter
+ ) {
CTxDestination address;
if (ExtractDestination(FindNonChangeParentOutput(*it->second.tx, output.n).scriptPubKey, address)) {
result[address].emplace_back(
@@ -2234,7 +2238,11 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
if (effective_value > 0) {
group.fee += coin.m_input_bytes < 0 ? 0 : coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
group.long_term_fee += coin.m_input_bytes < 0 ? 0 : long_term_feerate.GetFee(coin.m_input_bytes);
- group.effective_value += effective_value;
+ if (coin_selection_params.m_subtract_fee_outputs) {
+ group.effective_value += coin.txout.nValue;
+ } else {
+ group.effective_value += effective_value;
+ }
++it;
} else {
it = group.Discard(coin);
@@ -2260,6 +2268,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params, bool& bnb_used) const
{
std::vector<COutput> vCoins(vAvailableCoins);
+ CAmount value_to_select = nTargetValue;
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs)
@@ -2285,22 +2294,33 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
coin_control.ListSelected(vPresetInputs);
for (const COutPoint& outpoint : vPresetInputs)
{
- // For now, don't use BnB if preset inputs are selected. TODO: Enable this later
- bnb_used = false;
- coin_selection_params.use_bnb = false;
-
std::map<uint256, CWalletTx>::const_iterator it = mapWallet.find(outpoint.hash);
if (it != mapWallet.end())
{
const CWalletTx& wtx = it->second;
// Clearly invalid input, fail
- if (wtx.tx->vout.size() <= outpoint.n)
+ if (wtx.tx->vout.size() <= outpoint.n) {
+ bnb_used = false;
return false;
+ }
// Just to calculate the marginal byte size
- nValueFromPresetInputs += wtx.tx->vout[outpoint.n].nValue;
- setPresetCoins.insert(CInputCoin(wtx.tx, outpoint.n));
- } else
+ CInputCoin coin(wtx.tx, outpoint.n, wtx.GetSpendSize(outpoint.n, false));
+ nValueFromPresetInputs += coin.txout.nValue;
+ if (coin.m_input_bytes <= 0) {
+ bnb_used = false;
+ return false; // Not solvable, can't estimate size for fee
+ }
+ coin.effective_value = coin.txout.nValue - coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
+ if (coin_selection_params.use_bnb) {
+ value_to_select -= coin.effective_value;
+ } else {
+ value_to_select -= coin.txout.nValue;
+ }
+ setPresetCoins.insert(coin);
+ } else {
+ bnb_used = false;
return false; // TODO: Allow non-wallet inputs
+ }
}
// remove preset inputs from vCoins
@@ -2329,14 +2349,14 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
size_t max_descendants = (size_t)std::max<int64_t>(1, limit_descendant_count);
bool fRejectLongChains = gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS);
- bool res = nTargetValue <= nValueFromPresetInputs ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
- (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(nTargetValue - nValueFromPresetInputs, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
+ bool res = value_to_select <= 0 ||
+ SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 6, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
+ SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(1, 1, 0), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, 2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::min((size_t)4, max_ancestors/3), std::min((size_t)4, max_descendants/3)), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors/2, max_descendants/2), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, max_ancestors-1, max_descendants-1), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used)) ||
+ (m_spend_zero_conf_change && !fRejectLongChains && SelectCoinsMinConf(value_to_select, CoinEligibilityFilter(0, 1, std::numeric_limits<uint64_t>::max()), groups, setCoinsRet, nValueRet, coin_selection_params, bnb_used));
// because SelectCoinsMinConf clears the setCoinsRet, we now add the possible inputs to the coinset
util::insert(setCoinsRet, setPresetCoins);
@@ -2362,8 +2382,9 @@ bool CWallet::SignTransaction(CMutableTransaction& tx)
const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue;
SignatureData sigdata;
- const SigningProvider* provider = GetSigningProvider();
+ const SigningProvider* provider = GetSigningProvider(scriptPubKey);
if (!provider) {
+ // We don't know about this scriptpbuKey;
return false;
}
@@ -2519,7 +2540,8 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
CAmount nValue = 0;
- ReserveDestination reservedest(this);
+ const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
+ ReserveDestination reservedest(this, change_type);
int nChangePosRequest = nChangePosInOut;
unsigned int nSubtractFeeFromAmount = 0;
for (const auto& recipient : vecSend)
@@ -2578,8 +2600,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
return false;
}
CTxDestination dest;
- const OutputType change_type = TransactionChangeType(coin_control.m_change_type ? *coin_control.m_change_type : m_default_change_type, vecSend);
- bool ret = reservedest.GetReservedDestination(change_type, dest, true);
+ bool ret = reservedest.GetReservedDestination(dest, true);
if (!ret)
{
strFailReason = "Keypool ran out, please call keypoolrefill first";
@@ -2602,7 +2623,8 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// BnB selector is the only selector used when this is true.
// That should only happen on the first pass through the loop.
- coin_selection_params.use_bnb = nSubtractFeeFromAmount == 0; // If we are doing subtract fee from recipient, then don't use BnB
+ coin_selection_params.use_bnb = true;
+ coin_selection_params.m_subtract_fee_outputs = nSubtractFeeFromAmount != 0; // If we are doing subtract fee from recipient, don't use effective values
// Start with no fee and loop until there is enough fee
while (true)
{
@@ -2616,7 +2638,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
nValueToSelect += nFeeRet;
// vouts to the payees
- coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size = 11; // Static vsize overhead + outputs vsize. 4 nVersion, 4 nLocktime, 1 input count, 1 output count, 1 witness overhead (dummy, flag, stack size)
+ }
for (const auto& recipient : vecSend)
{
CTxOut txout(recipient.nAmount, recipient.scriptPubKey);
@@ -2633,7 +2657,9 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
}
// Include the fee cost for outputs. Note this is only used for BnB right now
- coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
+ if (!coin_selection_params.m_subtract_fee_outputs) {
+ coin_selection_params.tx_noinputs_size += ::GetSerializeSize(txout, PROTOCOL_VERSION);
+ }
if (IsDust(txout, chain().relayDustFee()))
{
@@ -2825,7 +2851,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
const CScript& scriptPubKey = coin.txout.scriptPubKey;
SignatureData sigdata;
- const SigningProvider* provider = GetSigningProvider();
+ const SigningProvider* provider = GetSigningProvider(scriptPubKey);
if (!provider || !ProduceSignature(*provider, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata))
{
strFailReason = _("Signing transaction failed").translated;
@@ -3098,8 +3124,8 @@ bool CWallet::GetNewChangeDestination(const OutputType type, CTxDestination& des
m_spk_man->TopUp();
- ReserveDestination reservedest(this);
- if (!reservedest.GetReservedDestination(type, dest, true)) {
+ ReserveDestination reservedest(this, type);
+ if (!reservedest.GetReservedDestination(dest, true)) {
error = "Error: Keypool ran out, please call keypoolrefill first";
return false;
}
@@ -3265,7 +3291,7 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co
return result;
}
-bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestination& dest, bool internal)
+bool ReserveDestination::GetReservedDestination(CTxDestination& dest, bool internal)
{
m_spk_man = pwallet->GetLegacyScriptPubKeyMan();
if (!m_spk_man) {
@@ -3286,7 +3312,6 @@ bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestin
fInternal = keypool.fInternal;
}
assert(vchPubKey.IsValid());
- m_spk_man->LearnRelatedScripts(vchPubKey, type);
address = GetDestinationForKey(vchPubKey, type);
dest = address;
return true;
@@ -3294,8 +3319,10 @@ bool ReserveDestination::GetReservedDestination(const OutputType type, CTxDestin
void ReserveDestination::KeepDestination()
{
- if (nIndex != -1)
+ if (nIndex != -1) {
m_spk_man->KeepDestination(nIndex);
+ m_spk_man->LearnRelatedScripts(vchPubKey, type);
+ }
nIndex = -1;
vchPubKey = CPubKey();
address = CNoDestination();
@@ -3651,9 +3678,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
if (auto spk_man = walletInstance->m_spk_man.get()) {
- std::string error;
if (!spk_man->Upgrade(prev_version, error)) {
- chain.initError(error);
return nullptr;
}
}
@@ -4024,12 +4049,17 @@ bool CWallet::Lock()
return true;
}
-ScriptPubKeyMan* CWallet::GetScriptPubKeyMan() const
+ScriptPubKeyMan* CWallet::GetScriptPubKeyMan(const CScript& script) const
+{
+ return m_spk_man.get();
+}
+
+const SigningProvider* CWallet::GetSigningProvider(const CScript& script) const
{
return m_spk_man.get();
}
-const SigningProvider* CWallet::GetSigningProvider() const
+const SigningProvider* CWallet::GetSigningProvider(const CScript& script, SignatureData& sigdata) const
{
return m_spk_man.get();
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index a6b9c0131a..fce49ec56c 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -140,8 +140,9 @@ class ReserveDestination
{
protected:
//! The wallet to reserve from
- CWallet* pwallet;
+ CWallet* const pwallet;
LegacyScriptPubKeyMan* m_spk_man{nullptr};
+ OutputType const type;
//! The index of the address's key in the keypool
int64_t nIndex{-1};
//! The public key for the address
@@ -153,10 +154,9 @@ protected:
public:
//! Construct a ReserveDestination object. This does NOT reserve an address yet
- explicit ReserveDestination(CWallet* pwalletIn)
- {
- pwallet = pwalletIn;
- }
+ explicit ReserveDestination(CWallet* pwallet, OutputType type)
+ : pwallet(pwallet)
+ , type(type) { }
ReserveDestination(const ReserveDestination&) = delete;
ReserveDestination& operator=(const ReserveDestination&) = delete;
@@ -168,7 +168,7 @@ public:
}
//! Reserve an address
- bool GetReservedDestination(const OutputType type, CTxDestination& pubkey, bool internal);
+ bool GetReservedDestination(CTxDestination& pubkey, bool internal);
//! Return reserved address
void ReturnDestination();
//! Keep the address. Do not return it's key to the keypool when this object goes out of scope
@@ -584,6 +584,8 @@ struct CoinSelectionParams
size_t change_spend_size = 0;
CFeeRate effective_fee = CFeeRate(0);
size_t tx_noinputs_size = 0;
+ //! Indicate that we are subtracting the fee from outputs
+ bool m_subtract_fee_outputs = false;
CoinSelectionParams(bool use_bnb, size_t change_output_size, size_t change_spend_size, CFeeRate effective_fee, size_t tx_noinputs_size) : use_bnb(use_bnb), change_output_size(change_output_size), change_spend_size(change_spend_size), effective_fee(effective_fee), tx_noinputs_size(tx_noinputs_size) {}
CoinSelectionParams() {}
@@ -1128,8 +1130,13 @@ public:
LogPrintf(("%s " + fmt).c_str(), GetDisplayName(), parameters...);
};
- ScriptPubKeyMan* GetScriptPubKeyMan() const;
- const SigningProvider* GetSigningProvider() const;
+ //! Get the ScriptPubKeyMan for a script
+ ScriptPubKeyMan* GetScriptPubKeyMan(const CScript& script) const;
+
+ //! Get the SigningProvider for a script
+ const SigningProvider* GetSigningProvider(const CScript& script) const;
+ const SigningProvider* GetSigningProvider(const CScript& script, SignatureData& sigdata) const;
+
LegacyScriptPubKeyMan* GetLegacyScriptPubKeyMan() const;
// Temporary LegacyScriptPubKeyMan accessors and aliases.