Skip to content

Commit 5308c97

Browse files
jimpojnewbery
authored andcommitted
[test] Add test for cfheaders
1 parent f6b58c1 commit 5308c97

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

test/functional/p2p_blockfilters.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@
55
"""Tests NODE_COMPACT_FILTERS (BIP 157/158).
66
77
Tests that a node configured with -blockfilterindex and -peerblockfilters can serve
8-
cfcheckpts.
8+
cfheaders and cfcheckpts.
99
"""
1010

1111
from test_framework.messages import (
1212
FILTER_TYPE_BASIC,
13+
hash256,
1314
msg_getcfcheckpt,
15+
msg_getcfheaders,
16+
ser_uint256,
17+
uint256_from_str,
1418
)
1519
from test_framework.mininode import P2PInterface
1620
from test_framework.test_framework import BitcoinTestFramework
@@ -100,12 +104,45 @@ def run_test(self):
100104
[int(header, 16) for header in (stale_cfcheckpt,)]
101105
)
102106

107+
self.log.info("Check that peers can fetch cfheaders on active chain.")
108+
request = msg_getcfheaders(
109+
filter_type=FILTER_TYPE_BASIC,
110+
start_height=1,
111+
stop_hash=int(main_block_hash, 16)
112+
)
113+
node0.send_and_ping(request)
114+
response = node0.last_message['cfheaders']
115+
assert_equal(len(response.hashes), 1000)
116+
assert_equal(
117+
compute_last_header(response.prev_header, response.hashes),
118+
int(main_cfcheckpt, 16)
119+
)
120+
121+
self.log.info("Check that peers can fetch cfheaders on stale chain.")
122+
request = msg_getcfheaders(
123+
filter_type=FILTER_TYPE_BASIC,
124+
start_height=1,
125+
stop_hash=int(stale_block_hash, 16)
126+
)
127+
node0.send_and_ping(request)
128+
response = node0.last_message['cfheaders']
129+
assert_equal(len(response.hashes), 1000)
130+
assert_equal(
131+
compute_last_header(response.prev_header, response.hashes),
132+
int(stale_cfcheckpt, 16)
133+
)
134+
103135
self.log.info("Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection.")
104136
requests = [
105137
msg_getcfcheckpt(
106138
filter_type=FILTER_TYPE_BASIC,
107139
stop_hash=int(main_block_hash, 16)
108140
),
141+
msg_getcfheaders(
142+
filter_type=FILTER_TYPE_BASIC,
143+
start_height=1000,
144+
stop_hash=int(main_block_hash, 16)
145+
),
109146
]
110147
for request in requests:
111148
node1 = self.nodes[1].add_p2p_connection(P2PInterface())
@@ -114,6 +151,12 @@ def run_test(self):
114151

115152
self.log.info("Check that invalid requests result in disconnection.")
116153
requests = [
154+
# Requesting too many filter headers results in disconnection.
155+
msg_getcfheaders(
156+
filter_type=FILTER_TYPE_BASIC,
157+
start_height=0,
158+
stop_hash=int(tip_hash, 16)
159+
),
117160
# Requesting unknown filter type results in disconnection.
118161
msg_getcfcheckpt(
119162
filter_type=255,
@@ -130,5 +173,12 @@ def run_test(self):
130173
node0.send_message(request)
131174
node0.wait_for_disconnect()
132175

176+
def compute_last_header(prev_header, hashes):
177+
"""Compute the last filter header from a starting header and a sequence of filter hashes."""
178+
header = ser_uint256(prev_header)
179+
for filter_hash in hashes:
180+
header = hash256(ser_uint256(filter_hash) + header)
181+
return uint256_from_str(header)
182+
133183
if __name__ == '__main__':
134184
CompactFiltersTest().main()

test/functional/test_framework/messages.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,6 +1516,59 @@ class msg_no_witness_blocktxn(msg_blocktxn):
15161516
def serialize(self):
15171517
return self.block_transactions.serialize(with_witness=False)
15181518

1519+
class msg_getcfheaders:
1520+
__slots__ = ("filter_type", "start_height", "stop_hash")
1521+
msgtype = b"getcfheaders"
1522+
1523+
def __init__(self, filter_type, start_height, stop_hash):
1524+
self.filter_type = filter_type
1525+
self.start_height = start_height
1526+
self.stop_hash = stop_hash
1527+
1528+
def deserialize(self, f):
1529+
self.filter_type = struct.unpack("<B", f.read(1))[0]
1530+
self.start_height = struct.unpack("<I", f.read(4))[0]
1531+
self.stop_hash = deser_uint256(f)
1532+
1533+
def serialize(self):
1534+
r = b""
1535+
r += struct.pack("<B", self.filter_type)
1536+
r += struct.pack("<I", self.start_height)
1537+
r += ser_uint256(self.stop_hash)
1538+
return r
1539+
1540+
def __repr__(self):
1541+
return "msg_getcfheaders(filter_type={:#x}, start_height={}, stop_hash={:x})".format(
1542+
self.filter_type, self.start_height, self.stop_hash)
1543+
1544+
class msg_cfheaders:
1545+
__slots__ = ("filter_type", "stop_hash", "prev_header", "hashes")
1546+
msgtype = b"cfheaders"
1547+
1548+
def __init__(self, filter_type=None, stop_hash=None, prev_header=None, hashes=None):
1549+
self.filter_type = filter_type
1550+
self.stop_hash = stop_hash
1551+
self.prev_header = prev_header
1552+
self.hashes = hashes
1553+
1554+
def deserialize(self, f):
1555+
self.filter_type = struct.unpack("<B", f.read(1))[0]
1556+
self.stop_hash = deser_uint256(f)
1557+
self.prev_header = deser_uint256(f)
1558+
self.hashes = deser_uint256_vector(f)
1559+
1560+
def serialize(self):
1561+
r = b""
1562+
r += struct.pack("<B", self.filter_type)
1563+
r += ser_uint256(self.stop_hash)
1564+
r += ser_uint256(self.prev_header)
1565+
r += ser_uint256_vector(self.hashes)
1566+
return r
1567+
1568+
def __repr__(self):
1569+
return "msg_cfheaders(filter_type={:#x}, stop_hash={:x})".format(
1570+
self.filter_type, self.stop_hash)
1571+
15191572
class msg_getcfcheckpt:
15201573
__slots__ = ("filter_type", "stop_hash")
15211574
msgtype = b"getcfcheckpt"

test/functional/test_framework/mininode.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
msg_block,
3232
MSG_BLOCK,
3333
msg_blocktxn,
34+
msg_cfheaders,
3435
msg_cfcheckpt,
3536
msg_cmpctblock,
3637
msg_feefilter,
@@ -68,6 +69,7 @@
6869
b"addr": msg_addr,
6970
b"block": msg_block,
7071
b"blocktxn": msg_blocktxn,
72+
b"cfheaders": msg_cfheaders,
7173
b"cfcheckpt": msg_cfcheckpt,
7274
b"cmpctblock": msg_cmpctblock,
7375
b"feefilter": msg_feefilter,
@@ -330,6 +332,7 @@ def on_close(self):
330332
def on_addr(self, message): pass
331333
def on_block(self, message): pass
332334
def on_blocktxn(self, message): pass
335+
def on_cfheaders(self, message): pass
333336
def on_cfcheckpt(self, message): pass
334337
def on_cmpctblock(self, message): pass
335338
def on_feefilter(self, message): pass

0 commit comments

Comments
 (0)