diff --git a/test/functional/node_network_limited.py b/test/functional/node_network_limited.py index 178a11318..4b05d20d7 100755 --- a/test/functional/node_network_limited.py +++ b/test/functional/node_network_limited.py @@ -1,91 +1,88 @@ #!/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.messages import CInv, msg_getdata, msg_verack -from test_framework.mininode import NetworkThread, P2PInterface +from test_framework.mininode import NODE_BLOOM, NODE_NETWORK_LIMITED, NODE_BITCOIN_CASH, NetworkThread, P2PInterface from test_framework.test_framework import BitcoinTestFramework from test_framework.util import assert_equal 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 get_signalled_service_flags(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 try_get_block_via_getdata(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_BITCOIN_CASH & NODE_NETWORK_LIMITED must now be signaled - # 1060 == 0x424 == 0100 0010 0100 - # | | | - # | | ^--- NODE_BLOOM - # | ^---- NODE_BITCOIN_CASH - # ^-- NODE_NETWORK_LIMITED - assert_equal(self.get_signalled_service_flags(), 1060) + assert_equal(self.get_signalled_service_flags(), + NODE_BLOOM | NODE_BITCOIN_CASH | 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) block_within_limited_range = blocks[-1] # Make sure we can max retrive block at tip-288 # requesting block at height 2 (tip-289) must fail (ignored) # first block must lead to disconnect self.try_get_block_via_getdata(firstblock, True) # last block in valid range self.try_get_block_via_getdata(blocks[1], False) # first block outside of the 288+2 limit self.try_get_block_via_getdata(blocks[0], True) # NODE_NETWORK_LIMITED must still be signaled after restart self.restart_node(0) - assert_equal(self.get_signalled_service_flags(), 1060) + assert_equal(self.get_signalled_service_flags(), + NODE_BLOOM | NODE_BITCOIN_CASH | NODE_NETWORK_LIMITED) # Test the RPC service flags - assert_equal(self.nodes[0].getnetworkinfo()[ - 'localservices'], "0000000000000424") + assert_equal(int(self.nodes[0].getnetworkinfo()[ + 'localservices'], 16), NODE_BLOOM | NODE_BITCOIN_CASH | NODE_NETWORK_LIMITED) # getdata a block above the NODE_NETWORK_LIMITED threshold must be possible self.try_get_block_via_getdata(block_within_limited_range, False) # getdata a block below the NODE_NETWORK_LIMITED threshold must be ignored self.try_get_block_via_getdata(firstblock, True) if __name__ == '__main__': NodeNetworkLimitedTest().main() diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index 7e21f0c1a..318137bc7 100644 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -1,1201 +1,1202 @@ #!/usr/bin/env python3 # Copyright (c) 2010 ArtForz -- public domain half-a-node # Copyright (c) 2012 Jeff Garzik # Copyright (c) 2010-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. """Bitcoin test framework primitive and message strcutures CBlock, CTransaction, CBlockHeader, CTxIn, CTxOut, etc....: data structures that should map to corresponding structures in bitcoin/primitives msg_block, msg_tx, msg_headers, etc.: data structures that represent network messages ser_*, deser_*: functions that handle serialization/deserialization.""" from codecs import encode import copy import hashlib from io import BytesIO import random import socket import struct import time from test_framework.siphash import siphash256 from test_framework.util import hex_str_to_bytes, bytes_to_hex_str, wait_until MIN_VERSION_SUPPORTED = 60001 # past bip-31 for ping/pong MY_VERSION = 70014 MY_SUBVERSION = b"/python-mininode-tester:0.0.3/" # from version 70001 onwards, fRelay should be appended to version messages (BIP37) MY_RELAY = 1 MAX_INV_SZ = 50000 MAX_BLOCK_BASE_SIZE = 1000000 # 1 BCH in satoshis COIN = 100000000 NODE_NETWORK = (1 << 0) # NODE_GETUTXO = (1 << 1) -# NODE_BLOOM = (1 << 2) +NODE_BLOOM = (1 << 2) NODE_WITNESS = (1 << 3) NODE_XTHIN = (1 << 4) NODE_BITCOIN_CASH = (1 << 5) +NODE_NETWORK_LIMITED = (1 << 10) # Howmuch data will be read from the network at once READ_BUFFER_SIZE = 8192 # Serialization/deserialization tools def sha256(s): return hashlib.new('sha256', s).digest() def ripemd160(s): return hashlib.new('ripemd160', s).digest() def hash256(s): return sha256(sha256(s)) def ser_compact_size(l): r = b"" if l < 253: r = struct.pack("B", l) elif l < 0x10000: r = struct.pack(">= 32 return rs def uint256_from_str(s): r = 0 t = struct.unpack("> 24) & 0xFF v = (c & 0xFFFFFF) << (8 * (nbytes - 3)) return v def deser_vector(f, c): nit = deser_compact_size(f) r = [] for i in range(nit): t = c() t.deserialize(f) r.append(t) return r # ser_function_name: Allow for an alternate serialization function on the # entries in the vector. def ser_vector(l, ser_function_name=None): r = ser_compact_size(len(l)) for i in l: if ser_function_name: r += getattr(i, ser_function_name)() else: r += i.serialize() return r def deser_uint256_vector(f): nit = deser_compact_size(f) r = [] for i in range(nit): t = deser_uint256(f) r.append(t) return r def ser_uint256_vector(l): r = ser_compact_size(len(l)) for i in l: r += ser_uint256(i) return r def deser_string_vector(f): nit = deser_compact_size(f) r = [] for i in range(nit): t = deser_string(f) r.append(t) return r def ser_string_vector(l): r = ser_compact_size(len(l)) for sv in l: r += ser_string(sv) return r def deser_int_vector(f): nit = deser_compact_size(f) r = [] for i in range(nit): t = struct.unpack("H", f.read(2))[0] def serialize(self): r = b"" r += struct.pack("H", self.port) return r def __repr__(self): return "CAddress(nServices=%i ip=%s port=%i)" % (self.nServices, self.ip, self.port) class CInv(): typemap = { 0: "Error", 1: "TX", 2: "Block", 4: "CompactBlock" } def __init__(self, t=0, h=0): self.type = t self.hash = h def deserialize(self, f): self.type = struct.unpack(" 21000000 * COIN: return False return True def __repr__(self): return "CTransaction(nVersion=%i vin=%s vout=%s nLockTime=%i)" \ % (self.nVersion, repr(self.vin), repr(self.vout), self.nLockTime) class CBlockHeader(): def __init__(self, header=None): if header is None: self.set_null() else: self.nVersion = header.nVersion self.hashPrevBlock = header.hashPrevBlock self.hashMerkleRoot = header.hashMerkleRoot self.nTime = header.nTime self.nBits = header.nBits self.nNonce = header.nNonce self.sha256 = header.sha256 self.hash = header.hash self.calc_sha256() def set_null(self): self.nVersion = 1 self.hashPrevBlock = 0 self.hashMerkleRoot = 0 self.nTime = 0 self.nBits = 0 self.nNonce = 0 self.sha256 = None self.hash = None def deserialize(self, f): self.nVersion = struct.unpack(" 1: newhashes = [] for i in range(0, len(hashes), 2): i2 = min(i + 1, len(hashes) - 1) newhashes.append(hash256(hashes[i] + hashes[i2])) hashes = newhashes return uint256_from_str(hashes[0]) def calc_merkle_root(self): hashes = [] for tx in self.vtx: tx.calc_sha256() hashes.append(ser_uint256(tx.sha256)) return self.get_merkle_root(hashes) def is_valid(self): self.calc_sha256() target = uint256_from_compact(self.nBits) if self.sha256 > target: return False for tx in self.vtx: if not tx.is_valid(): return False if self.calc_merkle_root() != self.hashMerkleRoot: return False return True def solve(self): self.rehash() target = uint256_from_compact(self.nBits) while self.sha256 > target: self.nNonce += 1 self.rehash() def __repr__(self): return "CBlock(nVersion=%i hashPrevBlock=%064x hashMerkleRoot=%064x nTime=%s nBits=%08x nNonce=%08x vtx=%s)" \ % (self.nVersion, self.hashPrevBlock, self.hashMerkleRoot, time.ctime(self.nTime), self.nBits, self.nNonce, repr(self.vtx)) class PrefilledTransaction(): def __init__(self, index=0, tx=None): self.index = index self.tx = tx def deserialize(self, f): self.index = deser_compact_size(f) self.tx = CTransaction() self.tx.deserialize(f) def serialize(self): r = b"" r += ser_compact_size(self.index) r += self.tx.serialize() return r def __repr__(self): return "PrefilledTransaction(index=%d, tx=%s)" % (self.index, repr(self.tx)) # This is what we send on the wire, in a cmpctblock message. class P2PHeaderAndShortIDs(): def __init__(self): self.header = CBlockHeader() self.nonce = 0 self.shortids_length = 0 self.shortids = [] self.prefilled_txn_length = 0 self.prefilled_txn = [] def deserialize(self, f): self.header.deserialize(f) self.nonce = struct.unpack("= 106: self.addrFrom = CAddress() self.addrFrom.deserialize(f) self.nNonce = struct.unpack("= 209: self.nStartingHeight = struct.unpack("= 70001: # Relay field is optional for version 70001 onwards try: self.nRelay = struct.unpack(" class msg_headers(): command = b"headers" def __init__(self, headers=None): self.headers = headers if headers is not None else [] def deserialize(self, f): # comment in bitcoind indicates these should be deserialized as blocks blocks = deser_vector(f, CBlock) for x in blocks: self.headers.append(CBlockHeader(x)) def serialize(self): blocks = [CBlock(x) for x in self.headers] return ser_vector(blocks) def __repr__(self): return "msg_headers(headers=%s)" % repr(self.headers) class msg_reject(): command = b"reject" REJECT_MALFORMED = 1 def __init__(self): self.message = b"" self.code = 0 self.reason = b"" self.data = 0 def deserialize(self, f): self.message = deser_string(f) self.code = struct.unpack("