def msg_inv(self, msg: InvOntMessage) -> None:
        if not self.node.should_process_block_hash():
            return

        contains_block = False
        inventory_requests = []
        block_hashes = []
        inventory_type, item_hashes = msg.inv_type()

        if inventory_type == InventoryOntType.MSG_BLOCK.value:
            for item_hash in item_hashes:
                block_hashes.append(item_hash)
                if item_hash not in self.node.blocks_seen.contents:
                    contains_block = True
                    inventory_requests.append(item_hash)
        else:
            for item_hash in item_hashes:
                inventory_requests.append(item_hash)

        # TODO: mark_blocks_and_request_cleanup

        for req in inventory_requests:
            get_data = GetDataOntMessage(
                magic=msg.magic(),
                inv_type=inventory_type,
                block=req
            )
            self.connection.enqueue_msg(get_data, prepend=contains_block)

        block_queuing_service = self.node.block_queuing_service_manager.get_block_queuing_service(self.connection)
        if block_queuing_service is not None:
            block_queuing_service.mark_blocks_seen_by_blockchain_node(block_hashes)
    def test_inv_and_get_data(self):
        seen_block_hash = OntObjectHash(buf=helpers.generate_bytearray(ONT_HASH_LEN), length=ONT_HASH_LEN)
        not_seen_block_hash = OntObjectHash(buf=helpers.generate_bytearray(ONT_HASH_LEN), length=ONT_HASH_LEN)
        self.node.blocks_seen.add(seen_block_hash)

        inv_msg_seen_block = InvOntMessage(123, InventoryOntType.MSG_BLOCK, [seen_block_hash, seen_block_hash])
        self.sut.msg_inv(inv_msg_seen_block)
        get_data_msg_msg_seen_bytes = self.sut.connection.get_bytes_to_send()
        self.assertEqual(0, len(get_data_msg_msg_seen_bytes))

        inv_msg = InvOntMessage(123, InventoryOntType.MSG_BLOCK, [seen_block_hash, not_seen_block_hash])
        self.sut.msg_inv(inv_msg)
        get_data_msg_bytes = self.sut.connection.get_bytes_to_send()
        get_data_msg = GetDataOntMessage(buf=get_data_msg_bytes)
        data_msg_inv_type, data_msg_block = get_data_msg.inv_type()
        self.assertEqual(InventoryOntType.MSG_BLOCK.value, data_msg_inv_type)
        self.assertEqual(not_seen_block_hash, data_msg_block)
    def test_peek_message_success_all_types(self):
        self.get_message_preview_successfully(self.VERSION_ONT_MESSAGE,
                                              VersionOntMessage.MESSAGE_TYPE,
                                              83)
        self.get_message_preview_successfully(
            VerAckOntMessage(self.MAGIC, True), VerAckOntMessage.MESSAGE_TYPE,
            1)
        self.get_message_preview_successfully(PingOntMessage(self.MAGIC),
                                              PingOntMessage.MESSAGE_TYPE, 8)
        self.get_message_preview_successfully(PongOntMessage(self.MAGIC, 123),
                                              PongOntMessage.MESSAGE_TYPE, 8)
        self.get_message_preview_successfully(GetAddrOntMessage(self.MAGIC),
                                              GetAddrOntMessage.MESSAGE_TYPE,
                                              0)
        self.get_message_preview_successfully(
            AddrOntMessage(
                self.MAGIC,
                [(int(time.time()), 123, "127.0.0.1", 20300, 20200, 1234)]),
            AddrOntMessage.MESSAGE_TYPE, 52)
        self.get_message_preview_successfully(
            OntConsensusMessage(self.MAGIC, self.VERSION, bytes(20)),
            OntConsensusMessage.MESSAGE_TYPE, 24)

        self.get_message_preview_successfully(
            InvOntMessage(self.MAGIC, InventoryOntType.MSG_TX,
                          [self.HASH, self.HASH]), InvOntMessage.MESSAGE_TYPE,
            69)
        self.get_message_preview_successfully(
            GetDataOntMessage(self.MAGIC, 1, self.HASH),
            GetDataOntMessage.MESSAGE_TYPE, 33)
        self.get_message_preview_successfully(
            GetHeadersOntMessage(self.MAGIC, 1, self.HASH, self.HASH),
            GetHeadersOntMessage.MESSAGE_TYPE, 65)
        self.get_message_preview_successfully(
            GetBlocksOntMessage(self.MAGIC, 1, self.HASH, self.HASH),
            GetBlocksOntMessage.MESSAGE_TYPE, 65)
        self.get_message_preview_successfully(
            TxOntMessage(self.MAGIC, self.VERSION, bytes(20)),
            TxOntMessage.MESSAGE_TYPE, 21)
        self.get_message_preview_successfully(
            BlockOntMessage(self.MAGIC, self.VERSION, self.HASH, self.HASH,
                            self.HASH, 0, 0, 0, bytes(10), bytes(20),
                            [bytes(33)] * 5, [bytes(2)] * 3, [bytes(32)] * 5,
                            self.HASH), BlockOntMessage.MESSAGE_TYPE, 524)
        self.get_message_preview_successfully(
            HeadersOntMessage(self.MAGIC, [bytes(1)] * 2),
            HeadersOntMessage.MESSAGE_TYPE, 6)
        self.get_message_preview_successfully(
            NotFoundOntMessage(self.MAGIC, self.HASH),
            NotFoundOntMessage.MESSAGE_TYPE, 32)
 def test_parse_message_success_all_types(self):
     self.create_message_successfully(self.VERSION_ONT_MESSAGE,
                                      VersionOntMessage)
     self.create_message_successfully(VerAckOntMessage(self.MAGIC, False),
                                      VerAckOntMessage)
     self.create_message_successfully(PingOntMessage(self.MAGIC),
                                      PingOntMessage)
     self.create_message_successfully(PongOntMessage(self.MAGIC, 123),
                                      PongOntMessage)
     self.create_message_successfully(GetAddrOntMessage(self.MAGIC),
                                      GetAddrOntMessage)
     self.create_message_successfully(
         AddrOntMessage(
             self.MAGIC,
             [(int(time.time()), 123, "127.0.0.1", 20300, 20200, 1234)]),
         AddrOntMessage)
     self.create_message_successfully(
         OntConsensusMessage(self.MAGIC, self.VERSION, bytes(20)),
         OntConsensusMessage)
     self.create_message_successfully(
         InvOntMessage(self.MAGIC, InventoryOntType.MSG_TX,
                       [self.HASH, self.HASH]), InvOntMessage)
     self.create_message_successfully(
         GetDataOntMessage(self.MAGIC, 1, self.HASH), GetDataOntMessage)
     self.create_message_successfully(
         GetHeadersOntMessage(self.MAGIC, 1, self.HASH, self.HASH),
         GetHeadersOntMessage)
     self.create_message_successfully(
         GetBlocksOntMessage(self.MAGIC, 1, self.HASH, self.HASH),
         GetBlocksOntMessage)
     self.create_message_successfully(
         TxOntMessage(self.MAGIC, self.VERSION, bytes(20)), TxOntMessage)
     self.create_message_successfully(
         BlockOntMessage(self.MAGIC, self.VERSION, self.HASH, self.HASH,
                         self.HASH, 0, 0, 0, bytes(10), bytes(20),
                         [bytes(33)] * 5, [bytes(2)] * 3, [bytes(32)] * 5,
                         self.HASH), BlockOntMessage)
     self.create_message_successfully(
         HeadersOntMessage(self.MAGIC, [bytes(1)] * 2), HeadersOntMessage)
     self.create_message_successfully(
         NotFoundOntMessage(self.MAGIC, self.HASH), NotFoundOntMessage)
    def msg_block(self, msg: BlockOntMessage) -> None:
        block_hash = msg.block_hash()

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

        if self.node.block_cleanup_service.is_marked_for_cleanup(block_hash):
            self.connection.log_trace("Marked block for cleanup: {}",
                                      block_hash)
            self.node.block_cleanup_service.clean_block_transactions(
                transaction_service=self.node.get_tx_service(), block_msg=msg)
        else:
            self.process_msg_block(msg)

        # After receiving block message sending INV message for the same block to Ontology node
        # This is needed to update Synced Headers value of the gateway peer on the Ontology node
        # If Synced Headers is not up-to-date than Ontology node does not push compact blocks to the gateway
        inv_msg = InvOntMessage(magic=self.node.opts.blockchain_net_magic,
                                inv_type=InventoryOntType.MSG_BLOCK,
                                blocks=[block_hash])
        self.connection.enqueue_msg(inv_msg)
        self.node.update_current_block_height(msg.height(), block_hash)
    def update_recovered_block(
        self, block_hash: Sha256Hash, block_msg: Union[BlockOntMessage, OntConsensusMessage]
    ):
        if block_hash not in self._blocks or block_hash not in self._blocks_waiting_for_recovery:
            return

        block_hash = cast(OntObjectHash, block_hash)

        self._blocks_waiting_for_recovery[block_hash] = False
        self.store_block_data(block_hash, block_msg)
        if isinstance(block_msg, BlockOntMessage):
            self.node.update_current_block_height(block_msg.height(), block_hash)
            inv_msg = InvOntMessage(
                magic=block_msg.magic(),
                inv_type=InventoryOntType.MSG_BLOCK,
                blocks=[block_hash],
            )
            self.connection.enqueue_msg(inv_msg)
        # pyre-fixme[25]: `block_msg` has type `OntConsensusMessage`,
        #  assertion `not isinstance(block_msg, bxgateway.messages.ont.consensus_ont_message.OntConsensusMessage)`
        #  will always fail.
        elif isinstance(block_msg, OntConsensusMessage):
            self.connection.enqueue_msg(block_msg)
    def push(
        self,
        block_hash: Sha256Hash,
        # pyre-fixme[9]: block_msg is declared to have type `Union[BlockOntMessage, OntConsensusMessage]`
        #  but is used as type `None`.
        block_msg: Union[BlockOntMessage, OntConsensusMessage] = None,
        waiting_for_recovery: bool = False,
    ):
        if self.node.opts.is_consensus and isinstance(block_msg,
                                                      BlockOntMessage):
            return
        super().push(block_hash, block_msg, waiting_for_recovery)
        self.connection.log_debug(
            "Added block {} to queuing service (waiting for recovery: {})",
            block_hash, waiting_for_recovery)
        self._clean_block_queue()

        if block_msg is None:
            return

        try:
            block_msg.validate_payload(block_msg.buf,
                                       block_msg.unpack(block_msg.buf))
        except ChecksumError:
            logger.debug(
                "Encountered checksum error, which can be caused by duplicate transaction. "
                "Stop processing block {}", block_hash)
            return
        block_hash = cast(OntObjectHash, block_hash)

        if isinstance(block_msg, BlockOntMessage):
            if block_hash in self._blocks and not waiting_for_recovery:
                self.node.update_current_block_height(block_msg.height(),
                                                      block_hash)
                inv_msg = InvOntMessage(
                    magic=block_msg.magic(),
                    inv_type=InventoryOntType.MSG_BLOCK,
                    blocks=[block_hash],
                )
                self.connection.enqueue_msg(inv_msg)
        # pyre-fixme[25]: `block_msg` has type `OntConsensusMessage`,
        #  assertion `not isinstance(block_msg, bxgateway.messages.ont.consensus_ont_message.OntConsensusMessage)`
        #  will always fail.
        elif isinstance(block_msg, OntConsensusMessage):
            if block_hash in self._blocks and not waiting_for_recovery:
                self.connection.log_info(
                    "Sending consensus message with block hash {} to blockchain node",
                    block_hash,
                )
                self.connection.enqueue_msg(block_msg)
                handling_time, relay_desc = self.node.track_block_from_bdn_handling_ended(
                    block_hash)
                block_stats.add_block_event_by_block_hash(
                    block_hash,
                    BlockStatEventType.BLOCK_SENT_TO_BLOCKCHAIN_NODE,
                    network_num=self.node.network_num,
                    broadcast_type=BroadcastMessageType.CONSENSUS,
                    more_info="{} bytes; Handled in {}; R - {}; {}".format(
                        len(block_msg.rawbytes()),
                        stats_format.duration(handling_time),
                        relay_desc,
                        block_msg.extra_stats_data(),
                    ),
                )
    def push(
        self,
        block_hash: Sha256Hash,
        # pyre-fixme[9]: block_msg is declared to have type `Union[BlockOntMessage, OntConsensusMessage]`
        #  but is used as type `None`.
        block_msg: Union[BlockOntMessage, OntConsensusMessage] = None,
        waiting_for_recovery: bool = False,
    ):
        if self.node.opts.is_consensus and isinstance(block_msg,
                                                      BlockOntMessage):
            return
        # pyre-fixme[6]: Expected `Optional[Variable[bxgateway.services.abstract_block_queuing_service.TBlockMessage
        #  (bound to bxcommon.messages.abstract_block_message.AbstractBlockMessage)]]` for 2nd positional only
        #  parameter to call `AbstractBlockQueuingService.push` but got `Union[BlockOntMessage, OntConsensusMessage]`.
        super().push(block_hash, block_msg, waiting_for_recovery)
        logger.debug("Added block {} to queuing service", block_hash)
        self._clean_block_queue()

        if block_msg is None:
            return

        try:
            block_msg.validate_payload(block_msg.buf,
                                       block_msg.unpack(block_msg.buf))
        except ChecksumError:
            logger.debug(
                "Encountered checksum error, which can be caused by duplicate transaction. "
                "Stop processing block {}", block_hash)
            return
        block_hash = cast(OntObjectHash, block_hash)

        if isinstance(block_msg, BlockOntMessage):
            if block_hash in self._blocks and not waiting_for_recovery:
                self.node.update_current_block_height(block_msg.height(),
                                                      block_hash)
                inv_msg = InvOntMessage(
                    magic=block_msg.magic(),
                    inv_type=InventoryOntType.MSG_BLOCK,
                    blocks=[block_hash],
                )
                self.node.send_msg_to_node(inv_msg)
        # pyre-fixme[25]: `block_msg` has type `OntConsensusMessage`,
        #  assertion `not isinstance(block_msg, bxgateway.messages.ont.consensus_ont_message.OntConsensusMessage)`
        #  will always fail.
        elif isinstance(block_msg, OntConsensusMessage):
            if block_hash in self._blocks and not waiting_for_recovery:
                logger.info(
                    "Sending consensus message with block hash {} to blockchain node",
                    block_hash)
                # self.node.block_queuing_service.send_block_to_node(block_hash)
                # the above one line is good, except for the wrong broadcast type in BLOCK_SENT_TO_BLOCKCHAIN_NODE
                self.node.send_msg_to_node(block_msg)
                handling_time, relay_desc = self.node.track_block_from_bdn_handling_ended(
                    block_hash)
                if not self.node.opts.track_detailed_sent_messages:
                    block_stats.add_block_event_by_block_hash(
                        block_hash,
                        BlockStatEventType.BLOCK_SENT_TO_BLOCKCHAIN_NODE,
                        network_num=self.node.network_num,
                        broadcast_type=BroadcastMessageType.CONSENSUS,
                        more_info="{} bytes; Handled in {}; R - {}; {}".format(
                            len(block_msg.rawbytes()),
                            stats_format.duration(handling_time),
                            relay_desc,
                            block_msg.extra_stats_data(),
                        ),
                    )