def setUp(self):
        self.node = MockGatewayNode(
            gateway_helpers.get_gateway_opts(8000,
                                             include_default_btc_args=True,
                                             include_default_eth_args=True))

        self.relay_connection = AbstractRelayConnection(
            MockSocketConnection(node=self.node,
                                 ip_address="127.0.0.1",
                                 port=12345), self.node)
        self.blockchain_connection = EthBaseConnection(
            MockSocketConnection(node=self.node,
                                 ip_address="127.0.0.1",
                                 port=12345), self.node)
        self.node.message_converter = converter_factory.create_eth_message_converter(
            self.node.opts)

        dummy_private_key = crypto_utils.make_private_key(
            helpers.generate_bytearray(111))
        dummy_public_key = crypto_utils.private_to_public_key(
            dummy_private_key)
        self.blockchain_connection_protocol = EthNodeConnectionProtocol(
            self.blockchain_connection, True, dummy_private_key,
            dummy_public_key)
        self.blockchain_connection.network_num = 0
        self.blockchain_connection_protocol.publish_transaction = MagicMock()

        self.relay_connection.state = ConnectionState.INITIALIZED
        gateway_transaction_stats_service.set_node(self.node)
예제 #2
0
    def setUp(self):
        self.node = MockGatewayNode(
            gateway_helpers.get_gateway_opts(
                8000,
                include_default_btc_args=True,
                include_default_eth_args=True,
                split_relays=True,
            )
        )
        self.connection = AbstractRelayConnection(
            MockSocketConnection(node=self.node, ip_address=constants.LOCALHOST, port=12345), self.node
        )
        self.connection.state = ConnectionState.INITIALIZED

        self.blockchain_connecton = AbstractGatewayBlockchainConnection(
            MockSocketConnection(node=self.node, ip_address=constants.LOCALHOST, port=333), self.node)
        self.blockchain_connecton.state = ConnectionState.ESTABLISHED
예제 #3
0
    def _handle_decrypted_block(
            self,
            bx_block: memoryview,
            connection: AbstractRelayConnection,
            encrypted_block_hash_hex: Optional[str] = None,
            recovered: bool = False,
            recovered_txs_source: Optional[RecoveredTxsSource] = None) -> None:
        transaction_service = self._node.get_tx_service()
        message_converter = self._node.message_converter
        assert message_converter is not None

        valid_block = self._validate_compressed_block_header(bx_block)
        if not valid_block.is_valid:
            reason = valid_block.reason
            assert reason is not None
            block_stats.add_block_event_by_block_hash(
                valid_block.block_hash,
                BlockStatEventType.BLOCK_DECOMPRESSED_FAILED_VALIDATION,
                connection.network_num,
                more_info=reason)
            return

        # TODO: determine if a real block or test block. Discard if test block.
        if self._node.remote_node_conn or self._node.has_active_blockchain_peer(
        ):
            try:
                (block_message, block_info, unknown_sids,
                 unknown_hashes) = message_converter.bx_block_to_block(
                     bx_block, transaction_service)
                block_content_debug_utils.log_compressed_block_debug_info(
                    transaction_service, bx_block)
            except MessageConversionError as e:
                block_stats.add_block_event_by_block_hash(
                    e.msg_hash,
                    BlockStatEventType.BLOCK_CONVERSION_FAILED,
                    network_num=connection.network_num,
                    conversion_type=e.conversion_type.value)
                transaction_service.on_block_cleaned_up(e.msg_hash)
                connection.log_warning(log_messages.FAILED_TO_DECOMPRESS_BLOCK,
                                       e.msg_hash, e)
                return
        else:
            connection.log_warning(log_messages.LACK_BLOCKCHAIN_CONNECTION)
            return

        block_hash = block_info.block_hash
        all_sids = block_info.short_ids

        if encrypted_block_hash_hex is not None:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_TO_ENC_BLOCK_MATCH,
                matching_block_hash=encrypted_block_hash_hex,
                matching_block_type=StatBlockType.ENCRYPTED.value,
                network_num=connection.network_num)

        self.cancel_hold_timeout(block_hash, connection)

        if recovered:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_COMPLETED,
                network_num=connection.network_num,
                more_info=str(recovered_txs_source))

        if block_hash in self._node.blocks_seen.contents:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_DECOMPRESSED_IGNORE_SEEN,
                start_date_time=block_info.start_datetime,
                end_date_time=block_info.end_datetime,
                network_num=connection.network_num,
                prev_block_hash=block_info.prev_block_hash,
                original_size=block_info.original_size,
                compressed_size=block_info.compressed_size,
                txs_count=block_info.txn_count,
                blockchain_network=self._node.opts.blockchain_network,
                blockchain_protocol=self._node.opts.blockchain_protocol,
                matching_block_hash=block_info.compressed_block_hash,
                matching_block_type=StatBlockType.COMPRESSED.value,
                more_info=stats_format.duration(block_info.duration_ms))
            self._node.track_block_from_bdn_handling_ended(block_hash)
            transaction_service.track_seen_short_ids(block_hash, all_sids)
            connection.log_info("Discarding duplicate block {} from the BDN.",
                                block_hash)
            if block_message is not None:
                self._node.on_block_received_from_bdn(block_hash,
                                                      block_message)
                if self._node.block_queuing_service_manager.get_block_data(
                        block_hash) is None:
                    self._node.block_queuing_service_manager.store_block_data(
                        block_hash, block_message)
            return

        if not recovered:
            connection.log_info("Received block {} from the BDN.", block_hash)
        else:
            connection.log_info("Successfully recovered block {}.", block_hash)

        if block_message is not None:
            compression_rate = block_info.compression_rate
            assert compression_rate is not None
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_DECOMPRESSED_SUCCESS,
                start_date_time=block_info.start_datetime,
                end_date_time=block_info.end_datetime,
                network_num=connection.network_num,
                prev_block_hash=block_info.prev_block_hash,
                original_size=block_info.original_size,
                compressed_size=block_info.compressed_size,
                txs_count=block_info.txn_count,
                blockchain_network=self._node.opts.blockchain_network,
                blockchain_protocol=self._node.opts.blockchain_protocol,
                matching_block_hash=block_info.compressed_block_hash,
                matching_block_type=StatBlockType.COMPRESSED.value,
                more_info="Compression rate {}, Decompression time {}, "
                "Queued behind {} blocks".format(
                    stats_format.percentage(compression_rate),
                    stats_format.duration(block_info.duration_ms),
                    self._node.block_queuing_service_manager.
                    get_length_of_each_queuing_service_stats_format()))

            self._on_block_decompressed(block_message)
            if recovered or self._node.block_queuing_service_manager.is_in_any_queuing_service(
                    block_hash):
                self._node.block_queuing_service_manager.update_recovered_block(
                    block_hash, block_message)
            else:
                self._node.block_queuing_service_manager.push(
                    block_hash, block_message)

            gateway_bdn_performance_stats_service.log_block_from_bdn()

            self._node.on_block_received_from_bdn(block_hash, block_message)
            transaction_service.track_seen_short_ids(block_hash, all_sids)

            self._node.publish_block(None, block_hash, block_message,
                                     FeedSource.BDN_SOCKET)
            self._node.log_blocks_network_content(self._node.network_num,
                                                  block_message)
        else:
            if self._node.block_queuing_service_manager.is_in_any_queuing_service(
                    block_hash) and not recovered:
                connection.log_trace(
                    "Handling already queued block again. Ignoring.")
                return

            self._node.block_recovery_service.add_block(
                bx_block, block_hash, unknown_sids, unknown_hashes)
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_DECOMPRESSED_WITH_UNKNOWN_TXS,
                start_date_time=block_info.start_datetime,
                end_date_time=block_info.end_datetime,
                network_num=connection.network_num,
                prev_block_hash=block_info.prev_block_hash,
                original_size=block_info.original_size,
                compressed_size=block_info.compressed_size,
                txs_count=block_info.txn_count,
                blockchain_network=self._node.opts.blockchain_network,
                blockchain_protocol=self._node.opts.blockchain_protocol,
                matching_block_hash=block_info.compressed_block_hash,
                matching_block_type=StatBlockType.COMPRESSED.value,
                more_info="{} sids, {} hashes, [{},...]".format(
                    len(unknown_sids), len(unknown_hashes), unknown_sids[:5]))

            connection.log_info(
                "Block {} requires short id recovery. Querying BDN...",
                block_hash)

            self.start_transaction_recovery(unknown_sids, unknown_hashes,
                                            block_hash, connection)
            if recovered:
                # should never happen –– this should not be called on blocks that have not recovered
                connection.log_error(log_messages.BLOCK_DECOMPRESSION_FAILURE,
                                     block_hash)
            else:
                self._node.block_queuing_service_manager.push(
                    block_hash, waiting_for_recovery=True)
