mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
[test] Add test for cfcheckpt
This commit is contained in:
parent
f9e00bb25a
commit
23083856a5
4 changed files with 187 additions and 0 deletions
134
test/functional/p2p_blockfilters.py
Executable file
134
test/functional/p2p_blockfilters.py
Executable file
|
@ -0,0 +1,134 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# Copyright (c) 2019 The Bitcoin Core developers
|
||||||
|
# Distributed under the MIT software license, see the accompanying
|
||||||
|
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
"""Tests NODE_COMPACT_FILTERS (BIP 157/158).
|
||||||
|
|
||||||
|
Tests that a node configured with -blockfilterindex and -peerblockfilters can serve
|
||||||
|
cfcheckpts.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from test_framework.messages import (
|
||||||
|
FILTER_TYPE_BASIC,
|
||||||
|
msg_getcfcheckpt,
|
||||||
|
)
|
||||||
|
from test_framework.mininode import P2PInterface
|
||||||
|
from test_framework.test_framework import BitcoinTestFramework
|
||||||
|
from test_framework.util import (
|
||||||
|
assert_equal,
|
||||||
|
connect_nodes,
|
||||||
|
disconnect_nodes,
|
||||||
|
wait_until,
|
||||||
|
)
|
||||||
|
|
||||||
|
class CompactFiltersTest(BitcoinTestFramework):
|
||||||
|
def set_test_params(self):
|
||||||
|
self.setup_clean_chain = True
|
||||||
|
self.rpc_timeout = 480
|
||||||
|
self.num_nodes = 2
|
||||||
|
self.extra_args = [
|
||||||
|
["-blockfilterindex", "-peerblockfilters"],
|
||||||
|
["-blockfilterindex"],
|
||||||
|
]
|
||||||
|
|
||||||
|
def run_test(self):
|
||||||
|
# Node 0 supports COMPACT_FILTERS, node 1 does not.
|
||||||
|
node0 = self.nodes[0].add_p2p_connection(P2PInterface())
|
||||||
|
node1 = self.nodes[1].add_p2p_connection(P2PInterface())
|
||||||
|
|
||||||
|
# Nodes 0 & 1 share the same first 999 blocks in the chain.
|
||||||
|
self.nodes[0].generate(999)
|
||||||
|
self.sync_blocks(timeout=600)
|
||||||
|
|
||||||
|
# Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting
|
||||||
|
disconnect_nodes(self.nodes[0], 1)
|
||||||
|
|
||||||
|
self.nodes[0].generate(1)
|
||||||
|
wait_until(lambda: self.nodes[0].getblockcount() == 1000)
|
||||||
|
stale_block_hash = self.nodes[0].getblockhash(1000)
|
||||||
|
|
||||||
|
self.nodes[1].generate(1001)
|
||||||
|
wait_until(lambda: self.nodes[1].getblockcount() == 2000)
|
||||||
|
|
||||||
|
self.log.info("get cfcheckpt on chain to be re-orged out.")
|
||||||
|
request = msg_getcfcheckpt(
|
||||||
|
filter_type=FILTER_TYPE_BASIC,
|
||||||
|
stop_hash=int(stale_block_hash, 16)
|
||||||
|
)
|
||||||
|
node0.send_and_ping(message=request)
|
||||||
|
response = node0.last_message['cfcheckpt']
|
||||||
|
assert_equal(response.filter_type, request.filter_type)
|
||||||
|
assert_equal(response.stop_hash, request.stop_hash)
|
||||||
|
assert_equal(len(response.headers), 1)
|
||||||
|
|
||||||
|
self.log.info("Reorg node 0 to a new chain.")
|
||||||
|
connect_nodes(self.nodes[0], 1)
|
||||||
|
self.sync_blocks(timeout=600)
|
||||||
|
|
||||||
|
main_block_hash = self.nodes[0].getblockhash(1000)
|
||||||
|
assert main_block_hash != stale_block_hash, "node 0 chain did not reorganize"
|
||||||
|
|
||||||
|
self.log.info("Check that peers can fetch cfcheckpt on active chain.")
|
||||||
|
tip_hash = self.nodes[0].getbestblockhash()
|
||||||
|
request = msg_getcfcheckpt(
|
||||||
|
filter_type=FILTER_TYPE_BASIC,
|
||||||
|
stop_hash=int(tip_hash, 16)
|
||||||
|
)
|
||||||
|
node0.send_and_ping(request)
|
||||||
|
response = node0.last_message['cfcheckpt']
|
||||||
|
assert_equal(response.filter_type, request.filter_type)
|
||||||
|
assert_equal(response.stop_hash, request.stop_hash)
|
||||||
|
|
||||||
|
main_cfcheckpt = self.nodes[0].getblockfilter(main_block_hash, 'basic')['header']
|
||||||
|
tip_cfcheckpt = self.nodes[0].getblockfilter(tip_hash, 'basic')['header']
|
||||||
|
assert_equal(
|
||||||
|
response.headers,
|
||||||
|
[int(header, 16) for header in (main_cfcheckpt, tip_cfcheckpt)]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log.info("Check that peers can fetch cfcheckpt on stale chain.")
|
||||||
|
request = msg_getcfcheckpt(
|
||||||
|
filter_type=FILTER_TYPE_BASIC,
|
||||||
|
stop_hash=int(stale_block_hash, 16)
|
||||||
|
)
|
||||||
|
node0.send_and_ping(request)
|
||||||
|
response = node0.last_message['cfcheckpt']
|
||||||
|
|
||||||
|
stale_cfcheckpt = self.nodes[0].getblockfilter(stale_block_hash, 'basic')['header']
|
||||||
|
assert_equal(
|
||||||
|
response.headers,
|
||||||
|
[int(header, 16) for header in (stale_cfcheckpt,)]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.log.info("Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection.")
|
||||||
|
requests = [
|
||||||
|
msg_getcfcheckpt(
|
||||||
|
filter_type=FILTER_TYPE_BASIC,
|
||||||
|
stop_hash=int(main_block_hash, 16)
|
||||||
|
),
|
||||||
|
]
|
||||||
|
for request in requests:
|
||||||
|
node1 = self.nodes[1].add_p2p_connection(P2PInterface())
|
||||||
|
node1.send_message(request)
|
||||||
|
node1.wait_for_disconnect()
|
||||||
|
|
||||||
|
self.log.info("Check that invalid requests result in disconnection.")
|
||||||
|
requests = [
|
||||||
|
# Requesting unknown filter type results in disconnection.
|
||||||
|
msg_getcfcheckpt(
|
||||||
|
filter_type=255,
|
||||||
|
stop_hash=int(main_block_hash, 16)
|
||||||
|
),
|
||||||
|
# Requesting unknown hash results in disconnection.
|
||||||
|
msg_getcfcheckpt(
|
||||||
|
filter_type=FILTER_TYPE_BASIC,
|
||||||
|
stop_hash=123456789,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
for request in requests:
|
||||||
|
node0 = self.nodes[0].add_p2p_connection(P2PInterface())
|
||||||
|
node0.send_message(request)
|
||||||
|
node0.wait_for_disconnect()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
CompactFiltersTest().main()
|
|
@ -57,6 +57,8 @@ MSG_FILTERED_BLOCK = 3
|
||||||
MSG_WITNESS_FLAG = 1 << 30
|
MSG_WITNESS_FLAG = 1 << 30
|
||||||
MSG_TYPE_MASK = 0xffffffff >> 2
|
MSG_TYPE_MASK = 0xffffffff >> 2
|
||||||
|
|
||||||
|
FILTER_TYPE_BASIC = 0
|
||||||
|
|
||||||
# Serialization/deserialization tools
|
# Serialization/deserialization tools
|
||||||
def sha256(s):
|
def sha256(s):
|
||||||
return hashlib.new('sha256', s).digest()
|
return hashlib.new('sha256', s).digest()
|
||||||
|
@ -1512,3 +1514,50 @@ class msg_no_witness_blocktxn(msg_blocktxn):
|
||||||
|
|
||||||
def serialize(self):
|
def serialize(self):
|
||||||
return self.block_transactions.serialize(with_witness=False)
|
return self.block_transactions.serialize(with_witness=False)
|
||||||
|
|
||||||
|
class msg_getcfcheckpt:
|
||||||
|
__slots__ = ("filter_type", "stop_hash")
|
||||||
|
msgtype = b"getcfcheckpt"
|
||||||
|
|
||||||
|
def __init__(self, filter_type, stop_hash):
|
||||||
|
self.filter_type = filter_type
|
||||||
|
self.stop_hash = stop_hash
|
||||||
|
|
||||||
|
def deserialize(self, f):
|
||||||
|
self.filter_type = struct.unpack("<B", f.read(1))[0]
|
||||||
|
self.stop_hash = deser_uint256(f)
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
r = b""
|
||||||
|
r += struct.pack("<B", self.filter_type)
|
||||||
|
r += ser_uint256(self.stop_hash)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "msg_getcfcheckpt(filter_type={:#x}, stop_hash={:x})".format(
|
||||||
|
self.filter_type, self.stop_hash)
|
||||||
|
|
||||||
|
class msg_cfcheckpt:
|
||||||
|
__slots__ = ("filter_type", "stop_hash", "headers")
|
||||||
|
msgtype = b"cfcheckpt"
|
||||||
|
|
||||||
|
def __init__(self, filter_type=None, stop_hash=None, headers=None):
|
||||||
|
self.filter_type = filter_type
|
||||||
|
self.stop_hash = stop_hash
|
||||||
|
self.headers = headers
|
||||||
|
|
||||||
|
def deserialize(self, f):
|
||||||
|
self.filter_type = struct.unpack("<B", f.read(1))[0]
|
||||||
|
self.stop_hash = deser_uint256(f)
|
||||||
|
self.headers = deser_uint256_vector(f)
|
||||||
|
|
||||||
|
def serialize(self):
|
||||||
|
r = b""
|
||||||
|
r += struct.pack("<B", self.filter_type)
|
||||||
|
r += ser_uint256(self.stop_hash)
|
||||||
|
r += ser_uint256_vector(self.headers)
|
||||||
|
return r
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "msg_cfcheckpt(filter_type={:#x}, stop_hash={:x})".format(
|
||||||
|
self.filter_type, self.stop_hash)
|
||||||
|
|
|
@ -31,6 +31,7 @@ from test_framework.messages import (
|
||||||
msg_block,
|
msg_block,
|
||||||
MSG_BLOCK,
|
MSG_BLOCK,
|
||||||
msg_blocktxn,
|
msg_blocktxn,
|
||||||
|
msg_cfcheckpt,
|
||||||
msg_cmpctblock,
|
msg_cmpctblock,
|
||||||
msg_feefilter,
|
msg_feefilter,
|
||||||
msg_filteradd,
|
msg_filteradd,
|
||||||
|
@ -67,6 +68,7 @@ MESSAGEMAP = {
|
||||||
b"addr": msg_addr,
|
b"addr": msg_addr,
|
||||||
b"block": msg_block,
|
b"block": msg_block,
|
||||||
b"blocktxn": msg_blocktxn,
|
b"blocktxn": msg_blocktxn,
|
||||||
|
b"cfcheckpt": msg_cfcheckpt,
|
||||||
b"cmpctblock": msg_cmpctblock,
|
b"cmpctblock": msg_cmpctblock,
|
||||||
b"feefilter": msg_feefilter,
|
b"feefilter": msg_feefilter,
|
||||||
b"filteradd": msg_filteradd,
|
b"filteradd": msg_filteradd,
|
||||||
|
@ -328,6 +330,7 @@ class P2PInterface(P2PConnection):
|
||||||
def on_addr(self, message): pass
|
def on_addr(self, message): pass
|
||||||
def on_block(self, message): pass
|
def on_block(self, message): pass
|
||||||
def on_blocktxn(self, message): pass
|
def on_blocktxn(self, message): pass
|
||||||
|
def on_cfcheckpt(self, message): pass
|
||||||
def on_cmpctblock(self, message): pass
|
def on_cmpctblock(self, message): pass
|
||||||
def on_feefilter(self, message): pass
|
def on_feefilter(self, message): pass
|
||||||
def on_filteradd(self, message): pass
|
def on_filteradd(self, message): pass
|
||||||
|
|
|
@ -225,6 +225,7 @@ BASE_SCRIPTS = [
|
||||||
'feature_loadblock.py',
|
'feature_loadblock.py',
|
||||||
'p2p_dos_header_tree.py',
|
'p2p_dos_header_tree.py',
|
||||||
'p2p_unrequested_blocks.py',
|
'p2p_unrequested_blocks.py',
|
||||||
|
'p2p_blockfilters.py',
|
||||||
'feature_includeconf.py',
|
'feature_includeconf.py',
|
||||||
'feature_asmap.py',
|
'feature_asmap.py',
|
||||||
'mempool_unbroadcast.py',
|
'mempool_unbroadcast.py',
|
||||||
|
|
Loading…
Add table
Reference in a new issue