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
pull/11797/merge
Wladimir J. van der Laan 7 years ago
commit 59d3dc85b6
No known key found for this signature in database
GPG Key ID: 1E4AED62986CD25D

@ -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;

@ -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…
Cancel
Save