예제 #4
0
    def process_block_key(self, msg,
                          connection: AbstractRelayConnection) -> None:
        """
        Handles key message receive from bloXroute.
        Looks for the encrypted block and decrypts; otherwise stores for later.
        """
        key = msg.key()
        block_hash = msg.block_hash()

        if not self._node.should_process_block_hash(block_hash):
            return

        block_stats.add_block_event_by_block_hash(
            block_hash,
            BlockStatEventType.ENC_BLOCK_KEY_RECEIVED_BY_GATEWAY_FROM_NETWORK,
            network_num=connection.network_num,
            peers=[connection])

        if self._node.in_progress_blocks.has_encryption_key_for_hash(
                block_hash):
            return

        if self._node.in_progress_blocks.has_ciphertext_for_hash(block_hash):
            connection.log_trace(
                "Cipher text found. Decrypting and sending to node.")
            decrypt_start_timestamp = time.time()
            decrypt_start_datetime = datetime.datetime.utcnow()
            block = self._node.in_progress_blocks.decrypt_and_get_payload(
                block_hash, key)

            if block is not None:
                block_stats.add_block_event_by_block_hash(
                    block_hash,
                    BlockStatEventType.ENC_BLOCK_DECRYPTED_SUCCESS,
                    start_date_time=decrypt_start_datetime,
                    end_date_time=datetime.datetime.utcnow(),
                    network_num=connection.network_num,
                    more_info=stats_format.timespan(decrypt_start_timestamp,
                                                    time.time()))
                self._handle_decrypted_block(
                    block,
                    connection,
                    encrypted_block_hash_hex=convert.bytes_to_hex(
                        block_hash.binary))
            else:
                block_stats.add_block_event_by_block_hash(
                    block_hash,
                    BlockStatEventType.ENC_BLOCK_DECRYPTION_ERROR,
                    network_num=connection.network_num)
        else:
            connection.log_trace(
                "No cipher text found on key message. Storing.")
            self._node.in_progress_blocks.add_key(block_hash, key)

        conns = self._node.broadcast(
            msg, connection, connection_types=(ConnectionType.GATEWAY, ))
        if len(conns) > 0:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.ENC_BLOCK_KEY_SENT_BY_GATEWAY_TO_PEERS,
                network_num=self._node.network_num,
                peers=conns)
예제 #5
0
    def process_block_broadcast(self, msg,
                                connection: AbstractRelayConnection) -> None:
        """
        Handle broadcast message receive from bloXroute.
        This is typically an encrypted block.
        """

        # TODO handle the situation where txs that received from relays while syncing are in the blocks that were
        #  ignored while syncing, so these txs won't be cleaned for 3 days
        if not self._node.should_process_block_hash(msg.block_hash()):
            return

        block_stats.add_block_event(
            msg,
            BlockStatEventType.ENC_BLOCK_RECEIVED_BY_GATEWAY_FROM_NETWORK,
            network_num=connection.network_num,
            more_info=stats_format.connection(connection))

        block_hash = msg.block_hash()
        is_encrypted = msg.is_encrypted()
        self._node.track_block_from_bdn_handling_started(
            block_hash, connection.peer_desc)

        if not is_encrypted:
            block = msg.blob()
            self._handle_decrypted_block(block, connection)
            return

        cipherblob = msg.blob()
        expected_hash = Sha256Hash(crypto.double_sha256(cipherblob))
        if block_hash != expected_hash:
            connection.log_warning(log_messages.BLOCK_WITH_INCONSISTENT_HASHES,
                                   expected_hash, block_hash)
            return

        if self._node.in_progress_blocks.has_encryption_key_for_hash(
                block_hash):
            connection.log_trace(
                "Already had key for received block. Sending block to node.")
            decrypt_start_timestamp = time.time()
            decrypt_start_datetime = datetime.datetime.utcnow()
            block = self._node.in_progress_blocks.decrypt_ciphertext(
                block_hash, cipherblob)

            if block is not None:
                block_stats.add_block_event(
                    msg,
                    BlockStatEventType.ENC_BLOCK_DECRYPTED_SUCCESS,
                    start_date_time=decrypt_start_datetime,
                    end_date_time=datetime.datetime.utcnow(),
                    network_num=connection.network_num,
                    more_info=stats_format.timespan(decrypt_start_timestamp,
                                                    time.time()))
                self._handle_decrypted_block(
                    block,
                    connection,
                    encrypted_block_hash_hex=convert.bytes_to_hex(
                        block_hash.binary))
            else:
                block_stats.add_block_event(
                    msg,
                    BlockStatEventType.ENC_BLOCK_DECRYPTION_ERROR,
                    network_num=connection.network_num)
        else:
            connection.log_trace("Received encrypted block. Storing.")
            self._node.in_progress_blocks.add_ciphertext(
                block_hash, cipherblob)
            block_received_message = BlockReceivedMessage(block_hash)
            conns = self._node.broadcast(
                block_received_message,
                connection,
                connection_types=(ConnectionType.GATEWAY, ))
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.ENC_BLOCK_SENT_BLOCK_RECEIPT,
                network_num=connection.network_num,
                peers=conns,
            )
