コード例 #1
0
    def run_test(self):
        # Node 0 supports COMPACT_FILTERS, node 1 does not.
        node0 = self.nodes[0].add_p2p_connection(CFiltersClient())
        node1 = self.nodes[1].add_p2p_connection(CFiltersClient())

        # 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], self.nodes[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)

        # Check that nodes have signalled NODE_COMPACT_FILTERS correctly.
        assert node0.nServices & NODE_COMPACT_FILTERS != 0
        assert node1.nServices & NODE_COMPACT_FILTERS == 0

        # Check that the localservices is as expected.
        assert int(self.nodes[0].getnetworkinfo()['localservices'],
                   16) & NODE_COMPACT_FILTERS != 0
        assert int(self.nodes[1].getnetworkinfo()['localservices'],
                   16) & NODE_COMPACT_FILTERS == 0

        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], self.nodes[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("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']
        main_cfhashes = response.hashes
        assert_equal(len(main_cfhashes), 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']
        stale_cfhashes = response.hashes
        assert_equal(len(stale_cfhashes), 1000)
        assert_equal(
            compute_last_header(response.prev_header, response.hashes),
            int(stale_cfcheckpt, 16))

        self.log.info("Check that peers can fetch cfilters.")
        stop_hash = self.nodes[0].getblockhash(10)
        request = msg_getcfilters(filter_type=FILTER_TYPE_BASIC,
                                  start_height=1,
                                  stop_hash=int(stop_hash, 16))
        node0.send_message(request)
        node0.sync_with_ping()
        response = node0.pop_cfilters()
        assert_equal(len(response), 10)

        self.log.info("Check that cfilter responses are correct.")
        for cfilter, cfhash, height in zip(response, main_cfhashes,
                                           range(1, 11)):
            block_hash = self.nodes[0].getblockhash(height)
            assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC)
            assert_equal(cfilter.block_hash, int(block_hash, 16))
            computed_cfhash = uint256_from_str(hash256(cfilter.filter_data))
            assert_equal(computed_cfhash, cfhash)

        self.log.info("Check that peers can fetch cfilters for stale blocks.")
        request = msg_getcfilters(filter_type=FILTER_TYPE_BASIC,
                                  start_height=1000,
                                  stop_hash=int(stale_block_hash, 16))
        node0.send_message(request)
        node0.sync_with_ping()
        response = node0.pop_cfilters()
        assert_equal(len(response), 1)

        cfilter = response[0]
        assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC)
        assert_equal(cfilter.block_hash, int(stale_block_hash, 16))
        computed_cfhash = uint256_from_str(hash256(cfilter.filter_data))
        assert_equal(computed_cfhash, stale_cfhashes[999])

        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)),
            msg_getcfheaders(filter_type=FILTER_TYPE_BASIC,
                             start_height=1000,
                             stop_hash=int(main_block_hash, 16)),
            msg_getcfilters(filter_type=FILTER_TYPE_BASIC,
                            start_height=1000,
                            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 too many filters results in disconnection.
            msg_getcfilters(filter_type=FILTER_TYPE_BASIC,
                            start_height=0,
                            stop_hash=int(main_block_hash, 16)),
            # 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.
            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()
コード例 #2
0
    def run_test(self):
        # Node 0 supports COMPACT_FILTERS, node 1 does not.
        peer_0 = self.nodes[0].add_p2p_connection(FiltersClient())
        peer_1 = self.nodes[1].add_p2p_connection(FiltersClient())

        # Nodes 0 & 1 share the same first 999 blocks in the chain.
        self.generate(self.nodes[0], 999)

        # Stale blocks by disconnecting nodes 0 & 1, mining, then reconnecting
        self.disconnect_nodes(0, 1)

        stale_block_hash = self.generate(self.nodes[0], 1,
                                         sync_fun=self.no_op)[0]
        self.nodes[0].syncwithvalidationinterfacequeue()
        assert_equal(self.nodes[0].getblockcount(), 1000)

        self.generate(self.nodes[1], 1001, sync_fun=self.no_op)
        assert_equal(self.nodes[1].getblockcount(), 2000)

        # Check that nodes have signalled NODE_COMPACT_FILTERS correctly.
        assert peer_0.nServices & NODE_COMPACT_FILTERS != 0
        assert peer_1.nServices & NODE_COMPACT_FILTERS == 0

        # Check that the localservices is as expected.
        assert int(self.nodes[0].getnetworkinfo()['localservices'],
                   16) & NODE_COMPACT_FILTERS != 0
        assert int(self.nodes[1].getnetworkinfo()['localservices'],
                   16) & NODE_COMPACT_FILTERS == 0

        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),
        )
        peer_0.send_and_ping(message=request)
        response = peer_0.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.")
        self.connect_nodes(0, 1)
        self.sync_blocks(timeout=600)
        self.nodes[0].syncwithvalidationinterfacequeue()

        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),
        )
        peer_0.send_and_ping(request)
        response = peer_0.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),
        )
        peer_0.send_and_ping(request)
        response = peer_0.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("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),
        )
        peer_0.send_and_ping(request)
        response = peer_0.last_message['cfheaders']
        main_cfhashes = response.hashes
        assert_equal(len(main_cfhashes), 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),
        )
        peer_0.send_and_ping(request)
        response = peer_0.last_message['cfheaders']
        stale_cfhashes = response.hashes
        assert_equal(len(stale_cfhashes), 1000)
        assert_equal(
            compute_last_header(response.prev_header, response.hashes),
            int(stale_cfcheckpt, 16),
        )

        self.log.info("Check that peers can fetch cfilters.")
        stop_hash = self.nodes[0].getblockhash(10)
        request = msg_getcfilters(
            filter_type=FILTER_TYPE_BASIC,
            start_height=1,
            stop_hash=int(stop_hash, 16),
        )
        peer_0.send_and_ping(request)
        response = peer_0.pop_cfilters()
        assert_equal(len(response), 10)

        self.log.info("Check that cfilter responses are correct.")
        for cfilter, cfhash, height in zip(response, main_cfhashes,
                                           range(1, 11)):
            block_hash = self.nodes[0].getblockhash(height)
            assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC)
            assert_equal(cfilter.block_hash, int(block_hash, 16))
            computed_cfhash = uint256_from_str(hash256(cfilter.filter_data))
            assert_equal(computed_cfhash, cfhash)

        self.log.info("Check that peers can fetch cfilters for stale blocks.")
        request = msg_getcfilters(
            filter_type=FILTER_TYPE_BASIC,
            start_height=1000,
            stop_hash=int(stale_block_hash, 16),
        )
        peer_0.send_and_ping(request)
        response = peer_0.pop_cfilters()
        assert_equal(len(response), 1)

        cfilter = response[0]
        assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC)
        assert_equal(cfilter.block_hash, int(stale_block_hash, 16))
        computed_cfhash = uint256_from_str(hash256(cfilter.filter_data))
        assert_equal(computed_cfhash, stale_cfhashes[999])

        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),
            ),
            msg_getcfheaders(
                filter_type=FILTER_TYPE_BASIC,
                start_height=1000,
                stop_hash=int(main_block_hash, 16),
            ),
            msg_getcfilters(
                filter_type=FILTER_TYPE_BASIC,
                start_height=1000,
                stop_hash=int(main_block_hash, 16),
            ),
        ]
        for request in requests:
            peer_1 = self.nodes[1].add_p2p_connection(P2PInterface())
            peer_1.send_message(request)
            peer_1.wait_for_disconnect()

        self.log.info("Check that invalid requests result in disconnection.")
        requests = [
            # Requesting too many filters results in disconnection.
            msg_getcfilters(
                filter_type=FILTER_TYPE_BASIC,
                start_height=0,
                stop_hash=int(main_block_hash, 16),
            ),
            # 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.
            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:
            peer_0 = self.nodes[0].add_p2p_connection(P2PInterface())
            peer_0.send_message(request)
            peer_0.wait_for_disconnect()

        self.log.info(
            "Test -peerblockfilters without -blockfilterindex raises an error")
        self.stop_node(0)
        self.nodes[0].extra_args = ["-peerblockfilters"]
        msg = "Error: Cannot set -peerblockfilters without -blockfilterindex."
        self.nodes[0].assert_start_raises_init_error(expected_msg=msg)

        self.log.info(
            "Test -blockfilterindex with -reindex-chainstate raises an error")
        self.nodes[0].assert_start_raises_init_error(
            expected_msg=
            'Error: -reindex-chainstate option is not compatible with -blockfilterindex. '
            'Please temporarily disable blockfilterindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.',
            extra_args=['-blockfilterindex', '-reindex-chainstate'],
        )
コード例 #3
0
    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("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(
            "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)),
            msg_getcfheaders(filter_type=FILTER_TYPE_BASIC,
                             start_height=1000,
                             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 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.
            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()
コード例 #4
0
    def run_test(self):
        # Node 0 supports COMPACT_FILTERS, node 1 does not.
        node0 = self.nodes[0].add_p2p_connection(CFiltersClient())
        node1 = self.nodes[1].add_p2p_connection(CFiltersClient())

        # Nodes 0 & 1 share the same first 999 blocks in the chain.
        self.nodes[0].generate(999)
        sync_blocks(self.nodes)

        # 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)

        # Fetch cfcheckpt on node 0. Since the implementation caches the checkpoints on the active
        # chain in memory, this checks that the cache is updated correctly upon subsequent queries
        # after the reorg.
        request = msg_getcfcheckpt(filter_type=FILTER_TYPE_BASIC,
                                   stop_hash=int(stale_block_hash, 16))
        node0.send_message(request)
        node0.sync_with_ping(timeout=5)
        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)

        # Reorg node 0 to a new chain
        connect_nodes(self.nodes[0], 1)
        sync_blocks(self.nodes)

        main_block_hash = self.nodes[0].getblockhash(1000)
        assert main_block_hash != stale_block_hash, "node 0 chain did not reorganize"

        default_services = node1.nServices

        # Check that nodes have signalled expected services.
        assert 0 == node1.nServices & NODE_COMPACT_FILTERS
        assert_equal(node0.nServices, default_services | NODE_COMPACT_FILTERS)

        # Check that the localservices is as expected.
        assert_equal(int(self.nodes[0].getnetworkinfo()['localservices'], 16),
                     default_services | NODE_COMPACT_FILTERS)
        assert_equal(int(self.nodes[1].getnetworkinfo()['localservices'], 16),
                     default_services)

        # 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_message(request)
        node0.sync_with_ping()
        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)])

        # 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_message(request)
        node0.sync_with_ping()
        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, )])

        # 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_message(request)
        node0.sync_with_ping()
        response = node0.last_message['cfheaders']
        main_cfhashes = response.hashes
        assert_equal(
            compute_last_header(response.prev_header, response.hashes),
            int(main_cfcheckpt, 16))

        # 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_message(request)
        node0.sync_with_ping()
        response = node0.last_message['cfheaders']
        stale_cfhashes = response.hashes
        assert_equal(
            compute_last_header(response.prev_header, response.hashes),
            int(stale_cfcheckpt, 16))

        # Check that peers can fetch cfilters.
        stop_hash = self.nodes[0].getblockhash(10)
        request = msg_getcfilters(filter_type=FILTER_TYPE_BASIC,
                                  start_height=1,
                                  stop_hash=int(stop_hash, 16))
        node0.send_message(request)
        node0.sync_with_ping()
        response = node0.pop_cfilters()
        assert_equal(len(response), 10)

        # Check that cfilter responses are correct.
        for cfilter, cfhash, height in zip(response, main_cfhashes,
                                           range(1, 11)):
            block_hash = self.nodes[0].getblockhash(height)
            assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC)
            assert_equal(cfilter.block_hash, int(block_hash, 16))
            computed_cfhash = uint256_from_str(hash256(cfilter.filter_data))
            assert_equal(computed_cfhash, cfhash)

        # Check that peers can fetch cfilters for stale blocks.
        stop_hash = self.nodes[0].getblockhash(10)
        request = msg_getcfilters(filter_type=FILTER_TYPE_BASIC,
                                  start_height=1000,
                                  stop_hash=int(stale_block_hash, 16))
        node0.send_message(request)
        node0.sync_with_ping()
        response = node0.pop_cfilters()
        assert_equal(len(response), 1)

        cfilter = response[0]
        assert_equal(cfilter.filter_type, FILTER_TYPE_BASIC)
        assert_equal(cfilter.block_hash, int(stale_block_hash, 16))
        computed_cfhash = uint256_from_str(hash256(cfilter.filter_data))
        assert_equal(computed_cfhash, stale_cfhashes[999])

        # 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)),
            msg_getcfheaders(filter_type=FILTER_TYPE_BASIC,
                             start_height=1000,
                             stop_hash=int(main_block_hash, 16)),
            msg_getcfilters(filter_type=FILTER_TYPE_BASIC,
                            start_height=1000,
                            stop_hash=int(main_block_hash, 16)),
        ]
        node1.sync_with_ping(
        )  # ensure 'ping' has at least one message before we copy
        node1_check_message_count = dict(node1.message_count)
        node1_check_message_count['pong'] += 1
        for request in requests:
            node1.send_message(request)
        node1.sync_with_ping()
        assert_equal(node1_check_message_count, dict(node1.message_count))

        # Check that invalid requests result in disconnection.
        requests = [
            # Requesting too many filters results in disconnection.
            msg_getcfilters(filter_type=FILTER_TYPE_BASIC,
                            start_height=0,
                            stop_hash=int(main_block_hash, 16)),
            # 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.
            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(CFiltersClient())
            node0.send_message(request)
            node0.wait_for_disconnect()