示例#1
0
    def test_invalid_snapshot(self):
        """
        This test creates the following nodes:
        1. snap_node - full node that has the the snapshot
        2. snap_p2p - mini node that is used as a helper to retrieve the snapshot content
        3. node - the node which syncs the snapshot
        4. broken_p2p - mini node that claims has the best snapshot but it's broken
        5. valid_p2p - mini node that sends a valid snapshot
        6. not_finalized_p2p - mini node that claims has the best snapshot but it's not finalized
        """

        snap_node = self.nodes[4]
        node = self.nodes[5]

        self.start_node(snap_node.index)
        self.start_node(node.index)

        self.setup_stake_coins(snap_node)

        # generate 1 epoch + 1 block to create the first finalized snapshot
        # and store it in valid_p2p
        generate_block(snap_node, count=5 + 1)
        assert_equal(snap_node.getblockcount(), 6)
        wait_until(lambda: has_valid_snapshot(snap_node, 4), timeout=10)

        valid_p2p = WaitNode()
        valid_p2p.update_snapshot_from(snap_node)

        # create the second snapshot and store it in broken_p2p
        generate_block(snap_node, count=9)
        assert_equal(snap_node.getblockcount(), 15)
        wait_until(lambda: has_valid_snapshot(snap_node, 9), timeout=10)
        wait_until(lambda: has_valid_snapshot(snap_node, 14), timeout=10)

        broken_p2p = WaitNode()
        broken_p2p.update_snapshot_from(snap_node)
        broken_p2p.snapshot_data[-1].outputs[0].nValue *= 2  # break snapshot
        broken_p2p.update_headers_and_blocks_from(snap_node)
        valid_p2p.update_headers_and_blocks_from(snap_node)

        not_finalized_p2p = WaitNode()
        not_finalized_p2p.update_snapshot_from(snap_node, finalized=False)
        not_finalized_p2p.update_headers_and_blocks_from(snap_node)

        broken_p2p.return_snapshot_header = False
        valid_p2p.return_snapshot_header = False
        not_finalized_p2p.return_snapshot_header = False
        node.add_p2p_connection(valid_p2p,
                                services=SERVICE_FLAGS_WITH_SNAPSHOT)
        node.add_p2p_connection(broken_p2p,
                                services=SERVICE_FLAGS_WITH_SNAPSHOT)
        node.add_p2p_connection(not_finalized_p2p,
                                services=SERVICE_FLAGS_WITH_SNAPSHOT)

        # make sure that node knows about all the peers
        valid_p2p.wait_for_verack()
        broken_p2p.wait_for_verack()
        not_finalized_p2p.wait_for_verack()

        valid_p2p.send_message(msg_snaphead(valid_p2p.snapshot_header))
        broken_p2p.send_message(msg_snaphead(broken_p2p.snapshot_header))
        not_finalized_p2p.send_message(
            msg_snaphead(not_finalized_p2p.snapshot_header))

        # node must pick the best snapshot
        wait_until(lambda: broken_p2p.snapshot_chunk1_requested, timeout=10)
        broken_p2p.return_snapshot_chunk1 = True
        broken_p2p.on_getsnapshot(broken_p2p.last_getsnapshot_message)
        wait_until(lambda: broken_p2p.snapshot_chunk2_requested, timeout=10)
        assert_has_snapshot_on_disk(node,
                                    broken_p2p.snapshot_header.snapshot_hash)
        assert_no_snapshot_on_disk(node,
                                   valid_p2p.snapshot_header.snapshot_hash)
        assert_equal(valid_p2p.snapshot_chunk1_requested, False)

        # node detects broken snapshot, removes it and switches to the second best
        broken_p2p.return_snapshot_chunk2 = True
        broken_p2p.on_getsnapshot(broken_p2p.last_getsnapshot_message)
        wait_until(lambda: valid_p2p.snapshot_chunk1_requested, timeout=10)
        assert_no_snapshot_on_disk(node,
                                   broken_p2p.snapshot_header.snapshot_hash)
        valid_p2p.return_snapshot_chunk1 = True
        valid_p2p.on_getsnapshot(valid_p2p.last_getsnapshot_message)
        wait_until(lambda: valid_p2p.snapshot_chunk2_requested, timeout=10)
        assert_has_snapshot_on_disk(node,
                                    valid_p2p.snapshot_header.snapshot_hash)
        valid_p2p.return_snapshot_chunk2 = True
        valid_p2p.return_parent_block = True
        valid_p2p.on_getsnapshot(valid_p2p.last_getsnapshot_message)

        # node doesn't request not finalized snapshot
        assert_equal(not_finalized_p2p.snapshot_header_requested, True)
        assert_equal(not_finalized_p2p.snapshot_chunk1_requested, False)

        # node requests parent block and finishes ISD
        wait_until(lambda: node.getblockcount() == 15, timeout=20)
        node.disconnect_p2ps()
        assert_chainstate_equal(snap_node, node)

        self.log.info('test_invalid_snapshot passed')