class AbstractRelayConnectionTest(AbstractTestCase):
    def setUp(self):
        self.node = MockGatewayNode(
            gateway_helpers.get_gateway_opts(8000,
                                             include_default_btc_args=True,
                                             include_default_eth_args=True))

        self.connection = AbstractRelayConnection(
            MockSocketConnection(node=self.node,
                                 ip_address="127.0.0.1",
                                 port=12345), self.node)
        self.connection.state = ConnectionState.INITIALIZED

        self.blockchain_connecton = AbstractGatewayBlockchainConnection(
            MockSocketConnection(node=self.node,
                                 ip_address="127.0.0.1",
                                 port=333), self.node)
        self.blockchain_connecton.state = ConnectionState.ESTABLISHED

        self.node.node_conn = self.blockchain_connecton

    @patch(
        "bxgateway.services.block_processing_service.BlockProcessingService._handle_decrypted_block"
    )
    def test_msg_broadcast_encrypted(self, mock_handle_decrypted_block):
        msg_bytes = helpers.generate_bytearray(50)
        msg_hash = Sha256Hash(crypto.double_sha256(msg_bytes))

        broadcast_msg = BroadcastMessage(message_hash=msg_hash,
                                         network_num=1,
                                         is_encrypted=True,
                                         blob=msg_bytes)

        self.connection.msg_broadcast(broadcast_msg)

        self.assertTrue(
            self.node.in_progress_blocks.has_ciphertext_for_hash(msg_hash))
        self.assertEqual(1, len(self.node.broadcast_messages))
        self.assertIsInstance(self.node.broadcast_messages[0][0],
                              BlockReceivedMessage)
        mock_handle_decrypted_block.assert_not_called()

    @patch(
        "bxgateway.services.block_processing_service.BlockProcessingService._handle_decrypted_block"
    )
    def test_msg_broadcast_unencrypted(self, mock_handle_decrypted_block):
        msg_bytes = helpers.generate_bytearray(50)
        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.connection.msg_broadcast(broadcast_msg)

        self.assertFalse(
            self.node.in_progress_blocks.has_ciphertext_for_hash(msg_hash))
        self.assertEqual(0, len(self.node.broadcast_messages))

        mock_handle_decrypted_block.assert_called_once()

    def test_msg_tx__full_message(self):
        tx_service = self.connection.node.get_tx_service()
        short_id = 1
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)

        self.connection.msg_tx(full_message)

        self.assertEqual(short_id, tx_service.get_short_id(tx_hash))
        self.assertEqual(tx_content,
                         tx_service.get_transaction_by_hash(tx_hash))

    def test_msg_tx__compact_then_full(self):
        tx_service = self.connection.node.get_tx_service()
        short_id = 1
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        compact_tx_msg = TxMessage(message_hash=tx_hash,
                                   network_num=1,
                                   short_id=short_id)
        no_short_id_tx_msg = TxMessage(message_hash=tx_hash,
                                       network_num=1,
                                       tx_val=tx_content)

        self.connection.msg_tx(compact_tx_msg)
        self.connection.msg_tx(no_short_id_tx_msg)

        self.assertEqual(short_id, tx_service.get_short_id(tx_hash))
        self.assertEqual(tx_content,
                         tx_service.get_transaction_by_hash(tx_hash))

    def test_ping_pong(self):
        hello_msg = HelloMessage(
            protocol_version=protocol_version.PROTOCOL_VERSION, network_num=1)
        self.connection.add_received_bytes(hello_msg.rawbytes())
        self.connection.process_message()

        hello_msg_bytes = self.connection.get_bytes_to_send()
        self.assertTrue(len(hello_msg_bytes) > 0)
        self.connection.advance_sent_bytes(len(hello_msg_bytes))

        ack_msg = AckMessage()
        self.connection.add_received_bytes(ack_msg.rawbytes())
        self.connection.process_message()

        ack_msg_bytes = self.connection.get_bytes_to_send()
        self.assertTrue(len(ack_msg_bytes) > 0)
        self.connection.advance_sent_bytes(len(ack_msg_bytes))

        ping_msg = PingMessage(nonce=12345)
        self.connection.add_received_bytes(ping_msg.rawbytes())
        self.connection.process_message()

        pong_msg_bytes = self.connection.get_bytes_to_send()
        self.assertTrue(len(pong_msg_bytes) > 0)

        msg_type, payload_len = AbstractBloxrouteMessage.unpack(
            pong_msg_bytes[:AbstractBloxrouteMessage.HEADER_LENGTH])
        self.assertEqual(BloxrouteMessageType.PONG, msg_type)
        self.connection.advance_sent_bytes(len(pong_msg_bytes))

        time.time = MagicMock(return_value=time.time() +
                              constants.PING_INTERVAL_S)
        self.node.alarm_queue.fire_alarms()

        ping_msg_bytes = self.connection.get_bytes_to_send()
        self.assertTrue(len(ping_msg_bytes) > 0)
        msg_type, payload_len = AbstractBloxrouteMessage.unpack(
            ping_msg_bytes[:AbstractBloxrouteMessage.HEADER_LENGTH])
        self.assertEqual(BloxrouteMessageType.PING, msg_type)

    @patch(
        "bxgateway.connections.abstract_relay_connection.AbstractRelayConnection.log"
    )
    def test_msg_notification(self, log):
        args_list = [
            "10",
            str(QuotaType.FREE_DAILY_QUOTA.value),
            str(EntityType.TRANSACTION.value), "100"
        ]
        args = ",".join(args_list)
        notification_msg = NotificationMessage(
            NotificationCode.QUOTA_FILL_STATUS, args)
        log.assert_not_called()
        self.connection.msg_notify(notification_msg)
        log.assert_called_once()
    def _handle_decrypted_consensus_block(
        self,
        bx_block: memoryview,
        connection: AbstractRelayConnection,
        encrypted_block_hash_hex: Optional[str] = None,
        recovered: bool = False,
        recovered_txs_source: Optional[RecoveredTxsSource] = None
    ):
        transaction_service = self._node.get_tx_service()

        if self._node.has_active_blockchain_peer() or self._node.remote_node_conn:
            try:
                block_message, block_info, unknown_sids, unknown_hashes = \
                    self._node.consensus_message_converter.bx_block_to_block(bx_block, transaction_service)
            except MessageConversionError as e:
                block_stats.add_block_event_by_block_hash(
                    e.msg_hash,
                    BlockStatEventType.BLOCK_CONVERSION_FAILED,
                    network_num=connection.network_num,
                    broadcast_type=BroadcastMessageType.CONSENSUS,
                    conversion_type=e.conversion_type.value
                )
                transaction_service.on_block_cleaned_up(e.msg_hash)
                connection.log_warning(log_messages.FAILED_TO_DECOMPRESS_BLOCK_ONT_CONSENSUS, e.msg_hash, e)
                return
        else:
            connection.log_warning(log_messages.LACK_BLOCKCHAIN_CONNECTION_ONT_CONSENSUS)
            return

        block_hash = block_info.block_hash
        all_sids = block_info.short_ids

        if encrypted_block_hash_hex is not None:
            block_stats.add_block_event_by_block_hash(block_hash,
                                                      BlockStatEventType.BLOCK_TO_ENC_BLOCK_MATCH,
                                                      matching_block_hash=encrypted_block_hash_hex,
                                                      matching_block_type=StatBlockType.ENCRYPTED.value,
                                                      network_num=connection.network_num,
                                                      broadcast_type=BroadcastMessageType.CONSENSUS)

        self.cancel_hold_timeout(block_hash, connection)

        if recovered:
            block_stats.add_block_event_by_block_hash(block_hash,
                                                      BlockStatEventType.BLOCK_RECOVERY_COMPLETED,
                                                      network_num=connection.network_num,
                                                      broadcast_type=BroadcastMessageType.CONSENSUS,
                                                      more_info=str(recovered_txs_source))

        if block_hash in self._node.blocks_seen.contents:
            block_stats.add_block_event_by_block_hash(block_hash, BlockStatEventType.BLOCK_DECOMPRESSED_IGNORE_SEEN,
                                                      start_date_time=block_info.start_datetime,
                                                      end_date_time=block_info.end_datetime,
                                                      network_num=connection.network_num,
                                                      broadcast_type=BroadcastMessageType.CONSENSUS,
                                                      prev_block_hash=block_info.prev_block_hash,
                                                      original_size=block_info.original_size,
                                                      compressed_size=block_info.compressed_size,
                                                      txs_count=block_info.txn_count,
                                                      blockchain_network=self._node.opts.blockchain_protocol,
                                                      blockchain_protocol=self._node.opts.blockchain_network,
                                                      matching_block_hash=block_info.compressed_block_hash,
                                                      matching_block_type=StatBlockType.COMPRESSED.value,
                                                      more_info=stats_format.duration(block_info.duration_ms))
            self._node.track_block_from_bdn_handling_ended(block_hash)
            transaction_service.track_seen_short_ids(block_hash, all_sids)
            connection.log_info(
                "Discarding duplicate consensus block {} from the BDN.",
                block_hash
            )
            return

        if not recovered:
            connection.log_info("Received consensus block {} from the BDN.", block_hash)
        else:
            connection.log_info("Successfully recovered consensus block {}.", block_hash)

        if block_message is not None:
            block_stats.add_block_event_by_block_hash(block_hash, BlockStatEventType.BLOCK_DECOMPRESSED_SUCCESS,
                                                      start_date_time=block_info.start_datetime,
                                                      end_date_time=block_info.end_datetime,
                                                      network_num=connection.network_num,
                                                      broadcast_type=BroadcastMessageType.CONSENSUS,
                                                      prev_block_hash=block_info.prev_block_hash,
                                                      original_size=block_info.original_size,
                                                      compressed_size=block_info.compressed_size,
                                                      txs_count=block_info.txn_count,
                                                      blockchain_network=self._node.opts.blockchain_protocol,
                                                      blockchain_protocol=self._node.opts.blockchain_network,
                                                      matching_block_hash=block_info.compressed_block_hash,
                                                      matching_block_type=StatBlockType.COMPRESSED.value,
                                                      peer=connection.peer_desc,
                                                      more_info="Consensus compression rate {}, Decompression time {}, "
                                                                "Queued behind {} blocks".format(
                                                          stats_format.percentage(block_info.compression_rate),
                                                          stats_format.duration(block_info.duration_ms),
                                                          self._node.block_queueing_service_manager.get_length_of_each_queuing_service_stats_format()))

            if recovered or block_hash in self._node.block_queueing_service_manager:
                self._node.block_queueing_service_manager.update_recovered_block(block_hash, block_message)
            else:
                self._node.block_queueing_service_manager.push(block_hash, block_message)

            self._node.block_recovery_service.cancel_recovery_for_block(block_hash)
            # self._node.blocks_seen.add(block_hash)
            transaction_service.track_seen_short_ids(block_hash, all_sids)
        else:
            if block_hash in self._node.block_queueing_service_manager and not recovered:
                connection.log_trace("Handling already queued consensus block again. Ignoring.")
                return

            self._node.block_recovery_service.add_block(bx_block, block_hash, unknown_sids, unknown_hashes)
            block_stats.add_block_event_by_block_hash(block_hash,
                                                      BlockStatEventType.BLOCK_DECOMPRESSED_WITH_UNKNOWN_TXS,
                                                      start_date_time=block_info.start_datetime,
                                                      end_date_time=block_info.end_datetime,
                                                      network_num=connection.network_num,
                                                      broadcast_type=BroadcastMessageType.CONSENSUS,
                                                      prev_block_hash=block_info.prev_block_hash,
                                                      original_size=block_info.original_size,
                                                      compressed_size=block_info.compressed_size,
                                                      txs_count=block_info.txn_count,
                                                      blockchain_network=self._node.opts.blockchain_protocol,
                                                      blockchain_protocol=self._node.opts.blockchain_network,
                                                      matching_block_hash=block_info.compressed_block_hash,
                                                      matching_block_type=StatBlockType.COMPRESSED.value,
                                                      more_info="{} sids, {} hashes".format(
                                                          len(unknown_sids), len(unknown_hashes)))

            connection.log_info("Consensus block {} requires short id recovery. Querying BDN...", block_hash)

            self.start_transaction_recovery(unknown_sids, unknown_hashes, block_hash, connection)
            if recovered:
                # should never happen –– this should not be called on blocks that have not recovered
                connection.log_error(log_messages.BLOCK_DECOMPRESSION_FAILURE_ONT_CONSENSUS,
                                     block_hash)
            else:
                self._node.block_queueing_service_manager.push(block_hash, waiting_for_recovery=True)
