mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -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_TYPE_MASK = 0xffffffff >> 2
|
||||
|
||||
FILTER_TYPE_BASIC = 0
|
||||
|
||||
# Serialization/deserialization tools
|
||||
def sha256(s):
|
||||
return hashlib.new('sha256', s).digest()
|
||||
|
@ -1512,3 +1514,50 @@ class msg_no_witness_blocktxn(msg_blocktxn):
|
|||
|
||||
def serialize(self):
|
||||
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_blocktxn,
|
||||
msg_cfcheckpt,
|
||||
msg_cmpctblock,
|
||||
msg_feefilter,
|
||||
msg_filteradd,
|
||||
|
@ -67,6 +68,7 @@ MESSAGEMAP = {
|
|||
b"addr": msg_addr,
|
||||
b"block": msg_block,
|
||||
b"blocktxn": msg_blocktxn,
|
||||
b"cfcheckpt": msg_cfcheckpt,
|
||||
b"cmpctblock": msg_cmpctblock,
|
||||
b"feefilter": msg_feefilter,
|
||||
b"filteradd": msg_filteradd,
|
||||
|
@ -328,6 +330,7 @@ class P2PInterface(P2PConnection):
|
|||
def on_addr(self, message): pass
|
||||
def on_block(self, message): pass
|
||||
def on_blocktxn(self, message): pass
|
||||
def on_cfcheckpt(self, message): pass
|
||||
def on_cmpctblock(self, message): pass
|
||||
def on_feefilter(self, message): pass
|
||||
def on_filteradd(self, message): pass
|
||||
|
|
|
@ -225,6 +225,7 @@ BASE_SCRIPTS = [
|
|||
'feature_loadblock.py',
|
||||
'p2p_dos_header_tree.py',
|
||||
'p2p_unrequested_blocks.py',
|
||||
'p2p_blockfilters.py',
|
||||
'feature_includeconf.py',
|
||||
'feature_asmap.py',
|
||||
'mempool_unbroadcast.py',
|
||||
|
|
Loading…
Add table
Reference in a new issue