示例#2
0
    def test_cannot_sync_with_snapshot(self):
        """
        This test creates the following nodes:
        1. snap_node - snapshot node that is used as a helper node to generate the snapshot
        2. helper_p2p - mini node that retrieves the content of the snapshot
        3. full_snap_p2p - mini node that has full 2nd best snapshot
        3. half_snap_p2p - mini node that has half of the best snapshot
        4. no_snap_p2p - mini node that doesn't have snapshot
        5. sync_node - the node which syncs with the snapshot
        """
        snap_node = self.nodes[6]
        sync_node = self.nodes[7]
        self.start_node(snap_node.index)
        self.start_node(sync_node.index)

        self.setup_stake_coins(snap_node)

        # add 2nd best snapshot to full_snap_p2p
        generate_block(snap_node, count=5 + 5 + 1)
        assert_equal(snap_node.getblockcount(), 11)
        wait_until(lambda: has_valid_snapshot(snap_node, 4), timeout=10)
        full_snap_p2p = WaitNode()
        no_snap_p2p = WaitNode()
        for p2p in [full_snap_p2p, no_snap_p2p]:
            p2p.update_snapshot_from(snap_node)

        # add the best snapshot to half_snap_p2p
        generate_block(snap_node, count=5)
        assert_equal(snap_node.getblockcount(), 16)
        wait_until(lambda: has_valid_snapshot(snap_node, 9), timeout=10)
        half_snap_p2p = WaitNode()
        half_snap_p2p.update_snapshot_from(snap_node)
        for p2p in [half_snap_p2p, full_snap_p2p, no_snap_p2p]:
            p2p.update_headers_and_blocks_from(snap_node)

        # retrieve snapshot data
        helper_p2p = snap_node.add_p2p_connection(BaseNode())
        helper_p2p.wait_for_verack()
        full_snap_p2p.snapshot_data = helper_p2p.fetch_snapshot_data(
            full_snap_p2p.snapshot_header)
        half_snap_p2p.snapshot_data = helper_p2p.fetch_snapshot_data(
            half_snap_p2p.snapshot_header)
        self.stop_node(snap_node.index)

        full_snap_p2p.return_snapshot_header = False
        half_snap_p2p.return_snapshot_header = False
        sync_node.add_p2p_connection(no_snap_p2p)
        sync_node.add_p2p_connection(full_snap_p2p,
                                     services=SERVICE_FLAGS_WITH_SNAPSHOT)
        sync_node.add_p2p_connection(half_snap_p2p,
                                     services=SERVICE_FLAGS_WITH_SNAPSHOT)

        # test 1. the node requests snapshot from peers that have service flag set
        full_snap_p2p.wait_for_verack()
        half_snap_p2p.wait_for_verack()
        no_snap_p2p.wait_for_verack()

        wait_until(lambda: full_snap_p2p.snapshot_header_requested, timeout=10)
        wait_until(lambda: half_snap_p2p.snapshot_header_requested, timeout=10)
        assert (full_snap_p2p.snapshot_header_requested is True)
        assert (half_snap_p2p.snapshot_header_requested is True)
        assert (no_snap_p2p.snapshot_header_requested is False)

        full_snap_p2p.send_message(msg_snaphead(full_snap_p2p.snapshot_header))
        half_snap_p2p.send_message(msg_snaphead(half_snap_p2p.snapshot_header))
        wait_until(lambda: half_snap_p2p.snapshot_chunk1_requested, timeout=10)
        assert (full_snap_p2p.snapshot_chunk1_requested is False
                )  # didn't start asking for the 2nd best
        self.log.info('Service flag are correctly recognized')

        # test 2. the node can't receive the 2nd part of the snapshot
        half_snap_p2p.return_snapshot_chunk1 = True
        half_snap_p2p.on_getsnapshot(half_snap_p2p.last_getsnapshot_message)
        wait_until(lambda: half_snap_p2p.snapshot_chunk2_requested, timeout=10)
        assert_has_snapshot_on_disk(
            sync_node, half_snap_p2p.snapshot_header.snapshot_hash)
        wait_until(lambda: full_snap_p2p.snapshot_chunk1_requested,
                   timeout=10)  # fallback to 2nd best
        assert_no_snapshot_on_disk(sync_node,
                                   half_snap_p2p.snapshot_header.snapshot_hash)
        self.log.info('Node cannot receive 2nd half of the snapshot')

        # test 3. the node can't receive the parent block
        full_snap_p2p.return_snapshot_chunk1 = True
        full_snap_p2p.return_snapshot_chunk2 = True
        full_snap_p2p.on_getsnapshot(full_snap_p2p.last_getsnapshot_message)
        wait_until(lambda: full_snap_p2p.parent_block_requested, timeout=10)
        wait_until(lambda: no_snap_p2p.parent_block_requested, timeout=10)
        assert_has_snapshot_on_disk(
            sync_node, full_snap_p2p.snapshot_header.snapshot_hash)
        self.log.info(
            'Node cannot receive parent block from already connected peers')

        # test 4. the node can't receive the parent block from new peers
        sync_node.disconnect_p2ps()

        for p2p in [full_snap_p2p, no_snap_p2p]:
            wait_until(lambda: p2p.is_connected is False, timeout=5)
            p2p.snapshot_chunk1_requested = False
            p2p.snapshot_chunk2_requested = False
            p2p.parent_block_requested = False

        sync_node.add_p2p_connection(full_snap_p2p)
        sync_node.add_p2p_connection(no_snap_p2p)
        full_snap_p2p.wait_for_verack()
        no_snap_p2p.wait_for_verack()

        wait_until(lambda: full_snap_p2p.parent_block_requested, timeout=10)
        wait_until(lambda: no_snap_p2p.parent_block_requested, timeout=10)
        assert full_snap_p2p.snapshot_chunk1_requested is False
        assert no_snap_p2p.snapshot_chunk1_requested is False
        assert_has_snapshot_on_disk(
            sync_node, full_snap_p2p.snapshot_header.snapshot_hash)
        self.log.info('Node cannot receive parent block from new peers')

        self.stop_node(sync_node.index)

        self.log.info('test_cannot_sync_with_snapshot passed')
示例#3
0
 def on_getsnaphead(self, message):
     if self.return_snapshot_header:
         assert self.snapshot_header.snapshot_hash > 0
         self.send_message(msg_snaphead(self.snapshot_header))