class GatewayTransactionStatsServiceTest(AbstractTestCase):
    def setUp(self):
        self.node = MockGatewayNode(
            gateway_helpers.get_gateway_opts(8000,
                                             include_default_btc_args=True,
                                             include_default_eth_args=True))

        self.relay_connection = AbstractRelayConnection(
            MockSocketConnection(node=self.node,
                                 ip_address="127.0.0.1",
                                 port=12345), self.node)
        self.blockchain_connection = EthBaseConnection(
            MockSocketConnection(node=self.node,
                                 ip_address="127.0.0.1",
                                 port=12345), self.node)
        self.node.message_converter = converter_factory.create_eth_message_converter(
            self.node.opts)

        dummy_private_key = crypto_utils.make_private_key(
            helpers.generate_bytearray(111))
        dummy_public_key = crypto_utils.private_to_public_key(
            dummy_private_key)
        self.blockchain_connection_protocol = EthNodeConnectionProtocol(
            self.blockchain_connection, True, dummy_private_key,
            dummy_public_key)
        self.blockchain_connection.network_num = 0
        self.blockchain_connection_protocol.publish_transaction = MagicMock()

        self.relay_connection.state = ConnectionState.INITIALIZED
        gateway_transaction_stats_service.set_node(self.node)

    def test_tx_stats_new_full_from_relay(self):
        short_id = 123
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)

        self.relay_connection.msg_tx(full_message)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)

    def test_tx_stats_new_compact_from_relay(self):
        short_id = 321
        tx_hash = helpers.generate_object_hash()

        compact_message = TxMessage(message_hash=tx_hash,
                                    network_num=1,
                                    short_id=short_id)

        self.relay_connection.msg_tx(compact_message)

        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)

    def test_tx_stats_duplicate_full_from_relay(self):
        short_id = 1
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)

        self.relay_connection.msg_tx(full_message)
        self.relay_connection.msg_tx(full_message)

        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            duplicate_full_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            redundant_transaction_content_messages)

    def test_tx_stats_duplicate_compact_from_relay(self):
        short_id = 1
        tx_hash = helpers.generate_object_hash()

        compact_message = TxMessage(message_hash=tx_hash,
                                    network_num=1,
                                    short_id=short_id)

        self.relay_connection.msg_tx(compact_message)
        self.relay_connection.msg_tx(compact_message)

        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_full_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            duplicate_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            redundant_transaction_content_messages)

    def test_tx_stats_duplicate_compact_after_full_from_relay(self):
        short_id = 1
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)
        compact_message = TxMessage(message_hash=tx_hash,
                                    network_num=1,
                                    short_id=short_id)

        self.relay_connection.msg_tx(full_message)
        self.relay_connection.msg_tx(compact_message)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            duplicate_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            redundant_transaction_content_messages)

    def test_tx_stats_new_full_after_new_compact_from_relay(self):
        short_id = 1
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        compact_message = TxMessage(message_hash=tx_hash,
                                    network_num=1,
                                    short_id=short_id)
        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)

        self.relay_connection.msg_tx(compact_message)
        self.relay_connection.msg_tx(full_message)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_full_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            redundant_transaction_content_messages)

    def test_tx_stats_redundant_tx_contents_from_relay(self):
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        short_id = 1
        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)
        self.relay_connection.msg_tx(full_message)

        short_id = 2
        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)
        self.relay_connection.msg_tx(full_message)

        self.assertEqual(
            2, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_compact_transactions_received_from_relays)
        self.assertEqual(
            2, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            redundant_transaction_content_messages)

    def test_tx_stats_multiple_new_compact_from_relay(self):
        tx_hash = helpers.generate_object_hash()

        short_id = 1
        compact_message = TxMessage(message_hash=tx_hash,
                                    network_num=1,
                                    short_id=short_id)
        self.relay_connection.msg_tx(compact_message)

        short_id = 2
        compact_message = TxMessage(message_hash=tx_hash,
                                    network_num=1,
                                    short_id=short_id)
        self.relay_connection.msg_tx(compact_message)

        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_full_transactions_received_from_relays)
        self.assertEqual(
            2, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_compact_transactions_received_from_relays)
        self.assertEqual(
            2, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            redundant_transaction_content_messages)

    def test_tx_stats_new_from_blockchain_node(self):
        txs = [
            mock_eth_messages.get_dummy_transaction(1),
        ]
        tx_msg = TransactionsEthProtocolMessage(None, txs)

        self.blockchain_connection_protocol.msg_tx(tx_msg)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_transactions_received_from_blockchain)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_transactions_received_from_blockchain)

    def test_tx_stats_duplicate_from_blockchain_node(self):
        txs = [
            mock_eth_messages.get_dummy_transaction(7),
        ]
        tx_msg = TransactionsEthProtocolMessage(None, txs)

        self.blockchain_connection_protocol.msg_tx(tx_msg)
        self.blockchain_connection_protocol.msg_tx(tx_msg)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_transactions_received_from_blockchain)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            duplicate_transactions_received_from_blockchain)

    def test_tx_stats_new_full_relay_duplicate_blockchain_node(self):
        blockchain_node_txs = [
            mock_eth_messages.get_dummy_transaction(7),
        ]
        blockchain_node_tx_msg = TransactionsEthProtocolMessage(
            None, blockchain_node_txs)
        bx_tx_message = self.node.message_converter.tx_to_bx_txs(
            blockchain_node_tx_msg, 1)
        for (msg, tx_hash, tx_bytes) in bx_tx_message:
            relay_full_message = TxMessage(message_hash=tx_hash,
                                           network_num=1,
                                           short_id=1,
                                           tx_val=tx_bytes)
            self.relay_connection.msg_tx(relay_full_message)

        self.blockchain_connection_protocol.msg_tx(blockchain_node_tx_msg)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            duplicate_transactions_received_from_blockchain)

    def test_tx_stats_new_compact_relay_new_blockchain_node(self):
        blockchain_node_txs = [
            mock_eth_messages.get_dummy_transaction(7),
        ]
        blockchain_node_tx_msg = TransactionsEthProtocolMessage(
            None, blockchain_node_txs)
        bx_tx_message = self.node.message_converter.tx_to_bx_txs(
            blockchain_node_tx_msg, 1)
        for (msg, tx_hash, tx_bytes) in bx_tx_message:
            relay_compact_message = TxMessage(message_hash=tx_hash,
                                              network_num=1,
                                              short_id=1)
            self.relay_connection.msg_tx(relay_compact_message)

        self.blockchain_connection_protocol.msg_tx(blockchain_node_tx_msg)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_transactions_received_from_blockchain)

    def test_tx_stats_new_blockchain_node_redundant_full_relay(self):
        blockchain_node_txs = [
            mock_eth_messages.get_dummy_transaction(7),
        ]
        blockchain_node_tx_msg = TransactionsEthProtocolMessage(
            None, blockchain_node_txs)

        self.blockchain_connection_protocol.msg_tx(blockchain_node_tx_msg)

        time.time = MagicMock(return_value=time.time() + 1)
        self.node.alarm_queue.fire_alarms()

        bx_tx_message = self.node.message_converter.tx_to_bx_txs(
            blockchain_node_tx_msg, 1)
        for (msg, tx_hash, tx_bytes) in bx_tx_message:
            relay_full_message = TxMessage(message_hash=tx_hash,
                                           network_num=1,
                                           short_id=1,
                                           tx_val=tx_bytes)
            self.relay_connection.msg_tx(relay_full_message)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_full_transactions_received_from_relays)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            duplicate_full_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_transactions_received_from_blockchain)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            redundant_transaction_content_messages)

    def test_tx_stats_new_blockchain_node_new_compact_relay(self):
        blockchain_node_txs = [
            mock_eth_messages.get_dummy_transaction(7),
        ]
        blockchain_node_tx_msg = TransactionsEthProtocolMessage(
            None, blockchain_node_txs)

        self.blockchain_connection_protocol.msg_tx(blockchain_node_tx_msg)

        bx_tx_message = self.node.message_converter.tx_to_bx_txs(
            blockchain_node_tx_msg, 1)
        for (msg, tx_hash, tx_bytes) in bx_tx_message:
            relay_compact_message = TxMessage(message_hash=tx_hash,
                                              network_num=1,
                                              short_id=1)
            self.relay_connection.msg_tx(relay_compact_message)

        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_transactions_received_from_blockchain)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            new_compact_transactions_received_from_relays)
        self.assertEqual(
            1, gateway_transaction_stats_service.interval_data.
            short_id_assignments_processed)
        self.assertEqual(
            0, gateway_transaction_stats_service.interval_data.
            redundant_transaction_content_messages)
