Changeset View
Changeset View
Standalone View
Standalone View
test/functional/p2p_blockfilters.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2019 The Bitcoin Core developers | # Copyright (c) 2019 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
"""Tests NODE_COMPACT_FILTERS (BIP 157/158). | """Tests NODE_COMPACT_FILTERS (BIP 157/158). | ||||
Tests that a node configured with -blockfilterindex and -peerblockfilters can serve | Tests that a node configured with -blockfilterindex and -peerblockfilters can serve | ||||
cfcheckpts. | cfheaders and cfcheckpts. | ||||
""" | """ | ||||
from test_framework.messages import ( | from test_framework.messages import ( | ||||
FILTER_TYPE_BASIC, | FILTER_TYPE_BASIC, | ||||
hash256, | |||||
msg_getcfcheckpt, | msg_getcfcheckpt, | ||||
msg_getcfheaders, | |||||
ser_uint256, | |||||
uint256_from_str, | |||||
) | ) | ||||
from test_framework.mininode import P2PInterface | from test_framework.mininode import P2PInterface | ||||
from test_framework.test_framework import BitcoinTestFramework | from test_framework.test_framework import BitcoinTestFramework | ||||
from test_framework.util import ( | from test_framework.util import ( | ||||
assert_equal, | assert_equal, | ||||
connect_nodes, | connect_nodes, | ||||
disconnect_nodes, | disconnect_nodes, | ||||
wait_until, | wait_until, | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | def run_test(self): | ||||
stale_cfcheckpt = self.nodes[0].getblockfilter( | stale_cfcheckpt = self.nodes[0].getblockfilter( | ||||
stale_block_hash, 'basic')['header'] | stale_block_hash, 'basic')['header'] | ||||
assert_equal( | assert_equal( | ||||
response.headers, | response.headers, | ||||
[int(header, 16) for header in (stale_cfcheckpt,)] | [int(header, 16) for header in (stale_cfcheckpt,)] | ||||
) | ) | ||||
self.log.info("Check that peers can fetch cfheaders on active chain.") | |||||
request = msg_getcfheaders( | |||||
filter_type=FILTER_TYPE_BASIC, | |||||
start_height=1, | |||||
stop_hash=int(main_block_hash, 16) | |||||
) | |||||
node0.send_and_ping(request) | |||||
response = node0.last_message['cfheaders'] | |||||
assert_equal(len(response.hashes), 1000) | |||||
assert_equal( | |||||
compute_last_header(response.prev_header, response.hashes), | |||||
int(main_cfcheckpt, 16) | |||||
) | |||||
self.log.info("Check that peers can fetch cfheaders on stale chain.") | |||||
request = msg_getcfheaders( | |||||
filter_type=FILTER_TYPE_BASIC, | |||||
start_height=1, | |||||
stop_hash=int(stale_block_hash, 16) | |||||
) | |||||
node0.send_and_ping(request) | |||||
response = node0.last_message['cfheaders'] | |||||
assert_equal(len(response.hashes), 1000) | |||||
assert_equal( | |||||
compute_last_header(response.prev_header, response.hashes), | |||||
int(stale_cfcheckpt, 16) | |||||
) | |||||
self.log.info( | self.log.info( | ||||
"Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection.") | "Requests to node 1 without NODE_COMPACT_FILTERS results in disconnection.") | ||||
requests = [ | requests = [ | ||||
msg_getcfcheckpt( | msg_getcfcheckpt( | ||||
filter_type=FILTER_TYPE_BASIC, | filter_type=FILTER_TYPE_BASIC, | ||||
stop_hash=int(main_block_hash, 16) | stop_hash=int(main_block_hash, 16) | ||||
), | ), | ||||
msg_getcfheaders( | |||||
filter_type=FILTER_TYPE_BASIC, | |||||
start_height=1000, | |||||
stop_hash=int(main_block_hash, 16) | |||||
), | |||||
] | ] | ||||
for request in requests: | for request in requests: | ||||
node1 = self.nodes[1].add_p2p_connection(P2PInterface()) | node1 = self.nodes[1].add_p2p_connection(P2PInterface()) | ||||
node1.send_message(request) | node1.send_message(request) | ||||
node1.wait_for_disconnect() | node1.wait_for_disconnect() | ||||
self.log.info("Check that invalid requests result in disconnection.") | self.log.info("Check that invalid requests result in disconnection.") | ||||
requests = [ | requests = [ | ||||
# Requesting too many filter headers results in disconnection. | |||||
msg_getcfheaders( | |||||
filter_type=FILTER_TYPE_BASIC, | |||||
start_height=0, | |||||
stop_hash=int(tip_hash, 16) | |||||
), | |||||
# Requesting unknown filter type results in disconnection. | # Requesting unknown filter type results in disconnection. | ||||
msg_getcfcheckpt( | msg_getcfcheckpt( | ||||
filter_type=255, | filter_type=255, | ||||
stop_hash=int(main_block_hash, 16) | stop_hash=int(main_block_hash, 16) | ||||
), | ), | ||||
# Requesting unknown hash results in disconnection. | # Requesting unknown hash results in disconnection. | ||||
msg_getcfcheckpt( | msg_getcfcheckpt( | ||||
filter_type=FILTER_TYPE_BASIC, | filter_type=FILTER_TYPE_BASIC, | ||||
stop_hash=123456789, | stop_hash=123456789, | ||||
), | ), | ||||
] | ] | ||||
for request in requests: | for request in requests: | ||||
node0 = self.nodes[0].add_p2p_connection(P2PInterface()) | node0 = self.nodes[0].add_p2p_connection(P2PInterface()) | ||||
node0.send_message(request) | node0.send_message(request) | ||||
node0.wait_for_disconnect() | node0.wait_for_disconnect() | ||||
def compute_last_header(prev_header, hashes): | |||||
"""Compute the last filter header from a starting header and a sequence of filter hashes.""" | |||||
header = ser_uint256(prev_header) | |||||
for filter_hash in hashes: | |||||
header = hash256(ser_uint256(filter_hash) + header) | |||||
return uint256_from_str(header) | |||||
if __name__ == '__main__': | if __name__ == '__main__': | ||||
CompactFiltersTest().main() | CompactFiltersTest().main() |