def remove_transaction_by_short_id(self, short_id: int, remove_related_short_ids: bool = False): """ Clean up short id mapping. Removes transaction contents and mapping if only one short id mapping. :param short_id: short id to clean up """ if short_id in self._short_id_to_tx_cache_key: if self.node.opts.dump_removed_short_ids: self._removed_short_ids.add(short_id) transaction_cache_key = self._short_id_to_tx_cache_key.pop( short_id) transaction_hash = self._tx_cache_key_to_hash( transaction_cache_key) tx_stats.add_tx_by_hash_event( transaction_hash, TransactionStatEventType.TX_REMOVED_FROM_MEMORY, self.network_num, short_id, reason="RemoveByShortId") if transaction_cache_key in self._tx_cache_key_to_short_ids: short_ids = self._tx_cache_key_to_short_ids[ transaction_cache_key] # Only clear mapping and txhash_to_contents if last SID assignment if len(short_ids) == 1 or remove_related_short_ids: for dup_short_id in short_ids: if dup_short_id != short_id: tx_stats.add_tx_by_hash_event( transaction_hash, TransactionStatEventType. TX_REMOVED_FROM_MEMORY, self.network_num, dup_short_id, reason="RemoveRelatedShortId") if dup_short_id in self._short_id_to_tx_cache_key: del self._short_id_to_tx_cache_key[ dup_short_id] self._tx_assignment_expire_queue.remove( dup_short_id) if self.node.opts.dump_removed_short_ids: self._removed_short_ids.add(dup_short_id) if transaction_cache_key in self._tx_cache_key_to_contents: self._total_tx_contents_size -= len( self. _tx_cache_key_to_contents[transaction_cache_key]) del self._tx_cache_key_to_contents[ transaction_cache_key] # Delete short ids from _tx_cache_key_to_short_ids after iterating short_ids. # Otherwise extension implementation disposes short_ids list after this line del self._tx_cache_key_to_short_ids[transaction_cache_key] if not remove_related_short_ids: # TODO : remove this after creating AbstractTransactionService self._track_seen_transaction(transaction_cache_key) else: short_ids.remove(short_id) self._tx_assignment_expire_queue.remove(short_id)
def update_removed_transactions(self, removed_content_size: int, short_ids: List[int]) -> None: self._total_tx_contents_size -= removed_content_size for short_id in short_ids: tx_stats.add_tx_by_hash_event( constants.UNKNOWN_TRANSACTION_HASH, TransactionStatEventType.TX_REMOVED_FROM_MEMORY, self.network_num, short_id, reason="ExtensionsTrackSeenShortId") self._tx_assignment_expire_queue.remove(short_id) if self.node.opts.dump_removed_short_ids: self._removed_short_ids.add(short_id)
def update_removed_transactions(self, removed_content_size: int, short_ids: List[int]) -> None: self._total_tx_contents_size -= removed_content_size for short_id in short_ids: tx_stats.add_tx_by_hash_event( UNKNOWN_TRANSACTION_HASH, TransactionStatEventType.TX_REMOVED_FROM_MEMORY, self.network_num, short_id, reason=TxRemovalReason.EXTENSION_BLOCK_CLEANUP.value) self._tx_assignment_expire_queue.remove(short_id) if self.node.opts.dump_removed_short_ids: self._removed_short_ids.add(short_id)
def msg_txs(self, msg: TxsMessage): if ConnectionType.RELAY_TRANSACTION not in self.CONNECTION_TYPE: self.log_error(log_messages.UNEXPECTED_TXS_ON_NON_RELAY_CONN, msg) return transactions = msg.get_txs() tx_service = self.node.get_tx_service() tx_stats.add_txs_by_short_ids_event( map(lambda x: x.short_id, transactions), TransactionStatEventType. TX_UNKNOWN_SHORT_IDS_REPLY_RECEIVED_BY_GATEWAY_FROM_RELAY, network_num=self.node.network_num, peer=stats_format.connection(self), found_tx_hashes=map(lambda x: convert.bytes_to_hex(x.hash.binary), transactions)) for transaction in transactions: tx_hash, transaction_contents, short_id = transaction assert tx_hash is not None assert short_id is not None self.node.block_recovery_service.check_missing_sid(short_id) if not tx_service.has_short_id(short_id): tx_service.assign_short_id(tx_hash, short_id) self.node.block_recovery_service.check_missing_tx_hash(tx_hash) if not tx_service.has_transaction_contents(tx_hash): assert transaction_contents is not None tx_service.set_transaction_contents(tx_hash, transaction_contents) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType. TX_UNKNOWN_TRANSACTION_RECEIVED_BY_GATEWAY_FROM_RELAY, self.node.network_num, short_id, peer=stats_format.connection(self)) self.node.block_processing_service.retry_broadcast_recovered_blocks( self) for block_awaiting_recovery in self.node.block_recovery_service.get_blocks_awaiting_recovery( ): self.node.block_processing_service.schedule_recovery_retry( block_awaiting_recovery)
async def post_process_transaction( self, network_num: int, account_id: str, quota_type: QuotaType, transaction_str: str ) -> JsonRpcResponse: try: message_converter = self.node.message_converter assert message_converter is not None, "Invalid server state!" transaction = message_converter.encode_raw_msg(transaction_str) bx_tx = message_converter.bdn_tx_to_bx_tx(transaction, network_num, quota_type) except (ValueError, ParseError) as e: logger.error(common_log_messages.RPC_COULD_NOT_PARSE_TRANSACTION, e) raise RpcInvalidParams( self.request_id, f"Invalid transaction param: {transaction_str}" ) tx_service = self.node.get_tx_service() tx_hash = bx_tx.tx_hash() if tx_service.has_transaction_contents(tx_hash): short_id = tx_service.get_short_id(tx_hash) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_RECEIVED_FROM_RPC_REQUEST_IGNORE_SEEN, network_num, account_id=account_id, short_id=short_id ) tx_json = { "tx_hash": str(tx_hash), "quota_type": quota_type.name.lower(), "account_id": account_id, } return self.ok(tx_json) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_RECEIVED_FROM_RPC_REQUEST, network_num, account_id=account_id ) if self.node.has_active_blockchain_peer(): blockchain_tx_message = self.node.message_converter.bx_tx_to_tx(bx_tx) self.node.broadcast(blockchain_tx_message, connection_types=[ConnectionType.BLOCKCHAIN_NODE]) # All connections outside of this one is a bloXroute server broadcast_peers = self.node.broadcast(bx_tx, connection_types=[ConnectionType.RELAY_TRANSACTION]) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_SENT_FROM_GATEWAY_TO_PEERS, network_num, peers=broadcast_peers ) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_GATEWAY_RPC_RESPONSE_SENT, network_num ) tx_service.set_transaction_contents(tx_hash, bx_tx.tx_val()) tx_json = { "tx_hash": str(tx_hash), "quota_type": quota_type.name.lower(), "account_id": account_id } return self.ok(tx_json)
def _test_should_log_event(self, last_byte_value: int, network_num: int, short_id: Optional[int], expected_to_log: bool): tx_stats.logger.log = MagicMock() tx_hash = helpers.generate_bytearray(crypto.SHA256_HASH_LEN) struct.pack_into("<B", tx_hash, crypto.SHA256_HASH_LEN - 1, last_byte_value) tx_stats.add_tx_by_hash_event( Sha256Hash(tx_hash), TransactionStatEventType.TX_SENT_FROM_GATEWAY_TO_PEERS, network_num, short_id) if expected_to_log: tx_stats.logger.log.assert_called_once() else: tx_stats.logger.log.assert_not_called()
def remove_transaction_by_short_id(self, short_id: int, remove_related_short_ids: bool = False): # overriding this in order to handle removes triggered by either the mem limit or expiration queue # if the remove_related_short_ids is True than we assume the call originated by the track seen call # else we assume it was triggered by the cleanup. # this is only a temporary fix and the whole class hierarchy requires some refactoring! if remove_related_short_ids: self._tx_assignment_expire_queue.remove(short_id) tx_stats.add_tx_by_hash_event( constants.UNKNOWN_TRANSACTION_HASH, TransactionStatEventType.TX_REMOVED_FROM_MEMORY, self.network_num, short_id, reason="ExtensionRemoveShortId") if self.node.opts.dump_removed_short_ids: self._removed_short_ids.add(short_id) else: super(ExtensionTransactionService, self).remove_transaction_by_short_id(short_id)
def msg_txs(self, msg: TxsMessage, recovered_txs_source: RecoveredTxsSource = RecoveredTxsSource. TXS_RECOVERED): transactions = msg.get_txs() tx_service = self.node.get_tx_service() missing_txs: Set[MissingTransactions] = tx_service.process_txs_message( msg) tx_stats.add_txs_by_short_ids_event( map(lambda x: x.short_id, transactions), TransactionStatEventType. TX_UNKNOWN_SHORT_IDS_REPLY_RECEIVED_BY_GATEWAY_FROM_RELAY, network_num=self.node.network_num, peers=[self], found_tx_hashes=map(lambda x: convert.bytes_to_hex(x.hash.binary), transactions)) for (short_id, transaction_hash) in missing_txs: self.node.block_recovery_service.check_missing_sid( short_id, recovered_txs_source) self.node.block_recovery_service.check_missing_tx_hash( transaction_hash, recovered_txs_source) tx_stats.add_tx_by_hash_event( transaction_hash, TransactionStatEventType. TX_UNKNOWN_TRANSACTION_RECEIVED_BY_GATEWAY_FROM_RELAY, self.node.network_num, short_id, peers=[self]) self.node.block_processing_service.retry_broadcast_recovered_blocks( self) for block_awaiting_recovery in self.node.block_recovery_service.get_blocks_awaiting_recovery( ): self.node.block_processing_service.schedule_recovery_retry( block_awaiting_recovery)
def remove_transaction_by_tx_hash( self, transaction_hash: Sha256Hash) -> Optional[Set[int]]: """ Clean up mapping. Removes transaction contents and mapping. :param transaction_hash: tx hash to clean up """ transaction_cache_key = self._tx_hash_to_cache_key(transaction_hash) removed_sids = 0 removed_txns = 0 if transaction_cache_key in self._tx_cache_key_to_short_ids: short_ids = self._tx_cache_key_to_short_ids.pop( transaction_cache_key) for short_id in short_ids: tx_stats.add_tx_by_hash_event( transaction_hash, TransactionStatEventType.TX_REMOVED_FROM_MEMORY, self.network_num, short_id, reason="RemoveByTransactionHash") removed_sids += 1 if short_id in self._short_id_to_tx_cache_key: del self._short_id_to_tx_cache_key[short_id] self._tx_assignment_expire_queue.remove(short_id) if self.node.opts.dump_removed_short_ids: self._removed_short_ids.add(short_id) else: short_ids = None if transaction_cache_key in self._tx_cache_key_to_contents: self._total_tx_contents_size -= len( self._tx_cache_key_to_contents[transaction_cache_key]) del self._tx_cache_key_to_contents[transaction_cache_key] removed_txns += 1 logger.trace( "Removed transaction: {}, with {} associated short ids and {} contents.", transaction_hash, removed_sids, removed_txns) return short_ids
def broadcast_transactions_to_nodes( self, msg: AbstractMessage, broadcasting_conn: Optional[AbstractConnection]) -> bool: msg = cast(TransactionsEthProtocolMessage, msg) if self.opts.filter_txs_factor > 0: average_block_gas_filter = self.average_block_gas_price.average * self.opts.filter_txs_factor else: average_block_gas_filter = 0 min_gas_price_from_node = self.min_tx_from_node_gas_price.current_minimum gas_price_filter = max(average_block_gas_filter, min_gas_price_from_node) if gas_price_filter > 0: assert len(msg.get_transactions()) == 1 transaction = msg.get_transactions()[0] gas_price = float(transaction.gas_price) if gas_price < gas_price_filter: logger.trace( "Skipping sending transaction {} with gas price: {}. Average was {}. Minimum from node was {}.", transaction.hash(), float(transaction.gas_price), average_block_gas_filter, min_gas_price_from_node) tx_stats.add_tx_by_hash_event( transaction.hash(), TransactionStatEventType.TX_FROM_BDN_IGNORE_LOW_GAS_PRICE, self.network_num, peers=[broadcasting_conn], more_info= "Tx gas price {}. Average block gas price: {}. Node min gas price {}." .format(gas_price, average_block_gas_filter, min_gas_price_from_node)) return False return super().broadcast_transactions_to_nodes(msg, broadcasting_conn)
async def post_process_transaction( self, network_num: int, account_id: str, transaction_flag: TransactionFlag, transaction_str: str) -> JsonRpcResponse: try: message_converter = self.node.message_converter assert message_converter is not None, "Invalid server state!" transaction = message_converter.encode_raw_msg(transaction_str) bx_tx = message_converter.bdn_tx_to_bx_tx(transaction, network_num, transaction_flag, account_id) except (ValueError, ParseError) as e: logger.error(common_log_messages.RPC_COULD_NOT_PARSE_TRANSACTION, e) raise RpcInvalidParams( self.request_id, f"Invalid transaction param: {transaction_str}") tx_service = self.node.get_tx_service() tx_hash = bx_tx.tx_hash() transaction_key = tx_service.get_transaction_key(tx_hash) if (tx_service.has_transaction_contents_by_key(transaction_key) or tx_service.removed_transaction_by_key(transaction_key)): short_id = tx_service.get_short_id_by_key(transaction_key) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType. TX_RECEIVED_FROM_RPC_REQUEST_IGNORE_SEEN, network_num, account_id=account_id, short_id=short_id) tx_json = { "tx_hash": str(tx_hash), } return self.ok(tx_json) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_RECEIVED_FROM_RPC_REQUEST, network_num, account_id=account_id) if self.node.has_active_blockchain_peer(): blockchain_tx_message = self.node.message_converter.bx_tx_to_tx( bx_tx) self.node.broadcast( blockchain_tx_message, connection_types=(ConnectionType.BLOCKCHAIN_NODE, )) # All connections outside of this one is a bloXroute server broadcast_peers = self.node.broadcast( bx_tx, connection_types=(ConnectionType.RELAY_TRANSACTION, )) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_SENT_FROM_GATEWAY_TO_PEERS, network_num, peers=broadcast_peers) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_GATEWAY_RPC_RESPONSE_SENT, network_num) tx_service.set_transaction_contents_by_key(transaction_key, bx_tx.tx_val()) tx_json = { "tx_hash": str(tx_hash), } if not self.node.account_model.is_account_valid(): raise RpcAccountIdError( self.request_id, "The account associated with this gateway has expired. " "Please visit https://portal.bloxroute.com to renew your subscription." ) if self.node.quota_level == constants.FULL_QUOTA_PERCENTAGE: raise RpcBlocked( self.request_id, "The account associated with this gateway has exceeded its daily transaction quota." ) else: return self.ok(tx_json)
def msg_tx(self, msg): """ Handle transactions receive from bloXroute network. """ start_time = time.time() if ConnectionType.RELAY_TRANSACTION not in self.CONNECTION_TYPE: self.log_error(log_messages.UNEXPECTED_TX_MESSAGE, msg) return tx_service = self.node.get_tx_service() tx_hash = msg.tx_hash() short_id = msg.short_id() tx_contents = msg.tx_val() is_compact = msg.is_compact() network_num = msg.network_num() attempt_recovery = False ext_start_time = time.time() processing_result = tx_service.process_gateway_transaction_from_bdn( tx_hash, short_id, tx_contents, is_compact) ext_end_time = time.time() if processing_result.ignore_seen: gateway_transaction_stats_service.log_duplicate_transaction_from_relay( ) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType. TX_RECEIVED_BY_GATEWAY_FROM_PEER_IGNORE_SEEN, network_num, short_id, peers=[self], is_compact_transaction=False) self.log_trace("Transaction has already been seen: {}", tx_hash) return if processing_result.existing_short_id: gateway_transaction_stats_service.log_duplicate_transaction_from_relay( is_compact) tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType. TX_RECEIVED_BY_GATEWAY_FROM_PEER_IGNORE_SEEN, network_num, short_id, peers=[self], is_compact_transaction=is_compact) return tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_RECEIVED_BY_GATEWAY_FROM_PEER, network_num, short_id, peers=[self], is_compact_transaction=msg.is_compact()) gateway_transaction_stats_service.log_transaction_from_relay( tx_hash, short_id is not None, msg.is_compact()) if processing_result.assigned_short_id: was_missing = self.node.block_recovery_service.check_missing_sid( short_id, RecoveredTxsSource.TXS_RECEIVED_FROM_BDN) attempt_recovery |= was_missing tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType.TX_SHORT_ID_STORED_BY_GATEWAY, network_num, short_id, was_missing=was_missing) gateway_transaction_stats_service.log_short_id_assignment_processed( ) if not is_compact and processing_result.existing_contents: gateway_transaction_stats_service.log_redundant_transaction_content( ) if processing_result.set_content: self.log_trace( "Adding hash value to tx service and forwarding it to node") gateway_bdn_performance_stats_service.log_tx_from_bdn( not self.node.is_gas_price_above_min_network_fee(tx_contents)) attempt_recovery |= self.node.block_recovery_service.check_missing_tx_hash( tx_hash, RecoveredTxsSource.TXS_RECEIVED_FROM_BDN) self.publish_new_transaction( tx_hash, tx_contents, TransactionFlag.LOCAL_REGION in msg.transaction_flag()) if self.node.has_active_blockchain_peer(): blockchain_tx_message = self.node.message_converter.bx_tx_to_tx( msg) transaction_feed_stats_service.log_new_transaction(tx_hash) sent = self.node.broadcast_transactions_to_nodes( blockchain_tx_message, self) if sent: tx_stats.add_tx_by_hash_event( tx_hash, TransactionStatEventType. TX_SENT_FROM_GATEWAY_TO_BLOCKCHAIN_NODE, network_num, short_id) gateway_bdn_performance_stats_service.log_tx_sent_to_nodes( ) else: gateway_transaction_stats_service.log_dropped_transaction_from_relay( ) if attempt_recovery: self.node.block_processing_service.retry_broadcast_recovered_blocks( self) end_time = time.time() total_duration_ms = (end_time - start_time) * 1000 duration_before_ext_ms = (ext_start_time - start_time) * 1000 duration_ext_ms = (ext_end_time - ext_start_time) * 1000 duration_after_ext_ms = (end_time - ext_end_time) * 1000 gateway_transaction_stats_service.log_processed_bdn_transaction( total_duration_ms, duration_before_ext_ms, duration_ext_ms, duration_after_ext_ms) performance_utils.log_operation_duration( msg_handling_logger, "Process single transaction from BDN", start_time, gateway_constants.BDN_TX_PROCESSING_TIME_WARNING_THRESHOLD_S, connection=self, message=msg, total_duration_ms=total_duration_ms, duration_before_ext_ms=duration_before_ext_ms, duration_ext_ms=duration_ext_ms, duration_after_ext_ms=duration_after_ext_ms)
def msg_tx(self, msg): """ Handle a TX message by broadcasting to the entire network """ start_time = time.time() txn_count = 0 broadcast_txs_count = 0 process_tx_msg_result = self.tx_service.process_transactions_message_from_node( msg, self.node.get_network_min_transaction_fee(), self.node.opts.transaction_validation) if not self.node.opts.has_fully_updated_tx_service: logger.debug( "Gateway received {} transaction messages while syncing, skipping..", len(process_tx_msg_result)) return broadcast_start_time = time.time() for tx_result in process_tx_msg_result: txn_count += 1 if TxValidationStatus.INVALID_FORMAT in tx_result.tx_validation_status: gateway_transaction_stats_service.log_tx_validation_failed_structure( ) logger.warning( log_messages.NODE_RECEIVED_TX_WITH_INVALID_FORMAT, self.node.NODE_TYPE, tx_result.transaction_hash, stats_format.connection(self.connection)) tx_stats.add_tx_by_hash_event( tx_result.transaction_hash, TransactionStatEventType.TX_VALIDATION_FAILED_STRUCTURE, self.connection.network_num, peers=[self.connection]) continue if TxValidationStatus.INVALID_SIGNATURE in tx_result.tx_validation_status: gateway_transaction_stats_service.log_tx_validation_failed_signature( ) logger.warning(log_messages.NODE_RECEIVED_TX_WITH_INVALID_SIG, self.node.NODE_TYPE, tx_result.transaction_hash, stats_format.connection(self.connection)) tx_stats.add_tx_by_hash_event( tx_result.transaction_hash, TransactionStatEventType.TX_VALIDATION_FAILED_SIGNATURE, self.connection.network_num, peers=[self.connection]) continue if TxValidationStatus.LOW_FEE in tx_result.tx_validation_status: gateway_transaction_stats_service.log_tx_validation_failed_gas_price( ) # log low fee transaction here for bdn_performance gateway_bdn_performance_stats_service.log_tx_from_blockchain_node( True) logger.trace( "transaction {} has gas price lower then the setting {}", tx_result.transaction_hash, self.node.get_network_min_transaction_fee()) tx_stats.add_tx_by_hash_event( tx_result.transaction_hash, TransactionStatEventType.TX_VALIDATION_FAILED_GAS_PRICE, self.connection.network_num, peers=[self.connection]) continue if tx_result.seen: tx_stats.add_tx_by_hash_event( tx_result.transaction_hash, TransactionStatEventType. TX_RECEIVED_FROM_BLOCKCHAIN_NODE_IGNORE_SEEN, self.connection.network_num, peers=[self.connection]) gateway_transaction_stats_service.log_duplicate_transaction_from_blockchain( ) continue broadcast_txs_count += 1 tx_stats.add_tx_by_hash_event( tx_result.transaction_hash, TransactionStatEventType.TX_RECEIVED_FROM_BLOCKCHAIN_NODE, self.connection.network_num, peers=[self.connection]) gateway_transaction_stats_service.log_transaction_from_blockchain( tx_result.transaction_hash) # log transactions that passed validation, according to fee gateway_bdn_performance_stats_service.log_tx_from_blockchain_node( not self.node.is_gas_price_above_min_network_fee( tx_result.transaction_contents)) # All connections outside of this one is a bloXroute server broadcast_peers = self.node.broadcast( tx_result.bdn_transaction_message, self.connection, connection_types=[ConnectionType.RELAY_TRANSACTION]) self.node.broadcast( msg, self.connection, connection_types=[ConnectionType.BLOCKCHAIN_NODE]) if self.node.opts.ws: self.publish_transaction( tx_result.transaction_hash, memoryview(tx_result.transaction_contents)) if broadcast_peers: tx_stats.add_tx_by_hash_event( tx_result.transaction_hash, TransactionStatEventType.TX_SENT_FROM_GATEWAY_TO_PEERS, self.connection.network_num, peers=broadcast_peers) else: logger.trace( "Tx Message: {} from BlockchainNode was dropped, no upstream relay connection available", tx_result.transaction_hash) set_content_start_time = time.time() end_time = time.time() total_duration_ms = (end_time - start_time) * 1000 duration_before_broadcast_ms = (broadcast_start_time - start_time) * 1000 duration_broadcast_ms = (set_content_start_time - broadcast_start_time) * 1000 duration_set_content_ms = (end_time - set_content_start_time) * 1000 gateway_transaction_stats_service.log_processed_node_transaction( total_duration_ms, duration_before_broadcast_ms, duration_broadcast_ms, duration_set_content_ms, broadcast_txs_count) performance_utils.log_operation_duration( msg_handling_logger, "Process single transaction from Blockchain", start_time, gateway_constants. BLOCKCHAIN_TX_PROCESSING_TIME_WARNING_THRESHOLD_S, connection=self, message=msg, total_duration_ms=total_duration_ms, duration_before_broadcast_ms=duration_before_broadcast_ms, duration_broadcast_ms=duration_broadcast_ms, duration_set_content_ms=duration_set_content_ms)
def msg_tx(self, msg): """ Handle a TX message by broadcasting to the entire network """ start_time = time.time() txn_count = 0 broadcast_txs_count = 0 tx_service = self.connection.node.get_tx_service() process_tx_msg_result = tx_service.process_transactions_message_from_node(msg) broadcast_start_time = time.time() for tx_result in process_tx_msg_result: txn_count += 1 if tx_result.seen: tx_stats.add_tx_by_hash_event(tx_result.transaction_hash, TransactionStatEventType.TX_RECEIVED_FROM_BLOCKCHAIN_NODE_IGNORE_SEEN, self.connection.network_num, peer=stats_format.connection(self.connection)) gateway_transaction_stats_service.log_duplicate_transaction_from_blockchain() continue broadcast_txs_count += 1 tx_stats.add_tx_by_hash_event( tx_result.transaction_hash, TransactionStatEventType.TX_RECEIVED_FROM_BLOCKCHAIN_NODE, self.connection.network_num, peer=stats_format.connection(self.connection) ) gateway_transaction_stats_service.log_transaction_from_blockchain(tx_result.transaction_hash) gateway_bdn_performance_stats_service.log_tx_from_blockchain_node() # All connections outside of this one is a bloXroute server broadcast_peers = self.connection.node.broadcast( tx_result.bdn_transaction_message, self.connection, connection_types=[ConnectionType.RELAY_TRANSACTION] ) if self.connection.node.opts.ws: self.publish_transaction( tx_result.transaction_hash, memoryview(tx_result.transaction_contents) ) if broadcast_peers: tx_stats.add_tx_by_hash_event( tx_result.transaction_hash, TransactionStatEventType.TX_SENT_FROM_GATEWAY_TO_PEERS, self.connection.network_num, peers=map(lambda conn: (stats_format.connection(conn)), broadcast_peers) ) else: logger.trace( "Tx Message: {} from BlockchainNode was dropped, no upstream relay connection available", tx_result.transaction_hash ) set_content_start_time = time.time() end_time = time.time() total_duration_ms = (end_time - start_time) * 1000 duration_before_broadcast_ms = (broadcast_start_time - start_time) * 1000 duration_broadcast_ms = (set_content_start_time - broadcast_start_time) * 1000 duration_set_content_ms = (end_time - set_content_start_time) * 1000 gateway_transaction_stats_service.log_processed_node_transaction( total_duration_ms, duration_before_broadcast_ms, duration_broadcast_ms, duration_set_content_ms, broadcast_txs_count ) performance_utils.log_operation_duration( msg_handling_logger, "Process single transaction from Blockchain", start_time, gateway_constants.BLOCKCHAIN_TX_PROCESSING_TIME_WARNING_THRESHOLD_S, connection=self, message=msg, total_duration_ms=total_duration_ms, duration_before_broadcast_ms=duration_before_broadcast_ms, duration_broadcast_ms=duration_broadcast_ms, duration_set_content_ms=duration_set_content_ms )