예제 #9
0
    def setUp(self):
        self.node = MockGatewayNode(gateway_helpers.get_gateway_opts(
            8000, include_default_eth_args=True, use_extensions=True),
                                    block_queueing_cls=MagicMock())
        self.node.message_converter = converter_factory.create_eth_message_converter(
            self.node.opts)
        self.node.block_processing_service = BlockProcessingService(self.node)

        is_handshake_initiator = True
        dummy_private_key = crypto_utils.make_private_key(
            helpers.generate_bytearray(111))
        dummy_public_key = crypto_utils.private_to_public_key(
            dummy_private_key)
        rlpx_cipher = RLPxCipher(is_handshake_initiator, dummy_private_key,
                                 dummy_public_key)

        node_ssl_service = MockNodeSSLService(EthGatewayNode.NODE_TYPE,
                                              MagicMock())
        local_ip = "127.0.0.1"
        eth_port = 30303
        eth_opts = gateway_helpers.get_gateway_opts(
            1234,
            include_default_eth_args=True,
            blockchain_address=(local_ip, eth_port),
            pub_key=convert.bytes_to_hex(dummy_public_key))
        self.eth_node = EthGatewayNode(eth_opts, node_ssl_service)
        self.blockchain_connection = EthNodeConnection(
            MockSocketConnection(1,
                                 node=self.node,
                                 ip_address=local_ip,
                                 port=30303), self.node)
        self.blockchain_connection.on_connection_established()
        self.node.connection_pool.add(19, local_ip, eth_port,
                                      self.blockchain_connection)

        self.blockchain_connection_1 = EthNodeConnection(
            MockSocketConnection(1,
                                 node=self.node,
                                 ip_address=local_ip,
                                 port=333), self.node)
        self.blockchain_connection_1.on_connection_established()
        self.node.mock_add_blockchain_peer(self.blockchain_connection_1)
        self.node_1_endpoint = IpEndpoint(local_ip, 333)
        self.node.connection_pool.add(20, self.node_1_endpoint.ip_address,
                                      self.node_1_endpoint.port,
                                      self.blockchain_connection_1)

        self.blockchain_connection_2 = EthNodeConnection(
            MockSocketConnection(1,
                                 node=self.node,
                                 ip_address=local_ip,
                                 port=444), self.node)
        self.blockchain_connection_2.on_connection_established()
        self.node.mock_add_blockchain_peer(self.blockchain_connection_2)
        self.node_2_endpoint = IpEndpoint(local_ip, 444)
        self.node.connection_pool.add(21, self.node_2_endpoint.ip_address,
                                      self.node_2_endpoint.port,
                                      self.blockchain_connection_2)

        self.blockchain_connection_1.network_num = 0

        self.tx_blockchain_connection_protocol = EthNodeConnectionProtocol(
            self.blockchain_connection_1, is_handshake_initiator, rlpx_cipher)
        self.block_blockchain_connection_protocol = EthNodeConnectionProtocol(
            self.blockchain_connection_1, is_handshake_initiator, rlpx_cipher)
        self.tx_blockchain_connection_protocol.publish_transaction = MagicMock(
        )
        self.block_blockchain_connection_protocol.publish_transaction = MagicMock(
        )

        self.tx_blockchain_connection_protocol_2 = EthNodeConnectionProtocol(
            self.blockchain_connection_2, is_handshake_initiator, rlpx_cipher)
        self.block_blockchain_connection_protocol_2 = EthNodeConnectionProtocol(
            self.blockchain_connection_2, is_handshake_initiator, rlpx_cipher)
        self.tx_blockchain_connection_protocol_2.publish_transaction = MagicMock(
        )
        self.block_blockchain_connection_protocol_2.publish_transaction = MagicMock(
        )

        self.relay_connection = AbstractRelayConnection(
            MockSocketConnection(1,
                                 node=self.node,
                                 ip_address=local_ip,
                                 port=12345), self.node)
        self.relay_connection.state = ConnectionState.INITIALIZED

        gateway_bdn_performance_stats_service.set_node(self.node)
        self.node.account_id = "12345"
