def test_handle_recovering_block_recovered_quickly(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self._assert_block_sent(block_hash_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) self.block_queuing_service.push(block_hash_2, waiting_for_recovery=True) self.block_queuing_service.push(block_hash_3, block_3) self._assert_no_blocks_sent() # block 2 recovers quickly enough, is sent ahead of block 3 self.block_queuing_service.update_recovered_block( block_hash_2, block_2) self._assert_block_sent(block_hash_2) self._progress_time() self._assert_block_sent(block_hash_3)
def test_accepted_blocks_pushes_next_immediately(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self.block_queuing_service.push(block_hash_2, block_2) self.block_queuing_service.push(block_hash_3, block_3) self._assert_block_sent(block_hash_1) # after block 1 is accepted, block 2 is immediately pushed self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) self._assert_block_sent(block_hash_2) self._progress_time() self._assert_block_sent(block_hash_3)
def test_handle_recovering_block_timeout(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self._assert_block_sent(block_hash_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) self.block_queuing_service.push(block_hash_2, waiting_for_recovery=True) self.block_queuing_service.push(block_hash_3, block_3) # sent block 3 anyway, block 2 is taking too long to recover self._progress_time() self._assert_block_sent(block_hash_3) # recovery times out in the end, nothing sent and queue cleared self._progress_recovery_timeout() self._assert_no_blocks_sent() self.assertEqual(0, len(self.block_queuing_service))
def test_handle_out_of_order_blocks(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() # second block won't be sent, was somehow not received from BDN block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() # third block should still be queued and sent after timeout block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self._assert_block_sent(block_hash_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) # block 3 sent after a delay, since no block 2 was ever seen self.block_queuing_service.push(block_hash_3, block_3) self._assert_no_blocks_sent() self._progress_time() self._assert_block_sent(block_hash_3)
def test_handle_out_of_order_blocks_confirmed_ancestor(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() # block 2 will be received after block 3, but still should be pushed to # blockchain node first since block 1 is confirmed and timeout not reached block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self._assert_block_sent(block_hash_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) self.block_queuing_service.push(block_hash_3, block_3) self._assert_no_blocks_sent() self.block_queuing_service.push(block_hash_2, block_2) self._assert_block_sent(block_hash_2) self._progress_time() self._assert_block_sent(block_hash_3)
def test_clear_out_stale_blocks(self): self._set_block_queue_in_progress() block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_2, block_2) self.block_queuing_service.push(block_hash_3, block_3) self._assert_no_blocks_sent() # block 3 marked seen by blockchain node, so eject all earlier blocks (2, 3) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_3, block_3) self._assert_no_blocks_sent() self.assertEqual(0, len(self.block_queuing_service))
def test_msg_get_block_headers_fork(self): self.node.opts.max_block_interval_s = 0 block_hashes = [] for i in range(20): block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(i, i + 1000) ) block_hash = block_message.block_hash() block_hashes.append(block_hash) self.node.block_queuing_service_manager.push(block_hash, block_message) # create fork block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(34, 1017) ) block_hash = block_message.block_hash() block_hashes.append(block_hash) self.node.block_queuing_service_manager.push(block_hash, block_message) self.enqueued_messages.clear() self.sut.msg_proxy_request = MagicMock() message = GetBlockHeadersEthProtocolMessage( None, block_hashes[-1].binary, 10, 0, 1 ) self.sut.msg_get_block_headers(message) self.sut.msg_proxy_request.assert_called_once() self.assertEqual(0, len(self.enqueued_messages))
def test_partial_chainstate_missing_entries(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() # 2b will never be pushed to block queue block_2b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 2, block_hash_1)) block_hash_2b = block_2b.block_hash() block_3b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 4, 3, block_hash_2b)) block_hash_3b = block_3b.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) self.block_queuing_service.push(block_hash_2, block_2) # establish an earlier chainstate first, just for testing self.block_queuing_service.partial_chainstate(10) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_2, block_2) self.block_queuing_service.push(block_hash_3b, block_3b) expected_result = deque([ EthBlockInfo(3, block_hash_3b), ]) self.assertEqual(expected_result, self.block_queuing_service.partial_chainstate(10))
def test_partial_chainstate_missing_entries_in_head(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) # establish an earlier chainstate first, just for extending later self.block_queuing_service.partial_chainstate(10) self.block_queuing_service.push(block_hash_3, block_3) self._progress_time() expected_result = deque([ EthBlockInfo(3, block_hash_3), ]) self.assertEqual(expected_result, self.block_queuing_service.partial_chainstate(10))
def test_handle_recovering_block_recovered_too_slow(self): self._set_block_queue_in_progress() block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() self.block_queuing_service.push(block_hash_1, waiting_for_recovery=True) self.block_queuing_service.push(block_hash_2, block_2) self._assert_no_blocks_sent() # block 1 takes too long to recover, so block 2 is just sent self._progress_time() self._assert_block_sent(block_hash_2) # block 1 is now stale, queue should be empty self.block_queuing_service.update_recovered_block( block_hash_1, block_1) self._assert_no_blocks_sent() self._progress_time() self._assert_no_blocks_sent()
def test_handle_out_of_order_blocks_unconfirmed_ancestor(self): self._set_block_queue_in_progress() block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() # block 2 will be received after block 3, but still should be pushed to # blockchain node first after timeout (no confirmation on block 1) block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() # after another timeout, block 3 will be pushed block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self._assert_block_sent(block_hash_1) self.block_queuing_service.push(block_hash_3, block_3) self._assert_no_blocks_sent() self.block_queuing_service.push(block_hash_2, block_2) self._assert_no_blocks_sent() self._progress_time() self._assert_block_sent(block_hash_2) self._progress_time() self._assert_block_sent(block_hash_3)
def test_handle_single_block_fork_accepted_later(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2a = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2a = block_2a.block_hash() block_2b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 2, block_hash_1)) block_hash_2b = block_2b.block_hash() block_3b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 4, 3, block_hash_2b)) block_hash_3b = block_3b.block_hash() block_4b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 5, 4, block_hash_3b)) block_hash_4b = block_4b.block_hash() # send + accept block 1 self.block_queuing_service.push(block_hash_1, block_1) self._assert_block_sent(block_hash_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) # send block 2a self.block_queuing_service.push(block_hash_2a, block_2a) self._assert_block_sent(block_hash_2a) # block 2b will never be sent (despite no confirmation) self.block_queuing_service.push(block_hash_2b, block_2b) self._assert_no_blocks_sent() self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_2a, block_2a) # block 3b will be sent immediately (block at height 2 confirmed) self.block_queuing_service.push(block_hash_3b, block_3b) self._assert_block_sent(block_hash_3b) # block 4b send later (no confirmation for height 3) self.block_queuing_service.push(block_hash_4b, block_4b) self._assert_no_blocks_sent() self._progress_time() self._assert_block_sent(block_hash_4b)
def test_msg_get_block_headers_future_block(self): self.node.opts.max_block_interval_s = 0 block_hashes = [] block_heights = [] for i in range(20): block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(i, i + 1000) ) block_hash = block_message.block_hash() block_heights.append(block_message.block_number()) block_hashes.append(block_hash) self.node.block_queuing_service_manager.push(block_hash, block_message) self.enqueued_messages.clear() self.sut.msg_proxy_request = MagicMock() block_height_bytes = block_heights[-1] + 4 block_height_bytes = block_height_bytes.to_bytes(8, "big") message = GetBlockHeadersEthProtocolMessage( None, block_height_bytes, 1, 0, 0 ) self.sut.msg_get_block_headers(message) self.sut.msg_proxy_request.assert_not_called() self.assertEqual(1, len(self.enqueued_messages)) headers_sent = self.enqueued_messages[0] self.assertIsInstance(headers_sent, BlockHeadersEthProtocolMessage) self.assertEqual(0, len(headers_sent.get_block_headers()))
def test_partial_chainstate(self): self.assertEqual(deque(), self.block_queuing_service.partial_chainstate(10)) block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) self.block_queuing_service.push(block_hash_2, block_2) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_2, block_2) self.block_queuing_service.push(block_hash_3, block_3) self.assertEqual(0, len(self.block_queuing_service)) expected_result = deque([ EthBlockInfo(1, block_hash_1), EthBlockInfo(2, block_hash_2), EthBlockInfo(3, block_hash_3), ]) self.assertEqual(expected_result, self.block_queuing_service.partial_chainstate(3)) # returns full chain if requested length is shorter self.assertEqual(expected_result, self.block_queuing_service.partial_chainstate(1)) # not more data, so can't meet asked length next_expected_result = deque([ EthBlockInfo(1, block_hash_1), EthBlockInfo(2, block_hash_2), EthBlockInfo(3, block_hash_3), ]) self.assertEqual(next_expected_result, self.block_queuing_service.partial_chainstate(10))
def setUp(self): self.node = MockGatewayNode( gateway_helpers.get_gateway_opts(8000, max_block_interval_s=0)) self.node.block_parts_storage = ExpiringDict( self.node.alarm_queue, gateway_constants.MAX_BLOCK_CACHE_TIME_S, "eth_block_queue_parts", ) self.node_connection = Mock() self.node_connection.is_active = MagicMock(return_value=True) self.node.set_known_total_difficulty = MagicMock() self.node_conn = self.node_connection self.block_queuing_service = EthBlockQueuingService( self.node, self.node_conn) self.node.block_queuing_service_manager.add_block_queuing_service( self.node_conn, self.block_queuing_service) self.node_conn.enqueue_msg = MagicMock() self.block_hashes = [] self.block_messages = [] self.block_headers = [] self.block_bodies = [] # block numbers: 1000-1019 prev_block_hash = None for i in range(20): block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( i, i + 1000, prev_block_hash=prev_block_hash)) block_hash = block_message.block_hash() self.block_hashes.append(block_hash) self.block_messages.append(block_message) block_parts = block_message.to_new_block_parts() self.block_headers.append( BlockHeadersEthProtocolMessage.from_header_bytes( block_parts.block_header_bytes).get_block_headers()[0]) self.block_bodies.append( BlockBodiesEthProtocolMessage.from_body_bytes( block_parts.block_body_bytes).get_blocks()[0]) self.node.block_queuing_service_manager.push( block_hash, block_message) prev_block_hash = block_hash for block_hash in self.block_hashes: self.block_queuing_service.remove_from_queue(block_hash) self.block_queuing_service.best_sent_block = (1019, self.block_hashes[-1], time.time()) self.block_queuing_service.best_accepted_block = ( 1019, self.block_hashes[-1])
def test_get_block_by_hash_orphaned_not_found(self): # create fork at block 17 block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(21, 1017)) block_hash = block_message.block_hash() self.block_queuing_service.push(block_hash, block_message) success, hashes = self.block_queuing_service.get_block_hashes_starting_from_hash( block_hash, 1, 0, False) self.assertTrue(success) self.assertEqual(0, len(hashes))
def test_dont_send_stale_blocks_newer_block_confirmed(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() self._mark_block_seen_by_blockchain_nodes(block_hash_2, block_2) # block 1 will be ignored by queue, since block 2 already confirmed self.node.block_queuing_service_manager.push(block_hash_1, block_1) self._assert_no_blocks_sent() self._progress_time() self._assert_no_blocks_sent() self.assertEqual(0, len(self.block_queuing_service))
def test_get_block_by_number_ambiguous_succeeds(self): # create fork at block 17 block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(21, 1017)) block_hash = block_message.block_hash() self.block_queuing_service.push(block_hash, block_message) success, hashes = self.block_queuing_service.get_block_hashes_starting_from_height( 1017, 1, 0, False) self.assertTrue(success) self.assertEqual(self.block_hashes[17], hashes[0])
def test_dont_send_seen_block(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() self.node.block_queuing_service_manager.push(block_hash_1, block_1) self.node.block_queuing_service_manager.push(block_hash_2, block_2) self._assert_block_sent(block_hash_1) self._mark_block_seen_by_blockchain_nodes(block_hash_2, block_2) # block 2 marked seen by blockchain node, so eject block 2 self._progress_time() self._assert_no_blocks_sent() self.assertEqual(0, len(self.block_queuing_service))
def test_get_block_hashes_fork_follows_last_sent(self): # create fork at block 17 block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(21, 1017)) block_hash = block_message.block_hash() self.block_queuing_service.push(block_hash, block_message) success, hashes = self.block_queuing_service.get_block_hashes_starting_from_hash( self.block_hashes[15], 5, 0, False) self.assertTrue(success) self.assertEqual(5, len(hashes)) self.assertEqual(self.block_hashes[15:20], hashes)
def test_partial_chainstate_reorganizes(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_2b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 2, block_hash_1)) block_hash_2b = block_2b.block_hash() block_3b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 4, 3, block_hash_2b)) block_hash_3b = block_3b.block_hash() self.block_queuing_service.push(block_hash_1, block_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) self.block_queuing_service.push(block_hash_2, block_2) self.block_queuing_service.push(block_hash_2b, block_2b) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_2, block_2) expected_result = deque([ EthBlockInfo(1, block_hash_1), EthBlockInfo(2, block_hash_2), ]) self.assertEqual(expected_result, self.block_queuing_service.partial_chainstate(10)) self.block_queuing_service.push(block_hash_3b, block_3b) next_expected_result = deque([ EthBlockInfo(1, block_hash_1), EthBlockInfo(2, block_hash_2b), EthBlockInfo(3, block_hash_3b), ]) self.assertEqual(next_expected_result, self.block_queuing_service.partial_chainstate(10))
def test_dont_send_stale_recovered_block(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() self._mark_block_seen_by_blockchain_nodes(block_hash_2, block_2) self.node.block_queuing_service_manager.push(block_hash_1, waiting_for_recovery=True) # block 1 will be ignored by queue after recovery (didn't know block # number before then), since block 2 already confirmed self.node.block_queuing_service_manager.update_recovered_block( block_hash_1, block_1) self._assert_no_blocks_sent() self._progress_time() self._assert_no_blocks_sent() self.assertEqual(0, len(self.block_queuing_service))
def test_msg_block_adds_headers_and_bodies(self): self.node.opts.max_block_interval = 0 self.sut.is_valid_block_timestamp = MagicMock(return_value=True) block_hashes = [] block_messages = [] block_hash = None for i in range(20): block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(i, i + 1000, block_hash) ) block_hash = block_message.block_hash() block_hashes.append(block_hash) block_messages.append(block_message) # push all blocks, except for # 17 for i in (j for j in range(20) if j != 17): self.node.block_queuing_service.push( block_hashes[i], block_messages[i] ) self.node.block_queuing_service.remove_from_queue(block_hashes[i]) self.node.block_queuing_service.best_sent_block = (1019, block_hashes[-1], 0) self.node.send_to_node_messages.clear() self.sut.msg_proxy_request = MagicMock() message = GetBlockHeadersEthProtocolMessage( None, block_hashes[10].binary, 10, 0, 0 ) self.sut.msg_get_block_headers(message) self.sut.msg_proxy_request.assert_called_once() self.assertEqual(0, len(self.node.send_to_node_messages)) self.sut.msg_proxy_request.reset_mock() self.sut.msg_block(block_messages[17].to_new_block_msg()) self.sut.msg_get_block_headers(message) self.sut.msg_proxy_request.assert_not_called() self.assertEqual(1, len(self.node.send_to_node_messages)) headers_sent = self.node.send_to_node_messages[0] self.assertIsInstance(headers_sent, BlockHeadersEthProtocolMessage) self.assertEqual(10, len(headers_sent.get_block_headers())) for i in range(10): self.assertEqual( block_hashes[i + 10], headers_sent.get_block_headers()[i].hash_object(), )
def test_handle_recovering_block_recovered_ordering(self): self._set_block_queue_in_progress() block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2 = block_2.block_hash() block_3 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 3, block_hash_2)) block_hash_3 = block_3.block_hash() # blocks in recovery get added out of order self.block_queuing_service.push(block_hash_2, waiting_for_recovery=True) self.block_queuing_service.push(block_hash_1, waiting_for_recovery=True) self.block_queuing_service.push(block_hash_3, block_3) self._assert_no_blocks_sent() # everything in recovery recovers quickly, so correct order re-established self.block_queuing_service.update_recovered_block( block_hash_2, block_2) self.block_queuing_service.update_recovered_block( block_hash_1, block_1) self._assert_block_sent(block_hash_1) self._progress_time() self._assert_block_sent(block_hash_2) self._progress_time() self._assert_block_sent(block_hash_3)
def test_bdn_stats_block_new_from_bdn(self): block_msg = mock_eth_messages.new_block_eth_protocol_message(21, 1017) internal_new_block_msg = InternalEthBlockInfo.from_new_block_msg( block_msg) msg_bytes, block_info = self.node.message_converter.block_to_bx_block( internal_new_block_msg, self.node._tx_service, True, self.node.network.min_tx_age_seconds) msg_hash = Sha256Hash(crypto.double_sha256(msg_bytes)) broadcast_msg = BroadcastMessage(message_hash=msg_hash, network_num=1, is_encrypted=False, blob=msg_bytes) self.relay_connection.msg_broadcast(broadcast_msg) self.assertEqual( 3, len(gateway_bdn_performance_stats_service.interval_data. blockchain_node_to_bdn_stats)) for stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.values( ): self.assertEqual(1, stats.new_blocks_received_from_bdn)
def setUp(self) -> None: self.node = MockGatewayNode( gateway_helpers.get_gateway_opts(8000, max_block_interval_s=0)) self.node.broadcast = MagicMock() self.block_queuing_service = EthBlockQueuingService(self.node) self.node.block_queuing_service = self.block_queuing_service self.block_hashes = [] self.block_messages = [] self.block_headers = [] self.block_bodies = [] # block numbers: 1000-1019 prev_block_hash = None for i in range(20): block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( i, i + 1000, prev_block_hash=prev_block_hash)) block_hash = block_message.block_hash() self.block_hashes.append(block_hash) self.block_messages.append(block_message) block_parts = block_message.to_new_block_parts() self.block_headers.append( BlockHeadersEthProtocolMessage.from_header_bytes( block_parts.block_header_bytes).get_block_headers()[0]) self.block_bodies.append( BlockBodiesEthProtocolMessage.from_body_bytes( block_parts.block_body_bytes).get_blocks()[0]) self.block_queuing_service.push(block_hash, block_message) prev_block_hash = block_hash self.block_processing_service = EthBlockProcessingService(self.node) self.node.broadcast.reset_mock()
def test_msg_get_block_headers_known_many(self): self.node.opts.max_block_interval_s = 0 block_hashes = [] block_hash = None for i in range(20): block_message = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( i, i + 1000, block_hash ) ) block_hash = block_message.block_hash() block_hashes.append(block_hash) self.node.block_queuing_service_manager.push(block_hash, block_message) self.block_queuing_service.best_sent_block = (1019, block_hashes[-1], 0) self.enqueued_messages.clear() self.sut.msg_proxy_request = MagicMock() message = GetBlockHeadersEthProtocolMessage( None, block_hashes[10].binary, 10, 0, 0 ) self.sut.msg_get_block_headers(message) self.sut.msg_proxy_request.assert_not_called() self.assertEqual(1, len(self.enqueued_messages)) headers_sent = self.enqueued_messages[0] self.assertIsInstance(headers_sent, BlockHeadersEthProtocolMessage) self.assertEqual(10, len(headers_sent.get_block_headers())) for i in range(10): self.assertEqual( block_hashes[i + 10], headers_sent.get_block_headers()[i].hash_object(), )
def test_handle_single_block_fork_already_accepted(self): block_1 = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message(1, 1)) block_hash_1 = block_1.block_hash() block_2a = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 2, 2, block_hash_1)) block_hash_2a = block_2a.block_hash() block_2b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 3, 2, block_hash_1)) block_hash_2b = block_2b.block_hash() block_3b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 4, 3, block_hash_2b)) block_hash_3b = block_3b.block_hash() block_4b = InternalEthBlockInfo.from_new_block_msg( mock_eth_messages.new_block_eth_protocol_message( 5, 4, block_hash_3b)) block_hash_4b = block_4b.block_hash() # accept block 1 self.block_queuing_service.push(block_hash_1, block_1) self._assert_block_sent(block_hash_1) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_1, block_1) # accept block 2a self.block_queuing_service.push(block_hash_2a, block_2a) self._assert_block_sent(block_hash_2a) self.block_queuing_service.mark_block_seen_by_blockchain_node( block_hash_2a, block_2a) # block 2b will never be sent self.block_queuing_service.push(block_hash_2b, block_2b) self._assert_no_blocks_sent() # block 3b will be sent self.block_queuing_service.push(block_hash_3b, block_3b) self._assert_block_sent(block_hash_3b) # sync triggered, requesting 2a by hash self.block_processing_service.try_process_get_block_headers_request( GetBlockHeadersEthProtocolMessage(None, block_hash_2a.binary, 1, 0, 0)) # response is empty self._assert_headers_sent([]) # request block 1 to establish common ancestor block_number_bytes = struct.pack(">I", 1) self.block_processing_service.try_process_get_block_headers_request( GetBlockHeadersEthProtocolMessage(None, block_number_bytes, 1, 0, 0)) self._assert_headers_sent([block_hash_1]) # request block 193, 193 + 191, etc to determine chain state block_number_bytes = struct.pack(">I", 193) self.block_processing_service.try_process_get_block_headers_request( GetBlockHeadersEthProtocolMessage(None, block_number_bytes, 128, 191, 0)) self._assert_headers_sent([]) # request block 2, 3, 4, ... to compare state block_number_bytes = struct.pack(">I", 2) self.block_processing_service.try_process_get_block_headers_request( GetBlockHeadersEthProtocolMessage(None, block_number_bytes, 192, 0, 0)) self._assert_headers_sent([block_hash_2b, block_hash_3b]) # request block 4, 5, 6, ... to compare state block_number_bytes = struct.pack(">I", 4) self.block_processing_service.try_process_get_block_headers_request( GetBlockHeadersEthProtocolMessage(None, block_number_bytes, 192, 0, 0)) self._assert_headers_sent([]) # 4b is sent after timeout (presumably Ethereum node didn't send back acceptance # because it's resolving chainstate) self.block_queuing_service.push(block_hash_4b, block_4b) self._assert_no_blocks_sent() self._progress_time() self._assert_block_sent(block_hash_4b)