mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-20 14:05:23 +01:00
Merge #16248: Make whitebind/whitelist permissions more flexible
c5b404e8f1
Add functional tests for flexible whitebind/list (nicolas.dorier)d541fa3918
Replace the use of fWhitelisted by permission checks (nicolas.dorier)ecd5cf7ea4
Do not disconnect peer for asking mempool if it has NO_BAN permission (nicolas.dorier)e5b26deaaa
Make whitebind/whitelist permissions more flexible (nicolas.dorier) Pull request description: # Motivation In 0.19, bloom filter will be disabled by default. I tried to make [a PR](https://github.com/bitcoin/bitcoin/pull/16176) to enable bloom filter for whitelisted peers regardless of `-peerbloomfilters`. Bloom filter have non existent privacy and server can omit filter's matches. However, both problems are completely irrelevant when you connect to your own node. If you connect to your own node, bloom filters are the most bandwidth efficient way to synchronize your light client without the need of some middleware like Electrum. It is also a superior alternative to BIP157 as it does not require to maintain an additional index and it would work well on pruned nodes. When I attempted to allow bloom filters for whitelisted peer, my proposal has been NACKed in favor of [a more flexible approach](https://github.com/bitcoin/bitcoin/pull/16176#issuecomment-500762907) which should allow node operator to set fine grained permissions instead of a global `whitelisted` attribute. Doing so will also make follow up idea very easy to implement in a backward compatible way. # Implementation details The PR propose a new format for `--white{list,bind}`. I added a way to specify permissions granted to inbound connection matching `white{list,bind}`. The following permissions exists: * ForceRelay * Relay * NoBan * BloomFilter * Mempool Example: * `-whitelist=bloomfilter@127.0.0.1/32`. * `-whitebind=bloomfilter,relay,noban@127.0.0.1:10020`. If no permissions are specified, `NoBan | Mempool` is assumed. (making this PR backward compatible) When we receive an inbound connection, we calculate the effective permissions for this peer by fetching the permissions granted from `whitelist` and add to it the permissions granted from `whitebind`. To keep backward compatibility, if no permissions are specified in `white{list,bind}` (e.g. `--whitelist=127.0.0.1`) then parameters `-whitelistforcerelay` and `-whiterelay` will add the permissions `ForceRelay` and `Relay` to the inbound node. `-whitelistforcerelay` and `-whiterelay` are ignored if the permissions flags are explicitly set in `white{bind,list}`. # Follow up idea Based on this PR, other changes become quite easy to code in a trivially review-able, backward compatible way: * Changing `connect` at rpc and config file level to understand the permissions flags. * Changing the permissions of a peer at RPC level. ACKs for top commit: laanwj: re-ACKc5b404e8f1
Tree-SHA512: adfefb373d09e68cae401247c8fc64034e305694cdef104bdcdacb9f1704277bd53b18f52a2427a5cffdbc77bda410d221aed252bc2ece698ffbb9cf1b830577
This commit is contained in:
commit
67be6d7a17
13 changed files with 450 additions and 66 deletions
|
@ -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 \
|
||||
|
|
20
src/init.cpp
20
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);
|
||||
}
|
||||
|
||||
|
|
63
src/net.cpp
63
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;
|
||||
}
|
||||
|
|
35
src/net.h
35
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;
|
||||
|
|
106
src/net_permissions.cpp
Normal file
106
src/net_permissions.cpp
Normal file
|
@ -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;
|
||||
}
|
62
src/net_permissions.h
Normal file
62
src/net_permissions.h
Normal file
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
97
test/functional/p2p_permissions.py
Normal file
97
test/functional/p2p_permissions.py
Normal file
|
@ -0,0 +1,97 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-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.
|
||||
"""Test p2p permission message.
|
||||
|
||||
Test that permissions are correctly calculated and applied
|
||||
"""
|
||||
|
||||
from test_framework.test_node import ErrorMatch
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
connect_nodes,
|
||||
p2p_port,
|
||||
)
|
||||
|
||||
class P2PPermissionsTests(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.num_nodes = 2
|
||||
self.setup_clean_chain = True
|
||||
self.extra_args = [[],[]]
|
||||
|
||||
def run_test(self):
|
||||
self.checkpermission(
|
||||
# relay permission added
|
||||
["-whitelist=127.0.0.1", "-whitelistrelay"],
|
||||
["relay", "noban", "mempool"],
|
||||
True)
|
||||
|
||||
self.checkpermission(
|
||||
# forcerelay and relay permission added
|
||||
# Legacy parameter interaction which set whitelistrelay to true
|
||||
# if whitelistforcerelay is true
|
||||
["-whitelist=127.0.0.1", "-whitelistforcerelay"],
|
||||
["forcerelay", "relay", "noban", "mempool"],
|
||||
True)
|
||||
|
||||
# Let's make sure permissions are merged correctly
|
||||
# For this, we need to use whitebind instead of bind
|
||||
# by modifying the configuration file.
|
||||
ip_port = "127.0.0.1:{}".format(p2p_port(1))
|
||||
self.replaceinconfig(1, "bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port)
|
||||
self.checkpermission(
|
||||
["-whitelist=noban@127.0.0.1" ],
|
||||
# Check parameter interaction forcerelay should activate relay
|
||||
["noban", "bloomfilter", "forcerelay", "relay" ],
|
||||
False)
|
||||
self.replaceinconfig(1, "whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1")
|
||||
|
||||
self.checkpermission(
|
||||
# legacy whitelistrelay should be ignored
|
||||
["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
|
||||
["noban", "mempool"],
|
||||
False)
|
||||
|
||||
self.checkpermission(
|
||||
# legacy whitelistforcerelay should be ignored
|
||||
["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
|
||||
["noban", "mempool"],
|
||||
False)
|
||||
|
||||
self.checkpermission(
|
||||
# missing mempool permission to be considered legacy whitelisted
|
||||
["-whitelist=noban@127.0.0.1"],
|
||||
["noban"],
|
||||
False)
|
||||
|
||||
self.checkpermission(
|
||||
# all permission added
|
||||
["-whitelist=all@127.0.0.1"],
|
||||
["forcerelay", "noban", "mempool", "bloomfilter", "relay"],
|
||||
False)
|
||||
|
||||
self.stop_node(1)
|
||||
self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX)
|
||||
self.nodes[1].assert_start_raises_init_error(["-whitelist=noban@127.0.0.1:230"], "Invalid netmask specified in", match=ErrorMatch.PARTIAL_REGEX)
|
||||
self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1/10"], "Cannot resolve -whitebind address", match=ErrorMatch.PARTIAL_REGEX)
|
||||
|
||||
def checkpermission(self, args, expectedPermissions, whitelisted):
|
||||
self.restart_node(1, args)
|
||||
connect_nodes(self.nodes[0], 1)
|
||||
peerinfo = self.nodes[1].getpeerinfo()[0]
|
||||
assert_equal(peerinfo['whitelisted'], whitelisted)
|
||||
assert_equal(len(expectedPermissions), len(peerinfo['permissions']))
|
||||
for p in expectedPermissions:
|
||||
if not p in peerinfo['permissions']:
|
||||
raise AssertionError("Expected permissions %r is not granted." % p)
|
||||
|
||||
def replaceinconfig(self, nodeid, old, new):
|
||||
with open(self.nodes[nodeid].bitcoinconf, encoding="utf8") as f:
|
||||
newText=f.read().replace(old, new)
|
||||
with open(self.nodes[nodeid].bitcoinconf, 'w', encoding="utf8") as f:
|
||||
f.write(newText)
|
||||
|
||||
if __name__ == '__main__':
|
||||
P2PPermissionsTests().main()
|
|
@ -68,6 +68,7 @@ class TestNode():
|
|||
|
||||
self.index = i
|
||||
self.datadir = datadir
|
||||
self.bitcoinconf = os.path.join(self.datadir, "bitcoin.conf")
|
||||
self.stdout_dir = os.path.join(self.datadir, "stdout")
|
||||
self.stderr_dir = os.path.join(self.datadir, "stderr")
|
||||
self.chain = chain
|
||||
|
|
|
@ -200,6 +200,7 @@ BASE_SCRIPTS = [
|
|||
'rpc_scantxoutset.py',
|
||||
'feature_logging.py',
|
||||
'p2p_node_network_limited.py',
|
||||
'p2p_permissions.py',
|
||||
'feature_blocksdir.py',
|
||||
'feature_config_args.py',
|
||||
'rpc_help.py',
|
||||
|
|
Loading…
Add table
Reference in a new issue