Skip to content

Commit

Permalink
net: Extract download permission from noban
Browse files Browse the repository at this point in the history
Summary:
```
It should be possible to grant nodes in a local network (e.g. home,
university, enterprise, ...) permission to download blocks even after
the maxuploadtarget is hit.

Currently this is only possible by setting the noban permission, which
has some adverse effects, especially if the peers can't be fully
trusted.

Fix this by extracting a download permission from noban.
```

Backport of [[bitcoin/bitcoin#19191 | core#19191]].

Depends on D9324 and D9325.

Test Plan:
  ninja all check-all

Reviewers: #bitcoin_abc, majcosta

Reviewed By: #bitcoin_abc, majcosta

Subscribers: majcosta

Differential Revision: https://reviews.bitcoinabc.org/D9326
  • Loading branch information
MarcoFalke authored and Fabcien committed Mar 22, 2021
1 parent 630e4f7 commit 56f6113
Show file tree
Hide file tree
Showing 11 changed files with 53 additions and 31 deletions.
2 changes: 1 addition & 1 deletion doc/reduce-traffic.md
Expand Up @@ -23,7 +23,7 @@ longer serving historic blocks (blocks older than one week).
Keep in mind that new nodes require other nodes that are willing to serve
historic blocks.

Peers with the `noban` permission will never be disconnected, although their traffic counts for
Peers with the `download` permission will never be disconnected, although their traffic counts for
calculating the target.

## 2. Disable "listening" (`-listen=0`)
Expand Down
4 changes: 4 additions & 0 deletions doc/release-notes.md
Expand Up @@ -5,3 +5,7 @@ Bitcoin ABC version 0.23.0 is now available from:
<https://download.bitcoinabc.org/0.23.0/>

This release includes the following features and fixes:
- A `download` permission has been extracted from the `noban` permission. For
compatibility, `noban` implies the `download` permission, but this may change
in future releases. Refer to the help of the affected settings `-whitebind`
and `-whitelist` for more details.
14 changes: 8 additions & 6 deletions src/init.cpp
Expand Up @@ -783,24 +783,26 @@ void SetupServerArgs(NodeContext &node) {
#endif
argsman.AddArg(
"-whitebind=<[permissions@]addr>",
"Bind to given address and whitelist peers connecting to it."
"Bind to the given address and add permission flags to the peers "
"connecting to it."
"Use [host]:port notation for IPv6. Allowed permissions: " +
Join(NET_PERMISSIONS_DOC, ", ") +
". "
"Specify multiple permissions separated by commas (default: "
"noban,mempool,relay). Can be specified multiple times.",
"download,noban,mempool,relay). Can be specified multiple times.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);

argsman.AddArg("-whitelist=<[permissions@]IP address or network>",
"Whitelist peers connecting from the given IP address "
"(e.g. 1.2.3.4) or CIDR notated network(e.g. 1.2.3.0/24). "
"Uses same permissions as -whitebind. Can be specified "
"Add permission flags to the peers connecting from the "
"given IP address (e.g. 1.2.3.4) or CIDR-notated network "
"(e.g. 1.2.3.0/24). "
"Uses the same permissions as -whitebind. Can be specified "
"multiple times.",
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
argsman.AddArg(
"-maxuploadtarget=<n>",
strprintf("Tries to keep outbound traffic under the given target (in "
"MiB per 24h). Limit does not apply to peers with 'noban' "
"MiB per 24h). Limit does not apply to peers with 'download' "
"permission. 0 = no limit (default: %d)",
DEFAULT_MAX_UPLOAD_TARGET),
ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION);
Expand Down
2 changes: 1 addition & 1 deletion src/net.cpp
Expand Up @@ -2884,7 +2884,7 @@ void CConnman::RecordBytesSent(uint64_t bytes) {
nMaxOutboundTotalBytesSentInCycle = 0;
}

// TODO, exclude peers with noban permission
// TODO, exclude peers with download permission
nMaxOutboundTotalBytesSentInCycle += bytes;
}

Expand Down
9 changes: 8 additions & 1 deletion src/net_permissions.cpp
Expand Up @@ -11,11 +11,13 @@

const std::vector<std::string> NET_PERMISSIONS_DOC{
"bloomfilter (allow requesting BIP37 filtered blocks and transactions)",
"noban (do not ban for misbehavior)",
"noban (do not ban for misbehavior; implies download)",
"forcerelay (relay transactions that are already in the mempool; implies "
"relay)",
"relay (relay even in -blocksonly mode)",
"mempool (allow requesting BIP35 mempool contents)",
"download (allow getheaders during IBD, no disconnect after "
"maxuploadtarget limit)",
};

namespace {
Expand Down Expand Up @@ -60,6 +62,8 @@ bool TryParsePermissionFlags(const std::string str, NetPermissionFlags &output,
NetPermissions::AddFlag(flags, PF_FORCERELAY);
} else if (permission == "mempool") {
NetPermissions::AddFlag(flags, PF_MEMPOOL);
} else if (permission == "download") {
NetPermissions::AddFlag(flags, PF_DOWNLOAD);
} else if (permission == "all") {
NetPermissions::AddFlag(flags, PF_ALL);
} else if (permission == "relay") {
Expand Down Expand Up @@ -99,6 +103,9 @@ std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags) {
if (NetPermissions::HasFlag(flags, PF_MEMPOOL)) {
strings.push_back("mempool");
}
if (NetPermissions::HasFlag(flags, PF_DOWNLOAD)) {
strings.push_back("download");
}
return strings;
}

Expand Down
8 changes: 6 additions & 2 deletions src/net_permissions.h
Expand Up @@ -23,14 +23,18 @@ enum NetPermissionFlags {
// 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,
// Allow getheaders during IBD and block-download after maxuploadtarget
// limit
PF_DOWNLOAD = (1U << 6),
// Can't be banned/disconnected/discouraged for misbehavior
PF_NOBAN = (1U << 4),
PF_NOBAN = (1U << 4) | PF_DOWNLOAD,
// 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,
PF_ALL = PF_BLOOMFILTER | PF_FORCERELAY | PF_RELAY | PF_NOBAN | PF_MEMPOOL |
PF_DOWNLOAD,
};

class NetPermissions {
Expand Down
6 changes: 3 additions & 3 deletions src/net_processing.cpp
Expand Up @@ -1858,8 +1858,8 @@ static void ProcessGetBlockData(const Config &config, CNode &pfrom,
(pindexBestHeader->GetBlockTime() - pindex->GetBlockTime() >
HISTORICAL_BLOCK_AGE)) ||
inv.type == MSG_FILTERED_BLOCK) &&
/* never disconnect nodes with the noban permission */
!pfrom.HasPermission(PF_NOBAN)) {
// nodes with the download permission may exceed target
!pfrom.HasPermission(PF_DOWNLOAD)) {
LogPrint(BCLog::NET,
"historical block serving limit reached, disconnect peer=%d\n",
pfrom.GetId());
Expand Down Expand Up @@ -3286,7 +3286,7 @@ void PeerManager::ProcessMessage(const Config &config, CNode &pfrom,

LOCK(cs_main);
if (::ChainstateActive().IsInitialBlockDownload() &&
!pfrom.HasPermission(PF_NOBAN)) {
!pfrom.HasPermission(PF_DOWNLOAD)) {
LogPrint(BCLog::NET,
"Ignoring getheaders from peer=%d because node is in "
"initial block download\n",
Expand Down
4 changes: 3 additions & 1 deletion src/test/netbase_tests.cpp
Expand Up @@ -487,7 +487,7 @@ BOOST_AUTO_TEST_CASE(netpermissions_test) {
error));

const auto strings = NetPermissions::ToStrings(PF_ALL);
BOOST_CHECK_EQUAL(strings.size(), 5U);
BOOST_CHECK_EQUAL(strings.size(), 6U);
BOOST_CHECK(std::find(strings.begin(), strings.end(), "bloomfilter") !=
strings.end());
BOOST_CHECK(std::find(strings.begin(), strings.end(), "forcerelay") !=
Expand All @@ -498,6 +498,8 @@ BOOST_AUTO_TEST_CASE(netpermissions_test) {
strings.end());
BOOST_CHECK(std::find(strings.begin(), strings.end(), "mempool") !=
strings.end());
BOOST_CHECK(std::find(strings.begin(), strings.end(), "download") !=
strings.end());
}

BOOST_AUTO_TEST_CASE(
Expand Down
13 changes: 7 additions & 6 deletions test/functional/feature_maxuploadtarget.py
Expand Up @@ -150,9 +150,9 @@ def run_test(self):

self.nodes[0].disconnect_p2ps()

self.log.info("Restarting node 0 with noban permission"
self.log.info("Restarting node 0 with download permission"
" and 1MB maxuploadtarget")
self.restart_node(0, ["-whitelist=noban@127.0.0.1",
self.restart_node(0, ["-whitelist=download@127.0.0.1",
"-maxuploadtarget=1", "-blockmaxsize=999000"])

# Reconnect to self.nodes[0]
Expand All @@ -167,12 +167,13 @@ def run_test(self):

getdata_request.inv = [CInv(MSG_BLOCK, big_old_block)]
self.nodes[0].p2p.send_and_ping(getdata_request)
# node is still connected because of the noban permission
assert_equal(len(self.nodes[0].getpeerinfo()), 1)

self.log.info(
"Peer still connected after trying to download old block "
"(noban permission)")
"Peer still connected after trying to download old block (download permission)")
peer_info = self.nodes[0].getpeerinfo()
# node is still connected
assert_equal(len(peer_info), 1)
assert_equal(peer_info[0]['permissions'], ['download'])


if __name__ == '__main__':
Expand Down
4 changes: 2 additions & 2 deletions test/functional/p2p_blocksonly.py
Expand Up @@ -80,12 +80,12 @@ def run_test(self):
assert_equal(peer_1_info['whitelisted'], True)
assert_equal(
peer_1_info['permissions'], [
'noban', 'forcerelay', 'relay', 'mempool'])
'noban', 'forcerelay', 'relay', 'mempool', 'download'])
peer_2_info = self.nodes[0].getpeerinfo()[1]
assert_equal(peer_2_info['whitelisted'], True)
assert_equal(
peer_2_info['permissions'], [
'noban', 'forcerelay', 'relay', 'mempool'])
'noban', 'forcerelay', 'relay', 'mempool', 'download'])
assert_equal(
self.nodes[0].testmempoolaccept(
[sigtx])[0]['allowed'], True)
Expand Down
18 changes: 10 additions & 8 deletions test/functional/p2p_permissions.py
Expand Up @@ -27,21 +27,23 @@ def run_test(self):
self.checkpermission(
# default permissions (no specific permissions)
["-whitelist=127.0.0.1"],
["relay", "noban", "mempool"],
# Make sure the default values in the command line documentation
# match the ones here
["relay", "noban", "mempool", "download"],
True)

self.checkpermission(
# relay permission removed (no specific permissions)
["-whitelist=127.0.0.1", "-whitelistrelay=0"],
["noban", "mempool"],
["noban", "mempool", "download"],
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"],
["forcerelay", "relay", "noban", "mempool", "download"],
True)

# Let's make sure permissions are merged correctly
Expand All @@ -56,7 +58,7 @@ def run_test(self):
self.checkpermission(
["-whitelist=noban@127.0.0.1"],
# Check parameter interaction forcerelay should activate relay
["noban", "bloomfilter", "forcerelay", "relay"],
["noban", "bloomfilter", "forcerelay", "relay", "download"],
False)
self.replaceinconfig(
1,
Expand All @@ -67,25 +69,25 @@ def run_test(self):
self.checkpermission(
# legacy whitelistrelay should be ignored
["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
["noban", "mempool"],
["noban", "mempool", "download"],
False)

self.checkpermission(
# legacy whitelistforcerelay should be ignored
["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
["noban", "mempool"],
["noban", "mempool", "download"],
False)

self.checkpermission(
# missing mempool permission to be considered legacy whitelisted
["-whitelist=noban@127.0.0.1"],
["noban"],
["noban", "download"],
False)

self.checkpermission(
# all permission added
["-whitelist=all@127.0.0.1"],
["forcerelay", "noban", "mempool", "bloomfilter", "relay"],
["forcerelay", "noban", "mempool", "bloomfilter", "relay", "download"],
False)

self.stop_node(1)
Expand Down

0 comments on commit 56f6113

Please sign in to comment.