aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build-aux/m4/ax_boost_base.m43
-rw-r--r--src/Makefile.test.include36
-rw-r--r--src/Makefile.test_util.include2
-rw-r--r--src/cuckoocache.h1
-rw-r--r--src/init.cpp15
-rw-r--r--src/interfaces/wallet.cpp11
-rw-r--r--src/net.cpp15
-rw-r--r--src/net.h20
-rw-r--r--src/net_processing.cpp70
-rw-r--r--src/net_processing.h1
-rw-r--r--src/qt/addresstablemodel.cpp2
-rw-r--r--src/qt/bitcoingui.cpp2
-rw-r--r--src/qt/test/addressbooktests.cpp2
-rw-r--r--src/rpc/blockchain.cpp4
-rw-r--r--src/rpc/mining.cpp4
-rw-r--r--src/rpc/misc.cpp4
-rw-r--r--src/rpc/net.cpp4
-rw-r--r--src/rpc/rawtransaction.cpp4
-rw-r--r--src/rpc/rawtransaction_util.cpp2
-rw-r--r--src/test/denialofservice_tests.cpp1
-rw-r--r--src/test/fuzz/addition_overflow.cpp55
-rw-r--r--src/test/fuzz/checkqueue.cpp65
-rw-r--r--src/test/fuzz/cuckoocache.cpp49
-rw-r--r--src/test/fuzz/fees.cpp26
-rw-r--r--src/test/fuzz/integer.cpp16
-rw-r--r--src/test/fuzz/process_message.cpp2
-rw-r--r--src/test/fuzz/process_messages.cpp75
-rw-r--r--src/test/fuzz/util.h11
-rw-r--r--src/test/util/net.cpp39
-rw-r--r--src/test/util/net.h33
-rw-r--r--src/test/util/setup_common.cpp5
-rw-r--r--src/test/validationinterface_tests.cpp60
-rw-r--r--src/validationinterface.cpp115
-rw-r--r--src/validationinterface.h4
-rw-r--r--src/wallet/rpcdump.cpp7
-rw-r--r--src/wallet/rpcwallet.cpp134
-rw-r--r--src/wallet/wallet.cpp52
-rw-r--r--src/wallet/wallet.h19
-rw-r--r--src/wallet/walletdb.cpp6
-rw-r--r--src/wallet/wallettool.cpp2
-rwxr-xr-xtest/functional/rpc_signrawtransaction.py43
-rwxr-xr-xtest/functional/wallet_avoidreuse.py25
-rwxr-xr-xtest/functional/wallet_bumpfee.py17
43 files changed, 849 insertions, 214 deletions
diff --git a/build-aux/m4/ax_boost_base.m4 b/build-aux/m4/ax_boost_base.m4
index 16fa69b41f..2ae33f7140 100644
--- a/build-aux/m4/ax_boost_base.m4
+++ b/build-aux/m4/ax_boost_base.m4
@@ -33,7 +33,7 @@
# and this notice are preserved. This file is offered as-is, without any
# warranty.
-#serial 47
+#serial 48
# example boost program (need to pass version)
m4_define([_AX_BOOST_BASE_PROGRAM],
@@ -123,6 +123,7 @@ AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
dnl are almost assuredly the ones desired.
AS_CASE([${host_cpu}],
[i?86],[multiarch_libsubdir="lib/i386-${host_os}"],
+ [armv7l],[multiarch_libsubdir="lib/arm-${host_os}"],
[multiarch_libsubdir="lib/${host_cpu}-${host_os}"]
)
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index cbce32429d..d49db4ddcc 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -3,6 +3,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
FUZZ_TARGETS = \
+ test/fuzz/addition_overflow \
test/fuzz/addr_info_deserialize \
test/fuzz/addrdb \
test/fuzz/address_deserialize \
@@ -27,13 +28,16 @@ FUZZ_TARGETS = \
test/fuzz/bloom_filter \
test/fuzz/bloomfilter_deserialize \
test/fuzz/chain \
+ test/fuzz/checkqueue \
test/fuzz/coins_deserialize \
+ test/fuzz/cuckoocache \
test/fuzz/decode_tx \
test/fuzz/descriptor_parse \
test/fuzz/diskblockindex_deserialize \
test/fuzz/eval_script \
test/fuzz/fee_rate \
test/fuzz/fee_rate_deserialize \
+ test/fuzz/fees \
test/fuzz/flat_file_pos_deserialize \
test/fuzz/flatfile \
test/fuzz/float \
@@ -63,6 +67,7 @@ FUZZ_TARGETS = \
test/fuzz/partially_signed_transaction_deserialize \
test/fuzz/pow \
test/fuzz/prefilled_transaction_deserialize \
+ test/fuzz/process_messages \
test/fuzz/process_message \
test/fuzz/process_message_addr \
test/fuzz/process_message_block \
@@ -240,6 +245,7 @@ BITCOIN_TESTS =\
test/util_tests.cpp \
test/validation_block_tests.cpp \
test/validation_flush_tests.cpp \
+ test/validationinterface_tests.cpp \
test/versionbits_tests.cpp
if ENABLE_WALLET
@@ -280,6 +286,12 @@ endif
if ENABLE_FUZZ
+test_fuzz_addition_overflow_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_addition_overflow_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_addition_overflow_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_addition_overflow_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_addition_overflow_SOURCES = test/fuzz/addition_overflow.cpp
+
test_fuzz_addr_info_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DADDR_INFO_DESERIALIZE=1
test_fuzz_addr_info_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_addr_info_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -424,12 +436,24 @@ test_fuzz_chain_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_chain_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_chain_SOURCES = test/fuzz/chain.cpp
+test_fuzz_checkqueue_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_checkqueue_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_checkqueue_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_checkqueue_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_checkqueue_SOURCES = test/fuzz/checkqueue.cpp
+
test_fuzz_coins_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DCOINS_DESERIALIZE=1
test_fuzz_coins_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_coins_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_coins_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_coins_deserialize_SOURCES = test/fuzz/deserialize.cpp
+test_fuzz_cuckoocache_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_cuckoocache_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_cuckoocache_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_cuckoocache_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_cuckoocache_SOURCES = test/fuzz/cuckoocache.cpp
+
test_fuzz_decode_tx_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_decode_tx_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_decode_tx_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -466,6 +490,12 @@ test_fuzz_fee_rate_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_fee_rate_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_fee_rate_deserialize_SOURCES = test/fuzz/deserialize.cpp
+test_fuzz_fees_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_fees_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_fees_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_fees_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_fees_SOURCES = test/fuzz/fees.cpp
+
test_fuzz_flat_file_pos_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DFLAT_FILE_POS_DESERIALIZE=1
test_fuzz_flat_file_pos_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_flat_file_pos_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
@@ -640,6 +670,12 @@ test_fuzz_prefilled_transaction_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)
test_fuzz_prefilled_transaction_deserialize_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
test_fuzz_prefilled_transaction_deserialize_SOURCES = test/fuzz/deserialize.cpp
+test_fuzz_process_messages_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
+test_fuzz_process_messages_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
+test_fuzz_process_messages_LDADD = $(FUZZ_SUITE_LD_COMMON)
+test_fuzz_process_messages_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
+test_fuzz_process_messages_SOURCES = test/fuzz/process_messages.cpp
+
test_fuzz_process_message_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
test_fuzz_process_message_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
test_fuzz_process_message_LDADD = $(FUZZ_SUITE_LD_COMMON)
diff --git a/src/Makefile.test_util.include b/src/Makefile.test_util.include
index 505d630b7d..d7bc73defb 100644
--- a/src/Makefile.test_util.include
+++ b/src/Makefile.test_util.include
@@ -11,6 +11,7 @@ TEST_UTIL_H = \
test/util/blockfilter.h \
test/util/logging.h \
test/util/mining.h \
+ test/util/net.h \
test/util/setup_common.h \
test/util/str.h \
test/util/transaction_utils.h \
@@ -22,6 +23,7 @@ libtest_util_a_SOURCES = \
test/util/blockfilter.cpp \
test/util/logging.cpp \
test/util/mining.cpp \
+ test/util/net.cpp \
test/util/setup_common.cpp \
test/util/str.cpp \
test/util/transaction_utils.cpp \
diff --git a/src/cuckoocache.h b/src/cuckoocache.h
index 4ad5818cdc..2daf676c4a 100644
--- a/src/cuckoocache.h
+++ b/src/cuckoocache.h
@@ -5,6 +5,7 @@
#ifndef BITCOIN_CUCKOOCACHE_H
#define BITCOIN_CUCKOOCACHE_H
+#include <algorithm> // std::find
#include <array>
#include <atomic>
#include <cmath>
diff --git a/src/init.cpp b/src/init.cpp
index 88caed9ded..437e934093 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -197,7 +197,20 @@ void Shutdown(NodeContext& node)
// Because these depend on each-other, we make sure that neither can be
// using the other before destroying them.
if (node.peer_logic) UnregisterValidationInterface(node.peer_logic.get());
- if (node.connman) node.connman->Stop();
+ // Follow the lock order requirements:
+ // * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraOutboundCount
+ // which locks cs_vNodes.
+ // * ProcessMessage locks cs_main and g_cs_orphans before indirectly calling ForEachNode which
+ // locks cs_vNodes.
+ // * CConnman::Stop calls DeleteNode, which calls FinalizeNode, which locks cs_main and calls
+ // EraseOrphansFor, which locks g_cs_orphans.
+ //
+ // Thus the implicit locking order requirement is: (1) cs_main, (2) g_cs_orphans, (3) cs_vNodes.
+ if (node.connman) {
+ node.connman->StopThreads();
+ LOCK2(::cs_main, ::g_cs_orphans);
+ node.connman->StopNodes();
+ }
StopTorControl();
diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp
index 645a8709d2..abce09ca4a 100644
--- a/src/interfaces/wallet.cpp
+++ b/src/interfaces/wallet.cpp
@@ -151,12 +151,12 @@ public:
std::string* purpose) override
{
LOCK(m_wallet->cs_wallet);
- auto it = m_wallet->mapAddressBook.find(dest);
- if (it == m_wallet->mapAddressBook.end()) {
+ auto it = m_wallet->m_address_book.find(dest);
+ if (it == m_wallet->m_address_book.end() || it->second.IsChange()) {
return false;
}
if (name) {
- *name = it->second.name;
+ *name = it->second.GetLabel();
}
if (is_mine) {
*is_mine = m_wallet->IsMine(dest);
@@ -170,8 +170,9 @@ public:
{
LOCK(m_wallet->cs_wallet);
std::vector<WalletAddress> result;
- for (const auto& item : m_wallet->mapAddressBook) {
- result.emplace_back(item.first, m_wallet->IsMine(item.first), item.second.name, item.second.purpose);
+ for (const auto& item : m_wallet->m_address_book) {
+ if (item.second.IsChange()) continue;
+ result.emplace_back(item.first, m_wallet->IsMine(item.first), item.second.GetLabel(), item.second.purpose);
}
return result;
}
diff --git a/src/net.cpp b/src/net.cpp
index 8352c40b98..dcc613ba88 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -2387,7 +2387,7 @@ void CConnman::Interrupt()
}
}
-void CConnman::Stop()
+void CConnman::StopThreads()
{
if (threadMessageHandler.joinable())
threadMessageHandler.join();
@@ -2399,14 +2399,17 @@ void CConnman::Stop()
threadDNSAddressSeed.join();
if (threadSocketHandler.joinable())
threadSocketHandler.join();
+}
- if (fAddressesInitialized)
- {
+void CConnman::StopNodes()
+{
+ if (fAddressesInitialized) {
DumpAddresses();
fAddressesInitialized = false;
}
// Close sockets
+ LOCK(cs_vNodes);
for (CNode* pnode : vNodes)
pnode->CloseSocketDisconnect();
for (ListenSocket& hListenSocket : vhListenSocket)
@@ -2415,10 +2418,10 @@ void CConnman::Stop()
LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
// clean up some globals (to help leak detection)
- for (CNode *pnode : vNodes) {
+ for (CNode* pnode : vNodes) {
DeleteNode(pnode);
}
- for (CNode *pnode : vNodesDisconnected) {
+ for (CNode* pnode : vNodesDisconnected) {
DeleteNode(pnode);
}
vNodes.clear();
@@ -2433,7 +2436,7 @@ void CConnman::DeleteNode(CNode* pnode)
assert(pnode);
bool fUpdateConnectionTime = false;
m_msgproc->FinalizeNode(pnode->GetId(), fUpdateConnectionTime);
- if(fUpdateConnectionTime) {
+ if (fUpdateConnectionTime) {
addrman.Connected(pnode->addr);
}
delete pnode;
diff --git a/src/net.h b/src/net.h
index 975d7f15d7..a72af83eef 100644
--- a/src/net.h
+++ b/src/net.h
@@ -188,16 +188,13 @@ public:
~CConnman();
bool Start(CScheduler& scheduler, const Options& options);
- // TODO: Remove NO_THREAD_SAFETY_ANALYSIS. Lock cs_vNodes before reading the variable vNodes.
- //
- // When removing NO_THREAD_SAFETY_ANALYSIS be aware of the following lock order requirements:
- // * CheckForStaleTipAndEvictPeers locks cs_main before indirectly calling GetExtraOutboundCount
- // which locks cs_vNodes.
- // * ProcessMessage locks cs_main and g_cs_orphans before indirectly calling ForEachNode which
- // locks cs_vNodes.
- //
- // Thus the implicit locking order requirement is: (1) cs_main, (2) g_cs_orphans, (3) cs_vNodes.
- void Stop() NO_THREAD_SAFETY_ANALYSIS;
+ void StopThreads();
+ void StopNodes();
+ void Stop()
+ {
+ StopThreads();
+ StopNodes();
+ };
void Interrupt();
bool GetNetworkActive() const { return fNetworkActive; };
@@ -482,6 +479,7 @@ private:
std::atomic<int64_t> m_next_send_inv_to_incoming{0};
friend struct CConnmanTest;
+ friend struct ConnmanTestMsg;
};
void Discover();
void StartMapPort();
@@ -721,6 +719,8 @@ public:
class CNode
{
friend class CConnman;
+ friend struct ConnmanTestMsg;
+
public:
std::unique_ptr<TransportDeserializer> m_deserializer;
std::unique_ptr<TransportSerializer> m_serializer;
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index ab430cbe19..f63d048aac 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -1911,9 +1911,9 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin
}
}
-bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
+bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc)
{
- LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(strCommand), vRecv.size(), pfrom->GetId());
+ LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom->GetId());
if (gArgs.IsArgSet("-dropmessagestest") && GetRand(gArgs.GetArg("-dropmessagestest", 0)) == 0)
{
LogPrintf("dropmessagestest DROPPING RECV MESSAGE\n");
@@ -1922,8 +1922,8 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
if (!(pfrom->GetLocalServices() & NODE_BLOOM) &&
- (strCommand == NetMsgType::FILTERLOAD ||
- strCommand == NetMsgType::FILTERADD))
+ (msg_type == NetMsgType::FILTERLOAD ||
+ msg_type == NetMsgType::FILTERADD))
{
if (pfrom->nVersion >= NO_BLOOM_VERSION) {
LOCK(cs_main);
@@ -1935,7 +1935,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
}
}
- if (strCommand == NetMsgType::VERSION) {
+ if (msg_type == NetMsgType::VERSION) {
// Each connection can only send one version message
if (pfrom->nVersion != 0)
{
@@ -2107,7 +2107,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
// At this point, the outgoing message serialization version can't change.
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
- if (strCommand == NetMsgType::VERACK)
+ if (msg_type == NetMsgType::VERACK)
{
pfrom->SetRecvVersion(std::min(pfrom->nVersion.load(), PROTOCOL_VERSION));
@@ -2152,7 +2152,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return false;
}
- if (strCommand == NetMsgType::ADDR) {
+ if (msg_type == NetMsgType::ADDR) {
std::vector<CAddress> vAddr;
vRecv >> vAddr;
@@ -2206,13 +2206,13 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::SENDHEADERS) {
+ if (msg_type == NetMsgType::SENDHEADERS) {
LOCK(cs_main);
State(pfrom->GetId())->fPreferHeaders = true;
return true;
}
- if (strCommand == NetMsgType::SENDCMPCT) {
+ if (msg_type == NetMsgType::SENDCMPCT) {
bool fAnnounceUsingCMPCTBLOCK = false;
uint64_t nCMPCTBLOCKVersion = 0;
vRecv >> fAnnounceUsingCMPCTBLOCK >> nCMPCTBLOCKVersion;
@@ -2235,7 +2235,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::INV) {
+ if (msg_type == NetMsgType::INV) {
std::vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
@@ -2297,7 +2297,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::GETDATA) {
+ if (msg_type == NetMsgType::GETDATA) {
std::vector<CInv> vInv;
vRecv >> vInv;
if (vInv.size() > MAX_INV_SZ)
@@ -2318,7 +2318,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::GETBLOCKS) {
+ if (msg_type == NetMsgType::GETBLOCKS) {
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
@@ -2386,7 +2386,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::GETBLOCKTXN) {
+ if (msg_type == NetMsgType::GETBLOCKTXN) {
BlockTransactionsRequest req;
vRecv >> req;
@@ -2435,7 +2435,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::GETHEADERS) {
+ if (msg_type == NetMsgType::GETHEADERS) {
CBlockLocator locator;
uint256 hashStop;
vRecv >> locator >> hashStop;
@@ -2502,7 +2502,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::TX) {
+ if (msg_type == NetMsgType::TX) {
// Stop processing the transaction early if
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
// or if this peer is supposed to be a block-relay-only peer
@@ -2644,7 +2644,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::CMPCTBLOCK)
+ if (msg_type == NetMsgType::CMPCTBLOCK)
{
// Ignore cmpctblock received while importing
if (fImporting || fReindex) {
@@ -2865,7 +2865,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::BLOCKTXN)
+ if (msg_type == NetMsgType::BLOCKTXN)
{
// Ignore blocktxn received while importing
if (fImporting || fReindex) {
@@ -2947,7 +2947,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::HEADERS)
+ if (msg_type == NetMsgType::HEADERS)
{
// Ignore headers received while importing
if (fImporting || fReindex) {
@@ -2973,7 +2973,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return ProcessHeadersMessage(pfrom, connman, mempool, headers, chainparams, /*via_compact_block=*/false);
}
- if (strCommand == NetMsgType::BLOCK)
+ if (msg_type == NetMsgType::BLOCK)
{
// Ignore block received while importing
if (fImporting || fReindex) {
@@ -3009,7 +3009,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::GETADDR) {
+ if (msg_type == NetMsgType::GETADDR) {
// This asymmetric behavior for inbound and outbound connections was introduced
// to prevent a fingerprinting attack: an attacker can send specific fake addresses
// to users' AddrMan and later request them by sending getaddr messages.
@@ -3043,7 +3043,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::MEMPOOL) {
+ if (msg_type == NetMsgType::MEMPOOL) {
if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->HasPermission(PF_MEMPOOL))
{
if (!pfrom->HasPermission(PF_NOBAN))
@@ -3071,7 +3071,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::PING) {
+ if (msg_type == NetMsgType::PING) {
if (pfrom->nVersion > BIP0031_VERSION)
{
uint64_t nonce = 0;
@@ -3092,7 +3092,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::PONG) {
+ if (msg_type == NetMsgType::PONG) {
int64_t pingUsecEnd = nTimeReceived;
uint64_t nonce = 0;
size_t nAvail = vRecv.in_avail();
@@ -3148,7 +3148,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::FILTERLOAD) {
+ if (msg_type == NetMsgType::FILTERLOAD) {
CBloomFilter filter;
vRecv >> filter;
@@ -3168,7 +3168,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::FILTERADD) {
+ if (msg_type == NetMsgType::FILTERADD) {
std::vector<unsigned char> vData;
vRecv >> vData;
@@ -3192,7 +3192,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::FILTERCLEAR) {
+ if (msg_type == NetMsgType::FILTERCLEAR) {
if (pfrom->m_tx_relay == nullptr) {
return true;
}
@@ -3204,7 +3204,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::FEEFILTER) {
+ if (msg_type == NetMsgType::FEEFILTER) {
CAmount newFeeFilter = 0;
vRecv >> newFeeFilter;
if (MoneyRange(newFeeFilter)) {
@@ -3217,7 +3217,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
return true;
}
- if (strCommand == NetMsgType::NOTFOUND) {
+ if (msg_type == NetMsgType::NOTFOUND) {
// Remove the NOTFOUND transactions from the peer
LOCK(cs_main);
CNodeState *state = State(pfrom->GetId());
@@ -3243,7 +3243,7 @@ bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vR
}
// Ignore unknown commands for extensibility
- LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(strCommand), pfrom->GetId());
+ LogPrint(BCLog::NET, "Unknown command \"%s\" from peer=%d\n", SanitizeString(msg_type), pfrom->GetId());
return true;
}
@@ -3338,7 +3338,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
LogPrint(BCLog::NET, "PROCESSMESSAGE: ERRORS IN HEADER %s peer=%d\n", SanitizeString(msg.m_command), pfrom->GetId());
return fMoreWork;
}
- const std::string& strCommand = msg.m_command;
+ const std::string& msg_type = msg.m_command;
// Message size
unsigned int nMessageSize = msg.m_message_size;
@@ -3348,7 +3348,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
if (!msg.m_valid_checksum)
{
LogPrint(BCLog::NET, "%s(%s, %u bytes): CHECKSUM ERROR peer=%d\n", __func__,
- SanitizeString(strCommand), nMessageSize, pfrom->GetId());
+ SanitizeString(msg_type), nMessageSize, pfrom->GetId());
return fMoreWork;
}
@@ -3356,19 +3356,19 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter
bool fRet = false;
try
{
- fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.m_time, chainparams, m_mempool, connman, m_banman, interruptMsgProc);
+ fRet = ProcessMessage(pfrom, msg_type, vRecv, msg.m_time, chainparams, m_mempool, connman, m_banman, interruptMsgProc);
if (interruptMsgProc)
return false;
if (!pfrom->vRecvGetData.empty())
fMoreWork = true;
} catch (const std::exception& e) {
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(strCommand), nMessageSize, e.what(), typeid(e).name());
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Exception '%s' (%s) caught\n", __func__, SanitizeString(msg_type), nMessageSize, e.what(), typeid(e).name());
} catch (...) {
- LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", __func__, SanitizeString(strCommand), nMessageSize);
+ LogPrint(BCLog::NET, "%s(%s, %u bytes): Unknown exception caught\n", __func__, SanitizeString(msg_type), nMessageSize);
}
if (!fRet) {
- LogPrint(BCLog::NET, "%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(strCommand), nMessageSize, pfrom->GetId());
+ LogPrint(BCLog::NET, "%s(%s, %u bytes) FAILED peer=%d\n", __func__, SanitizeString(msg_type), nMessageSize, pfrom->GetId());
}
LOCK(cs_main);
diff --git a/src/net_processing.h b/src/net_processing.h
index 65e3963c41..3d9bc65a3a 100644
--- a/src/net_processing.h
+++ b/src/net_processing.h
@@ -14,6 +14,7 @@
class CTxMemPool;
extern RecursiveMutex cs_main;
+extern RecursiveMutex g_cs_orphans;
/** Default for -maxorphantx, maximum number of orphan transactions kept in memory */
static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100;
diff --git a/src/qt/addresstablemodel.cpp b/src/qt/addresstablemodel.cpp
index 3ac98a5970..3869318fea 100644
--- a/src/qt/addresstablemodel.cpp
+++ b/src/qt/addresstablemodel.cpp
@@ -55,7 +55,7 @@ struct AddressTableEntryLessThan
static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
{
AddressTableEntry::Type addressType = AddressTableEntry::Hidden;
- // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
+ // "refund" addresses aren't shown, and change addresses aren't returned by getAddresses at all.
if (strPurpose == "send")
addressType = AddressTableEntry::Sending;
else if (strPurpose == "receive")
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index 2918676c22..75f3e9bf45 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -456,7 +456,7 @@ void BitcoinGUI::createMenuBar()
QAction* minimize_action = window_menu->addAction(tr("Minimize"));
minimize_action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));
connect(minimize_action, &QAction::triggered, [] {
- qApp->focusWindow()->showMinimized();
+ QApplication::activeWindow()->showMinimized();
});
connect(qApp, &QApplication::focusWindowChanged, [minimize_action] (QWindow* window) {
minimize_action->setEnabled(window != nullptr && (window->flags() & Qt::Dialog) != Qt::Dialog && window->windowState() != Qt::WindowMinimized);
diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp
index 0f082802cc..042387286a 100644
--- a/src/qt/test/addressbooktests.cpp
+++ b/src/qt/test/addressbooktests.cpp
@@ -97,7 +97,7 @@ void TestAddAddressesToSendBook(interfaces::Node& node)
auto check_addbook_size = [&wallet](int expected_size) {
LOCK(wallet->cs_wallet);
- QCOMPARE(static_cast<int>(wallet->mapAddressBook.size()), expected_size);
+ QCOMPARE(static_cast<int>(wallet->m_address_book.size()), expected_size);
};
// We should start with the two addresses we added earlier and nothing else.
diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp
index c132f265d2..f71ed99652 100644
--- a/src/rpc/blockchain.cpp
+++ b/src/rpc/blockchain.cpp
@@ -2347,6 +2347,8 @@ UniValue dumptxoutset(const JSONRPCRequest& request)
return result;
}
+void RegisterBlockchainRPCCommands(CRPCTable &t)
+{
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -2387,8 +2389,6 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterBlockchainRPCCommands(CRPCTable &t)
-{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index bde19d8e79..da9d583fa7 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1022,6 +1022,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
return result;
}
+void RegisterMiningRPCCommands(CRPCTable &t)
+{
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -1043,8 +1045,6 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterMiningRPCCommands(CRPCTable &t)
-{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp
index c87c1a5418..51a9581349 100644
--- a/src/rpc/misc.cpp
+++ b/src/rpc/misc.cpp
@@ -589,6 +589,8 @@ static UniValue echo(const JSONRPCRequest& request)
return request.params;
}
+void RegisterMiscRPCCommands(CRPCTable &t)
+{
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -610,8 +612,6 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterMiscRPCCommands(CRPCTable &t)
-{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index caa62ca958..10562126db 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -760,6 +760,8 @@ static UniValue getnodeaddresses(const JSONRPCRequest& request)
return ret;
}
+void RegisterNetRPCCommands(CRPCTable &t)
+{
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -780,8 +782,6 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterNetRPCCommands(CRPCTable &t)
-{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index ae3f15cec2..3c0b060439 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -1813,6 +1813,8 @@ UniValue analyzepsbt(const JSONRPCRequest& request)
return result;
}
+void RegisterRawTransactionRPCCommands(CRPCTable &t)
+{
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -1839,8 +1841,6 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterRawTransactionRPCCommands(CRPCTable &t)
-{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp
index 54baec6c6f..7b701a2bbe 100644
--- a/src/rpc/rawtransaction_util.cpp
+++ b/src/rpc/rawtransaction_util.cpp
@@ -216,7 +216,7 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst
keystore->AddCScript(script);
// Automatically also add the P2WSH wrapped version of the script (to deal with P2SH-P2WSH).
// This is done for redeemScript only for compatibility, it is encouraged to use the explicit witnessScript field instead.
- CScript witness_output_script{GetScriptForWitness(script)};
+ CScript witness_output_script{GetScriptForDestination(WitnessV0ScriptHash(script))};
keystore->AddCScript(witness_output_script);
if (!ws.isNull() && !rs.isNull()) {
diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp
index 7310498eb6..6314c1a42f 100644
--- a/src/test/denialofservice_tests.cpp
+++ b/src/test/denialofservice_tests.cpp
@@ -52,7 +52,6 @@ struct COrphanTx {
NodeId fromPeer;
int64_t nTimeExpire;
};
-extern RecursiveMutex g_cs_orphans;
extern std::map<uint256, COrphanTx> mapOrphanTransactions GUARDED_BY(g_cs_orphans);
static CService ip(uint32_t i)
diff --git a/src/test/fuzz/addition_overflow.cpp b/src/test/fuzz/addition_overflow.cpp
new file mode 100644
index 0000000000..a455992b13
--- /dev/null
+++ b/src/test/fuzz/addition_overflow.cpp
@@ -0,0 +1,55 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#if defined(__has_builtin)
+#if __has_builtin(__builtin_add_overflow)
+#define HAVE_BUILTIN_ADD_OVERFLOW
+#endif
+#elif defined(__GNUC__) && (__GNUC__ >= 5)
+#define HAVE_BUILTIN_ADD_OVERFLOW
+#endif
+
+namespace {
+template <typename T>
+void TestAdditionOverflow(FuzzedDataProvider& fuzzed_data_provider)
+{
+ const T i = fuzzed_data_provider.ConsumeIntegral<T>();
+ const T j = fuzzed_data_provider.ConsumeIntegral<T>();
+ const bool is_addition_overflow_custom = AdditionOverflow(i, j);
+#if defined(HAVE_BUILTIN_ADD_OVERFLOW)
+ T result_builtin;
+ const bool is_addition_overflow_builtin = __builtin_add_overflow(i, j, &result_builtin);
+ assert(is_addition_overflow_custom == is_addition_overflow_builtin);
+ if (!is_addition_overflow_custom) {
+ assert(i + j == result_builtin);
+ }
+#else
+ if (!is_addition_overflow_custom) {
+ (void)(i + j);
+ }
+#endif
+}
+} // namespace
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ TestAdditionOverflow<int64_t>(fuzzed_data_provider);
+ TestAdditionOverflow<uint64_t>(fuzzed_data_provider);
+ TestAdditionOverflow<int32_t>(fuzzed_data_provider);
+ TestAdditionOverflow<uint32_t>(fuzzed_data_provider);
+ TestAdditionOverflow<int16_t>(fuzzed_data_provider);
+ TestAdditionOverflow<uint16_t>(fuzzed_data_provider);
+ TestAdditionOverflow<char>(fuzzed_data_provider);
+ TestAdditionOverflow<unsigned char>(fuzzed_data_provider);
+ TestAdditionOverflow<signed char>(fuzzed_data_provider);
+}
diff --git a/src/test/fuzz/checkqueue.cpp b/src/test/fuzz/checkqueue.cpp
new file mode 100644
index 0000000000..2ed097b827
--- /dev/null
+++ b/src/test/fuzz/checkqueue.cpp
@@ -0,0 +1,65 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <checkqueue.h>
+#include <optional.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace {
+struct DumbCheck {
+ const bool result = false;
+
+ DumbCheck() = default;
+
+ explicit DumbCheck(const bool _result) : result(_result)
+ {
+ }
+
+ bool operator()() const
+ {
+ return result;
+ }
+
+ void swap(DumbCheck& x)
+ {
+ }
+};
+} // namespace
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ const unsigned int batch_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1024);
+ CCheckQueue<DumbCheck> check_queue_1{batch_size};
+ CCheckQueue<DumbCheck> check_queue_2{batch_size};
+ std::vector<DumbCheck> checks_1;
+ std::vector<DumbCheck> checks_2;
+ const int size = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, 1024);
+ for (int i = 0; i < size; ++i) {
+ const bool result = fuzzed_data_provider.ConsumeBool();
+ checks_1.emplace_back(result);
+ checks_2.emplace_back(result);
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ check_queue_1.Add(checks_1);
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ (void)check_queue_1.Wait();
+ }
+
+ CCheckQueueControl<DumbCheck> check_queue_control{&check_queue_2};
+ if (fuzzed_data_provider.ConsumeBool()) {
+ check_queue_control.Add(checks_2);
+ }
+ if (fuzzed_data_provider.ConsumeBool()) {
+ (void)check_queue_control.Wait();
+ }
+}
diff --git a/src/test/fuzz/cuckoocache.cpp b/src/test/fuzz/cuckoocache.cpp
new file mode 100644
index 0000000000..f674efe1b1
--- /dev/null
+++ b/src/test/fuzz/cuckoocache.cpp
@@ -0,0 +1,49 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <cuckoocache.h>
+#include <optional.h>
+#include <script/sigcache.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/setup_common.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+namespace {
+FuzzedDataProvider* fuzzed_data_provider_ptr = nullptr;
+
+struct RandomHasher {
+ template <uint8_t>
+ uint32_t operator()(const bool& /* unused */) const
+ {
+ assert(fuzzed_data_provider_ptr != nullptr);
+ return fuzzed_data_provider_ptr->ConsumeIntegral<uint32_t>();
+ }
+};
+} // namespace
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ fuzzed_data_provider_ptr = &fuzzed_data_provider;
+ CuckooCache::cache<bool, RandomHasher> cuckoo_cache{};
+ if (fuzzed_data_provider.ConsumeBool()) {
+ const size_t megabytes = fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 16);
+ cuckoo_cache.setup_bytes(megabytes << 20);
+ } else {
+ cuckoo_cache.setup(fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, 4096));
+ }
+ while (fuzzed_data_provider.ConsumeBool()) {
+ if (fuzzed_data_provider.ConsumeBool()) {
+ cuckoo_cache.insert(fuzzed_data_provider.ConsumeBool());
+ } else {
+ cuckoo_cache.contains(fuzzed_data_provider.ConsumeBool(), fuzzed_data_provider.ConsumeBool());
+ }
+ }
+ fuzzed_data_provider_ptr = nullptr;
+}
diff --git a/src/test/fuzz/fees.cpp b/src/test/fuzz/fees.cpp
new file mode 100644
index 0000000000..090994263e
--- /dev/null
+++ b/src/test/fuzz/fees.cpp
@@ -0,0 +1,26 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <amount.h>
+#include <optional.h>
+#include <policy/fees.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+ const CFeeRate minimal_incremental_fee{ConsumeMoney(fuzzed_data_provider)};
+ FeeFilterRounder fee_filter_rounder{minimal_incremental_fee};
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const CAmount current_minimum_fee = ConsumeMoney(fuzzed_data_provider);
+ const CAmount rounded_fee = fee_filter_rounder.round(current_minimum_fee);
+ assert(MoneyRange(rounded_fee));
+ }
+}
diff --git a/src/test/fuzz/integer.cpp b/src/test/fuzz/integer.cpp
index fff2fabd17..7c2537aaf5 100644
--- a/src/test/fuzz/integer.cpp
+++ b/src/test/fuzz/integer.cpp
@@ -23,6 +23,7 @@
#include <streams.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
#include <time.h>
#include <uint256.h>
#include <util/moneystr.h>
@@ -35,6 +36,7 @@
#include <cassert>
#include <chrono>
#include <limits>
+#include <set>
#include <vector>
void initialize()
@@ -90,8 +92,12 @@ void test_one_input(const std::vector<uint8_t>& buffer)
}
(void)GetSizeOfCompactSize(u64);
(void)GetSpecialScriptSize(u32);
- // (void)GetVirtualTransactionSize(i64, i64); // function defined only for a subset of int64_t inputs
- // (void)GetVirtualTransactionSize(i64, i64, u32); // function defined only for a subset of int64_t/uint32_t inputs
+ if (!MultiplicationOverflow(i64, static_cast<int64_t>(::nBytesPerSigOp)) && !AdditionOverflow(i64 * ::nBytesPerSigOp, static_cast<int64_t>(4))) {
+ (void)GetVirtualTransactionSize(i64, i64);
+ }
+ if (!MultiplicationOverflow(i64, static_cast<int64_t>(u32)) && !AdditionOverflow(i64, static_cast<int64_t>(4)) && !AdditionOverflow(i64 * u32, static_cast<int64_t>(4))) {
+ (void)GetVirtualTransactionSize(i64, i64, u32);
+ }
(void)HexDigit(ch);
(void)MoneyRange(i64);
(void)ToString(i64);
@@ -109,6 +115,12 @@ void test_one_input(const std::vector<uint8_t>& buffer)
(void)memusage::DynamicUsage(u8);
const unsigned char uch = static_cast<unsigned char>(u8);
(void)memusage::DynamicUsage(uch);
+ {
+ const std::set<int64_t> i64s{i64, static_cast<int64_t>(u64)};
+ const size_t dynamic_usage = memusage::DynamicUsage(i64s);
+ const size_t incremental_dynamic_usage = memusage::IncrementalDynamicUsage(i64s);
+ assert(dynamic_usage == incremental_dynamic_usage * i64s.size());
+ }
(void)MillisToTimeval(i64);
const double d = ser_uint64_to_double(u64);
assert(ser_double_to_uint64(d) == u64);
diff --git a/src/test/fuzz/process_message.cpp b/src/test/fuzz/process_message.cpp
index dc49dd499a..9e3586d162 100644
--- a/src/test/fuzz/process_message.cpp
+++ b/src/test/fuzz/process_message.cpp
@@ -32,7 +32,7 @@
#include <string>
#include <vector>
-bool ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc);
+bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc);
namespace {
diff --git a/src/test/fuzz/process_messages.cpp b/src/test/fuzz/process_messages.cpp
new file mode 100644
index 0000000000..12a5dbb607
--- /dev/null
+++ b/src/test/fuzz/process_messages.cpp
@@ -0,0 +1,75 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <consensus/consensus.h>
+#include <net.h>
+#include <net_processing.h>
+#include <protocol.h>
+#include <test/fuzz/FuzzedDataProvider.h>
+#include <test/fuzz/fuzz.h>
+#include <test/fuzz/util.h>
+#include <test/util/mining.h>
+#include <test/util/net.h>
+#include <test/util/setup_common.h>
+#include <util/memory.h>
+#include <validation.h>
+#include <validationinterface.h>
+
+const RegTestingSetup* g_setup;
+
+void initialize()
+{
+ static RegTestingSetup setup{};
+ g_setup = &setup;
+
+ for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
+ MineBlock(g_setup->m_node, CScript() << OP_TRUE);
+ }
+ SyncWithValidationInterfaceQueue();
+}
+
+void test_one_input(const std::vector<uint8_t>& buffer)
+{
+ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
+
+ ConnmanTestMsg& connman = *(ConnmanTestMsg*)g_setup->m_node.connman.get();
+ std::vector<CNode*> peers;
+
+ const auto num_peers_to_add = fuzzed_data_provider.ConsumeIntegralInRange(1, 3);
+ for (int i = 0; i < num_peers_to_add; ++i) {
+ const ServiceFlags service_flags = ServiceFlags(fuzzed_data_provider.ConsumeIntegral<uint64_t>());
+ const bool inbound{fuzzed_data_provider.ConsumeBool()};
+ const bool block_relay_only{fuzzed_data_provider.ConsumeBool()};
+ peers.push_back(MakeUnique<CNode>(i, service_flags, 0, INVALID_SOCKET, CAddress{CService{in_addr{0x0100007f}, 7777}, NODE_NETWORK}, 0, 0, CAddress{}, std::string{}, inbound, block_relay_only).release());
+ CNode& p2p_node = *peers.back();
+
+ p2p_node.fSuccessfullyConnected = true;
+ p2p_node.fPauseSend = false;
+ p2p_node.nVersion = PROTOCOL_VERSION;
+ p2p_node.SetSendVersion(PROTOCOL_VERSION);
+ g_setup->m_node.peer_logic->InitializeNode(&p2p_node);
+
+ connman.AddTestNode(p2p_node);
+ }
+
+ while (fuzzed_data_provider.ConsumeBool()) {
+ const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::COMMAND_SIZE).c_str()};
+
+ CSerializedNetMsg net_msg;
+ net_msg.command = random_message_type;
+ net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider);
+
+ CNode& random_node = *peers.at(fuzzed_data_provider.ConsumeIntegralInRange<int>(0, peers.size() - 1));
+
+ (void)connman.ReceiveMsgFrom(random_node, net_msg);
+ random_node.fPauseSend = false;
+
+ try {
+ connman.ProcessMessagesOnce(random_node);
+ } catch (const std::ios_base::failure&) {
+ }
+ }
+ connman.ClearTestNodes();
+ SyncWithValidationInterfaceQueue();
+}
diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h
index b70ea6d90e..ba4b012f95 100644
--- a/src/test/fuzz/util.h
+++ b/src/test/fuzz/util.h
@@ -120,4 +120,15 @@ NODISCARD bool MultiplicationOverflow(const T i, const T j) noexcept
}
}
+template <class T>
+NODISCARD bool AdditionOverflow(const T i, const T j) noexcept
+{
+ static_assert(std::is_integral<T>::value, "Integral required.");
+ if (std::numeric_limits<T>::is_signed) {
+ return (i > 0 && j > std::numeric_limits<T>::max() - i) ||
+ (i < 0 && j < std::numeric_limits<T>::min() - i);
+ }
+ return std::numeric_limits<T>::max() - i < j;
+}
+
#endif // BITCOIN_TEST_FUZZ_UTIL_H
diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp
new file mode 100644
index 0000000000..09f2f1807f
--- /dev/null
+++ b/src/test/util/net.cpp
@@ -0,0 +1,39 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <test/util/net.h>
+
+#include <chainparams.h>
+#include <net.h>
+
+void ConnmanTestMsg::NodeReceiveMsgBytes(CNode& node, const char* pch, unsigned int nBytes, bool& complete) const
+{
+ assert(node.ReceiveMsgBytes(pch, nBytes, complete));
+ if (complete) {
+ size_t nSizeAdded = 0;
+ auto it(node.vRecvMsg.begin());
+ for (; it != node.vRecvMsg.end(); ++it) {
+ // vRecvMsg contains only completed CNetMessage
+ // the single possible partially deserialized message are held by TransportDeserializer
+ nSizeAdded += it->m_raw_message_size;
+ }
+ {
+ LOCK(node.cs_vProcessMsg);
+ node.vProcessMsg.splice(node.vProcessMsg.end(), node.vRecvMsg, node.vRecvMsg.begin(), it);
+ node.nProcessQueueSize += nSizeAdded;
+ node.fPauseRecv = node.nProcessQueueSize > nReceiveFloodSize;
+ }
+ }
+}
+
+bool ConnmanTestMsg::ReceiveMsgFrom(CNode& node, CSerializedNetMsg& ser_msg) const
+{
+ std::vector<unsigned char> ser_msg_header;
+ node.m_serializer->prepareForTransport(ser_msg, ser_msg_header);
+
+ bool complete;
+ NodeReceiveMsgBytes(node, (const char*)ser_msg_header.data(), ser_msg_header.size(), complete);
+ NodeReceiveMsgBytes(node, (const char*)ser_msg.data.data(), ser_msg.data.size(), complete);
+ return complete;
+}
diff --git a/src/test/util/net.h b/src/test/util/net.h
new file mode 100644
index 0000000000..ca8cb7fad5
--- /dev/null
+++ b/src/test/util/net.h
@@ -0,0 +1,33 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_NET_H
+#define BITCOIN_TEST_UTIL_NET_H
+
+#include <net.h>
+
+struct ConnmanTestMsg : public CConnman {
+ using CConnman::CConnman;
+ void AddTestNode(CNode& node)
+ {
+ LOCK(cs_vNodes);
+ vNodes.push_back(&node);
+ }
+ void ClearTestNodes()
+ {
+ LOCK(cs_vNodes);
+ for (CNode* node : vNodes) {
+ delete node;
+ }
+ vNodes.clear();
+ }
+
+ void ProcessMessagesOnce(CNode& node) { m_msgproc->ProcessMessages(&node, flagInterruptMsgProc); }
+
+ void NodeReceiveMsgBytes(CNode& node, const char* pch, unsigned int nBytes, bool& complete) const;
+
+ bool ReceiveMsgFrom(CNode& node, CSerializedNetMsg& ser_msg) const;
+};
+
+#endif // BITCOIN_TEST_UTIL_NET_H
diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp
index d684b97787..a4d0126925 100644
--- a/src/test/util/setup_common.cpp
+++ b/src/test/util/setup_common.cpp
@@ -139,6 +139,11 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
m_node.banman = MakeUnique<BanMan>(GetDataDir() / "banlist.dat", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = MakeUnique<CConnman>(0x1337, 0x1337); // Deterministic randomness for tests.
m_node.peer_logic = MakeUnique<PeerLogicValidation>(m_node.connman.get(), m_node.banman.get(), *m_node.scheduler, *m_node.mempool);
+ {
+ CConnman::Options options;
+ options.m_msgproc = m_node.peer_logic.get();
+ m_node.connman->Init(options);
+ }
}
TestingSetup::~TestingSetup()
diff --git a/src/test/validationinterface_tests.cpp b/src/test/validationinterface_tests.cpp
new file mode 100644
index 0000000000..208be92852
--- /dev/null
+++ b/src/test/validationinterface_tests.cpp
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include <boost/test/unit_test.hpp>
+#include <consensus/validation.h>
+#include <primitives/block.h>
+#include <scheduler.h>
+#include <test/util/setup_common.h>
+#include <util/check.h>
+#include <validationinterface.h>
+
+BOOST_FIXTURE_TEST_SUITE(validationinterface_tests, TestingSetup)
+
+class TestInterface : public CValidationInterface
+{
+public:
+ TestInterface(std::function<void()> on_call = nullptr, std::function<void()> on_destroy = nullptr)
+ : m_on_call(std::move(on_call)), m_on_destroy(std::move(on_destroy))
+ {
+ }
+ virtual ~TestInterface()
+ {
+ if (m_on_destroy) m_on_destroy();
+ }
+ void BlockChecked(const CBlock& block, const BlockValidationState& state) override
+ {
+ if (m_on_call) m_on_call();
+ }
+ static void Call()
+ {
+ CBlock block;
+ BlockValidationState state;
+ GetMainSignals().BlockChecked(block, state);
+ }
+ std::function<void()> m_on_call;
+ std::function<void()> m_on_destroy;
+};
+
+// Regression test to ensure UnregisterAllValidationInterfaces calls don't
+// destroy a validation interface while it is being called. Bug:
+// https://github.com/bitcoin/bitcoin/pull/18551
+BOOST_AUTO_TEST_CASE(unregister_all_during_call)
+{
+ bool destroyed = false;
+ RegisterSharedValidationInterface(std::make_shared<TestInterface>(
+ [&] {
+ // First call should decrements reference count 2 -> 1
+ UnregisterAllValidationInterfaces();
+ BOOST_CHECK(!destroyed);
+ // Second call should not decrement reference count 1 -> 0
+ UnregisterAllValidationInterfaces();
+ BOOST_CHECK(!destroyed);
+ },
+ [&] { destroyed = true; }));
+ TestInterface::Call();
+ BOOST_CHECK(destroyed);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp
index f9f61e8a09..11000774c0 100644
--- a/src/validationinterface.cpp
+++ b/src/validationinterface.cpp
@@ -16,36 +16,75 @@
#include <unordered_map>
#include <utility>
-#include <boost/signals2/signal.hpp>
-
-struct ValidationInterfaceConnections {
- boost::signals2::scoped_connection UpdatedBlockTip;
- boost::signals2::scoped_connection TransactionAddedToMempool;
- boost::signals2::scoped_connection BlockConnected;
- boost::signals2::scoped_connection BlockDisconnected;
- boost::signals2::scoped_connection TransactionRemovedFromMempool;
- boost::signals2::scoped_connection ChainStateFlushed;
- boost::signals2::scoped_connection BlockChecked;
- boost::signals2::scoped_connection NewPoWValidBlock;
-};
-
+//! The MainSignalsInstance manages a list of shared_ptr<CValidationInterface>
+//! callbacks.
+//!
+//! A std::unordered_map is used to track what callbacks are currently
+//! registered, and a std::list is to used to store the callbacks that are
+//! currently registered as well as any callbacks that are just unregistered
+//! and about to be deleted when they are done executing.
struct MainSignalsInstance {
- boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
- boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
- boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex)> BlockConnected;
- boost::signals2::signal<void (const std::shared_ptr<const CBlock>&, const CBlockIndex* pindex)> BlockDisconnected;
- boost::signals2::signal<void (const CTransactionRef &)> TransactionRemovedFromMempool;
- boost::signals2::signal<void (const CBlockLocator &)> ChainStateFlushed;
- boost::signals2::signal<void (const CBlock&, const BlockValidationState&)> BlockChecked;
- boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
-
+private:
+ Mutex m_mutex;
+ //! List entries consist of a callback pointer and reference count. The
+ //! count is equal to the number of current executions of that entry, plus 1
+ //! if it's registered. It cannot be 0 because that would imply it is
+ //! unregistered and also not being executed (so shouldn't exist).
+ struct ListEntry { std::shared_ptr<CValidationInterface> callbacks; int count = 1; };
+ std::list<ListEntry> m_list GUARDED_BY(m_mutex);
+ std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY(m_mutex);
+
+public:
// We are not allowed to assume the scheduler only runs in one thread,
// but must ensure all callbacks happen in-order, so we end up creating
// our own queue here :(
SingleThreadedSchedulerClient m_schedulerClient;
- std::unordered_map<CValidationInterface*, ValidationInterfaceConnections> m_connMainSignals;
explicit MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {}
+
+ void Register(std::shared_ptr<CValidationInterface> callbacks)
+ {
+ LOCK(m_mutex);
+ auto inserted = m_map.emplace(callbacks.get(), m_list.end());
+ if (inserted.second) inserted.first->second = m_list.emplace(m_list.end());
+ inserted.first->second->callbacks = std::move(callbacks);
+ }
+
+ void Unregister(CValidationInterface* callbacks)
+ {
+ LOCK(m_mutex);
+ auto it = m_map.find(callbacks);
+ if (it != m_map.end()) {
+ if (!--it->second->count) m_list.erase(it->second);
+ m_map.erase(it);
+ }
+ }
+
+ //! Clear unregisters every previously registered callback, erasing every
+ //! map entry. After this call, the list may still contain callbacks that
+ //! are currently executing, but it will be cleared when they are done
+ //! executing.
+ void Clear()
+ {
+ LOCK(m_mutex);
+ for (const auto& entry : m_map) {
+ if (!--entry.second->count) m_list.erase(entry.second);
+ }
+ m_map.clear();
+ }
+
+ template<typename F> void Iterate(F&& f)
+ {
+ WAIT_LOCK(m_mutex, lock);
+ for (auto it = m_list.begin(); it != m_list.end();) {
+ ++it->count;
+ {
+ REVERSE_LOCK(lock);
+ f(*it->callbacks);
+ }
+ it = --it->count ? std::next(it) : m_list.erase(it);
+ }
+ }
};
static CMainSignals g_signals;
@@ -78,15 +117,7 @@ CMainSignals& GetMainSignals()
void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> pwalletIn) {
// Each connection captures pwalletIn to ensure that each callback is
// executed before pwalletIn is destroyed. For more details see #18338.
- ValidationInterfaceConnections& conns = g_signals.m_internals->m_connMainSignals[pwalletIn.get()];
- conns.UpdatedBlockTip = g_signals.m_internals->UpdatedBlockTip.connect(std::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
- conns.TransactionAddedToMempool = g_signals.m_internals->TransactionAddedToMempool.connect(std::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, std::placeholders::_1));
- conns.BlockConnected = g_signals.m_internals->BlockConnected.connect(std::bind(&CValidationInterface::BlockConnected, pwalletIn, std::placeholders::_1, std::placeholders::_2));
- conns.BlockDisconnected = g_signals.m_internals->BlockDisconnected.connect(std::bind(&CValidationInterface::BlockDisconnected, pwalletIn, std::placeholders::_1, std::placeholders::_2));
- conns.TransactionRemovedFromMempool = g_signals.m_internals->TransactionRemovedFromMempool.connect(std::bind(&CValidationInterface::TransactionRemovedFromMempool, pwalletIn, std::placeholders::_1));
- conns.ChainStateFlushed = g_signals.m_internals->ChainStateFlushed.connect(std::bind(&CValidationInterface::ChainStateFlushed, pwalletIn, std::placeholders::_1));
- conns.BlockChecked = g_signals.m_internals->BlockChecked.connect(std::bind(&CValidationInterface::BlockChecked, pwalletIn, std::placeholders::_1, std::placeholders::_2));
- conns.NewPoWValidBlock = g_signals.m_internals->NewPoWValidBlock.connect(std::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, std::placeholders::_1, std::placeholders::_2));
+ g_signals.m_internals->Register(std::move(pwalletIn));
}
void RegisterValidationInterface(CValidationInterface* callbacks)
@@ -103,7 +134,7 @@ void UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> c
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
if (g_signals.m_internals) {
- g_signals.m_internals->m_connMainSignals.erase(pwalletIn);
+ g_signals.m_internals->Unregister(pwalletIn);
}
}
@@ -111,7 +142,7 @@ void UnregisterAllValidationInterfaces() {
if (!g_signals.m_internals) {
return;
}
- g_signals.m_internals->m_connMainSignals.clear();
+ g_signals.m_internals->Clear();
}
void CallFunctionInValidationInterfaceQueue(std::function<void ()> func) {
@@ -151,7 +182,7 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
// in the same critical section where the chain is updated
auto event = [pindexNew, pindexFork, fInitialDownload, this] {
- m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__,
pindexNew->GetBlockHash().ToString(),
@@ -161,7 +192,7 @@ void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockInd
void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
auto event = [ptx, this] {
- m_internals->TransactionAddedToMempool(ptx);
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(ptx); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
ptx->GetHash().ToString(),
@@ -170,7 +201,7 @@ void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
auto event = [ptx, this] {
- m_internals->TransactionRemovedFromMempool(ptx);
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(ptx); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
ptx->GetHash().ToString(),
@@ -179,7 +210,7 @@ void CMainSignals::TransactionRemovedFromMempool(const CTransactionRef &ptx) {
void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
auto event = [pblock, pindex, this] {
- m_internals->BlockConnected(pblock, pindex);
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(pblock, pindex); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
pblock->GetHash().ToString(),
@@ -189,7 +220,7 @@ void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, c
void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
{
auto event = [pblock, pindex, this] {
- m_internals->BlockDisconnected(pblock, pindex);
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockDisconnected(pblock, pindex); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
pblock->GetHash().ToString(),
@@ -198,7 +229,7 @@ void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock
void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
auto event = [locator, this] {
- m_internals->ChainStateFlushed(locator);
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ChainStateFlushed(locator); });
};
ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__,
locator.IsNull() ? "null" : locator.vHave.front().ToString());
@@ -207,10 +238,10 @@ void CMainSignals::ChainStateFlushed(const CBlockLocator &locator) {
void CMainSignals::BlockChecked(const CBlock& block, const BlockValidationState& state) {
LOG_EVENT("%s: block hash=%s state=%s", __func__,
block.GetHash().ToString(), state.ToString());
- m_internals->BlockChecked(block, state);
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockChecked(block, state); });
}
void CMainSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
- m_internals->NewPoWValidBlock(pindex, block);
+ m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
}
diff --git a/src/validationinterface.h b/src/validationinterface.h
index f9a359b7ad..cb0204a555 100644
--- a/src/validationinterface.h
+++ b/src/validationinterface.h
@@ -171,9 +171,7 @@ protected:
* Notifies listeners that a block which builds directly on our current tip
* has been received and connected to the headers tree, though not validated yet */
virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {};
- friend void ::RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface>);
- friend void ::UnregisterValidationInterface(CValidationInterface*);
- friend void ::UnregisterAllValidationInterfaces();
+ friend class CMainSignals;
};
struct MainSignalsInstance;
diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp
index e4d0a3fa6d..ea54027c48 100644
--- a/src/wallet/rpcdump.cpp
+++ b/src/wallet/rpcdump.cpp
@@ -60,12 +60,13 @@ static bool GetWalletAddressesForKey(LegacyScriptPubKeyMan* spk_man, const CWall
CKey key;
spk_man->GetKey(keyid, key);
for (const auto& dest : GetAllDestinationsForKey(key.GetPubKey())) {
- if (pwallet->mapAddressBook.count(dest)) {
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
+ if (address_book_entry) {
if (!strAddr.empty()) {
strAddr += ",";
}
strAddr += EncodeDestination(dest);
- strLabel = EncodeDumpString(pwallet->mapAddressBook.at(dest).name);
+ strLabel = EncodeDumpString(address_book_entry->GetLabel());
fLabelFound = true;
}
}
@@ -168,7 +169,7 @@ UniValue importprivkey(const JSONRPCRequest& request)
// label all new addresses, and label existing addresses if a
// label was passed.
for (const auto& dest : GetAllDestinationsForKey(pubkey)) {
- if (!request.params[1].isNull() || pwallet->mapAddressBook.count(dest) == 0) {
+ if (!request.params[1].isNull() || !pwallet->FindAddressBookEntry(dest)) {
pwallet->SetAddressBook(dest, strLabel, "receive");
}
}
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index d8b3b57b61..61ad2f1198 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -499,8 +499,9 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request)
addressInfo.push_back(EncodeDestination(address));
addressInfo.push_back(ValueFromAmount(balances[address]));
{
- if (pwallet->mapAddressBook.find(address) != pwallet->mapAddressBook.end()) {
- addressInfo.push_back(pwallet->mapAddressBook.find(address)->second.name);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
+ if (address_book_entry) {
+ addressInfo.push_back(address_book_entry->GetLabel());
}
}
jsonGrouping.push_back(addressInfo);
@@ -1092,13 +1093,13 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWalle
UniValue ret(UniValue::VARR);
std::map<std::string, tallyitem> label_tally;
- // Create mapAddressBook iterator
+ // Create m_address_book iterator
// If we aren't filtering, go from begin() to end()
- auto start = pwallet->mapAddressBook.begin();
- auto end = pwallet->mapAddressBook.end();
+ auto start = pwallet->m_address_book.begin();
+ auto end = pwallet->m_address_book.end();
// If we are filtering, find() the applicable entry
if (has_filtered_address) {
- start = pwallet->mapAddressBook.find(filtered_address);
+ start = pwallet->m_address_book.find(filtered_address);
if (start != end) {
end = std::next(start);
}
@@ -1106,8 +1107,9 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, const CWalle
for (auto item_it = start; item_it != end; ++item_it)
{
+ if (item_it->second.IsChange()) continue;
const CTxDestination& address = item_it->first;
- const std::string& label = item_it->second.name;
+ const std::string& label = item_it->second.GetLabel();
auto it = mapTally.find(address);
if (it == mapTally.end() && !fIncludeEmpty)
continue;
@@ -1307,8 +1309,9 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
MaybePushAddress(entry, s.destination);
entry.pushKV("category", "send");
entry.pushKV("amount", ValueFromAmount(-s.amount));
- if (pwallet->mapAddressBook.count(s.destination)) {
- entry.pushKV("label", pwallet->mapAddressBook.at(s.destination).name);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(s.destination);
+ if (address_book_entry) {
+ entry.pushKV("label", address_book_entry->GetLabel());
}
entry.pushKV("vout", s.vout);
entry.pushKV("fee", ValueFromAmount(-nFee));
@@ -1324,8 +1327,9 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
for (const COutputEntry& r : listReceived)
{
std::string label;
- if (pwallet->mapAddressBook.count(r.destination)) {
- label = pwallet->mapAddressBook.at(r.destination).name;
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(r.destination);
+ if (address_book_entry) {
+ label = address_book_entry->GetLabel();
}
if (filter_label && label != *filter_label) {
continue;
@@ -1349,7 +1353,7 @@ static void ListTransactions(interfaces::Chain::Lock& locked_chain, const CWalle
entry.pushKV("category", "receive");
}
entry.pushKV("amount", ValueFromAmount(r.amount));
- if (pwallet->mapAddressBook.count(r.destination)) {
+ if (address_book_entry) {
entry.pushKV("label", label);
}
entry.pushKV("vout", r.vout);
@@ -1912,44 +1916,52 @@ static UniValue walletpassphrase(const JSONRPCRequest& request)
},
}.Check(request);
- auto locked_chain = pwallet->chain().lock();
- LOCK(pwallet->cs_wallet);
-
- if (!pwallet->IsCrypted()) {
- throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
- }
+ int64_t nSleepTime;
+ {
+ auto locked_chain = pwallet->chain().lock();
+ LOCK(pwallet->cs_wallet);
- // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
- SecureString strWalletPass;
- strWalletPass.reserve(100);
- // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
- // Alternately, find a way to make request.params[0] mlock()'d to begin with.
- strWalletPass = request.params[0].get_str().c_str();
+ if (!pwallet->IsCrypted()) {
+ throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called.");
+ }
- // Get the timeout
- int64_t nSleepTime = request.params[1].get_int64();
- // Timeout cannot be negative, otherwise it will relock immediately
- if (nSleepTime < 0) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
- }
- // Clamp timeout
- constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
- if (nSleepTime > MAX_SLEEP_TIME) {
- nSleepTime = MAX_SLEEP_TIME;
- }
+ // Note that the walletpassphrase is stored in request.params[0] which is not mlock()ed
+ SecureString strWalletPass;
+ strWalletPass.reserve(100);
+ // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
+ // Alternately, find a way to make request.params[0] mlock()'d to begin with.
+ strWalletPass = request.params[0].get_str().c_str();
+
+ // Get the timeout
+ nSleepTime = request.params[1].get_int64();
+ // Timeout cannot be negative, otherwise it will relock immediately
+ if (nSleepTime < 0) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Timeout cannot be negative.");
+ }
+ // Clamp timeout
+ constexpr int64_t MAX_SLEEP_TIME = 100000000; // larger values trigger a macos/libevent bug?
+ if (nSleepTime > MAX_SLEEP_TIME) {
+ nSleepTime = MAX_SLEEP_TIME;
+ }
- if (strWalletPass.empty()) {
- throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
- }
+ if (strWalletPass.empty()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "passphrase can not be empty");
+ }
- if (!pwallet->Unlock(strWalletPass)) {
- throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
- }
+ if (!pwallet->Unlock(strWalletPass)) {
+ throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect.");
+ }
- pwallet->TopUpKeyPool();
+ pwallet->TopUpKeyPool();
- pwallet->nRelockTime = GetTime() + nSleepTime;
+ pwallet->nRelockTime = GetTime() + nSleepTime;
+ }
+ // rpcRunLater must be called without cs_wallet held otherwise a deadlock
+ // can occur. The deadlock would happen when RPCRunLater removes the
+ // previous timer (and waits for the callback to finish if already running)
+ // and the callback locks cs_wallet.
+ AssertLockNotHeld(wallet->cs_wallet);
// Keep a weak pointer to the wallet so that it is possible to unload the
// wallet before the following callback is called. If a valid shared pointer
// is acquired in the callback then the wallet is still loaded.
@@ -2949,9 +2961,9 @@ static UniValue listunspent(const JSONRPCRequest& request)
if (fValidAddress) {
entry.pushKV("address", EncodeDestination(address));
- auto i = pwallet->mapAddressBook.find(address);
- if (i != pwallet->mapAddressBook.end()) {
- entry.pushKV("label", i->second.name);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(address);
+ if (address_book_entry) {
+ entry.pushKV("label", address_book_entry->GetLabel());
}
std::unique_ptr<SigningProvider> provider = pwallet->GetSolvingProvider(scriptPubKey);
@@ -3698,7 +3710,7 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v
{
UniValue ret(UniValue::VOBJ);
if (verbose) {
- ret.pushKV("name", data.name);
+ ret.pushKV("name", data.GetLabel());
}
ret.pushKV("purpose", data.purpose);
return ret;
@@ -3808,8 +3820,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// DEPRECATED: Return label field if existing. Currently only one label can
// be associated with an address, so the label should be equivalent to the
// value of the name key/value pair in the labels array below.
- if ((pwallet->chain().rpcEnableDeprecated("label")) && (pwallet->mapAddressBook.count(dest))) {
- ret.pushKV("label", pwallet->mapAddressBook.at(dest).name);
+ const auto* address_book_entry = pwallet->FindAddressBookEntry(dest);
+ if (pwallet->chain().rpcEnableDeprecated("label") && address_book_entry) {
+ ret.pushKV("label", address_book_entry->GetLabel());
}
ret.pushKV("ischange", pwallet->IsChange(scriptPubKey));
@@ -3832,14 +3845,13 @@ UniValue getaddressinfo(const JSONRPCRequest& request)
// stable if we allow multiple labels to be associated with an address in
// the future.
UniValue labels(UniValue::VARR);
- std::map<CTxDestination, CAddressBookData>::const_iterator mi = pwallet->mapAddressBook.find(dest);
- if (mi != pwallet->mapAddressBook.end()) {
+ if (address_book_entry) {
// DEPRECATED: The previous behavior of returning an array containing a
// JSON object of `name` and `purpose` key/value pairs is deprecated.
if (pwallet->chain().rpcEnableDeprecated("labelspurpose")) {
- labels.push_back(AddressBookDataToJSON(mi->second, true));
+ labels.push_back(AddressBookDataToJSON(*address_book_entry, true));
} else {
- labels.push_back(mi->second.name);
+ labels.push_back(address_book_entry->GetLabel());
}
}
ret.pushKV("labels", std::move(labels));
@@ -3883,10 +3895,11 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request)
// Find all addresses that have the given label
UniValue ret(UniValue::VOBJ);
std::set<std::string> addresses;
- for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->mapAddressBook) {
- if (item.second.name == label) {
+ for (const std::pair<const CTxDestination, CAddressBookData>& item : pwallet->m_address_book) {
+ if (item.second.IsChange()) continue;
+ if (item.second.GetLabel() == label) {
std::string address = EncodeDestination(item.first);
- // CWallet::mapAddressBook is not expected to contain duplicate
+ // CWallet::m_address_book is not expected to contain duplicate
// address strings, but build a separate set as a precaution just in
// case it does.
bool unique = addresses.emplace(address).second;
@@ -3947,9 +3960,10 @@ static UniValue listlabels(const JSONRPCRequest& request)
// Add to a set to sort by label name, then insert into Univalue array
std::set<std::string> label_set;
- for (const std::pair<const CTxDestination, CAddressBookData>& entry : pwallet->mapAddressBook) {
+ for (const std::pair<const CTxDestination, CAddressBookData>& entry : pwallet->m_address_book) {
+ if (entry.second.IsChange()) continue;
if (purpose.empty() || entry.second.purpose == purpose) {
- label_set.insert(entry.second.name);
+ label_set.insert(entry.second.GetLabel());
}
}
@@ -4242,6 +4256,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request);
UniValue removeprunedfunds(const JSONRPCRequest& request);
UniValue importmulti(const JSONRPCRequest& request);
+void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
+{
// clang-format off
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
@@ -4305,8 +4321,6 @@ static const CRPCCommand commands[] =
};
// clang-format on
-void RegisterWalletRPCCommands(interfaces::Chain& chain, std::vector<std::unique_ptr<interfaces::Handler>>& handlers)
-{
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
handlers.emplace_back(chain.handleRpc(commands[vcidx]));
}
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index bc9f84a11d..45f5542cad 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -1242,8 +1242,9 @@ bool CWallet::IsChange(const CScript& script) const
return true;
LOCK(cs_wallet);
- if (!mapAddressBook.count(address))
+ if (!FindAddressBookEntry(address)) {
return true;
+ }
}
return false;
}
@@ -3196,11 +3197,11 @@ bool CWallet::SetAddressBookWithDB(WalletBatch& batch, const CTxDestination& add
bool fUpdated = false;
{
LOCK(cs_wallet);
- std::map<CTxDestination, CAddressBookData>::iterator mi = mapAddressBook.find(address);
- fUpdated = mi != mapAddressBook.end();
- mapAddressBook[address].name = strName;
+ std::map<CTxDestination, CAddressBookData>::iterator mi = m_address_book.find(address);
+ fUpdated = (mi != m_address_book.end() && !mi->second.IsChange());
+ m_address_book[address].SetLabel(strName);
if (!strPurpose.empty()) /* update purpose only if requested */
- mapAddressBook[address].purpose = strPurpose;
+ m_address_book[address].purpose = strPurpose;
}
NotifyAddressBookChanged(this, address, strName, IsMine(address) != ISMINE_NO,
strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
@@ -3217,16 +3218,24 @@ bool CWallet::SetAddressBook(const CTxDestination& address, const std::string& s
bool CWallet::DelAddressBook(const CTxDestination& address)
{
+ // If we want to delete receiving addresses, we need to take care that DestData "used" (and possibly newer DestData) gets preserved (and the "deleted" address transformed into a change entry instead of actually being deleted)
+ // NOTE: This isn't a problem for sending addresses because they never have any DestData yet!
+ // When adding new DestData, it should be considered here whether to retain or delete it (or move it?).
+ if (IsMine(address)) {
+ WalletLogPrintf("%s called with IsMine address, NOT SUPPORTED. Please report this bug! %s\n", __func__, PACKAGE_BUGREPORT);
+ return false;
+ }
+
{
LOCK(cs_wallet);
// Delete destdata tuples associated with address
std::string strAddress = EncodeDestination(address);
- for (const std::pair<const std::string, std::string> &item : mapAddressBook[address].destdata)
+ for (const std::pair<const std::string, std::string> &item : m_address_book[address].destdata)
{
WalletBatch(*database).EraseDestData(strAddress, item.first);
}
- mapAddressBook.erase(address);
+ m_address_book.erase(address);
}
NotifyAddressBookChanged(this, address, "", IsMine(address) != ISMINE_NO, "", CT_DELETED);
@@ -3462,10 +3471,11 @@ std::set<CTxDestination> CWallet::GetLabelAddresses(const std::string& label) co
{
LOCK(cs_wallet);
std::set<CTxDestination> result;
- for (const std::pair<const CTxDestination, CAddressBookData>& item : mapAddressBook)
+ for (const std::pair<const CTxDestination, CAddressBookData>& item : m_address_book)
{
+ if (item.second.IsChange()) continue;
const CTxDestination& address = item.first;
- const std::string& strName = item.second.name;
+ const std::string& strName = item.second.GetLabel();
if (strName == label)
result.insert(address);
}
@@ -3666,26 +3676,26 @@ bool CWallet::AddDestData(WalletBatch& batch, const CTxDestination &dest, const
if (boost::get<CNoDestination>(&dest))
return false;
- mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
+ m_address_book[dest].destdata.insert(std::make_pair(key, value));
return batch.WriteDestData(EncodeDestination(dest), key, value);
}
bool CWallet::EraseDestData(WalletBatch& batch, const CTxDestination &dest, const std::string &key)
{
- if (!mapAddressBook[dest].destdata.erase(key))
+ if (!m_address_book[dest].destdata.erase(key))
return false;
return batch.EraseDestData(EncodeDestination(dest), key);
}
void CWallet::LoadDestData(const CTxDestination &dest, const std::string &key, const std::string &value)
{
- mapAddressBook[dest].destdata.insert(std::make_pair(key, value));
+ m_address_book[dest].destdata.insert(std::make_pair(key, value));
}
bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, std::string *value) const
{
- std::map<CTxDestination, CAddressBookData>::const_iterator i = mapAddressBook.find(dest);
- if(i != mapAddressBook.end())
+ std::map<CTxDestination, CAddressBookData>::const_iterator i = m_address_book.find(dest);
+ if(i != m_address_book.end())
{
CAddressBookData::StringMap::const_iterator j = i->second.destdata.find(key);
if(j != i->second.destdata.end())
@@ -3701,7 +3711,7 @@ bool CWallet::GetDestData(const CTxDestination &dest, const std::string &key, st
std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const
{
std::vector<std::string> values;
- for (const auto& address : mapAddressBook) {
+ for (const auto& address : m_address_book) {
for (const auto& data : address.second.destdata) {
if (!data.first.compare(0, prefix.size(), prefix)) {
values.emplace_back(data.second);
@@ -4103,12 +4113,22 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
{
walletInstance->WalletLogPrintf("setKeyPool.size() = %u\n", walletInstance->GetKeyPoolSize());
walletInstance->WalletLogPrintf("mapWallet.size() = %u\n", walletInstance->mapWallet.size());
- walletInstance->WalletLogPrintf("mapAddressBook.size() = %u\n", walletInstance->mapAddressBook.size());
+ walletInstance->WalletLogPrintf("m_address_book.size() = %u\n", walletInstance->m_address_book.size());
}
return walletInstance;
}
+const CAddressBookData* CWallet::FindAddressBookEntry(const CTxDestination& dest, bool allow_change) const
+{
+ const auto& address_book_it = m_address_book.find(dest);
+ if (address_book_it == m_address_book.end()) return nullptr;
+ if ((!allow_change) && address_book_it->second.IsChange()) {
+ return nullptr;
+ }
+ return &address_book_it->second;
+}
+
void CWallet::postInitProcess()
{
auto locked_chain = chain().lock();
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index fe59773488..6c54c72e76 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -181,14 +181,23 @@ public:
/** Address book data */
class CAddressBookData
{
+private:
+ bool m_change{true};
+ std::string m_label;
public:
- std::string name;
std::string purpose;
CAddressBookData() : purpose("unknown") {}
typedef std::map<std::string, std::string> StringMap;
StringMap destdata;
+
+ bool IsChange() const { return m_change; }
+ const std::string& GetLabel() const { return m_label; }
+ void SetLabel(const std::string& label) {
+ m_change = false;
+ m_label = label;
+ }
};
struct CRecipient
@@ -775,7 +784,8 @@ public:
int64_t nOrderPosNext GUARDED_BY(cs_wallet) = 0;
uint64_t nAccountingEntryNumber = 0;
- std::map<CTxDestination, CAddressBookData> mapAddressBook GUARDED_BY(cs_wallet);
+ std::map<CTxDestination, CAddressBookData> m_address_book GUARDED_BY(cs_wallet);
+ const CAddressBookData* FindAddressBookEntry(const CTxDestination&, bool allow_change = false) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
std::set<COutPoint> setLockedCoins GUARDED_BY(cs_wallet);
@@ -842,7 +852,10 @@ public:
bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; }
- //! Adds a destination data tuple to the store, and saves it to disk
+ /**
+ * Adds a destination data tuple to the store, and saves it to disk
+ * When adding new fields, take care to consider how DelAddressBook should handle it!
+ */
bool AddDestData(WalletBatch& batch, const CTxDestination& dest, const std::string& key, const std::string& value) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
//! Erases a destination data tuple in the store and on disk
bool EraseDestData(WalletBatch& batch, const CTxDestination& dest, const std::string& key) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet);
diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp
index a1928f45c4..568b21ed00 100644
--- a/src/wallet/walletdb.cpp
+++ b/src/wallet/walletdb.cpp
@@ -206,11 +206,13 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue,
if (strType == DBKeys::NAME) {
std::string strAddress;
ssKey >> strAddress;
- ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].name;
+ std::string label;
+ ssValue >> label;
+ pwallet->m_address_book[DecodeDestination(strAddress)].SetLabel(label);
} else if (strType == DBKeys::PURPOSE) {
std::string strAddress;
ssKey >> strAddress;
- ssValue >> pwallet->mapAddressBook[DecodeDestination(strAddress)].purpose;
+ ssValue >> pwallet->m_address_book[DecodeDestination(strAddress)].purpose;
} else if (strType == DBKeys::TX) {
uint256 hash;
ssKey >> hash;
diff --git a/src/wallet/wallettool.cpp b/src/wallet/wallettool.cpp
index fbfdf9dd6b..8c918f4eb0 100644
--- a/src/wallet/wallettool.cpp
+++ b/src/wallet/wallettool.cpp
@@ -99,7 +99,7 @@ static void WalletShowInfo(CWallet* wallet_instance)
tfm::format(std::cout, "HD (hd seed available): %s\n", wallet_instance->IsHDEnabled() ? "yes" : "no");
tfm::format(std::cout, "Keypool Size: %u\n", wallet_instance->GetKeyPoolSize());
tfm::format(std::cout, "Transactions: %zu\n", wallet_instance->mapWallet.size());
- tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->mapAddressBook.size());
+ tfm::format(std::cout, "Address Book: %zu\n", wallet_instance->m_address_book.size());
}
bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
diff --git a/test/functional/rpc_signrawtransaction.py b/test/functional/rpc_signrawtransaction.py
index 780758e219..17686f3a78 100755
--- a/test/functional/rpc_signrawtransaction.py
+++ b/test/functional/rpc_signrawtransaction.py
@@ -4,10 +4,11 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test transaction signing using the signrawtransaction* RPCs."""
+from test_framework.address import check_script, script_to_p2sh
from test_framework.test_framework import BitcoinTestFramework
-from test_framework.util import assert_equal, assert_raises_rpc_error, hex_str_to_bytes
+from test_framework.util import assert_equal, assert_raises_rpc_error, find_vout_for_address, hex_str_to_bytes
from test_framework.messages import sha256
-from test_framework.script import CScript, OP_0
+from test_framework.script import CScript, OP_0, OP_CHECKSIG
from decimal import Decimal
@@ -168,6 +169,44 @@ class SignRawTransactionsTest(BitcoinTestFramework):
assert 'complete' in spending_tx_signed
assert_equal(spending_tx_signed['complete'], True)
+ self.log.info('Try with a P2PKH script as the witnessScript')
+ embedded_addr_info = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress('', 'legacy'))
+ embedded_privkey = self.nodes[1].dumpprivkey(embedded_addr_info['address'])
+ witness_script = embedded_addr_info['scriptPubKey']
+ redeem_script = CScript([OP_0, sha256(check_script(witness_script))]).hex()
+ addr = script_to_p2sh(redeem_script)
+ script_pub_key = self.nodes[1].validateaddress(addr)['scriptPubKey']
+ # Fund that address
+ txid = self.nodes[0].sendtoaddress(addr, 10)
+ vout = find_vout_for_address(self.nodes[0], txid, addr)
+ self.nodes[0].generate(1)
+ # Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
+ spending_tx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): Decimal("9.999")})
+ spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [{'txid': txid, 'vout': vout, 'scriptPubKey': script_pub_key, 'redeemScript': redeem_script, 'witnessScript': witness_script, 'amount': 10}])
+ # Check the signing completed successfully
+ assert 'complete' in spending_tx_signed
+ assert_equal(spending_tx_signed['complete'], True)
+ self.nodes[0].sendrawtransaction(spending_tx_signed['hex'])
+
+ self.log.info('Try with a P2PK script as the witnessScript')
+ embedded_addr_info = self.nodes[1].getaddressinfo(self.nodes[1].getnewaddress('', 'legacy'))
+ embedded_privkey = self.nodes[1].dumpprivkey(embedded_addr_info['address'])
+ witness_script = CScript([hex_str_to_bytes(embedded_addr_info['pubkey']), OP_CHECKSIG]).hex()
+ redeem_script = CScript([OP_0, sha256(check_script(witness_script))]).hex()
+ addr = script_to_p2sh(redeem_script)
+ script_pub_key = self.nodes[1].validateaddress(addr)['scriptPubKey']
+ # Fund that address
+ txid = self.nodes[0].sendtoaddress(addr, 10)
+ vout = find_vout_for_address(self.nodes[0], txid, addr)
+ self.nodes[0].generate(1)
+ # Now create and sign a transaction spending that output on node[0], which doesn't know the scripts or keys
+ spending_tx = self.nodes[0].createrawtransaction([{'txid': txid, 'vout': vout}], {self.nodes[1].getnewaddress(): Decimal("9.999")})
+ spending_tx_signed = self.nodes[0].signrawtransactionwithkey(spending_tx, [embedded_privkey], [{'txid': txid, 'vout': vout, 'scriptPubKey': script_pub_key, 'redeemScript': redeem_script, 'witnessScript': witness_script, 'amount': 10}])
+ # Check the signing completed successfully
+ assert 'complete' in spending_tx_signed
+ assert_equal(spending_tx_signed['complete'], True)
+ self.nodes[0].sendrawtransaction(spending_tx_signed['hex'])
+
def run_test(self):
self.successful_signing_test()
self.script_verification_error_test()
diff --git a/test/functional/wallet_avoidreuse.py b/test/functional/wallet_avoidreuse.py
index 8e2dc03ac2..2ce8d459c6 100755
--- a/test/functional/wallet_avoidreuse.py
+++ b/test/functional/wallet_avoidreuse.py
@@ -83,6 +83,7 @@ class AvoidReuseTest(BitcoinTestFramework):
self.nodes[0].generate(110)
self.sync_all()
+ self.test_change_remains_change(self.nodes[1])
reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
self.test_fund_send_fund_senddirty()
reset_balance(self.nodes[1], self.nodes[0].getnewaddress())
@@ -137,6 +138,30 @@ class AvoidReuseTest(BitcoinTestFramework):
# Unload temp wallet
self.nodes[1].unloadwallet(tempwallet)
+ def test_change_remains_change(self, node):
+ self.log.info("Test that change doesn't turn into non-change when spent")
+
+ reset_balance(node, node.getnewaddress())
+ addr = node.getnewaddress()
+ txid = node.sendtoaddress(addr, 1)
+ out = node.listunspent(minconf=0, query_options={'minimumAmount': 2})
+ assert_equal(len(out), 1)
+ assert_equal(out[0]['txid'], txid)
+ changeaddr = out[0]['address']
+
+ # Make sure it's starting out as change as expected
+ assert node.getaddressinfo(changeaddr)['ischange']
+ for logical_tx in node.listtransactions():
+ assert logical_tx.get('address') != changeaddr
+
+ # Spend it
+ reset_balance(node, node.getnewaddress())
+
+ # It should still be change
+ assert node.getaddressinfo(changeaddr)['ischange']
+ for logical_tx in node.listtransactions():
+ assert logical_tx.get('address') != changeaddr
+
def test_fund_send_fund_senddirty(self):
'''
Test the same as test_fund_send_fund_send, except send the 10 BTC with
diff --git a/test/functional/wallet_bumpfee.py b/test/functional/wallet_bumpfee.py
index 38c9807757..0b3dea94d5 100755
--- a/test/functional/wallet_bumpfee.py
+++ b/test/functional/wallet_bumpfee.py
@@ -253,12 +253,17 @@ def test_dust_to_fee(self, rbf_node, dest_address):
self.log.info('Test that bumped output that is dust is dropped to fee')
rbfid = spend_one_input(rbf_node, dest_address)
fulltx = rbf_node.getrawtransaction(rbfid, 1)
- # size of transaction (p2wpkh, 1 input, 2 outputs): 141 vbytes
- assert_equal(fulltx["vsize"], 141)
- # bump with fee_rate of 0.00350000 BTC per 1000 vbytes
- # expected bump fee of 141 vbytes * fee_rate 0.00350000 BTC / 1000 vbytes = 0.00049350 BTC
- # but dust is dropped, so actual bump fee is 0.00050000
- bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": 0.0035})
+ # The DER formatting used by Bitcoin to serialize ECDSA signatures means that signatures can have a
+ # variable size of 70-72 bytes (or possibly even less), with most being 71 or 72 bytes. The signature
+ # in the witness is divided by 4 for the vsize, so this variance can take the weight across a 4-byte
+ # boundary. Thus expected transaction size (p2wpkh, 1 input, 2 outputs) is 140-141 vbytes, usually 141.
+ if not 140 <= fulltx["vsize"] <= 141:
+ raise AssertionError("Invalid tx vsize of {} (140-141 expected), full tx: {}".format(fulltx["vsize"], fulltx))
+ # Bump with fee_rate of 0.00350250 BTC per 1000 vbytes to create dust.
+ # Expected fee is 141 vbytes * fee_rate 0.00350250 BTC / 1000 vbytes = 0.00049385 BTC.
+ # or occasionally 140 vbytes * fee_rate 0.00350250 BTC / 1000 vbytes = 0.00049035 BTC.
+ # Dust should be dropped to the fee, so actual bump fee is 0.00050000 BTC.
+ bumped_tx = rbf_node.bumpfee(rbfid, {"fee_rate": 0.00350250})
full_bumped_tx = rbf_node.getrawtransaction(bumped_tx["txid"], 1)
assert_equal(bumped_tx["fee"], Decimal("0.00050000"))
assert_equal(len(fulltx["vout"]), 2)