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())
Пример #4
0
    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)
Пример #5
0
    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)