예제 #10
0
class GatewayBdnPerformanceStatsTest(AbstractTestCase):
    def setUp(self):
        self.node = MockGatewayNode(gateway_helpers.get_gateway_opts(
            8000, include_default_eth_args=True, use_extensions=True),
                                    block_queueing_cls=MagicMock())
        self.node.message_converter = converter_factory.create_eth_message_converter(
            self.node.opts)
        self.node.block_processing_service = BlockProcessingService(self.node)

        is_handshake_initiator = True
        dummy_private_key = crypto_utils.make_private_key(
            helpers.generate_bytearray(111))
        dummy_public_key = crypto_utils.private_to_public_key(
            dummy_private_key)
        rlpx_cipher = RLPxCipher(is_handshake_initiator, dummy_private_key,
                                 dummy_public_key)

        node_ssl_service = MockNodeSSLService(EthGatewayNode.NODE_TYPE,
                                              MagicMock())
        local_ip = "127.0.0.1"
        eth_port = 30303
        eth_opts = gateway_helpers.get_gateway_opts(
            1234,
            include_default_eth_args=True,
            blockchain_address=(local_ip, eth_port),
            pub_key=convert.bytes_to_hex(dummy_public_key))
        self.eth_node = EthGatewayNode(eth_opts, node_ssl_service)
        self.blockchain_connection = EthNodeConnection(
            MockSocketConnection(1,
                                 node=self.node,
                                 ip_address=local_ip,
                                 port=30303), self.node)
        self.blockchain_connection.on_connection_established()
        self.node.connection_pool.add(19, local_ip, eth_port,
                                      self.blockchain_connection)

        self.blockchain_connection_1 = EthNodeConnection(
            MockSocketConnection(1,
                                 node=self.node,
                                 ip_address=local_ip,
                                 port=333), self.node)
        self.blockchain_connection_1.on_connection_established()
        self.node.mock_add_blockchain_peer(self.blockchain_connection_1)
        self.node_1_endpoint = IpEndpoint(local_ip, 333)
        self.node.connection_pool.add(20, self.node_1_endpoint.ip_address,
                                      self.node_1_endpoint.port,
                                      self.blockchain_connection_1)

        self.blockchain_connection_2 = EthNodeConnection(
            MockSocketConnection(1,
                                 node=self.node,
                                 ip_address=local_ip,
                                 port=444), self.node)
        self.blockchain_connection_2.on_connection_established()
        self.node.mock_add_blockchain_peer(self.blockchain_connection_2)
        self.node_2_endpoint = IpEndpoint(local_ip, 444)
        self.node.connection_pool.add(21, self.node_2_endpoint.ip_address,
                                      self.node_2_endpoint.port,
                                      self.blockchain_connection_2)

        self.blockchain_connection_1.network_num = 0

        self.tx_blockchain_connection_protocol = EthNodeConnectionProtocol(
            self.blockchain_connection_1, is_handshake_initiator, rlpx_cipher)
        self.block_blockchain_connection_protocol = EthNodeConnectionProtocol(
            self.blockchain_connection_1, is_handshake_initiator, rlpx_cipher)
        self.tx_blockchain_connection_protocol.publish_transaction = MagicMock(
        )
        self.block_blockchain_connection_protocol.publish_transaction = MagicMock(
        )

        self.tx_blockchain_connection_protocol_2 = EthNodeConnectionProtocol(
            self.blockchain_connection_2, is_handshake_initiator, rlpx_cipher)
        self.block_blockchain_connection_protocol_2 = EthNodeConnectionProtocol(
            self.blockchain_connection_2, is_handshake_initiator, rlpx_cipher)
        self.tx_blockchain_connection_protocol_2.publish_transaction = MagicMock(
        )
        self.block_blockchain_connection_protocol_2.publish_transaction = MagicMock(
        )

        self.relay_connection = AbstractRelayConnection(
            MockSocketConnection(1,
                                 node=self.node,
                                 ip_address=local_ip,
                                 port=12345), self.node)
        self.relay_connection.state = ConnectionState.INITIALIZED

        gateway_bdn_performance_stats_service.set_node(self.node)
        self.node.account_id = "12345"

    def test_bdn_stats_tx_new_full_from_bdn(self):
        short_id = 1
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)

        self.relay_connection.msg_tx(full_message)

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

    def test_bdn_stats_tx_new_full_from_bdn_ignore_duplicate(self):
        short_id = 1
        tx_hash = helpers.generate_object_hash()
        tx_content = helpers.generate_bytearray(250)

        full_message = TxMessage(message_hash=tx_hash,
                                 network_num=1,
                                 short_id=short_id,
                                 tx_val=tx_content)

        self.relay_connection.msg_tx(full_message)
        self.relay_connection.msg_tx(full_message)

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

    def test_bdn_stats_tx_new_from_node_low_fee(self):
        self.node.opts.blockchain_networks[
            self.node.network_num].min_tx_network_fee = 500
        blockchain_network = self.tx_blockchain_connection_protocol.connection.node.get_blockchain_network(
        )
        blockchain_network.protocol = "ethereum"

        txs = [
            mock_eth_messages.get_dummy_transaction(1, gas_price=5),
        ]

        tx_msg = TransactionsEthProtocolMessage(None, txs)
        self.assertEqual(1, len(tx_msg.get_transactions()))
        self.tx_blockchain_connection_protocol.msg_tx(tx_msg)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(0, stats.new_tx_received_from_bdn)

        self.assertEqual(0, node_1_stats.new_tx_received_from_blockchain_node)
        self.assertEqual(
            1, node_1_stats.new_tx_received_from_blockchain_node_low_fee)

    def test_bdn_stats_tx_new_from_node(self):
        txs = [
            mock_eth_messages.get_dummy_transaction(1),
        ]
        tx_msg = TransactionsEthProtocolMessage(None, txs)

        self.tx_blockchain_connection_protocol.msg_tx(tx_msg)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(1, stats.new_tx_received_from_bdn)

        self.assertEqual(1, node_1_stats.new_tx_received_from_blockchain_node)

    def test_bdn_stats_tx_new_from_node_ignore_duplicate(self):
        txs = [
            mock_eth_messages.get_dummy_transaction(1),
        ]
        tx_msg = TransactionsEthProtocolMessage(None, txs)

        self.tx_blockchain_connection_protocol.msg_tx(tx_msg)
        self.tx_blockchain_connection_protocol.msg_tx(tx_msg)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(1, stats.new_tx_received_from_bdn)

        self.assertEqual(1, node_1_stats.new_tx_received_from_blockchain_node)

    def test_bdn_stats_tx_new_from_node_ignore_duplicate_from_second_node(
            self):
        txs = [
            mock_eth_messages.get_dummy_transaction(1),
        ]
        tx_msg = TransactionsEthProtocolMessage(None, txs)

        self.tx_blockchain_connection_protocol.msg_tx(tx_msg)
        self.tx_blockchain_connection_protocol_2.msg_tx(tx_msg)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(1, stats.new_tx_received_from_bdn)

        self.assertEqual(1, node_1_stats.new_tx_received_from_blockchain_node)

    def test_bdn_stats_tx_new_full_from_bdn_ignore_from_node(self):
        blockchain_node_txs = [
            mock_eth_messages.get_dummy_transaction(7),
        ]
        blockchain_node_tx_msg = TransactionsEthProtocolMessage(
            None, blockchain_node_txs)
        bx_tx_message = self.node.message_converter.tx_to_bx_txs(
            blockchain_node_tx_msg, 1)
        for (msg, tx_hash, tx_bytes) in bx_tx_message:
            relay_full_message = TxMessage(message_hash=tx_hash,
                                           network_num=1,
                                           short_id=1,
                                           tx_val=tx_bytes)
            self.relay_connection.msg_tx(relay_full_message)

        self.tx_blockchain_connection_protocol.msg_tx(blockchain_node_tx_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_tx_received_from_bdn)
            self.assertEqual(0, stats.new_tx_received_from_blockchain_node)

    def test_bdn_stats_tx_new_from_node_ignore_from_bdn(self):
        blockchain_node_txs = [
            mock_eth_messages.get_dummy_transaction(7),
        ]
        blockchain_node_tx_msg = TransactionsEthProtocolMessage(
            None, blockchain_node_txs)

        self.tx_blockchain_connection_protocol.msg_tx(blockchain_node_tx_msg)
        time.time = MagicMock(return_value=time.time() + 1)
        self.node.alarm_queue.fire_alarms()
        bx_tx_message = self.node.message_converter.tx_to_bx_txs(
            blockchain_node_tx_msg, 1)
        for (msg, tx_hash, tx_bytes) in bx_tx_message:
            relay_full_message = TxMessage(message_hash=tx_hash,
                                           network_num=1,
                                           short_id=1,
                                           tx_val=tx_bytes)
            self.relay_connection.msg_tx(relay_full_message)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(1, stats.new_tx_received_from_bdn)
            self.assertEqual(0, stats.new_tx_received_from_blockchain_node)
        self.assertEqual(1, node_1_stats.new_tx_received_from_blockchain_node)
        self.assertEqual(0, node_1_stats.new_tx_received_from_bdn)

    def test_bdn_stats_tx_ignore_new_compact_from_bdn_log_new_from_node(self):
        blockchain_node_txs = [
            mock_eth_messages.get_dummy_transaction(7),
        ]
        blockchain_node_tx_msg = TransactionsEthProtocolMessage(
            None, blockchain_node_txs)
        bx_tx_message = self.node.message_converter.tx_to_bx_txs(
            blockchain_node_tx_msg, 1)
        for (msg, tx_hash, tx_bytes) in bx_tx_message:
            relay_compact_message = TxMessage(message_hash=tx_hash,
                                              network_num=1,
                                              short_id=1)
            self.relay_connection.msg_tx(relay_compact_message)

        self.tx_blockchain_connection_protocol.msg_tx(blockchain_node_tx_msg)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(1, stats.new_tx_received_from_bdn)

        self.assertEqual(1, node_1_stats.new_tx_received_from_blockchain_node)
        self.assertEqual(0, node_1_stats.new_tx_received_from_bdn)

    def test_bdn_stats_block_new_from_node(self):
        block_msg = NewBlockEthProtocolMessage(
            None,
            _block_with_timestamp(
                time.time() + 1 -
                self.node.opts.blockchain_ignore_block_interval_count *
                self.node.opts.blockchain_block_interval), 10)
        block_msg.serialize()
        self.block_blockchain_connection_protocol.msg_block(block_msg)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(1, stats.new_blocks_received_from_bdn)

        self.assertEqual(1,
                         node_1_stats.new_blocks_received_from_blockchain_node)

    def test_bdn_stats_block_new_from_node_ignore_duplicate(self):
        block_msg = NewBlockEthProtocolMessage(
            None,
            _block_with_timestamp(
                time.time() + 1 -
                self.node.opts.blockchain_ignore_block_interval_count *
                self.node.opts.blockchain_block_interval), 10)
        block_msg.serialize()
        self.block_blockchain_connection_protocol.msg_block(block_msg)
        self.block_blockchain_connection_protocol.msg_block(block_msg)

        self.assertEqual(
            3,
            len(gateway_bdn_performance_stats_service.interval_data.
                blockchain_node_to_bdn_stats))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(1, stats.new_blocks_received_from_bdn)

        self.assertEqual(1,
                         node_1_stats.new_blocks_received_from_blockchain_node)

    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 test_bdn_stats_block_new_from_bdn_ignore_duplicate(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.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 test_bdn_stats_block_new_from_bdn_ignore_from_node(self):
        block_msg = NewBlockEthProtocolMessage(
            None,
            _block_with_timestamp(time.time() + 1 - (
                self.node.opts.blockchain_ignore_block_interval_count *
                self.node.opts.blockchain_block_interval)), 10)

        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)

        block_msg.serialize()
        self.block_blockchain_connection_protocol.msg_block(block_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)
            self.assertEqual(0, stats.new_blocks_received_from_blockchain_node)

    def test_bdn_stats_block_new_from_node_ignore_from_bdn(self):
        block_msg = NewBlockEthProtocolMessage(
            None,
            _block_with_timestamp(
                time.time() + 1 -
                self.node.opts.blockchain_ignore_block_interval_count *
                self.node.opts.blockchain_block_interval), 10)

        block_msg.serialize()
        self.block_blockchain_connection_protocol.msg_block(block_msg)

        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))
        node_1_stats = gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats[
            self.node_1_endpoint]
        for endpoint, stats in gateway_bdn_performance_stats_service.interval_data.blockchain_node_to_bdn_stats.items(
        ):
            if endpoint == self.node_1_endpoint:
                continue
            self.assertEqual(1, stats.new_blocks_received_from_bdn)

        self.assertEqual(1,
                         node_1_stats.new_blocks_received_from_blockchain_node)
        self.assertEqual(0, node_1_stats.new_blocks_received_from_bdn)