class GatewayConnection(InternalNodeConnection["AbstractGatewayNode"]): """ Connection handler for Gateway-Gateway connections. """ CONNECTION_TYPE = ConnectionType.EXTERNAL_GATEWAY NULL_ORDERING = -1 ACK_MESSAGE = AckMessage() def __init__(self, sock: AbstractSocketConnectionProtocol, node: "AbstractGatewayNode"): super(GatewayConnection, self).__init__(sock, node) self.hello_messages = gateway_constants.GATEWAY_HELLO_MESSAGES self.header_size = constants.STARTING_SEQUENCE_BYTES_LEN + constants.BX_HDR_COMMON_OFF self.message_handlers = { GatewayMessageType.HELLO: self.msg_hello, BloxrouteMessageType.ACK: self.msg_ack, GatewayMessageType.BLOCK_RECEIVED: self.msg_block_received, GatewayMessageType.BLOCK_PROPAGATION_REQUEST: self.msg_block_propagation_request, BloxrouteMessageType.BLOCK_HOLDING: self.msg_block_holding, BloxrouteMessageType.KEY: self.msg_key, GatewayMessageType.CONFIRMED_TX: self.msg_confirmed_tx, GatewayMessageType.REQUEST_TX_STREAM: self.msg_request_tx_stream, } self.version_manager = gateway_version_manager self.protocol_version = self.version_manager.CURRENT_PROTOCOL_VERSION if self.from_me: self._initialize_ordered_handshake() else: self.ordering = self.NULL_ORDERING def connection_message_factory(self) -> AbstractMessageFactory: return gateway_message_factory def on_connection_established(self): super().on_connection_established() peer_model = self.peer_model if (self.node.opts.request_remote_transaction_streaming and peer_model is not None and peer_model.is_transaction_streamer()): self.enqueue_msg(RequestTxStreamMessage()) def msg_hello(self, msg): """ Updates connection pool references to this connection to match the port described in the hello message. Identifies any duplicate connections (i.e. from if both nodes connect to each other) and drops them based on the ordering of the messages. Connections may end up with two entries in the connection pool if they are initiated by the remote peer, i.e. one on the randomly assigned incoming socket and one on the actual provided address of the peer here. """ network_num = msg.network_num() if network_num != self.node.network_num: self.log_error(log_messages.NETWORK_NUMBER_MISMATCH, self.network_num, network_num) self.mark_for_close(should_retry=False) return ip = msg.ip() if ip != self.peer_ip: self.log_warning(log_messages.MISMATCH_IP_IN_HELLO_MSG, ip, self.peer_ip) port = msg.port() ordering = msg.ordering() peer_id = msg.node_id() if not self._is_authenticated: # naively set the the peer id to what reported if peer_id is None: self.log_warning(log_messages.HELLO_MSG_WITH_NO_PEER_ID) self.peer_id = peer_id if not self.from_me: self.log_trace("Connection established with peer: {}.", peer_id) self.node.requester.send_threaded_request( sdn_http_service.submit_gateway_inbound_connection, self.node.opts.node_id, self.peer_id) if self.node.connection_exists(ip, port): connection = self.node.connection_pool.get_by_ipport(ip, port) # connection already has correct ip / port info assigned if connection == self: self.log_debug("Duplicate hello message received. Ignoring.") self.enqueue_msg(self.ack_message) return if connection.is_active(): self.log_debug("Duplicate established connection. Dropping.") self.mark_for_close(should_retry=False) return # ordering numbers were the same; take over the slot and try again if connection.ordering == ordering: self.log_debug( "Duplicate connection orderings could not be resolved. Investigate if this " "message appears repeatedly.") self.node.connection_pool.update_port(self.peer_port, port, self) self._initialize_ordered_handshake() return # part one of duplicate connection resolution # if connection has same ordering or no ordering, it hasn't yet received its connection # so don't do anything and wait for the other connection to receive a HELLO message and itself or this one if connection.ordering == self.NULL_ORDERING: self.log_debug( "Duplicate connections. Two connections have been opened on ip port: {}:{}. Awaiting other " "connection's resolution.", ip, port) self.ordering = ordering return if connection.ordering > ordering: self.log_warning(log_messages.REDUNDANT_CONNECTION, self.file_no, connection.file_no) self.mark_for_close(should_retry=False) connection.on_connection_established() connection.enqueue_msg(connection.ack_message) else: self.log_warning(log_messages.REDUNDANT_CONNECTION, connection.file_no, self.file_no) connection.mark_for_close(should_retry=False) self.on_connection_established() self.enqueue_msg(self.ack_message) self.node.connection_pool.update_port(self.peer_port, port, self) else: self.log_debug( "Connection is only one of its kind. Updating port reference.") self.node.connection_pool.update_port(self.peer_port, port, self) self.enqueue_msg(self.ack_message) self.on_connection_established() self._update_port_info(port) self.ordering = ordering def msg_ack(self, _msg): """ Acks hello message and establishes connection. """ self.on_connection_established() def msg_block_received(self, msg): self.node.neutrality_service.record_block_receipt( msg.block_hash(), self) def msg_block_propagation_request(self, msg): bx_block = msg.blob() bx_block_hash = crypto.double_sha256(bx_block) block_stats.add_block_event_by_block_hash( bx_block_hash, BlockStatEventType.BX_BLOCK_PROPAGATION_REQUESTED_BY_PEER, network_num=self.network_num, peers=[self], ) self.node.neutrality_service.propagate_block_to_network(bx_block, self, from_peer=True) def msg_block_holding(self, msg): block_hash = msg.block_hash() self.node.block_processing_service.place_hold(block_hash, self) def msg_key(self, msg): """ Handles key message receive from bloXroute. Looks for the encrypted block and decrypts; otherwise stores for later. """ self.node.block_processing_service.process_block_key(msg, self) def msg_confirmed_tx(self, msg: ConfirmedTxMessage) -> None: tx_hash = msg.tx_hash() transaction_key = self.node.get_tx_service().get_transaction_key( tx_hash) tx_contents = msg.tx_val() # shouldn't ever happen, but just in case if tx_contents == ConfirmedTxMessage.EMPTY_TX_VAL: tx_contents = cast( Optional[memoryview], self.node.get_tx_service().get_transaction_by_key( transaction_key)) if tx_contents is None: transaction_feed_stats_service.log_pending_transaction_missing_contents( ) return self.node.feed_manager.publish_to_feed( FeedKey(EthPendingTransactionFeed.NAME), EthRawTransaction(tx_hash, tx_contents, FeedSource.BDN_SOCKET, local_region=True)) transaction_feed_stats_service.log_pending_transaction_from_internal( tx_hash) def msg_request_tx_stream(self, msg: RequestTxStreamMessage) -> None: pass def _initialize_ordered_handshake(self): self.ordering = random.getrandbits(constants.UL_INT_SIZE_IN_BYTES * 8) self.enqueue_msg( GatewayHelloMessage(self.protocol_version, self.network_num, self.node.opts.external_ip, self.node.opts.external_port, self.ordering, self.node.opts.node_id)) def _update_port_info(self, new_port): self.peer_port = new_port self.peer_desc = "%s %d" % (self.peer_ip, self.peer_port) def dispose(self): if not self.from_me: if self.peer_id: self.node.requester.send_threaded_request( sdn_http_service.delete_gateway_inbound_connection, self.node.opts.node_id, self.peer_id) super().dispose()
def ack_message(self) -> AckMessage: return AckMessage()
def test_ack_message(self): self._test_to_old_version(AckMessage()) self._test_to_new_version(AckMessageV4())
def test_message_preview_success_all_types(self): self.get_message_preview_successfully( HelloMessage(protocol_version=1, network_num=2), HelloMessage.MESSAGE_TYPE, VersionMessage.VERSION_MESSAGE_LENGTH + UL_INT_SIZE_IN_BYTES + NODE_ID_SIZE_IN_BYTES - UL_INT_SIZE_IN_BYTES) self.get_message_preview_successfully(AckMessage(), AckMessage.MESSAGE_TYPE, constants.CONTROL_FLAGS_LEN) self.get_message_preview_successfully(PingMessage(), PingMessage.MESSAGE_TYPE, 9) self.get_message_preview_successfully(PongMessage(), PongMessage.MESSAGE_TYPE, 17) blob = bytearray(1 for _ in range(4)) self.get_message_preview_successfully( BroadcastMessage(self.HASH, 1, self.NODE_ID, self.BROADCAST_TYPE, True, blob), BroadcastMessage.MESSAGE_TYPE, SHA256_HASH_LEN + NETWORK_NUM_LEN + constants.BROADCAST_TYPE_LEN + BLOCK_ENCRYPTED_FLAG_LEN + constants.NODE_ID_SIZE_IN_BYTES + len(blob) + constants.CONTROL_FLAGS_LEN) self.get_message_preview_successfully( TxMessage(self.HASH, 1, self.NODE_ID, 12, blob), TxMessage.MESSAGE_TYPE, SHA256_HASH_LEN + NETWORK_NUM_LEN + UL_INT_SIZE_IN_BYTES + TRANSACTION_FLAG_LEN + constants.NODE_ID_SIZE_IN_BYTES + len(blob) + constants.DOUBLE_SIZE_IN_BYTES + constants.CONTROL_FLAGS_LEN) self.get_message_preview_successfully( KeyMessage(self.HASH, 1, self.NODE_ID, bytearray(1 for _ in range(KEY_SIZE))), KeyMessage.MESSAGE_TYPE, SHA256_HASH_LEN + KEY_SIZE + NETWORK_NUM_LEN + constants.NODE_ID_SIZE_IN_BYTES + constants.CONTROL_FLAGS_LEN) self.get_message_preview_successfully( BlockHoldingMessage(self.HASH, 1, self.NODE_ID), BlockHoldingMessage.MESSAGE_TYPE, SHA256_HASH_LEN + NETWORK_NUM_LEN + constants.NODE_ID_SIZE_IN_BYTES + constants.CONTROL_FLAGS_LEN) get_txs = [1, 2, 3] self.get_message_preview_successfully( GetTxsMessage(get_txs), GetTxsMessage.MESSAGE_TYPE, UL_INT_SIZE_IN_BYTES + UL_INT_SIZE_IN_BYTES * len(get_txs) + constants.CONTROL_FLAGS_LEN) txs = [ TransactionInfo(Sha256Hash(crypto.double_sha256(b"123")), bytearray(4), 1), TransactionInfo(Sha256Hash(crypto.double_sha256(b"234")), bytearray(8), 2) ] expected_length = (UL_INT_SIZE_IN_BYTES + sum(UL_INT_SIZE_IN_BYTES + SHA256_HASH_LEN + UL_INT_SIZE_IN_BYTES + len(tx.contents) for tx in txs) + constants.CONTROL_FLAGS_LEN) self.get_message_preview_successfully(TxsMessage(txs), TxsMessage.MESSAGE_TYPE, expected_length) expected_length = (2 * constants.DOUBLE_SIZE_IN_BYTES) + (5 * constants.UL_SHORT_SIZE_IN_BYTES) + \ (7 * constants.UL_INT_SIZE_IN_BYTES) + constants.IP_ADDR_SIZE_IN_BYTES + \ constants.CONTROL_FLAGS_LEN node_stats = {} helpers.add_stats_to_node_stats(node_stats, "127.0.0.1", 8001, 200, 300, 400, 500, 600, 700, 800, 100, 50) self.get_message_preview_successfully( BdnPerformanceStatsMessage(datetime.utcnow(), datetime.utcnow(), 100, node_stats), BdnPerformanceStatsMessage.MESSAGE_TYPE, expected_length) # multi node bdn stats message expected_length = ( constants.CONTROL_FLAGS_LEN + (2 * constants.DOUBLE_SIZE_IN_BYTES) + # start/end time constants.UL_SHORT_SIZE_IN_BYTES + # memory constants.UL_SHORT_SIZE_IN_BYTES + # num blockchain peers ( 3 * # num blockchain peers ( constants.IP_ADDR_SIZE_IN_BYTES + # ip constants.UL_SHORT_SIZE_IN_BYTES + # port (2 * constants.UL_SHORT_SIZE_IN_BYTES) + # original block stats (7 * constants.UL_INT_SIZE_IN_BYTES)))) # rest of stats node_stats = {} helpers.add_stats_to_node_stats(node_stats, "127.0.0.1", 8001, 200, 300, 400, 500, 600, 700, 800, 100, 50) helpers.add_stats_to_node_stats(node_stats, "127.0.0.2", 8002, 200, 300, 400, 500, 600, 700, 800, 100, 50) helpers.add_stats_to_node_stats(node_stats, "127.0.0.3", 8003, 200, 300, 400, 500, 600, 700, 800, 100, 50) self.get_message_preview_successfully( BdnPerformanceStatsMessage(datetime.utcnow(), datetime.utcnow(), 100, node_stats), BdnPerformanceStatsMessage.MESSAGE_TYPE, expected_length) tx_info = TransactionInfo(crypto.double_sha256(b"123"), bytearray(4), 1) expected_length = constants.NETWORK_NUM_LEN + constants.SID_LEN + SHA256_HASH_LEN + \ constants.UL_INT_SIZE_IN_BYTES + constants.CONTROL_FLAGS_LEN + len(tx_info.contents) self.get_message_preview_successfully(TxContentsMessage(5, tx_info), TxContentsMessage.MESSAGE_TYPE, expected_length) expected_length = constants.NETWORK_NUM_LEN + constants.SID_LEN + constants.CONTROL_FLAGS_LEN self.get_message_preview_successfully( GetTxContentsMessage(1, 2), GetTxContentsMessage.MESSAGE_TYPE, expected_length)
def test_create_message_success_all_types(self): test_network_num = 10 test_protocol_version = bloxroute_version_manager.CURRENT_PROTOCOL_VERSION hello_message = self.create_message_successfully( HelloMessage(protocol_version=test_protocol_version, network_num=test_network_num, node_id=self.NODE_ID), HelloMessage) self.assertEqual(test_protocol_version, hello_message.protocol_version()) self.assertEqual(test_network_num, hello_message.network_num()) self.assertEqual(self.NODE_ID, hello_message.node_id()) self.create_message_successfully(AckMessage(), AckMessage) self.create_message_successfully(PingMessage(), PingMessage) self.create_message_successfully(PongMessage(), PongMessage) blob = bytearray(4) broadcast_message = self.create_message_successfully( BroadcastMessage(self.HASH, network_num=test_network_num, is_encrypted=True, source_id=self.NODE_ID, blob=blob), BroadcastMessage) self.assertEqual(self.HASH, broadcast_message.block_hash()) self.assertEqual(test_network_num, broadcast_message.network_num()) self.assertEqual(self.NODE_ID, broadcast_message.source_id()) self.assertTrue(broadcast_message.is_encrypted()) self.assertEqual(blob, broadcast_message.blob().tobytes()) sid = 12 tx_val = bytes(1 for _ in range(5)) tx_message = self.create_message_successfully( TxMessage(self.HASH, network_num=test_network_num, source_id=self.NODE_ID, short_id=sid, tx_val=tx_val), TxMessage) self.assertEqual(self.HASH, tx_message.tx_hash()) self.assertEqual(self.NODE_ID, tx_message.source_id()) self.assertEqual(sid, tx_message.short_id()) self.assertEqual(test_network_num, tx_message.network_num()) self.assertEqual(tx_val, tx_message.tx_val()) key = bytearray(1 for _ in range(KEY_SIZE)) key_message = self.create_message_successfully( KeyMessage(self.HASH, test_network_num, self.NODE_ID, bytearray(1 for _ in range(KEY_SIZE))), KeyMessage) self.assertEqual(key, key_message.key()) self.assertEqual(self.NODE_ID, key_message.source_id()) self.assertEqual(test_network_num, key_message.network_num()) self.assertEqual(self.HASH, key_message.block_hash()) block_holding_message = self.create_message_successfully( BlockHoldingMessage(self.HASH, test_network_num, self.NODE_ID), BlockHoldingMessage) self.assertEqual(self.NODE_ID, block_holding_message.source_id()) self.assertEqual(test_network_num, block_holding_message.network_num()) self.assertEqual(self.HASH, block_holding_message.block_hash()) get_txs = [1, 2, 3] get_txs_message = self.create_message_successfully( GetTxsMessage(get_txs), GetTxsMessage) self.assertEqual(get_txs, get_txs_message.get_short_ids()) txs = [ TransactionInfo(Sha256Hash(crypto.double_sha256(b"123")), bytearray(4), 1), TransactionInfo(Sha256Hash(crypto.double_sha256(b"234")), bytearray(8), 2) ] txs_message = self.create_message_successfully(TxsMessage(txs), TxsMessage) result_txs = txs_message.get_txs() for i, result_tx in enumerate(result_txs): self.assertEqual(txs[i].hash, result_tx.hash) self.assertEqual(txs[i].contents, result_tx.contents) self.assertEqual(txs[i].short_id, result_tx.short_id) get_tx_contents_message = self.create_message_successfully( GetTxContentsMessage(test_network_num, sid), GetTxContentsMessage) self.assertEqual(sid, get_tx_contents_message.get_short_id()) self.assertEqual(test_network_num, get_tx_contents_message.network_num()) tx_info = TransactionInfo(Sha256Hash(crypto.double_sha256(b"123")), bytearray(4), 1) tx_contents_message = self.create_message_successfully( TxContentsMessage(test_network_num, tx_info), TxContentsMessage) self.assertEqual(test_network_num, tx_contents_message.network_num()) result_tx_info = tx_contents_message.get_tx_info() self.assertEqual(tx_info.hash, result_tx_info.hash) self.assertEqual(tx_info.contents, result_tx_info.contents) self.assertEqual(tx_info.short_id, result_tx_info.short_id) short_ids = [1, 2, 33, 4444, 1234] block_hash = Sha256Hash(helpers.generate_bytearray(32)) get_block_txs_message: GetCompressedBlockTxsMessage = self.create_message_successfully( GetCompressedBlockTxsMessage(self.NETWORK_NUM, block_hash, short_ids), GetCompressedBlockTxsMessage) self.assertEqual(self.NETWORK_NUM, get_block_txs_message.network_num()) self.assertEqual(block_hash, get_block_txs_message.block_hash()) self.assertEqual(len(short_ids), len(get_block_txs_message)) self.assertEqual(short_ids, get_block_txs_message.get_short_ids()) txs_info = [ TransactionInfo(Sha256Hash(helpers.generate_bytearray(32)), helpers.generate_bytearray(200), 111), TransactionInfo(Sha256Hash(helpers.generate_bytearray(32)), helpers.generate_bytearray(300), 222), TransactionInfo(Sha256Hash(helpers.generate_bytearray(32)), helpers.generate_bytearray(400), 333) ] block_txs_message: CompressedBlockTxsMessage = self.create_message_successfully( CompressedBlockTxsMessage(self.NETWORK_NUM, block_hash, txs_info), CompressedBlockTxsMessage) self.assertEqual(self.NETWORK_NUM, block_txs_message.network_num()) self.assertEqual(block_hash, block_txs_message.block_hash()) self.assertEqual(len(txs_info), len(block_txs_message)) parsed_txs = block_txs_message.get_txs() for index in range(len(txs_info)): self.assertEqual(parsed_txs[index].short_id, txs_info[index].short_id) self.assertEqual(parsed_txs[index].contents, txs_info[index].contents) self.assertEqual(parsed_txs[index].hash, txs_info[index].hash)
def test_create_message_success_all_types(self): test_network_num = 10 test_protocol_version = bloxroute_version_manager.CURRENT_PROTOCOL_VERSION hello_message = self.create_message_successfully(HelloMessage(protocol_version=test_protocol_version, network_num=test_network_num, node_id=self.NODE_ID ), HelloMessage) self.assertEqual(test_protocol_version, hello_message.protocol_version()) self.assertEqual(test_network_num, hello_message.network_num()) self.assertEqual(self.NODE_ID, hello_message.node_id()) self.create_message_successfully(AckMessage(), AckMessage) self.create_message_successfully(PingMessage(), PingMessage) self.create_message_successfully(PongMessage(), PongMessage) blob = bytearray(4) broadcast_message = self.create_message_successfully(BroadcastMessage(self.HASH, network_num=test_network_num, is_encrypted=True, source_id=self.NODE_ID, blob=blob), BroadcastMessage) self.assertEqual(self.HASH, broadcast_message.block_hash()) self.assertEqual(test_network_num, broadcast_message.network_num()) self.assertEqual(self.NODE_ID, broadcast_message.source_id()) self.assertTrue(broadcast_message.is_encrypted()) self.assertEqual(blob, broadcast_message.blob().tobytes()) sid = 12 tx_val = bytes(1 for _ in range(5)) tx_message = self.create_message_successfully(TxMessage(self.HASH, network_num=test_network_num, source_id=self.NODE_ID, short_id=sid, tx_val=tx_val), TxMessage) self.assertEqual(self.HASH, tx_message.tx_hash()) self.assertEqual(self.NODE_ID, tx_message.source_id()) self.assertEqual(sid, tx_message.short_id()) self.assertEqual(test_network_num, tx_message.network_num()) self.assertEqual(tx_val, tx_message.tx_val()) key = bytearray(1 for _ in range(KEY_SIZE)) key_message = self.create_message_successfully( KeyMessage(self.HASH, test_network_num, self.NODE_ID, bytearray(1 for _ in range(KEY_SIZE))), KeyMessage ) self.assertEqual(key, key_message.key()) self.assertEqual(self.NODE_ID, key_message.source_id()) self.assertEqual(test_network_num, key_message.network_num()) self.assertEqual(self.HASH, key_message.block_hash()) block_holding_message = self.create_message_successfully( BlockHoldingMessage(self.HASH, test_network_num, self.NODE_ID), BlockHoldingMessage ) self.assertEqual(self.NODE_ID, block_holding_message.source_id()) self.assertEqual(test_network_num, block_holding_message.network_num()) self.assertEqual(self.HASH, block_holding_message.block_hash()) get_txs = [1, 2, 3] get_txs_message = self.create_message_successfully(GetTxsMessage(get_txs), GetTxsMessage) self.assertEqual(get_txs, get_txs_message.get_short_ids()) txs = [TransactionInfo(Sha256Hash(crypto.double_sha256(b"123")), bytearray(4), 1), TransactionInfo(Sha256Hash(crypto.double_sha256(b"234")), bytearray(8), 2)] txs_message = self.create_message_successfully(TxsMessage(txs), TxsMessage) result_txs = txs_message.get_txs() for i, result_tx in enumerate(result_txs): self.assertEqual(txs[i].hash, result_tx.hash) self.assertEqual(txs[i].contents, result_tx.contents) self.assertEqual(txs[i].short_id, result_tx.short_id)