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