aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.bench.include5
-rw-r--r--src/Makefile.qt.include1
-rw-r--r--src/Makefile.qttest.include12
-rw-r--r--src/Makefile.test.include20
-rw-r--r--src/Makefile.test_util.include31
-rw-r--r--src/bitcoin-cli.cpp3
-rw-r--r--src/bitcoin-wallet.cpp4
-rw-r--r--src/consensus/validation.h4
-rw-r--r--src/cuckoocache.h4
-rw-r--r--src/init.cpp2
-rw-r--r--src/interfaces/wallet.cpp29
-rw-r--r--src/interfaces/wallet.h12
-rw-r--r--src/miner.cpp2
-rw-r--r--src/net.cpp10
-rw-r--r--src/prevector.h1
-rw-r--r--src/qt/bantablemodel.cpp2
-rw-r--r--src/qt/coincontroldialog.cpp2
-rw-r--r--src/qt/forms/modaloverlay.ui10
-rw-r--r--src/qt/forms/receivecoinsdialog.ui2
-rw-r--r--src/qt/forms/sendcoinsdialog.ui2
-rw-r--r--src/qt/guiutil.cpp86
-rw-r--r--src/qt/openuridialog.cpp2
-rw-r--r--src/qt/optionsdialog.cpp4
-rw-r--r--src/qt/paymentserver.h6
-rw-r--r--src/qt/peertablemodel.cpp2
-rw-r--r--src/qt/receiverequestdialog.cpp1
-rw-r--r--src/qt/receiverequestdialog.h4
-rw-r--r--src/qt/recentrequeststablemodel.cpp10
-rw-r--r--src/qt/recentrequeststablemodel.h6
-rw-r--r--src/qt/sendcoinsdialog.cpp84
-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/wallettests.cpp10
-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.cpp66
-rw-r--r--src/random.h24
-rw-r--r--src/randomenv.cpp25
-rw-r--r--src/rpc/mining.cpp5
-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/rpc/util.cpp6
-rw-r--r--src/rpc/util.h2
-rw-r--r--src/test/checkqueue_tests.cpp1
-rw-r--r--src/test/crypto_tests.cpp15
-rw-r--r--src/test/dbwrapper_tests.cpp12
-rw-r--r--src/test/transaction_tests.cpp23
-rw-r--r--src/txmempool.cpp8
-rw-r--r--src/util/system.cpp8
-rw-r--r--src/util/system.h4
-rw-r--r--src/validation.cpp3
-rw-r--r--src/validation.h2
-rw-r--r--src/validationinterface.cpp31
-rw-r--r--src/validationinterface.h9
-rw-r--r--src/wallet/coincontrol.h6
-rw-r--r--src/wallet/feebumper.cpp7
-rw-r--r--src/wallet/init.cpp4
-rw-r--r--src/wallet/psbtwallet.cpp27
-rw-r--r--src/wallet/rpcwallet.cpp191
-rw-r--r--src/wallet/test/coinselector_tests.cpp52
-rw-r--r--src/wallet/wallet.cpp123
-rw-r--r--src/wallet/wallet.h25
71 files changed, 739 insertions, 496 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ffb97f26d7..27c87688b4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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 acd712c8a2..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) \
diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include
index 93e60ff832..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 \
diff --git a/src/Makefile.qttest.include b/src/Makefile.qttest.include
index 37e1da2a78..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
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index f287d23024..6ae15cc553 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -76,31 +76,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) \
@@ -216,7 +204,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
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/bitcoin-cli.cpp b/src/bitcoin-cli.cpp
index 592fcbe8dd..1035e730b8 100644
--- a/src/bitcoin-cli.cpp
+++ b/src/bitcoin-cli.cpp
@@ -256,7 +256,6 @@ public:
return batch[ID_BLOCKCHAININFO];
}
result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]);
- result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]);
result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]);
result.pushKV("headers", batch[ID_BLOCKCHAININFO]["result"]["headers"]);
result.pushKV("verificationprogress", batch[ID_BLOCKCHAININFO]["result"]["verificationprogress"]);
@@ -266,9 +265,7 @@ public:
result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
result.pushKV("chain", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"]));
if (!batch[ID_WALLETINFO]["result"].isNull()) {
- result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
- result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]);
result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]);
if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) {
result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]);
diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp
index 917ecd71c5..a2013edc05 100644
--- a/src/bitcoin-wallet.cpp
+++ b/src/bitcoin-wallet.cpp
@@ -41,8 +41,8 @@ static bool WalletAppInit(int argc, char* argv[])
}
if (argc < 2 || HelpRequested(gArgs)) {
std::string usage = strprintf("%s bitcoin-wallet version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n\n" +
- "wallet-tool is an offline tool for creating and interacting with Bitcoin Core wallet files.\n" +
- "By default wallet-tool will act on wallets in the default mainnet wallet directory in the datadir.\n" +
+ "bitcoin-wallet is an offline tool for creating and interacting with Bitcoin Core wallet files.\n" +
+ "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n" +
"To change the target wallet, use the -datadir, -wallet and -testnet/-regtest arguments.\n\n" +
"Usage:\n" +
" bitcoin-wallet [options] <command>\n\n" +
diff --git a/src/consensus/validation.h b/src/consensus/validation.h
index e602b9d5f3..3401eb64ca 100644
--- a/src/consensus/validation.h
+++ b/src/consensus/validation.h
@@ -114,7 +114,7 @@ inline ValidationState::~ValidationState() {};
class TxValidationState : public ValidationState {
private:
- TxValidationResult m_result;
+ TxValidationResult m_result = TxValidationResult::TX_RESULT_UNSET;
public:
bool Invalid(TxValidationResult result,
const std::string &reject_reason="",
@@ -129,7 +129,7 @@ public:
class BlockValidationState : public ValidationState {
private:
- BlockValidationResult m_result;
+ BlockValidationResult m_result = BlockValidationResult::BLOCK_RESULT_UNSET;
public:
bool Invalid(BlockValidationResult result,
const std::string &reject_reason="",
diff --git a/src/cuckoocache.h b/src/cuckoocache.h
index 674f47b956..4ad5818cdc 100644
--- a/src/cuckoocache.h
+++ b/src/cuckoocache.h
@@ -6,11 +6,11 @@
#define BITCOIN_CUCKOOCACHE_H
#include <array>
-#include <algorithm>
#include <atomic>
-#include <cstring>
#include <cmath>
+#include <cstring>
#include <memory>
+#include <utility>
#include <vector>
diff --git a/src/init.cpp b/src/init.cpp
index 60209eb60a..e7dda59590 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -281,7 +281,6 @@ void Shutdown(NodeContext& node)
node.chain_clients.clear();
UnregisterAllValidationInterfaces();
GetMainSignals().UnregisterBackgroundSignalScheduler();
- GetMainSignals().UnregisterWithMempoolSignals(mempool);
globalVerifyHandle.reset();
ECC_Stop();
if (node.mempool) node.mempool = nullptr;
@@ -1263,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
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/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/net.cpp b/src/net.cpp
index 84692d2a79..99dae88bab 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -16,6 +16,7 @@
#include <crypto/sha256.h>
#include <netbase.h>
#include <net_permissions.h>
+#include <random.h>
#include <scheduler.h>
#include <ui_interface.h>
#include <util/strencodings.h>
@@ -445,6 +446,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false, block_relay_only);
pnode->AddRef();
+ // We're making a new connection, harvest entropy from the time (and our peer count)
+ RandAddEvent((uint32_t)id);
+
return pnode;
}
@@ -693,6 +697,9 @@ CNetMessage V1TransportDeserializer::GetMessage(const CMessageHeader::MessageSta
msg.m_message_size = hdr.nMessageSize;
msg.m_raw_message_size = hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
+ // We just received a message off the wire, harvest entropy from the time (and the message checksum)
+ RandAddEvent(ReadLE32(hash.begin()));
+
msg.m_valid_checksum = (memcmp(hash.begin(), hdr.pchChecksum, CMessageHeader::CHECKSUM_SIZE) == 0);
if (!msg.m_valid_checksum) {
LogPrint(BCLog::NET, "CHECKSUM ERROR (%s, %u bytes), expected %s was %s\n",
@@ -1017,6 +1024,9 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
LOCK(cs_vNodes);
vNodes.push_back(pnode);
}
+
+ // We received a new connection, harvest entropy from the time (and our peer count)
+ RandAddEvent((uint32_t)id);
}
void CConnman::DisconnectNodes()
diff --git a/src/prevector.h b/src/prevector.h
index d307495fbe..4fb07688ff 100644
--- a/src/prevector.h
+++ b/src/prevector.h
@@ -13,6 +13,7 @@
#include <algorithm>
#include <cstddef>
#include <type_traits>
+#include <utility>
#pragma pack(push, 1)
/** Implements a drop-in replacement for std::vector<T> which stores up to N
diff --git a/src/qt/bantablemodel.cpp b/src/qt/bantablemodel.cpp
index 48201b420e..b6c6984b10 100644
--- a/src/qt/bantablemodel.cpp
+++ b/src/qt/bantablemodel.cpp
@@ -8,7 +8,7 @@
#include <net_types.h> // For banmap_t
#include <qt/clientmodel.h>
-#include <algorithm>
+#include <utility>
#include <QDebug>
#include <QList>
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/modaloverlay.ui b/src/qt/forms/modaloverlay.ui
index b5a69c578d..da19a6fa2e 100644
--- a/src/qt/forms/modaloverlay.ui
+++ b/src/qt/forms/modaloverlay.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ModalOverlay</class>
- <widget class="ModalOverlay" name="ModalOverlay">
+ <widget class="QWidget" name="ModalOverlay">
<property name="geometry">
<rect>
<x>0</x>
@@ -369,14 +369,6 @@ QLabel { color: rgb(40,40,40); }</string>
</item>
</layout>
</widget>
- <customwidgets>
- <customwidget>
- <class>ModalOverlay</class>
- <extends>QWidget</extends>
- <header>qt/modaloverlay.h</header>
- <container>1</container>
- </customwidget>
- </customwidgets>
<resources/>
<connections/>
</ui>
diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui
index 64f52fcf45..7dbee6d689 100644
--- a/src/qt/forms/receivecoinsdialog.ui
+++ b/src/qt/forms/receivecoinsdialog.ui
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ReceiveCoinsDialog</class>
- <widget class="QWidget" name="ReceiveCoinsDialog">
+ <widget class="QDialog" name="ReceiveCoinsDialog">
<property name="geometry">
<rect>
<x>0</x>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index 7190d59240..cfd4bf33d4 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -1190,7 +1190,7 @@ Note: Since the fee is calculated on a per-byte basis, a fee of "100 satoshis p
<number>3</number>
</property>
<item>
- <widget class="QLabel" name="label">
+ <widget class="QLabel" name="labelBalanceName">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp
index c4e0321f28..2bb9535441 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>
@@ -54,10 +54,7 @@
#include <QUrlQuery>
#if defined(Q_OS_MAC)
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-#include <CoreServices/CoreServices.h>
#include <QProcess>
void ForceActivation();
@@ -691,87 +688,6 @@ bool SetStartOnSystemStartup(bool fAutoStart)
return true;
}
-
-#elif defined(Q_OS_MAC) && defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED <= 101100
-// based on: https://github.com/Mozketo/LaunchAtLoginController/blob/master/LaunchAtLoginController.m
-
-LSSharedFileListItemRef findStartupItemInList(CFArrayRef listSnapshot, LSSharedFileListRef list, CFURLRef findUrl)
-{
- if (listSnapshot == nullptr) {
- return nullptr;
- }
-
- // loop through the list of startup items and try to find the bitcoin app
- for(int i = 0; i < CFArrayGetCount(listSnapshot); i++) {
- LSSharedFileListItemRef item = (LSSharedFileListItemRef)CFArrayGetValueAtIndex(listSnapshot, i);
- UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
- CFURLRef currentItemURL = nullptr;
-
-#if defined(MAC_OS_X_VERSION_MAX_ALLOWED) && MAC_OS_X_VERSION_MAX_ALLOWED >= 10100
- if(&LSSharedFileListItemCopyResolvedURL)
- currentItemURL = LSSharedFileListItemCopyResolvedURL(item, resolutionFlags, nullptr);
-#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED < 10100
- else
- LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, nullptr);
-#endif
-#else
- LSSharedFileListItemResolve(item, resolutionFlags, &currentItemURL, nullptr);
-#endif
-
- if(currentItemURL) {
- if (CFEqual(currentItemURL, findUrl)) {
- // found
- CFRelease(currentItemURL);
- return item;
- }
- CFRelease(currentItemURL);
- }
- }
- return nullptr;
-}
-
-bool GetStartOnSystemStartup()
-{
- CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
- if (bitcoinAppUrl == nullptr) {
- return false;
- }
-
- LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr);
- CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(loginItems, nullptr);
- bool res = (findStartupItemInList(listSnapshot, loginItems, bitcoinAppUrl) != nullptr);
- CFRelease(bitcoinAppUrl);
- CFRelease(loginItems);
- CFRelease(listSnapshot);
- return res;
-}
-
-bool SetStartOnSystemStartup(bool fAutoStart)
-{
- CFURLRef bitcoinAppUrl = CFBundleCopyBundleURL(CFBundleGetMainBundle());
- if (bitcoinAppUrl == nullptr) {
- return false;
- }
-
- LSSharedFileListRef loginItems = LSSharedFileListCreate(nullptr, kLSSharedFileListSessionLoginItems, nullptr);
- CFArrayRef listSnapshot = LSSharedFileListCopySnapshot(loginItems, nullptr);
- LSSharedFileListItemRef foundItem = findStartupItemInList(listSnapshot, loginItems, bitcoinAppUrl);
-
- if(fAutoStart && !foundItem) {
- // add bitcoin app to startup item list
- LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, nullptr, nullptr, bitcoinAppUrl, nullptr, nullptr);
- }
- else if(!fAutoStart && foundItem) {
- // remove item
- LSSharedFileListItemRemove(loginItems, foundItem);
- }
-
- CFRelease(bitcoinAppUrl);
- CFRelease(loginItems);
- CFRelease(listSnapshot);
- return true;
-}
-#pragma GCC diagnostic pop
#else
bool GetStartOnSystemStartup() { return false; }
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/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index 57cafaaac0..d48c537c75 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -71,13 +71,11 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
#ifdef Q_OS_MAC
/* remove Window tab on Mac */
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->tabWindow));
-#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && MAC_OS_X_VERSION_MIN_REQUIRED > 101100
- /* hide launch at startup option if compiled against macOS > 10.11 (removed API) */
+ /* hide launch at startup option on macOS */
ui->bitcoinAtStartup->setVisible(false);
ui->verticalLayout_Main->removeWidget(ui->bitcoinAtStartup);
ui->verticalLayout_Main->removeItem(ui->horizontalSpacer_0_Main);
#endif
-#endif
/* remove Wallet tab in case of -disablewallet */
if (!enableWallet) {
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/peertablemodel.cpp b/src/qt/peertablemodel.cpp
index af2a1bb0e5..514ff35bcd 100644
--- a/src/qt/peertablemodel.cpp
+++ b/src/qt/peertablemodel.cpp
@@ -10,7 +10,7 @@
#include <interfaces/node.h>
-#include <algorithm>
+#include <utility>
#include <QDebug>
#include <QList>
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..18fa5f417f 100644
--- a/src/qt/recentrequeststablemodel.cpp
+++ b/src/qt/recentrequeststablemodel.cpp
@@ -7,12 +7,12 @@
#include <qt/bitcoinunits.h>
#include <qt/guiutil.h>
#include <qt/optionsmodel.h>
+#include <qt/walletmodel.h>
#include <clientversion.h>
#include <streams.h>
-#include <algorithm>
-
+#include <utility>
RecentRequestsTableModel::RecentRequestsTableModel(WalletModel *parent) :
QAbstractTableModel(parent), walletModel(parent)
@@ -213,10 +213,10 @@ void RecentRequestsTableModel::updateDisplayUnit()
updateAmountColumnTitle();
}
-bool RecentRequestEntryLessThan::operator()(RecentRequestEntry &left, RecentRequestEntry &right) const
+bool RecentRequestEntryLessThan::operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const
{
- RecentRequestEntry *pLeft = &left;
- RecentRequestEntry *pRight = &right;
+ const RecentRequestEntry* pLeft = &left;
+ const RecentRequestEntry* pRight = &right;
if (order == Qt::DescendingOrder)
std::swap(pLeft, pRight);
diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h
index 130b709d46..f5085f7268 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:
@@ -43,7 +45,7 @@ class RecentRequestEntryLessThan
public:
RecentRequestEntryLessThan(int nColumn, Qt::SortOrder fOrder):
column(nColumn), order(fOrder) {}
- bool operator()(RecentRequestEntry &left, RecentRequestEntry &right) const;
+ bool operator()(const RecentRequestEntry& left, const RecentRequestEntry& right) const;
private:
int column;
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 8edcca684d..f1ea3e23e5 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -21,11 +21,13 @@
#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 <wallet/wallet.h>
#include <QFontMetrics>
#include <QScrollBar>
@@ -186,6 +188,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 +312,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 +375,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 +387,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;
}
@@ -526,7 +562,12 @@ void SendCoinsDialog::setBalance(const interfaces::WalletBalances& balances)
{
if(model && model->getOptionsModel())
{
- ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balances.balance));
+ CAmount balance = balances.balance;
+ if (model->privateKeysDisabled()) {
+ balance = balances.watch_only_balance;
+ ui->labelBalanceName->setText(tr("Watch-only balance:"));
+ }
+ ui->labelBalance->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), balance));
}
}
@@ -611,6 +652,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 +707,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 +916,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 +954,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/wallettests.cpp b/src/qt/test/wallettests.cpp
index 980de711db..dfd56511ea 100644
--- a/src/qt/test/wallettests.cpp
+++ b/src/qt/test/wallettests.cpp
@@ -170,6 +170,16 @@ void TestGUI(interfaces::Node& node)
sendCoinsDialog.setModel(&walletModel);
transactionView.setModel(&walletModel);
+ {
+ // Check balance in send dialog
+ QLabel* balanceLabel = sendCoinsDialog.findChild<QLabel*>("labelBalance");
+ QString balanceText = balanceLabel->text();
+ int unit = walletModel.getOptionsModel()->getDisplayUnit();
+ CAmount balance = walletModel.wallet().getBalance();
+ QString balanceComparison = BitcoinUnits::formatWithUnit(unit, balance, false, BitcoinUnits::separatorAlways);
+ QCOMPARE(balanceText, balanceComparison);
+ }
+
// Send two transactions, and verify they are added to transaction list.
TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel();
QCOMPARE(transactionTableModel->rowCount({}), 105);
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 47d76d8700..50b8477733 100644
--- a/src/random.cpp
+++ b/src/random.cpp
@@ -6,6 +6,7 @@
#include <random.h>
#include <compat/cpuid.h>
+#include <crypto/sha256.h>
#include <crypto/sha512.h>
#include <support/cleanse.h>
#ifdef WIN32
@@ -178,7 +179,7 @@ static uint64_t GetRdSeed() noexcept
/* Access to other hardware random number generators could be added here later,
* assuming it is sufficiently fast (in the order of a few hundred CPU cycles).
* Slower sources should probably be invoked separately, and/or only from
- * RandAddSeedSleep (which is called during idle background operation).
+ * RandAddPeriodic (which is called once a minute).
*/
static void InitHardwareRand() {}
static void ReportHardwareRand() {}
@@ -359,6 +360,9 @@ class RNGState {
uint64_t m_counter GUARDED_BY(m_mutex) = 0;
bool m_strongly_seeded GUARDED_BY(m_mutex) = false;
+ Mutex m_events_mutex;
+ CSHA256 m_events_hasher GUARDED_BY(m_events_mutex);
+
public:
RNGState() noexcept
{
@@ -369,6 +373,35 @@ public:
{
}
+ void AddEvent(uint32_t event_info) noexcept
+ {
+ LOCK(m_events_mutex);
+
+ m_events_hasher.Write((const unsigned char *)&event_info, sizeof(event_info));
+ // Get the low four bytes of the performance counter. This translates to roughly the
+ // subsecond part.
+ uint32_t perfcounter = (GetPerformanceCounter() & 0xffffffff);
+ m_events_hasher.Write((const unsigned char*)&perfcounter, sizeof(perfcounter));
+ }
+
+ /**
+ * Feed (the hash of) all events added through AddEvent() to hasher.
+ */
+ void SeedEvents(CSHA512& hasher) noexcept
+ {
+ // We use only SHA256 for the events hashing to get the ASM speedups we have for SHA256,
+ // since we want it to be fast as network peers may be able to trigger it repeatedly.
+ LOCK(m_events_mutex);
+
+ unsigned char events_hash[32];
+ m_events_hasher.Finalize(events_hash);
+ hasher.Write(events_hash, 32);
+
+ // Re-initialize the hasher with the finalized state to use later.
+ m_events_hasher.Reset();
+ m_events_hasher.Write(events_hash, 32);
+ }
+
/** Extract up to 32 bytes of entropy from the RNG state, mixing in new entropy from hasher.
*
* If this function has never been called with strong_seed = true, false is returned.
@@ -415,17 +448,7 @@ RNGState& GetRNGState() noexcept
/* 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
- * of MilliSleep in SeedSleep, which can (and does) support interruptions which
- * cause a boost::thread_interrupted to be thrown.
- *
- * This means that SeedSleep, and all functions that invoke it are throwing.
- * However, we know that GetRandBytes() and GetStrongRandBytes() never trigger
- * this sleeping logic, so they are noexcept. The same is true for all the
- * GetRand*() functions that use GetRandBytes() indirectly.
- *
- * TODO: After moving away from interruptible boost-based thread management,
- * everything can become noexcept here.
+ * None of the RNG code should ever throw any exception.
*/
static void SeedTimestamp(CSHA512& hasher) noexcept
@@ -449,7 +472,7 @@ static void SeedFast(CSHA512& hasher) noexcept
SeedTimestamp(hasher);
}
-static void SeedSlow(CSHA512& hasher) noexcept
+static void SeedSlow(CSHA512& hasher, RNGState& rng) noexcept
{
unsigned char buffer[32];
@@ -460,6 +483,9 @@ static void SeedSlow(CSHA512& hasher) noexcept
GetOSRand(buffer);
hasher.Write(buffer, sizeof(buffer));
+ // Add the events hasher into the mix
+ rng.SeedEvents(hasher);
+
// High-precision timestamp.
//
// Note that we also commit to a timestamp in the Fast seeder, so we indirectly commit to a
@@ -477,7 +503,7 @@ static void SeedStrengthen(CSHA512& hasher, RNGState& rng, int microseconds) noe
Strengthen(strengthen_seed, microseconds, hasher);
}
-static void SeedPeriodic(CSHA512& hasher, RNGState& rng)
+static void SeedPeriodic(CSHA512& hasher, RNGState& rng) noexcept
{
// Everything that the 'fast' seeder includes
SeedFast(hasher);
@@ -485,6 +511,9 @@ static void SeedPeriodic(CSHA512& hasher, RNGState& rng)
// High-precision timestamp
SeedTimestamp(hasher);
+ // Add the events hasher into the mix
+ rng.SeedEvents(hasher);
+
// Dynamic environment data (performance monitoring, ...)
auto old_size = hasher.Size();
RandAddDynamicEnv(hasher);
@@ -500,7 +529,7 @@ static void SeedStartup(CSHA512& hasher, RNGState& rng) noexcept
SeedHardwareSlow(hasher);
// Everything that the 'slow' seeder includes.
- SeedSlow(hasher);
+ SeedSlow(hasher, rng);
// Dynamic environment data (performance monitoring, ...)
auto old_size = hasher.Size();
@@ -520,7 +549,7 @@ enum class RNGLevel {
PERIODIC, //!< Called by RandAddPeriodic()
};
-static void ProcRand(unsigned char* out, int num, RNGLevel level)
+static void ProcRand(unsigned char* out, int num, RNGLevel level) noexcept
{
// Make sure the RNG is initialized first (as all Seed* function possibly need hwrand to be available).
RNGState& rng = GetRNGState();
@@ -533,7 +562,7 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
SeedFast(hasher);
break;
case RNGLevel::SLOW:
- SeedSlow(hasher);
+ SeedSlow(hasher, rng);
break;
case RNGLevel::PERIODIC:
SeedPeriodic(hasher, rng);
@@ -551,7 +580,8 @@ static void ProcRand(unsigned char* out, int num, RNGLevel level)
void GetRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::FAST); }
void GetStrongRandBytes(unsigned char* buf, int num) noexcept { ProcRand(buf, num, RNGLevel::SLOW); }
-void RandAddPeriodic() { ProcRand(nullptr, 0, RNGLevel::PERIODIC); }
+void RandAddPeriodic() noexcept { ProcRand(nullptr, 0, RNGLevel::PERIODIC); }
+void RandAddEvent(const uint32_t event_info) noexcept { GetRNGState().AddEvent(event_info); }
bool g_mock_deterministic_tests{false};
diff --git a/src/random.h b/src/random.h
index 2d8ab085e3..e1b105168d 100644
--- a/src/random.h
+++ b/src/random.h
@@ -40,17 +40,17 @@
* These entropy sources are slower, but designed to make sure the RNG state contains
* fresh data that is unpredictable to attackers.
*
- * - RandAddSeedSleep() seeds everything that fast seeding includes, but additionally:
- * - A high-precision timestamp before and after sleeping 1ms.
- * - (On Windows) Once every 10 minutes, performance monitoring data from the OS.
- - - Once every minute, strengthen the entropy for 10 ms using repeated SHA512.
- * These just exploit the fact the system is idle to improve the quality of the RNG
- * slightly.
+ * - RandAddPeriodic() seeds everything that fast seeding includes, but additionally:
+ * - A high-precision timestamp
+ * - Dynamic environment data (performance monitoring, ...)
+ * - Strengthen the entropy for 10 ms using repeated SHA512.
+ * This is run once every minute.
*
* On first use of the RNG (regardless of what function is called first), all entropy
* sources used in the 'slow' seeder are included, but also:
* - 256 bits from the hardware RNG (rdseed or rdrand) when available.
- * - (On Windows) Performance monitoring data from the OS.
+ * - Dynamic environment data (performance monitoring, ...)
+ * - Static environment data
* - Strengthen the entropy for 100 ms using repeated SHA512.
*
* When mixing in new entropy, H = SHA512(entropy || old_rng_state) is computed, and
@@ -87,7 +87,15 @@ void GetStrongRandBytes(unsigned char* buf, int num) noexcept;
*
* Thread-safe.
*/
-void RandAddPeriodic();
+void RandAddPeriodic() noexcept;
+
+/**
+ * Gathers entropy from the low bits of the time at which events occur. Should
+ * be called with a uint32_t describing the event at the time an event occurs.
+ *
+ * Thread-safe.
+ */
+void RandAddEvent(const uint32_t event_info) noexcept;
/**
* Fast randomness source. This is seeded once with secure random data, but
diff --git a/src/randomenv.cpp b/src/randomenv.cpp
index ec42ddabc3..6992c720ff 100644
--- a/src/randomenv.cpp
+++ b/src/randomenv.cpp
@@ -197,19 +197,30 @@ void AddAllCPUID(CSHA512& hasher)
// Iterate over all standard leaves
AddCPUID(hasher, 0, 0, ax, bx, cx, dx); // Returns max leaf in ax
uint32_t max = ax;
- for (uint32_t leaf = 1; leaf <= max; ++leaf) {
- for (uint32_t subleaf = 0;; ++subleaf) {
+ for (uint32_t leaf = 1; leaf <= max && leaf <= 0xFF; ++leaf) {
+ uint32_t maxsub = 0;
+ for (uint32_t subleaf = 0; subleaf <= 0xFF; ++subleaf) {
AddCPUID(hasher, leaf, subleaf, ax, bx, cx, dx);
- // Iterate over subleaves for leaf 4, 11, 13
- if (leaf != 4 && leaf != 11 && leaf != 13) break;
- if ((leaf == 4 || leaf == 13) && ax == 0) break;
- if (leaf == 11 && (cx & 0xFF00) == 0) break;
+ // Iterate subleafs for leaf values 4, 7, 11, 13
+ if (leaf == 4) {
+ if ((ax & 0x1f) == 0) break;
+ } else if (leaf == 7) {
+ if (subleaf == 0) maxsub = ax;
+ if (subleaf == maxsub) break;
+ } else if (leaf == 11) {
+ if ((cx & 0xff00) == 0) break;
+ } else if (leaf == 13) {
+ if (ax == 0 && bx == 0 && cx == 0 && dx == 0) break;
+ } else {
+ // For any other leaf, stop after subleaf 0.
+ break;
+ }
}
}
// Iterate over all extended leaves
AddCPUID(hasher, 0x80000000, 0, ax, bx, cx, dx); // Returns max extended leaf in ax
uint32_t ext_max = ax;
- for (uint32_t leaf = 0x80000001; leaf <= ext_max; ++leaf) {
+ for (uint32_t leaf = 0x80000001; leaf <= ext_max && leaf <= 0x800000FF; ++leaf) {
AddCPUID(hasher, leaf, 0, ax, bx, cx, dx);
}
}
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/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/rpc/util.cpp b/src/rpc/util.cpp
index cfa3509c65..0791a365fe 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -131,18 +131,18 @@ CPubKey HexToPubKey(const std::string& hex_in)
}
// Retrieves a public key for an address from the given FillableSigningProvider
-CPubKey AddrToPubKey(FillableSigningProvider* const keystore, const std::string& addr_in)
+CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in)
{
CTxDestination dest = DecodeDestination(addr_in);
if (!IsValidDestination(dest)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address: " + addr_in);
}
- CKeyID key = GetKeyForDestination(*keystore, dest);
+ CKeyID key = GetKeyForDestination(keystore, dest);
if (key.IsNull()) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("%s does not refer to a key", addr_in));
}
CPubKey vchPubKey;
- if (!keystore->GetPubKey(key, vchPubKey)) {
+ if (!keystore.GetPubKey(key, vchPubKey)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("no full public key for address %s", addr_in));
}
if (!vchPubKey.IsFullyValid()) {
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 221638aa9e..9304e1fefb 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -69,7 +69,7 @@ extern std::string HelpExampleCli(const std::string& methodname, const std::stri
extern std::string HelpExampleRpc(const std::string& methodname, const std::string& args);
CPubKey HexToPubKey(const std::string& hex_in);
-CPubKey AddrToPubKey(FillableSigningProvider* const keystore, const std::string& addr_in);
+CPubKey AddrToPubKey(const FillableSigningProvider& keystore, const std::string& addr_in);
CTxDestination AddAndGetMultisigDestination(const int required, const std::vector<CPubKey>& pubkeys, OutputType type, FillableSigningProvider& keystore, CScript& script_out);
UniValue DescribeAddress(const CTxDestination& dest);
diff --git a/src/test/checkqueue_tests.cpp b/src/test/checkqueue_tests.cpp
index 6745bb9015..482fe3772c 100644
--- a/src/test/checkqueue_tests.cpp
+++ b/src/test/checkqueue_tests.cpp
@@ -17,6 +17,7 @@
#include <condition_variable>
#include <unordered_set>
+#include <utility>
BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup)
diff --git a/src/test/crypto_tests.cpp b/src/test/crypto_tests.cpp
index 591a317d17..2deb0c5bfc 100644
--- a/src/test/crypto_tests.cpp
+++ b/src/test/crypto_tests.cpp
@@ -186,14 +186,15 @@ static void TestHKDF_SHA256_32(const std::string &ikm_hex, const std::string &sa
BOOST_CHECK(HexStr(out, out + 32) == okm_check_hex);
}
-static std::string LongTestString() {
+static std::string LongTestString()
+{
std::string ret;
- for (int i=0; i<200000; i++) {
- ret += (unsigned char)(i);
- ret += (unsigned char)(i >> 4);
- ret += (unsigned char)(i >> 8);
- ret += (unsigned char)(i >> 12);
- ret += (unsigned char)(i >> 16);
+ for (int i = 0; i < 200000; i++) {
+ ret += (char)(i);
+ ret += (char)(i >> 4);
+ ret += (char)(i >> 8);
+ ret += (char)(i >> 12);
+ ret += (char)(i >> 16);
}
return ret;
}
diff --git a/src/test/dbwrapper_tests.cpp b/src/test/dbwrapper_tests.cpp
index 57d5b2bb5c..b647c0f70b 100644
--- a/src/test/dbwrapper_tests.cpp
+++ b/src/test/dbwrapper_tests.cpp
@@ -397,6 +397,18 @@ BOOST_AUTO_TEST_CASE(iterator_string_ordering)
}
}
+BOOST_AUTO_TEST_CASE(unicodepath)
+{
+ // Attempt to create a database with a utf8 character in the path.
+ // On Windows this test will fail if the directory is created using
+ // the ANSI CreateDirectoryA call and the code page isn't UTF8.
+ // It will succeed if the created with CreateDirectoryW.
+ fs::path ph = GetDataDir() / "test_runner_₿_🏃_20191128_104644";
+ CDBWrapper dbw(ph, (1 << 20));
+
+ fs::path lockPath = ph / "LOCK";
+ BOOST_CHECK(boost::filesystem::exists(lockPath));
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index eb0050a4a3..2f7a3132d8 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -713,6 +713,29 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].nValue = nDustThreshold;
BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+ // Disallowed nVersion
+ t.nVersion = -1;
+ reason.clear();
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
+ BOOST_CHECK_EQUAL(reason, "version");
+
+ t.nVersion = 0;
+ reason.clear();
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
+ BOOST_CHECK_EQUAL(reason, "version");
+
+ t.nVersion = 3;
+ reason.clear();
+ BOOST_CHECK(!IsStandardTx(CTransaction(t), reason));
+ BOOST_CHECK_EQUAL(reason, "version");
+
+ // Allowed nVersion
+ t.nVersion = 1;
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+
+ t.nVersion = 2;
+ BOOST_CHECK(IsStandardTx(CTransaction(t), reason));
+
// Check dust with odd relay fee to verify rounding:
// nDustThreshold = 182 * 3702 / 1000
dustRelayFee = CFeeRate(3702);
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/util/system.cpp b/src/util/system.cpp
index 2a2ae6fdf5..563ff6a54b 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -1126,17 +1126,13 @@ fs::path AbsPathForConfigVal(const fs::path& path, bool net_specific)
return fs::absolute(path, GetDataDir(net_specific));
}
-int ScheduleBatchPriority()
+void ScheduleBatchPriority()
{
#ifdef SCHED_BATCH
const static sched_param param{};
- if (int ret = pthread_setschedparam(pthread_self(), SCHED_BATCH, &param)) {
+ if (pthread_setschedparam(pthread_self(), SCHED_BATCH, &param) != 0) {
LogPrintf("Failed to pthread_setschedparam: %s\n", strerror(errno));
- return ret;
}
- return 0;
-#else
- return 1;
#endif
}
diff --git a/src/util/system.h b/src/util/system.h
index e0b6371dc9..82903b5187 100644
--- a/src/util/system.h
+++ b/src/util/system.h
@@ -367,10 +367,8 @@ std::string CopyrightHolders(const std::string& strPrefix);
* On platforms that support it, tell the kernel the calling thread is
* CPU-intensive and non-interactive. See SCHED_BATCH in sched(7) for details.
*
- * @return The return value of sched_setschedule(), or 1 on systems without
- * sched_setschedule().
*/
-int ScheduleBatchPriority();
+void ScheduleBatchPriority();
namespace util {
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..54f97e7213 100644
--- a/src/validation.h
+++ b/src/validation.h
@@ -23,7 +23,6 @@
#include <versionbits.h>
#include <serialize.h>
-#include <algorithm>
#include <atomic>
#include <map>
#include <memory>
@@ -137,7 +136,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/coincontrol.h b/src/wallet/coincontrol.h
index fca4b75c45..2893d0ab3d 100644
--- a/src/wallet/coincontrol.h
+++ b/src/wallet/coincontrol.h
@@ -6,14 +6,18 @@
#define BITCOIN_WALLET_COINCONTROL_H
#include <optional.h>
+#include <outputtype.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <primitives/transaction.h>
-#include <wallet/wallet.h>
+#include <script/standard.h>
const int DEFAULT_MIN_DEPTH = 0;
const int DEFAULT_MAX_DEPTH = 9999999;
+//! Default for -avoidpartialspends
+static constexpr bool DEFAULT_AVOIDPARTIALSPENDS = false;
+
/** Coin Control Features. */
class CCoinControl
{
diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp
index 8f0b495ac4..36588eb7d1 100644
--- a/src/wallet/feebumper.cpp
+++ b/src/wallet/feebumper.cpp
@@ -108,12 +108,11 @@ static feebumper::Result CheckFeeRate(const CWallet& wallet, const CWalletTx& wt
return feebumper::Result::OK;
}
-static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, CCoinControl& coin_control, CAmount& old_fee)
+static CFeeRate EstimateFeeRate(const CWallet& wallet, const CWalletTx& wtx, const CAmount old_fee, CCoinControl& coin_control)
{
// Get the fee rate of the original transaction. This is calculated from
// the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the
// result.
- old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
int64_t txSize = GetVirtualTransactionSize(*(wtx.tx));
CFeeRate feerate(old_fee, txSize);
feerate += CFeeRate(1);
@@ -309,6 +308,8 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
}
}
+ old_fee = wtx.GetDebit(ISMINE_SPENDABLE) - wtx.tx->GetValueOut();
+
if (coin_control.m_feerate) {
// The user provided a feeRate argument.
// We calculate this here to avoid compiler warning on the cs_wallet lock
@@ -319,7 +320,7 @@ Result CreateRateBumpTransaction(CWallet& wallet, const uint256& txid, const CCo
}
} else {
// The user did not provide a feeRate argument
- new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, new_coin_control, old_fee);
+ new_coin_control.m_feerate = EstimateFeeRate(wallet, wtx, old_fee, new_coin_control);
}
// Fill in required inputs we are double-spending(all of them)
diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp
index a8b3df1f2e..dd0d2ffbd7 100644
--- a/src/wallet/init.cpp
+++ b/src/wallet/init.cpp
@@ -8,9 +8,11 @@
#include <net.h>
#include <node/context.h>
#include <outputtype.h>
+#include <ui_interface.h>
#include <util/moneystr.h>
#include <util/system.h>
#include <util/translation.h>
+#include <wallet/coincontrol.h>
#include <wallet/wallet.h>
#include <walletinitinterface.h>
@@ -58,7 +60,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..d906f6ddf0 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);
@@ -947,7 +951,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
}
RPCHelpMan{"addmultisigaddress",
- "\nAdd a nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
+ "\nAdd an nrequired-to-sign multisignature address to the wallet. Requires a new wallet backup.\n"
"Each key is a Bitcoin address or hex-encoded public key.\n"
"This functionality is only intended for use with non-watchonly addresses.\n"
"See `importaddress` for watchonly p2sh address support.\n"
@@ -994,7 +998,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
if (IsHex(keys_or_addrs[i].get_str()) && (keys_or_addrs[i].get_str().length() == 66 || keys_or_addrs[i].get_str().length() == 130)) {
pubkeys.push_back(HexToPubKey(keys_or_addrs[i].get_str()));
} else {
- pubkeys.push_back(AddrToPubKey(&spk_man, keys_or_addrs[i].get_str()));
+ pubkeys.push_back(AddrToPubKey(spk_man, keys_or_addrs[i].get_str()));
}
}
@@ -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));
@@ -3683,52 +3709,57 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
}
RPCHelpMan{"getaddressinfo",
- "\nReturn information about the given bitcoin address. Some information requires the address\n"
- "to be in the wallet.\n",
+ "\nReturn information about the given bitcoin address.\n"
+ "Some of the information will only be present if the address is in the active wallet.\n",
{
- {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address to get the information of."},
+ {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The bitcoin address for which to get information."},
},
RPCResult{
"{\n"
- " \"address\" : \"address\", (string) The bitcoin address validated\n"
- " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address\n"
- " \"ismine\" : true|false, (boolean) If the address is yours or not\n"
- " \"iswatchonly\" : true|false, (boolean) If the address is watchonly\n"
- " \"solvable\" : true|false, (boolean) Whether we know how to spend coins sent to this address, ignoring the possible lack of private keys\n"
- " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable)\n"
- " \"isscript\" : true|false, (boolean) If the key is a script\n"
- " \"ischange\" : true|false, (boolean) If the address was used for change output\n"
- " \"iswitness\" : true|false, (boolean) If the address is a witness address\n"
- " \"witness_version\" : version (numeric, optional) The version number of the witness program\n"
- " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program\n"
- " \"script\" : \"type\" (string, optional) The output script type. Only if \"isscript\" is true and the redeemscript is known. Possible types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash, witness_v0_scripthash, witness_unknown\n"
- " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address\n"
- " \"pubkeys\" (string, optional) Array of pubkeys associated with the known redeemscript (only if \"script\" is \"multisig\")\n"
+ " \"address\" : \"address\", (string) The bitcoin address validated.\n"
+ " \"scriptPubKey\" : \"hex\", (string) The hex-encoded scriptPubKey generated by the address.\n"
+ " \"ismine\" : true|false, (boolean) If the address is yours.\n"
+ " \"iswatchonly\" : true|false, (boolean) If the address is watchonly.\n"
+ " \"solvable\" : true|false, (boolean) If we know how to spend coins sent to this address, ignoring the possible lack of private keys.\n"
+ " \"desc\" : \"desc\", (string, optional) A descriptor for spending coins sent to this address (only when solvable).\n"
+ " \"isscript\" : true|false, (boolean) If the key is a script.\n"
+ " \"ischange\" : true|false, (boolean) If the address was used for change output.\n"
+ " \"iswitness\" : true|false, (boolean) If the address is a witness address.\n"
+ " \"witness_version\" : version (numeric, optional) The version number of the witness program.\n"
+ " \"witness_program\" : \"hex\" (string, optional) The hex value of the witness program.\n"
+ " \"script\" : \"type\" (string, optional) The output script type. Only if isscript is true and the redeemscript is known. Possible\n"
+ " types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
+ " witness_v0_scripthash, witness_unknown.\n"
+ " \"hex\" : \"hex\", (string, optional) The redeemscript for the p2sh address.\n"
+ " \"pubkeys\" (array, optional) Array of pubkeys associated with the known redeemscript (only if script is multisig).\n"
" [\n"
- " \"pubkey\"\n"
+ " \"pubkey\" (string)\n"
" ,...\n"
" ]\n"
- " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n"
- " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n"
- " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\").\n"
- " \"iscompressed\" : true|false, (boolean, optional) If the pubkey is compressed\n"
- " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default label\n"
- " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n"
- " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n"
- " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed\n"
- " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingperint of the master key.\n"
- " \"labels\" (object) Array of labels associated with the address.\n"
+ " \"sigsrequired\" : xxxxx (numeric, optional) The number of signatures required to spend multisig output (only if script is multisig).\n"
+ " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH).\n"
+ " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. Includes all\n"
+ " getaddressinfo output fields for the embedded address, excluding metadata (timestamp, hdkeypath,\n"
+ " hdseedid) and relation to the wallet (ismine, iswatchonly).\n"
+ " \"iscompressed\" : true|false, (boolean, optional) If the pubkey is compressed.\n"
+ " \"label\" : \"label\" (string) The label associated with the address. Defaults to \"\". Equivalent to the name field in the labels array.\n"
+ " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available, expressed in seconds since Epoch Time (Jan 1 1970 GMT).\n"
+ " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath, if the key is HD and available.\n"
+ " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed.\n"
+ " \"hdmasterfingerprint\" : \"<hash160>\" (string, optional) The fingerprint of the master key.\n"
+ " \"labels\" (object) An array of labels associated with the address. Currently limited to one label but returned\n"
+ " as an array to keep the API stable if multiple labels are enabled in the future.\n"
" [\n"
" { (json object of label data)\n"
- " \"name\": \"labelname\" (string) The label\n"
- " \"purpose\": \"string\" (string) Purpose of address (\"send\" for sending address, \"receive\" for receiving address)\n"
+ " \"name\": \"label name\" (string) The label name. Defaults to \"\". Equivalent to the label field above.\n"
+ " \"purpose\": \"purpose\" (string) The purpose of the associated address (send or receive).\n"
" },...\n"
" ]\n"
"}\n"
},
RPCExamples{
- HelpExampleCli("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
- + HelpExampleRpc("getaddressinfo", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"")
+ HelpExampleCli("getaddressinfo", "\"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl\"") +
+ HelpExampleRpc("getaddressinfo", "\"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl\"")
},
}.Check(request);
@@ -3747,24 +3778,40 @@ 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());
}
+
ret.pushKV("iswatchonly", bool(mine & ISMINE_WATCH_ONLY));
+
+ // Return DescribeWalletAddress fields.
+ // Always returned: isscript, ischange, iswitness.
+ // Optional: witness_version, witness_program, script, hex, pubkeys (array),
+ // sigsrequired, pubkey, embedded, iscompressed.
UniValue detail = DescribeWalletAddress(pwallet, dest);
ret.pushKVs(detail);
+
+ // Return label field if existing. Currently only one label can be
+ // associated with an address, so the label should be equivalent to the
+ // value of the name key/value pair in the labels hash array below.
if (pwallet->mapAddressBook.count(dest)) {
ret.pushKV("label", pwallet->mapAddressBook[dest].name);
}
+
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
- ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan();
+ // Fetch KeyMetadata, if present, for the timestamp, hdkeypath, hdseedid,
+ // and hdmasterfingerprint fields.
+ ScriptPubKeyMan* spk_man = pwallet->GetScriptPubKeyMan(scriptPubKey);
if (spk_man) {
if (const CKeyMetadata* meta = spk_man->GetMetadata(dest)) {
ret.pushKV("timestamp", meta->nCreateTime);
@@ -3776,9 +3823,11 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
}
}
- // Currently only one label can be associated with an address, return an array
- // so the API remains stable if we allow multiple labels to be associated with
- // an address.
+ // Return a labels array containing a hash of key/value pairs for the label
+ // name and address purpose. The name value is equivalent to the label field
+ // above. Currently only one label can be associated with an address, but we
+ // return an array so the API remains stable if we allow multiple labels to
+ // be associated with an address in the future.
UniValue labels(UniValue::VARR);
std::map<CTxDestination, CAddressBookData>::iterator mi = pwallet->mapAddressBook.find(dest);
if (mi != pwallet->mapAddressBook.end()) {
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 ff182c847f..0a8ce7caf1 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -822,7 +822,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
void CWallet::LoadToWallet(CWalletTx& wtxIn)
{
- // If wallet doesn't have a chain (e.g wallet-tool), lock can't be taken.
+ // If wallet doesn't have a chain (e.g bitcoin-wallet), lock can't be taken.
auto locked_chain = LockChain();
if (locked_chain) {
Optional<int> block_height = locked_chain->getBlockHeight(wtxIn.m_confirm.hashBlock);
@@ -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,13 +2268,14 @@ 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;
+
+ // Default to bnb was not used. If we use it, we set it later
+ bnb_used = false;
// coin control -> return all selected outputs (we want all selected to go into the transaction for sure)
if (coin_control.HasSelected() && !coin_control.fAllowOtherInputs)
{
- // We didn't use BnB here, so set it to false.
- bnb_used = false;
-
for (const COutput& out : vCoins)
{
if (!out.fSpendable)
@@ -2285,22 +2294,30 @@ 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) {
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) {
+ 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 {
return false; // TODO: Allow non-wallet inputs
+ }
}
// remove preset inputs from vCoins
@@ -2329,14 +2346,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 +2379,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 +2537,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 +2597,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 +2620,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 +2635,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 +2654,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()))
{
@@ -2652,7 +2675,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
}
// Choose coins to use
- bool bnb_used;
+ bool bnb_used = false;
if (pick_new_inputs) {
nValueIn = 0;
setCoins.clear();
@@ -2825,7 +2848,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;
@@ -2921,7 +2944,7 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet)
{
// Even if we don't use this lock in this function, we want to preserve
// lock order in LoadToWallet if query of chain state is needed to know
- // tx status. If lock can't be taken (e.g wallet-tool), tx confirmation
+ // tx status. If lock can't be taken (e.g bitcoin-wallet), tx confirmation
// status may be not reliable.
auto locked_chain = LockChain();
LOCK(cs_wallet);
@@ -3098,8 +3121,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 +3288,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 +3309,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 +3316,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();
@@ -4022,12 +4046,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..51df4a9a8b 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -71,8 +71,6 @@ static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
static const bool DEFAULT_SPEND_ZEROCONF_CHANGE = true;
//! Default for -walletrejectlongchains
static const bool DEFAULT_WALLET_REJECT_LONG_CHAINS = false;
-//! Default for -avoidpartialspends
-static const bool DEFAULT_AVOIDPARTIALSPENDS = false;
//! -txconfirmtarget default
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 6;
//! -walletrbf default
@@ -140,8 +138,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 +152,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 +166,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 +582,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 +1128,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.