aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/init.cpp20
-rw-r--r--src/net.cpp63
-rw-r--r--src/net.h35
-rw-r--r--src/net_permissions.cpp106
-rw-r--r--src/net_permissions.h62
-rw-r--r--src/net_processing.cpp40
-rw-r--r--src/netbase.cpp4
-rw-r--r--src/netbase.h2
-rw-r--r--src/obj-test/.gitignore2
-rw-r--r--src/qt/rpcconsole.cpp2
-rw-r--r--src/qt/walletcontroller.cpp19
-rw-r--r--src/qt/walletcontroller.h3
-rw-r--r--src/rpc/mining.cpp2
-rw-r--r--src/rpc/net.cpp8
-rw-r--r--src/script/descriptor.cpp36
-rw-r--r--src/script/descriptor.h6
-rw-r--r--src/test/netbase_tests.cpp79
-rw-r--r--src/test/timedata_tests.cpp65
-rw-r--r--src/test/util_tests.cpp18
-rw-r--r--src/util/strencodings.cpp13
-rw-r--r--src/util/strencodings.h33
-rw-r--r--src/util/system.cpp2
-rw-r--r--src/wallet/wallet.cpp18
-rw-r--r--src/wallet/wallet.h2
25 files changed, 504 insertions, 138 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ef5b1900d9..141d8e68ea 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -150,6 +150,7 @@ BITCOIN_CORE_H = \
merkleblock.h \
miner.h \
net.h \
+ net_permissions.h \
net_processing.h \
netaddress.h \
netbase.h \
@@ -454,6 +455,7 @@ libbitcoin_common_a_SOURCES = \
merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
+ net_permissions.cpp \
outputtype.cpp \
policy/feerate.cpp \
policy/policy.cpp \
diff --git a/src/init.cpp b/src/init.cpp
index bb3ff8d88f..25c964205a 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -27,6 +27,7 @@
#include <key.h>
#include <miner.h>
#include <net.h>
+#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
#include <policy/feerate.h>
@@ -1775,21 +1776,16 @@ bool AppInitMain(InitInterfaces& interfaces)
connOptions.vBinds.push_back(addrBind);
}
for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
- CService addrBind;
- if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
- return InitError(ResolveErrMsg("whitebind", strBind));
- }
- if (addrBind.GetPort() == 0) {
- return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind));
- }
- connOptions.vWhiteBinds.push_back(addrBind);
+ NetWhitebindPermissions whitebind;
+ std::string error;
+ if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
+ connOptions.vWhiteBinds.push_back(whitebind);
}
for (const auto& net : gArgs.GetArgs("-whitelist")) {
- CSubNet subnet;
- LookupSubNet(net.c_str(), subnet);
- if (!subnet.IsValid())
- return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net));
+ NetWhitelistPermissions subnet;
+ std::string error;
+ if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}
diff --git a/src/net.cpp b/src/net.cpp
index 7d6eb31a7c..0464a6e9ea 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -16,6 +16,7 @@
#include <crypto/common.h>
#include <crypto/sha256.h>
#include <netbase.h>
+#include <net_permissions.h>
#include <primitives/transaction.h>
#include <scheduler.h>
#include <ui_interface.h>
@@ -67,7 +68,6 @@ enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1),
- BF_WHITELIST = (1U << 2),
};
// The set of sockets cannot be modified while waiting
@@ -459,12 +459,10 @@ void CNode::CloseSocketDisconnect()
}
}
-bool CConnman::IsWhitelistedRange(const CNetAddr &addr) {
- for (const CSubNet& subnet : vWhitelistedRange) {
- if (subnet.Match(addr))
- return true;
+void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const {
+ for (const auto& subnet : vWhitelistedRange) {
+ if (subnet.m_subnet.Match(addr)) NetPermissions::AddFlag(flags, subnet.m_flags);
}
- return false;
}
std::string CNode::GetAddrName() const {
@@ -528,7 +526,8 @@ void CNode::copyStats(CNodeStats &stats)
X(mapRecvBytesPerMsgCmd);
X(nRecvBytes);
}
- X(fWhitelisted);
+ X(m_legacyWhitelisted);
+ X(m_permissionFlags);
{
LOCK(cs_feeFilter);
X(minFeeFilter);
@@ -813,7 +812,7 @@ bool CConnman::AttemptToEvictConnection()
LOCK(cs_vNodes);
for (const CNode* node : vNodes) {
- if (node->fWhitelisted)
+ if (node->HasPermission(PF_NOBAN))
continue;
if (!node->fInbound)
continue;
@@ -904,7 +903,20 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}
- bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
+ NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
+ hListenSocket.AddSocketPermissionFlags(permissionFlags);
+ AddWhitelistPermissionFlags(permissionFlags, addr);
+ const bool noban = NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN);
+ bool legacyWhitelisted = false;
+ if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
+ NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
+ if (gArgs.GetBoolArg("-whitelistforcerelay", false)) NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY);
+ if (gArgs.GetBoolArg("-whitelistrelay", false)) NetPermissions::AddFlag(permissionFlags, PF_RELAY);
+ NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL);
+ NetPermissions::AddFlag(permissionFlags, PF_NOBAN);
+ legacyWhitelisted = true;
+ }
+
{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
@@ -941,7 +953,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
// Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept
// if the only banning reason was an automatic misbehavior ban.
- if (!whitelisted && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
+ if (!noban && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
@@ -962,9 +974,15 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
CAddress addr_bind = GetBindAddress(hSocket);
- CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
+ ServiceFlags nodeServices = nLocalServices;
+ if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
+ nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
+ }
+ CNode* pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
pnode->AddRef();
- pnode->fWhitelisted = whitelisted;
+ pnode->m_permissionFlags = permissionFlags;
+ // If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
+ pnode->m_legacyWhitelisted = legacyWhitelisted;
pnode->m_prefer_evict = bannedlevel > 0;
m_msgproc->InitializeNode(pnode);
@@ -1983,7 +2001,7 @@ void CConnman::ThreadMessageHandler()
-bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)
+bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, NetPermissionFlags permissions)
{
strError = "";
int nOne = 1;
@@ -2044,9 +2062,9 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
return false;
}
- vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
+ vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));
- if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
+ if (addrBind.IsRoutable() && fDiscover && (permissions & PF_NOBAN) == 0)
AddLocal(addrBind, LOCAL_BIND);
return true;
@@ -2130,11 +2148,11 @@ NodeId CConnman::GetNewNodeId()
}
-bool CConnman::Bind(const CService &addr, unsigned int flags) {
+bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) {
if (!(flags & BF_EXPLICIT) && !IsReachable(addr))
return false;
std::string strError;
- if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) {
+ if (!BindListenPort(addr, strError, permissions)) {
if ((flags & BF_REPORT_ERROR) && clientInterface) {
clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR);
}
@@ -2143,20 +2161,21 @@ bool CConnman::Bind(const CService &addr, unsigned int flags) {
return true;
}
-bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds) {
+bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds)
+{
bool fBound = false;
for (const auto& addrBind : binds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
+ fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::PF_NONE);
}
for (const auto& addrBind : whiteBinds) {
- fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
+ fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
}
if (binds.empty() && whiteBinds.empty()) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
- fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE);
- fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
+ fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE);
+ fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE);
}
return fBound;
}
diff --git a/src/net.h b/src/net.h
index 37aaf1a63b..75c05c9cb5 100644
--- a/src/net.h
+++ b/src/net.h
@@ -15,6 +15,7 @@
#include <hash.h>
#include <limitedmap.h>
#include <netaddress.h>
+#include <net_permissions.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
@@ -138,8 +139,9 @@ public:
uint64_t nMaxOutboundLimit = 0;
int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT;
std::vector<std::string> vSeedNodes;
- std::vector<CSubNet> vWhitelistedRange;
- std::vector<CService> vBinds, vWhiteBinds;
+ std::vector<NetWhitelistPermissions> vWhitelistedRange;
+ std::vector<NetWhitebindPermissions> vWhiteBinds;
+ std::vector<CService> vBinds;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
@@ -314,15 +316,17 @@ public:
private:
struct ListenSocket {
+ public:
SOCKET socket;
- bool whitelisted;
-
- ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {}
+ inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); }
+ ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {}
+ private:
+ NetPermissionFlags m_permissions;
};
- bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
- bool Bind(const CService &addr, unsigned int flags);
- bool InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds);
+ bool BindListenPort(const CService& bindAddr, std::string& strError, NetPermissionFlags permissions);
+ bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
+ bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds);
void ThreadOpenAddedConnections();
void AddOneShot(const std::string& strDest);
void ProcessOneShot();
@@ -347,7 +351,7 @@ private:
bool AttemptToEvictConnection();
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection);
- bool IsWhitelistedRange(const CNetAddr &addr);
+ void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;
void DeleteNode(CNode* pnode);
@@ -380,7 +384,7 @@ private:
// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
- std::vector<CSubNet> vWhitelistedRange;
+ std::vector<NetWhitelistPermissions> vWhitelistedRange;
unsigned int nSendBufferMaxSize{0};
unsigned int nReceiveFloodSize{0};
@@ -448,7 +452,6 @@ void StartMapPort();
void InterruptMapPort();
void StopMapPort();
unsigned short GetListenPort();
-bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
struct CombinerAll
{
@@ -555,7 +558,8 @@ public:
mapMsgCmdSize mapSendBytesPerMsgCmd;
uint64_t nRecvBytes;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
- bool fWhitelisted;
+ NetPermissionFlags m_permissionFlags;
+ bool m_legacyWhitelisted;
double dPingTime;
double dPingWait;
double dMinPing;
@@ -657,7 +661,11 @@ public:
*/
std::string cleanSubVer GUARDED_BY(cs_SubVer){};
bool m_prefer_evict{false}; // This peer is preferred for eviction.
- bool fWhitelisted{false}; // This peer can bypass DoS banning.
+ bool HasPermission(NetPermissionFlags permission) const {
+ return NetPermissions::HasFlag(m_permissionFlags, permission);
+ }
+ // This boolean is unusued in actual processing, only present for backward compatibility at RPC/QT level
+ bool m_legacyWhitelisted{false};
bool fFeeler{false}; // If true this node is being used as a short lived feeler.
bool fOneShot{false};
bool m_manual_connection{false};
@@ -753,6 +761,7 @@ private:
const ServiceFlags nLocalServices;
const int nMyStartingHeight;
int nSendVersion{0};
+ NetPermissionFlags m_permissionFlags{ PF_NONE };
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
mutable CCriticalSection cs_addrName;
diff --git a/src/net_permissions.cpp b/src/net_permissions.cpp
new file mode 100644
index 0000000000..736f19293a
--- /dev/null
+++ b/src/net_permissions.cpp
@@ -0,0 +1,106 @@
+// Copyright (c) 2009-2018 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 <net_permissions.h>
+#include <util/system.h>
+#include <util/translation.h>
+#include <netbase.h>
+
+// The parse the following format "perm1,perm2@xxxxxx"
+bool TryParsePermissionFlags(const std::string str, NetPermissionFlags& output, size_t& readen, std::string& error)
+{
+ NetPermissionFlags flags = PF_NONE;
+ const auto atSeparator = str.find('@');
+
+ // if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
+ if (atSeparator == std::string::npos) {
+ NetPermissions::AddFlag(flags, PF_ISIMPLICIT);
+ readen = 0;
+ }
+ // else (ie, "perm1,perm2@xxxxx"), let's enumerate the permissions by splitting by ',' and calculate the flags
+ else {
+ readen = 0;
+ // permissions == perm1,perm2
+ const auto permissions = str.substr(0, atSeparator);
+ while (readen < permissions.length()) {
+ const auto commaSeparator = permissions.find(',', readen);
+ const auto len = commaSeparator == std::string::npos ? permissions.length() - readen : commaSeparator - readen;
+ // permission == perm1
+ const auto permission = permissions.substr(readen, len);
+ readen += len; // We read "perm1"
+ if (commaSeparator != std::string::npos) readen++; // We read ","
+
+ if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, PF_BLOOMFILTER);
+ else if (permission == "noban") NetPermissions::AddFlag(flags, PF_NOBAN);
+ else if (permission == "forcerelay") NetPermissions::AddFlag(flags, PF_FORCERELAY);
+ else if (permission == "mempool") NetPermissions::AddFlag(flags, PF_MEMPOOL);
+ else if (permission == "all") NetPermissions::AddFlag(flags, PF_ALL);
+ else if (permission == "relay") NetPermissions::AddFlag(flags, PF_RELAY);
+ else if (permission.length() == 0); // Allow empty entries
+ else {
+ error = strprintf(_("Invalid P2P permission: '%s'").translated, permission);
+ return false;
+ }
+ }
+ readen++;
+ }
+
+ output = flags;
+ error = "";
+ return true;
+}
+
+std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
+{
+ std::vector<std::string> strings;
+ if (NetPermissions::HasFlag(flags, PF_BLOOMFILTER)) strings.push_back("bloomfilter");
+ if (NetPermissions::HasFlag(flags, PF_NOBAN)) strings.push_back("noban");
+ if (NetPermissions::HasFlag(flags, PF_FORCERELAY)) strings.push_back("forcerelay");
+ if (NetPermissions::HasFlag(flags, PF_RELAY)) strings.push_back("relay");
+ if (NetPermissions::HasFlag(flags, PF_MEMPOOL)) strings.push_back("mempool");
+ return strings;
+}
+
+bool NetWhitebindPermissions::TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error)
+{
+ NetPermissionFlags flags;
+ size_t offset;
+ if (!TryParsePermissionFlags(str, flags, offset, error)) return false;
+
+ const std::string strBind = str.substr(offset);
+ CService addrBind;
+ if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
+ error = strprintf(_("Cannot resolve -%s address: '%s'").translated, "whitebind", strBind);
+ return false;
+ }
+ if (addrBind.GetPort() == 0) {
+ error = strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind);
+ return false;
+ }
+
+ output.m_flags = flags;
+ output.m_service = addrBind;
+ error = "";
+ return true;
+}
+
+bool NetWhitelistPermissions::TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error)
+{
+ NetPermissionFlags flags;
+ size_t offset;
+ if (!TryParsePermissionFlags(str, flags, offset, error)) return false;
+
+ const std::string net = str.substr(offset);
+ CSubNet subnet;
+ LookupSubNet(net.c_str(), subnet);
+ if (!subnet.IsValid()) {
+ error = strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net);
+ return false;
+ }
+
+ output.m_flags = flags;
+ output.m_subnet = subnet;
+ error = "";
+ return true;
+}
diff --git a/src/net_permissions.h b/src/net_permissions.h
new file mode 100644
index 0000000000..b3987de65f
--- /dev/null
+++ b/src/net_permissions.h
@@ -0,0 +1,62 @@
+// Copyright (c) 2009-2018 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 <string>
+#include <vector>
+#include <netaddress.h>
+
+#ifndef BITCOIN_NET_PERMISSIONS_H
+#define BITCOIN_NET_PERMISSIONS_H
+enum NetPermissionFlags
+{
+ PF_NONE = 0,
+ // Can query bloomfilter even if -peerbloomfilters is false
+ PF_BLOOMFILTER = (1U << 1),
+ // Relay and accept transactions from this peer, even if -blocksonly is true
+ PF_RELAY = (1U << 3),
+ // Always relay transactions from this peer, even if already in mempool or rejected from policy
+ // Keep parameter interaction: forcerelay implies relay
+ PF_FORCERELAY = (1U << 2) | PF_RELAY,
+ // Can't be banned for misbehavior
+ PF_NOBAN = (1U << 4),
+ // Can query the mempool
+ PF_MEMPOOL = (1U << 5),
+
+ // True if the user did not specifically set fine grained permissions
+ PF_ISIMPLICIT = (1U << 31),
+ PF_ALL = PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY | PF_NOBAN | PF_MEMPOOL,
+};
+class NetPermissions
+{
+public:
+ NetPermissionFlags m_flags;
+ static std::vector<std::string> ToStrings(NetPermissionFlags flags);
+ static inline bool HasFlag(const NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ return (flags & f) == f;
+ }
+ static inline void AddFlag(NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ flags = static_cast<NetPermissionFlags>(flags | f);
+ }
+ static inline void ClearFlag(NetPermissionFlags& flags, NetPermissionFlags f)
+ {
+ flags = static_cast<NetPermissionFlags>(flags & ~f);
+ }
+};
+class NetWhitebindPermissions : public NetPermissions
+{
+public:
+ static bool TryParse(const std::string str, NetWhitebindPermissions& output, std::string& error);
+ CService m_service;
+};
+
+class NetWhitelistPermissions : public NetPermissions
+{
+public:
+ static bool TryParse(const std::string str, NetWhitelistPermissions& output, std::string& error);
+ CSubNet m_subnet;
+};
+
+#endif // BITCOIN_NET_PERMISSIONS_H \ No newline at end of file
diff --git a/src/net_processing.cpp b/src/net_processing.cpp
index 5efb4adee6..3db460d444 100644
--- a/src/net_processing.cpp
+++ b/src/net_processing.cpp
@@ -408,7 +408,7 @@ static void UpdatePreferredDownload(CNode* node, CNodeState* state) EXCLUSIVE_LO
nPreferredDownload -= state->fPreferredDownload;
// Whether this node should be marked as a preferred download node.
- state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
+ state->fPreferredDownload = (!node->fInbound || node->HasPermission(PF_NOBAN)) && !node->fOneShot && !node->fClient;
nPreferredDownload += state->fPreferredDownload;
}
@@ -1398,7 +1398,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
const CNetMsgMaker msgMaker(pfrom->GetSendVersion());
// disconnect node in case we have reached the outbound limit for serving historical blocks
// never disconnect whitelisted nodes
- if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->fWhitelisted)
+ if (send && connman->OutboundTargetReached(true) && ( ((pindexBestHeader != nullptr) && (pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() > HISTORICAL_BLOCK_AGE)) || inv.type == MSG_FILTERED_BLOCK) && !pfrom->HasPermission(PF_NOBAN))
{
LogPrint(BCLog::NET, "historical block serving limit reached, disconnect peer=%d\n", pfrom->GetId());
@@ -1407,7 +1407,7 @@ void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, c
send = false;
}
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
- if (send && !pfrom->fWhitelisted && (
+ if (send && !pfrom->HasPermission(PF_NOBAN) && (
(((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (::ChainActive().Tip()->nHeight - pindex->nHeight > (int)NODE_NETWORK_LIMITED_MIN_BLOCKS + 2 /* add two blocks buffer extension for possible races */) )
)) {
LogPrint(BCLog::NET, "Ignore block request below NODE_NETWORK_LIMITED threshold from peer=%d\n", pfrom->GetId());
@@ -2217,7 +2217,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
bool fBlocksOnly = !g_relay_txes;
// Allow whitelisted peers to send data other than blocks in blocks only mode if whitelistrelay is true
- if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY))
+ if (pfrom->HasPermission(PF_RELAY))
fBlocksOnly = false;
LOCK(cs_main);
@@ -2412,7 +2412,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
LOCK(cs_main);
- if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->fWhitelisted) {
+ if (::ChainstateActive().IsInitialBlockDownload() && !pfrom->HasPermission(PF_NOBAN)) {
LogPrint(BCLog::NET, "Ignoring getheaders from peer=%d because node is in initial block download\n", pfrom->GetId());
return true;
}
@@ -2470,7 +2470,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
if (strCommand == NetMsgType::TX) {
// Stop processing the transaction early if
// We are in blocks only mode and peer is either not whitelisted or whitelistrelay is off
- if (!g_relay_txes && (!pfrom->fWhitelisted || !gArgs.GetBoolArg("-whitelistrelay", DEFAULT_WHITELISTRELAY)))
+ if (!g_relay_txes && !pfrom->HasPermission(PF_RELAY))
{
LogPrint(BCLog::NET, "transaction sent in violation of protocol peer=%d\n", pfrom->GetId());
return true;
@@ -2565,7 +2565,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
AddToCompactExtraTransactions(ptx);
}
- if (pfrom->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY)) {
+ if (pfrom->HasPermission(PF_FORCERELAY)) {
// Always relay transactions received from whitelisted peers, even
// if they were already in the mempool or rejected from it due
// to policy, allowing the node to function as a gateway for
@@ -3010,17 +3010,23 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr
}
if (strCommand == NetMsgType::MEMPOOL) {
- if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->fWhitelisted)
+ if (!(pfrom->GetLocalServices() & NODE_BLOOM) && !pfrom->HasPermission(PF_MEMPOOL))
{
- LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ if (!pfrom->HasPermission(PF_NOBAN))
+ {
+ LogPrint(BCLog::NET, "mempool request with bloom filters disabled, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ }
return true;
}
- if (connman->OutboundTargetReached(false) && !pfrom->fWhitelisted)
+ if (connman->OutboundTargetReached(false) && !pfrom->HasPermission(PF_MEMPOOL))
{
- LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
- pfrom->fDisconnect = true;
+ if (!pfrom->HasPermission(PF_NOBAN))
+ {
+ LogPrint(BCLog::NET, "mempool request with bandwidth limit reached, disconnect peer=%d\n", pfrom->GetId());
+ pfrom->fDisconnect = true;
+ }
return true;
}
@@ -3216,7 +3222,7 @@ bool PeerLogicValidation::SendRejectsAndCheckIfBanned(CNode* pnode, bool enable_
if (state.fShouldBan) {
state.fShouldBan = false;
- if (pnode->fWhitelisted)
+ if (pnode->HasPermission(PF_NOBAN))
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pnode->addr.ToString());
else if (pnode->m_manual_connection)
LogPrintf("Warning: not punishing manually-connected peer %s!\n", pnode->addr.ToString());
@@ -3786,7 +3792,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
pto->vInventoryBlockToSend.clear();
// Check whether periodic sends should happen
- bool fSendTrickle = pto->fWhitelisted;
+ bool fSendTrickle = pto->HasPermission(PF_NOBAN);
if (pto->nNextInvSend < nNow) {
fSendTrickle = true;
if (pto->fInbound) {
@@ -3942,7 +3948,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
// Note: If all our peers are inbound, then we won't
// disconnect our sync peer for stalling; we have bigger
// problems if we can't get any outbound peers.
- if (!pto->fWhitelisted) {
+ if (!pto->HasPermission(PF_NOBAN)) {
LogPrintf("Timeout downloading headers from peer=%d, disconnecting\n", pto->GetId());
pto->fDisconnect = true;
return true;
@@ -4060,7 +4066,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto)
//
// We don't want white listed peers to filter txs to us if we have -whitelistforcerelay
if (pto->nVersion >= FEEFILTER_VERSION && gArgs.GetBoolArg("-feefilter", DEFAULT_FEEFILTER) &&
- !(pto->fWhitelisted && gArgs.GetBoolArg("-whitelistforcerelay", DEFAULT_WHITELISTFORCERELAY))) {
+ !pto->HasPermission(PF_FORCERELAY)) {
CAmount currentFilter = mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFeePerK();
int64_t timeNow = GetTimeMicros();
if (timeNow > pto->nextSendTimeFeeFilter) {
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 6d4738c835..0148aea428 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -37,8 +37,8 @@ bool fNameLookup = DEFAULT_NAME_LOOKUP;
static const int SOCKS5_RECV_TIMEOUT = 20 * 1000;
static std::atomic<bool> interruptSocks5Recv(false);
-enum Network ParseNetwork(std::string net) {
- Downcase(net);
+enum Network ParseNetwork(const std::string& net_in) {
+ std::string net = ToLower(net_in);
if (net == "ipv4") return NET_IPV4;
if (net == "ipv6") return NET_IPV6;
if (net == "onion") return NET_ONION;
diff --git a/src/netbase.h b/src/netbase.h
index 708df5b8e2..313a575687 100644
--- a/src/netbase.h
+++ b/src/netbase.h
@@ -37,7 +37,7 @@ public:
bool randomize_credentials;
};
-enum Network ParseNetwork(std::string net);
+enum Network ParseNetwork(const std::string& net);
std::string GetNetworkName(enum Network net);
bool SetProxy(enum Network net, const proxyType &addrProxy);
bool GetProxy(enum Network net, proxyType &proxyInfoOut);
diff --git a/src/obj-test/.gitignore b/src/obj-test/.gitignore
deleted file mode 100644
index d6b7ef32c8..0000000000
--- a/src/obj-test/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp
index cdf84eae9a..19b11ba1cd 100644
--- a/src/qt/rpcconsole.cpp
+++ b/src/qt/rpcconsole.cpp
@@ -1120,7 +1120,7 @@ void RPCConsole::updateNodeDetail(const CNodeCombinedStats *stats)
ui->peerSubversion->setText(QString::fromStdString(stats->nodeStats.cleanSubVer));
ui->peerDirection->setText(stats->nodeStats.fInbound ? tr("Inbound") : tr("Outbound"));
ui->peerHeight->setText(QString("%1").arg(QString::number(stats->nodeStats.nStartingHeight)));
- ui->peerWhitelisted->setText(stats->nodeStats.fWhitelisted ? tr("Yes") : tr("No"));
+ ui->peerWhitelisted->setText(stats->nodeStats.m_legacyWhitelisted ? tr("Yes") : tr("No"));
// This check fails for example if the lock was busy and
// nodeStateStats couldn't be fetched.
diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp
index 2aedb77798..a8e7bce6b5 100644
--- a/src/qt/walletcontroller.cpp
+++ b/src/qt/walletcontroller.cpp
@@ -99,6 +99,9 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
// Instantiate model and register it.
WalletModel* wallet_model = new WalletModel(std::move(wallet), m_node, m_platform_style, m_options_model, nullptr);
+ // Handler callback runs in a different thread so fix wallet model thread affinity.
+ wallet_model->moveToThread(thread());
+ wallet_model->setParent(this);
m_wallets.push_back(wallet_model);
connect(wallet_model, &WalletModel::unload, [this, wallet_model] {
@@ -119,25 +122,11 @@ WalletModel* WalletController::getOrCreateWallet(std::unique_ptr<interfaces::Wal
connect(wallet_model, &WalletModel::coinsSent, this, &WalletController::coinsSent);
// Notify walletAdded signal on the GUI thread.
- if (QThread::currentThread() == thread()) {
- addWallet(wallet_model);
- } else {
- // Handler callback runs in a different thread so fix wallet model thread affinity.
- wallet_model->moveToThread(thread());
- bool invoked = QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, Q_ARG(WalletModel*, wallet_model));
- assert(invoked);
- }
+ Q_EMIT walletAdded(wallet_model);
return wallet_model;
}
-void WalletController::addWallet(WalletModel* wallet_model)
-{
- // Take ownership of the wallet model and register it.
- wallet_model->setParent(this);
- Q_EMIT walletAdded(wallet_model);
-}
-
void WalletController::removeAndDeleteWallet(WalletModel* wallet_model)
{
// Unregister wallet model.
diff --git a/src/qt/walletcontroller.h b/src/qt/walletcontroller.h
index 03039dd795..be1c282919 100644
--- a/src/qt/walletcontroller.h
+++ b/src/qt/walletcontroller.h
@@ -50,9 +50,6 @@ public:
OpenWalletActivity* openWallet(const std::string& name, QWidget* parent = nullptr);
void closeWallet(WalletModel* wallet_model, QWidget* parent = nullptr);
-private Q_SLOTS:
- void addWallet(WalletModel* wallet_model);
-
Q_SIGNALS:
void walletAdded(WalletModel* wallet_model);
void walletRemoved(WalletModel* wallet_model);
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 48bc88823a..92a0e33769 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -352,7 +352,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
"}\n"
},
RPCExamples{
- HelpExampleCli("getblocktemplate", "{\"rules\": [\"segwit\"]}")
+ HelpExampleCli("getblocktemplate", "'{\"rules\": [\"segwit\"]}'")
+ HelpExampleRpc("getblocktemplate", "{\"rules\": [\"segwit\"]}")
},
}.Check(request);
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index 16b59e3d58..25dda924a4 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -9,6 +9,7 @@
#include <core_io.h>
#include <net.h>
#include <net_processing.h>
+#include <net_permissions.h>
#include <netbase.h>
#include <policy/policy.h>
#include <policy/settings.h>
@@ -177,7 +178,12 @@ static UniValue getpeerinfo(const JSONRPCRequest& request)
}
obj.pushKV("inflight", heights);
}
- obj.pushKV("whitelisted", stats.fWhitelisted);
+ obj.pushKV("whitelisted", stats.m_legacyWhitelisted);
+ UniValue permissions(UniValue::VARR);
+ for (const auto& permission : NetPermissions::ToStrings(stats.m_permissionFlags)) {
+ permissions.push_back(permission);
+ }
+ obj.pushKV("permissions", permissions);
obj.pushKV("minfeefilter", ValueFromAmount(stats.minFeeFilter));
UniValue sendPerMsgCmd(UniValue::VOBJ);
diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp
index 50119ba184..327af62a4f 100644
--- a/src/script/descriptor.cpp
+++ b/src/script/descriptor.cpp
@@ -335,10 +335,12 @@ public:
/** Base class for all Descriptor implementations. */
class DescriptorImpl : public Descriptor
{
- //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size of Multisig).
+ //! Public key arguments for this descriptor (size 1 for PK, PKH, WPKH; any size for Multisig).
const std::vector<std::unique_ptr<PubkeyProvider>> m_pubkey_args;
//! The sub-descriptor argument (nullptr for everything but SH and WSH).
- const std::unique_ptr<DescriptorImpl> m_script_arg;
+ //! In doc/descriptors.m this is referred to as SCRIPT expressions sh(SCRIPT)
+ //! and wsh(SCRIPT), and distinct from KEY expressions and ADDR expressions.
+ const std::unique_ptr<DescriptorImpl> m_subdescriptor_arg;
//! The string name of the descriptor function.
const std::string m_name;
@@ -349,10 +351,10 @@ protected:
/** A helper function to construct the scripts for this descriptor.
*
* This function is invoked once for every CScript produced by evaluating
- * m_script_arg, or just once in case m_script_arg is nullptr.
+ * m_subdescriptor_arg, or just once in case m_subdescriptor_arg is nullptr.
* @param pubkeys The evaluations of the m_pubkey_args field.
- * @param script The evaluation of m_script_arg (or nullptr when m_script_arg is nullptr).
+ * @param script The evaluation of m_subdescriptor_arg (or nullptr when m_subdescriptor_arg is nullptr).
* @param out A FlatSigningProvider to put scripts or public keys in that are necessary to the solver.
* The script arguments to this function are automatically added, as is the origin info of the provided pubkeys.
* @return A vector with scriptPubKeys for this descriptor.
@@ -360,12 +362,12 @@ protected:
virtual std::vector<CScript> MakeScripts(const std::vector<CPubKey>& pubkeys, const CScript* script, FlatSigningProvider& out) const = 0;
public:
- DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_script_arg(std::move(script)), m_name(name) {}
+ DescriptorImpl(std::vector<std::unique_ptr<PubkeyProvider>> pubkeys, std::unique_ptr<DescriptorImpl> script, const std::string& name) : m_pubkey_args(std::move(pubkeys)), m_subdescriptor_arg(std::move(script)), m_name(name) {}
bool IsSolvable() const override
{
- if (m_script_arg) {
- if (!m_script_arg->IsSolvable()) return false;
+ if (m_subdescriptor_arg) {
+ if (!m_subdescriptor_arg->IsSolvable()) return false;
}
return true;
}
@@ -375,8 +377,8 @@ public:
for (const auto& pubkey : m_pubkey_args) {
if (pubkey->IsRange()) return true;
}
- if (m_script_arg) {
- if (m_script_arg->IsRange()) return true;
+ if (m_subdescriptor_arg) {
+ if (m_subdescriptor_arg->IsRange()) return true;
}
return false;
}
@@ -396,10 +398,10 @@ public:
}
ret += std::move(tmp);
}
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
if (pos++) ret += ",";
std::string tmp;
- if (!m_script_arg->ToStringHelper(arg, tmp, priv)) return false;
+ if (!m_subdescriptor_arg->ToStringHelper(arg, tmp, priv)) return false;
ret += std::move(tmp);
}
out = std::move(ret) + ")";
@@ -428,6 +430,8 @@ public:
// Construct temporary data in `entries` and `subscripts`, to avoid producing output in case of failure.
for (const auto& p : m_pubkey_args) {
entries.emplace_back();
+ // If we have a cache, we don't need GetPubKey to compute the public key.
+ // Pass in nullptr to signify only origin info is desired.
if (!p->GetPubKey(pos, arg, cache_read ? nullptr : &entries.back().first, entries.back().second)) return false;
if (cache_read) {
// Cached expanded public key exists, use it.
@@ -444,9 +448,9 @@ public:
}
}
std::vector<CScript> subscripts;
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
FlatSigningProvider subprovider;
- if (!m_script_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
+ if (!m_subdescriptor_arg->ExpandHelper(pos, arg, cache_read, subscripts, subprovider, cache_write)) return false;
out = Merge(out, subprovider);
}
@@ -456,7 +460,7 @@ public:
pubkeys.push_back(entry.first);
out.origins.emplace(entry.first.GetID(), std::make_pair<CPubKey, KeyOriginInfo>(CPubKey(entry.first), std::move(entry.second)));
}
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
for (const auto& subscript : subscripts) {
out.scripts.emplace(CScriptID(subscript), subscript);
std::vector<CScript> addscripts = MakeScripts(pubkeys, &subscript, out);
@@ -488,9 +492,9 @@ public:
if (!p->GetPrivKey(pos, provider, key)) continue;
out.keys.emplace(key.GetPubKey().GetID(), key);
}
- if (m_script_arg) {
+ if (m_subdescriptor_arg) {
FlatSigningProvider subprovider;
- m_script_arg->ExpandPrivate(pos, provider, subprovider);
+ m_subdescriptor_arg->ExpandPrivate(pos, provider, subprovider);
out = Merge(out, subprovider);
}
}
diff --git a/src/script/descriptor.h b/src/script/descriptor.h
index 29915c6c92..a34e9f0d8a 100644
--- a/src/script/descriptor.h
+++ b/src/script/descriptor.h
@@ -47,9 +47,9 @@ struct Descriptor {
*
* pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
* provider: the provider to query for private keys in case of hardened derivation.
- * output_script: the expanded scriptPubKeys will be put here.
+ * output_scripts: the expanded scriptPubKeys will be put here.
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
- * cache: vector which will be overwritten with cache data necessary to-evaluate the descriptor at this point without access to private keys.
+ * cache: vector which will be overwritten with cache data necessary to evaluate the descriptor at this point without access to private keys.
*/
virtual bool Expand(int pos, const SigningProvider& provider, std::vector<CScript>& output_scripts, FlatSigningProvider& out, std::vector<unsigned char>* cache = nullptr) const = 0;
@@ -57,7 +57,7 @@ struct Descriptor {
*
* pos: the position at which to expand the descriptor. If IsRange() is false, this is ignored.
* cache: vector from which cached expansion data will be read.
- * output_script: the expanded scriptPubKeys will be put here.
+ * output_scripts: the expanded scriptPubKeys will be put here.
* out: scripts and public keys necessary for solving the expanded scriptPubKeys will be put here (may be equal to provider).
*/
virtual bool ExpandFromCache(int pos, const std::vector<unsigned char>& cache, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const = 0;
diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp
index 86c0cecbf1..a3d0831624 100644
--- a/src/test/netbase_tests.cpp
+++ b/src/test/netbase_tests.cpp
@@ -3,6 +3,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <netbase.h>
+#include <net_permissions.h>
#include <test/setup_common.h>
#include <util/strencodings.h>
@@ -321,4 +322,82 @@ BOOST_AUTO_TEST_CASE(netbase_parsenetwork)
BOOST_CHECK_EQUAL(ParseNetwork(""), NET_UNROUTABLE);
}
+BOOST_AUTO_TEST_CASE(netpermissions_test)
+{
+ std::string error;
+ NetWhitebindPermissions whitebindPermissions;
+ NetWhitelistPermissions whitelistPermissions;
+
+ // Detect invalid white bind
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Cannot resolve -whitebind address") != std::string::npos);
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("127.0.0.1", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Need to specify a port with -whitebind") != std::string::npos);
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("", whitebindPermissions, error));
+
+ // If no permission flags, assume backward compatibility
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(error.empty());
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+ NetPermissions::ClearFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(!NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ NetPermissions::AddFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT);
+ BOOST_CHECK(NetPermissions::HasFlag(whitebindPermissions.m_flags, PF_ISIMPLICIT));
+
+ // Can set one permission
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+
+ // Happy path, can parse flags
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay@1.2.3.4:32", whitebindPermissions, error));
+ // forcerelay should also activate the relay permission
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("all@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_ALL);
+
+ // Allow dups
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,noban,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+
+ // Allow empty
+ BOOST_CHECK(NetWhitebindPermissions::TryParse("bloom,relay,,noban@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_BLOOMFILTER | PF_RELAY | PF_NOBAN);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse(",@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+ BOOST_CHECK(NetWhitebindPermissions::TryParse(",,@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK_EQUAL(whitebindPermissions.m_flags, PF_NONE);
+
+ // Detect invalid flag
+ BOOST_CHECK(!NetWhitebindPermissions::TryParse("bloom,forcerelay,oopsie@1.2.3.4:32", whitebindPermissions, error));
+ BOOST_CHECK(error.find("Invalid P2P permission") != std::string::npos);
+
+ // Check whitelist error
+ BOOST_CHECK(!NetWhitelistPermissions::TryParse("bloom,forcerelay,noban@1.2.3.4:32", whitelistPermissions, error));
+ BOOST_CHECK(error.find("Invalid netmask specified in -whitelist") != std::string::npos);
+
+ // Happy path for whitelist parsing
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("noban@1.2.3.4", whitelistPermissions, error));
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_NOBAN);
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay@1.2.3.4/32", whitelistPermissions, error));
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_flags, PF_BLOOMFILTER | PF_FORCERELAY | PF_NOBAN | PF_RELAY);
+ BOOST_CHECK(error.empty());
+ BOOST_CHECK_EQUAL(whitelistPermissions.m_subnet.ToString(), "1.2.3.4/32");
+ BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, error));
+
+ const auto strings = NetPermissions::ToStrings(PF_ALL);
+ BOOST_CHECK_EQUAL(strings.size(), 5);
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "bloomfilter") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "forcerelay") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "relay") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "noban") != strings.end());
+ BOOST_CHECK(std::find(strings.begin(), strings.end(), "mempool") != strings.end());
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp
index b4c0e6a0f4..7b00222ab7 100644
--- a/src/test/timedata_tests.cpp
+++ b/src/test/timedata_tests.cpp
@@ -2,8 +2,14 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
-#include <timedata.h>
+
+#include <netaddress.h>
+#include <noui.h>
#include <test/setup_common.h>
+#include <timedata.h>
+#include <warnings.h>
+
+#include <string>
#include <boost/test/unit_test.hpp>
@@ -34,4 +40,61 @@ BOOST_AUTO_TEST_CASE(util_MedianFilter)
BOOST_CHECK_EQUAL(filter.median(), 7);
}
+static void MultiAddTimeData(int n, int64_t offset)
+{
+ static int cnt = 0;
+ for (int i = 0; i < n; ++i) {
+ CNetAddr addr;
+ addr.SetInternal(std::to_string(++cnt));
+ AddTimeData(addr, offset);
+ }
+}
+
+
+BOOST_AUTO_TEST_CASE(addtimedata)
+{
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
+
+ //Part 1: Add large offsets to test a warning message that our clock may be wrong.
+ MultiAddTimeData(3, DEFAULT_MAX_TIME_ADJUSTMENT + 1);
+ // Filter size is 1 + 3 = 4: It is always initialized with a single element (offset 0)
+
+ noui_suppress();
+ MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); //filter size 5
+ noui_reconnect();
+
+ BOOST_CHECK(GetWarnings("gui").find("clock is wrong") != std::string::npos);
+
+ // nTimeOffset is not changed if the median of offsets exceeds DEFAULT_MAX_TIME_ADJUSTMENT
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
+
+ // Part 2: Test positive and negative medians by adding more offsets
+ MultiAddTimeData(4, 100); // filter size 9
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 100);
+ MultiAddTimeData(10, -100); //filter size 19
+ BOOST_CHECK_EQUAL(GetTimeOffset(), -100);
+
+ // Part 3: Test behaviour when filter has reached maximum number of offsets
+ const int MAX_SAMPLES = 200;
+ int nfill = (MAX_SAMPLES - 3 - 19) / 2; //89
+ MultiAddTimeData(nfill, 100);
+ MultiAddTimeData(nfill, -100); //filter size MAX_SAMPLES - 3
+ BOOST_CHECK_EQUAL(GetTimeOffset(), -100);
+
+ MultiAddTimeData(2, 100);
+ //filter size MAX_SAMPLES -1, median is the initial 0 offset
+ //since we added same number of positive/negative offsets
+
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
+
+ // After the number of offsets has reached MAX_SAMPLES -1 (=199), nTimeOffset will never change
+ // because it is only updated when the number of elements in the filter becomes odd. It was decided
+ // not to fix this because it prevents possible attacks. See the comment in AddTimeData() or issue #4521
+ // for a more detailed explanation.
+ MultiAddTimeData(2, 100); // filter median is 100 now, but nTimeOffset will not change
+ BOOST_CHECK_EQUAL(GetTimeOffset(), 0);
+
+ // We want this test to end with nTimeOffset==0, otherwise subsequent tests of the suite will fail.
+}
+
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp
index 15fe1148fe..7119f56fc3 100644
--- a/src/test/util_tests.cpp
+++ b/src/test/util_tests.cpp
@@ -1532,17 +1532,9 @@ BOOST_AUTO_TEST_CASE(test_ToLower)
BOOST_CHECK_EQUAL(ToLower(0), 0);
BOOST_CHECK_EQUAL(ToLower('\xff'), '\xff');
- std::string testVector;
- Downcase(testVector);
- BOOST_CHECK_EQUAL(testVector, "");
-
- testVector = "#HODL";
- Downcase(testVector);
- BOOST_CHECK_EQUAL(testVector, "#hodl");
-
- testVector = "\x00\xfe\xff";
- Downcase(testVector);
- BOOST_CHECK_EQUAL(testVector, "\x00\xfe\xff");
+ BOOST_CHECK_EQUAL(ToLower(""), "");
+ BOOST_CHECK_EQUAL(ToLower("#HODL"), "#hodl");
+ BOOST_CHECK_EQUAL(ToLower("\x00\xfe\xff"), "\x00\xfe\xff");
}
BOOST_AUTO_TEST_CASE(test_ToUpper)
@@ -1553,6 +1545,10 @@ BOOST_AUTO_TEST_CASE(test_ToUpper)
BOOST_CHECK_EQUAL(ToUpper('{'), '{');
BOOST_CHECK_EQUAL(ToUpper(0), 0);
BOOST_CHECK_EQUAL(ToUpper('\xff'), '\xff');
+
+ BOOST_CHECK_EQUAL(ToUpper(""), "");
+ BOOST_CHECK_EQUAL(ToUpper("#hodl"), "#HODL");
+ BOOST_CHECK_EQUAL(ToUpper("\x00\xfe\xff"), "\x00\xfe\xff");
}
BOOST_AUTO_TEST_CASE(test_Capitalize)
diff --git a/src/util/strencodings.cpp b/src/util/strencodings.cpp
index 0acbb4f117..1e7d24c71c 100644
--- a/src/util/strencodings.cpp
+++ b/src/util/strencodings.cpp
@@ -546,9 +546,18 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out)
return true;
}
-void Downcase(std::string& str)
+std::string ToLower(const std::string& str)
{
- std::transform(str.begin(), str.end(), str.begin(), [](char c){return ToLower(c);});
+ std::string r;
+ for (auto ch : str) r += ToLower((unsigned char)ch);
+ return r;
+}
+
+std::string ToUpper(const std::string& str)
+{
+ std::string r;
+ for (auto ch : str) r += ToUpper((unsigned char)ch);
+ return r;
}
std::string Capitalize(std::string str)
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index 7c4364a082..e35b2ab857 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -199,6 +199,8 @@ bool ConvertBits(const O& outfn, I it, I end) {
* Converts the given character to its lowercase equivalent.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
+ * This is a feature, not a limitation.
+ *
* @param[in] c the character to convert to lowercase.
* @return the lowercase equivalent of c; or the argument
* if no conversion is possible.
@@ -209,17 +211,22 @@ constexpr char ToLower(char c)
}
/**
- * Converts the given string to its lowercase equivalent.
+ * Returns the lowercase equivalent of the given string.
* This function is locale independent. It only converts uppercase
* characters in the standard 7-bit ASCII range.
- * @param[in,out] str the string to convert to lowercase.
+ * This is a feature, not a limitation.
+ *
+ * @param[in] str the string to convert to lowercase.
+ * @returns lowercased equivalent of str
*/
-void Downcase(std::string& str);
+std::string ToLower(const std::string& str);
/**
* Converts the given character to its uppercase equivalent.
* This function is locale independent. It only converts lowercase
* characters in the standard 7-bit ASCII range.
+ * This is a feature, not a limitation.
+ *
* @param[in] c the character to convert to uppercase.
* @return the uppercase equivalent of c; or the argument
* if no conversion is possible.
@@ -230,12 +237,24 @@ constexpr char ToUpper(char c)
}
/**
+ * Returns the uppercase equivalent of the given string.
+ * This function is locale independent. It only converts lowercase
+ * characters in the standard 7-bit ASCII range.
+ * This is a feature, not a limitation.
+ *
+ * @param[in] str the string to convert to uppercase.
+ * @returns UPPERCASED EQUIVALENT OF str
+ */
+std::string ToUpper(const std::string& str);
+
+/**
* Capitalizes the first character of the given string.
- * This function is locale independent. It only capitalizes the
- * first character of the argument if it has an uppercase equivalent
- * in the standard 7-bit ASCII range.
+ * This function is locale independent. It only converts lowercase
+ * characters in the standard 7-bit ASCII range.
+ * This is a feature, not a limitation.
+ *
* @param[in] str the string to capitalize.
- * @return string with the first letter capitalized.
+ * @returns string with the first letter capitalized.
*/
std::string Capitalize(std::string str);
diff --git a/src/util/system.cpp b/src/util/system.cpp
index f8fcbc1206..f8bcc45a6a 100644
--- a/src/util/system.cpp
+++ b/src/util/system.cpp
@@ -388,7 +388,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
key.erase(is_index);
}
#ifdef WIN32
- std::transform(key.begin(), key.end(), key.begin(), ToLower);
+ key = ToLower(key);
if (key[0] == '/')
key[0] = '-';
#endif
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index b3269083ec..03acf23508 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -2150,16 +2150,21 @@ void CWallet::ReacceptWalletTransactions(interfaces::Chain::Lock& locked_chain)
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
std::string unused_err_string;
- wtx.SubmitMemoryPoolAndRelay(unused_err_string, false);
+ wtx.SubmitMemoryPoolAndRelay(unused_err_string, false, locked_chain);
}
}
-bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay)
+bool CWalletTx::SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, interfaces::Chain::Lock& locked_chain)
{
// Can't relay if wallet is not broadcasting
if (!pwallet->GetBroadcastTransactions()) return false;
// Don't relay abandoned transactions
if (isAbandoned()) return false;
+ // Don't try to submit coinbase transactions. These would fail anyway but would
+ // cause log spam.
+ if (IsCoinBase()) return false;
+ // Don't try to submit conflicted or confirmed transactions.
+ if (GetDepthInMainChain(locked_chain) != 0) return false;
// Submit transaction to mempool for relay
pwallet->WalletLogPrintf("Submitting wtx %s to mempool for relay\n", GetHash().ToString());
@@ -2374,11 +2379,12 @@ void CWallet::ResendWalletTransactions()
// Relay transactions
for (std::pair<const uint256, CWalletTx>& item : mapWallet) {
CWalletTx& wtx = item.second;
- // only rebroadcast unconfirmed txes older than 5 minutes before the
- // last block was found
+ // Attempt to rebroadcast all txes more than 5 minutes older than
+ // the last block. SubmitMemoryPoolAndRelay() will not rebroadcast
+ // any confirmed or conflicting txs.
if (wtx.nTimeReceived > m_best_block_time - 5 * 60) continue;
std::string unused_err_string;
- if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true)) ++submitted_tx_count;
+ if (wtx.SubmitMemoryPoolAndRelay(unused_err_string, true, *locked_chain)) ++submitted_tx_count;
}
} // locked_chain and cs_wallet
@@ -3323,7 +3329,7 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (fBroadcastTransactions)
{
std::string err_string;
- if (!wtx.SubmitMemoryPoolAndRelay(err_string, true)) {
+ if (!wtx.SubmitMemoryPoolAndRelay(err_string, true, *locked_chain)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", err_string);
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
}
diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h
index 3a45c1ccc5..cf388ad827 100644
--- a/src/wallet/wallet.h
+++ b/src/wallet/wallet.h
@@ -580,7 +580,7 @@ public:
int64_t GetTxTime() const;
// Pass this transaction to node for mempool insertion and relay to peers if flag set to true
- bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay);
+ bool SubmitMemoryPoolAndRelay(std::string& err_string, bool relay, interfaces::Chain::Lock& locked_chain);
// TODO: Remove "NO_THREAD_SAFETY_ANALYSIS" and replace it with the correct
// annotation "EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)". The annotation