Merge #11740: Implement BIP159 NODE_NETWORK_LIMITED (pruned peers) *signaling only*
de74c62
[Doc] Update bip.md, add support for BIP 159 (Jonas Schnelli)e054d0e
[QA] Add node_network_limited test (Jonas Schnelli)bd09416
Avoid leaking the prune height through getdata (fingerprinting countermeasure) (Jonas Schnelli)27df193
Always set NODE_NETWORK_LIMITED bit (Jonas Schnelli)7caba38
Add NODE_NETWORK_LIMITED flags and min block amount constants (Jonas Schnelli) Pull request description: Extracted from #10387. Does implement BIP159, but only the signalling part. No connections are made to NODE_NETWORK_LIMITED in this PR. The address relay and connection work (the more complicated part) can then be separated (probably in #10387). Tree-SHA512: e3218eb4789a9320b0f42dc10f62d30c13c49bdef00443fbe653bee22933477adcfc1cf8f6a95269324560b5721203ed41f3c5e2dd8a98ec2791f6a9d8346b1a
This commit is contained in:
commit
59d3dc85b6
7 changed files with 102 additions and 4 deletions
|
@ -33,3 +33,4 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.13.0**):
|
||||||
* [`BIP 145`](https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki): getblocktemplate updates for Segregated Witness as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)).
|
* [`BIP 145`](https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki): getblocktemplate updates for Segregated Witness as of **v0.13.0** ([PR 8149](https://github.com/bitcoin/bitcoin/pull/8149)).
|
||||||
* [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)).
|
* [`BIP 147`](https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki): NULLDUMMY softfork as of **v0.13.1** ([PR 8636](https://github.com/bitcoin/bitcoin/pull/8636) and [PR 8937](https://github.com/bitcoin/bitcoin/pull/8937)).
|
||||||
* [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)).
|
* [`BIP 152`](https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki): Compact block transfer and related optimizations are used as of **v0.13.0** ([PR 8068](https://github.com/bitcoin/bitcoin/pull/8068)).
|
||||||
|
* [`BIP 159`](https://github.com/bitcoin/bips/blob/master/bip-0159.mediawiki): NODE_NETWORK_LIMITED service bit [signaling only] is supported as of **v0.16.0** ([PR 10740](https://github.com/bitcoin/bitcoin/pull/10740)).
|
||||||
|
|
|
@ -816,7 +816,7 @@ namespace { // Variables internal to initialization process only
|
||||||
int nMaxConnections;
|
int nMaxConnections;
|
||||||
int nUserMaxConnections;
|
int nUserMaxConnections;
|
||||||
int nFD;
|
int nFD;
|
||||||
ServiceFlags nLocalServices = NODE_NETWORK;
|
ServiceFlags nLocalServices = ServiceFlags(NODE_NETWORK | NODE_NETWORK_LIMITED);
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
|
|
@ -1091,6 +1091,16 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam
|
||||||
pfrom->fDisconnect = true;
|
pfrom->fDisconnect = true;
|
||||||
send = false;
|
send = false;
|
||||||
}
|
}
|
||||||
|
// Avoid leaking prune-height by never sending blocks below the NODE_NETWORK_LIMITED threshold
|
||||||
|
if (send && !pfrom->fWhitelisted && (
|
||||||
|
(((pfrom->GetLocalServices() & NODE_NETWORK_LIMITED) == NODE_NETWORK_LIMITED) && ((pfrom->GetLocalServices() & NODE_NETWORK) != NODE_NETWORK) && (chainActive.Tip()->nHeight - mi->second->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());
|
||||||
|
|
||||||
|
//disconnect node and prevent it from stalling (would otherwise wait for the missing block)
|
||||||
|
pfrom->fDisconnect = true;
|
||||||
|
send = false;
|
||||||
|
}
|
||||||
// Pruned nodes may have deleted the block, so check whether
|
// Pruned nodes may have deleted the block, so check whether
|
||||||
// it's available before trying to send.
|
// it's available before trying to send.
|
||||||
if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
|
if (send && (mi->second->nStatus & BLOCK_HAVE_DATA))
|
||||||
|
|
|
@ -246,9 +246,8 @@ const std::vector<std::string> &getAllNetMessageTypes();
|
||||||
enum ServiceFlags : uint64_t {
|
enum ServiceFlags : uint64_t {
|
||||||
// Nothing
|
// Nothing
|
||||||
NODE_NONE = 0,
|
NODE_NONE = 0,
|
||||||
// NODE_NETWORK means that the node is capable of serving the block chain. It is currently
|
// NODE_NETWORK means that the node is capable of serving the complete block chain. It is currently
|
||||||
// set by all Bitcoin Core nodes, and is unset by SPV clients or other peers that just want
|
// set by all Bitcoin Core non pruned nodes, and is unset by SPV clients or other light clients.
|
||||||
// network services but don't provide them.
|
|
||||||
NODE_NETWORK = (1 << 0),
|
NODE_NETWORK = (1 << 0),
|
||||||
// NODE_GETUTXO means the node is capable of responding to the getutxo protocol request.
|
// NODE_GETUTXO means the node is capable of responding to the getutxo protocol request.
|
||||||
// Bitcoin Core does not support this but a patch set called Bitcoin XT does.
|
// Bitcoin Core does not support this but a patch set called Bitcoin XT does.
|
||||||
|
@ -264,6 +263,10 @@ enum ServiceFlags : uint64_t {
|
||||||
// NODE_XTHIN means the node supports Xtreme Thinblocks
|
// NODE_XTHIN means the node supports Xtreme Thinblocks
|
||||||
// If this is turned off then the node will not service nor make xthin requests
|
// If this is turned off then the node will not service nor make xthin requests
|
||||||
NODE_XTHIN = (1 << 4),
|
NODE_XTHIN = (1 << 4),
|
||||||
|
// NODE_NETWORK_LIMITED means the same as NODE_NETWORK with the limitation of only
|
||||||
|
// serving the last 288 (2 day) blocks
|
||||||
|
// See BIP159 for details on how this is implemented.
|
||||||
|
NODE_NETWORK_LIMITED = (1 << 10),
|
||||||
|
|
||||||
// Bits 24-31 are reserved for temporary experiments. Just pick a bit that
|
// Bits 24-31 are reserved for temporary experiments. Just pick a bit that
|
||||||
// isn't getting used, or one not being used much, and notify the
|
// isn't getting used, or one not being used much, and notify the
|
||||||
|
|
|
@ -203,6 +203,8 @@ extern bool fPruneMode;
|
||||||
extern uint64_t nPruneTarget;
|
extern uint64_t nPruneTarget;
|
||||||
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
|
/** Block files containing a block-height within MIN_BLOCKS_TO_KEEP of chainActive.Tip() will not be pruned. */
|
||||||
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
|
static const unsigned int MIN_BLOCKS_TO_KEEP = 288;
|
||||||
|
/** Minimum blocks required to signal NODE_NETWORK_LIMITED */
|
||||||
|
static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288;
|
||||||
|
|
||||||
static const signed int DEFAULT_CHECKBLOCKS = 6;
|
static const signed int DEFAULT_CHECKBLOCKS = 6;
|
||||||
static const unsigned int DEFAULT_CHECKLEVEL = 3;
|
static const unsigned int DEFAULT_CHECKLEVEL = 3;
|
||||||
|
|
81
test/functional/node_network_limited.py
Executable file
81
test/functional/node_network_limited.py
Executable file
|
@ -0,0 +1,81 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2017 The Bitcoin Core developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
from test_framework.util import *
|
||||||
|
from test_framework.mininode import *
|
||||||
|
|
||||||
|
class BaseNode(P2PInterface):
|
||||||
|
nServices = 0
|
||||||
|
firstAddrnServices = 0
|
||||||
|
def on_version(self, message):
|
||||||
|
self.nServices = message.nServices
|
||||||
|
|
||||||
|
class NodeNetworkLimitedTest(BitcoinTestFramework):
|
||||||
|
def set_test_params(self):
|
||||||
|
self.setup_clean_chain = True
|
||||||
|
self.num_nodes = 1
|
||||||
|
self.extra_args = [['-prune=550']]
|
||||||
|
|
||||||
|
def getSignaledServiceFlags(self):
|
||||||
|
node = self.nodes[0].add_p2p_connection(BaseNode())
|
||||||
|
NetworkThread().start()
|
||||||
|
node.wait_for_verack()
|
||||||
|
services = node.nServices
|
||||||
|
self.nodes[0].disconnect_p2ps()
|
||||||
|
node.wait_for_disconnect()
|
||||||
|
return services
|
||||||
|
|
||||||
|
def tryGetBlockViaGetData(self, blockhash, must_disconnect):
|
||||||
|
node = self.nodes[0].add_p2p_connection(BaseNode())
|
||||||
|
NetworkThread().start()
|
||||||
|
node.wait_for_verack()
|
||||||
|
node.send_message(msg_verack())
|
||||||
|
getdata_request = msg_getdata()
|
||||||
|
getdata_request.inv.append(CInv(2, int(blockhash, 16)))
|
||||||
|
node.send_message(getdata_request)
|
||||||
|
|
||||||
|
if (must_disconnect):
|
||||||
|
#ensure we get disconnected
|
||||||
|
node.wait_for_disconnect(5)
|
||||||
|
else:
|
||||||
|
# check if the peer sends us the requested block
|
||||||
|
node.wait_for_block(int(blockhash, 16), 3)
|
||||||
|
self.nodes[0].disconnect_p2ps()
|
||||||
|
node.wait_for_disconnect()
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
#NODE_BLOOM & NODE_WITNESS & NODE_NETWORK_LIMITED must now be signaled
|
||||||
|
assert_equal(self.getSignaledServiceFlags(), 1036) #1036 == 0x40C == 0100 0000 1100
|
||||||
|
# | ||
|
||||||
|
# | |^--- NODE_BLOOM
|
||||||
|
# | ^---- NODE_WITNESS
|
||||||
|
# ^-- NODE_NETWORK_LIMITED
|
||||||
|
|
||||||
|
#now mine some blocks over the NODE_NETWORK_LIMITED + 2(racy buffer ext.) target
|
||||||
|
firstblock = self.nodes[0].generate(1)[0]
|
||||||
|
blocks = self.nodes[0].generate(292)
|
||||||
|
blockWithinLimitedRange = blocks[-1]
|
||||||
|
|
||||||
|
#make sure we can max retrive block at tip-288
|
||||||
|
#requesting block at height 2 (tip-289) must fail (ignored)
|
||||||
|
self.tryGetBlockViaGetData(firstblock, True) #first block must lead to disconnect
|
||||||
|
self.tryGetBlockViaGetData(blocks[1], False) #last block in valid range
|
||||||
|
self.tryGetBlockViaGetData(blocks[0], True) #first block outside of the 288+2 limit
|
||||||
|
|
||||||
|
#NODE_NETWORK_LIMITED must still be signaled after restart
|
||||||
|
self.restart_node(0)
|
||||||
|
assert_equal(self.getSignaledServiceFlags(), 1036)
|
||||||
|
|
||||||
|
#test the RPC service flags
|
||||||
|
assert_equal(self.nodes[0].getnetworkinfo()['localservices'], "000000000000040c")
|
||||||
|
|
||||||
|
# getdata a block above the NODE_NETWORK_LIMITED threshold must be possible
|
||||||
|
self.tryGetBlockViaGetData(blockWithinLimitedRange, False)
|
||||||
|
|
||||||
|
# getdata a block below the NODE_NETWORK_LIMITED threshold must be ignored
|
||||||
|
self.tryGetBlockViaGetData(firstblock, True)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
NodeNetworkLimitedTest().main()
|
|
@ -128,6 +128,7 @@ BASE_SCRIPTS= [
|
||||||
'uacomment.py',
|
'uacomment.py',
|
||||||
'p2p-acceptblock.py',
|
'p2p-acceptblock.py',
|
||||||
'feature_logging.py',
|
'feature_logging.py',
|
||||||
|
'node_network_limited.py',
|
||||||
]
|
]
|
||||||
|
|
||||||
EXTENDED_SCRIPTS = [
|
EXTENDED_SCRIPTS = [
|
||||||
|
|
Loading…
Reference in a new issue