Beispiel #1
0
    def place_hold(self, block_hash, connection) -> None:
        """
        Places hold on block hash and propagates message.
        :param block_hash: ObjectHash
        :param connection:
        """
        block_stats.add_block_event_by_block_hash(
            block_hash,
            BlockStatEventType.BLOCK_HOLD_REQUESTED,
            network_num=connection.network_num,
            peers=[connection],
        )

        if block_hash in self._node.blocks_seen.contents:
            return

        if block_hash not in self._holds.contents:
            self._holds.add(block_hash, BlockHold(time.time(), connection))
            conns = self._node.broadcast(
                BlockHoldingMessage(block_hash, self._node.network_num),
                broadcasting_conn=connection,
                connection_types=(ConnectionType.RELAY_BLOCK,
                                  ConnectionType.GATEWAY))
            if len(conns) > 0:
                block_stats.add_block_event_by_block_hash(
                    block_hash,
                    BlockStatEventType.BLOCK_HOLD_SENT_BY_GATEWAY_TO_PEERS,
                    network_num=self._node.network_num,
                    peers=conns,
                )
    def advance_bytes_written_to_socket(self, bytes_sent):
        if self.message_tracker and self.message_tracker.is_sending_block_message():
            assert self.node.opts.track_detailed_sent_messages

            entry = self.message_tracker.messages[0]
            super(AbstractGatewayBlockchainConnection, self).advance_bytes_written_to_socket(bytes_sent)

            if not self.message_tracker.is_sending_block_message():
                block_message = typing.cast(AbstractBlockMessage, entry.message)
                block_message_queue_time = entry.queued_time
                block_message_length = entry.length
                block_hash = block_message.block_hash()
                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.network_num,
                                                          more_info="{} in {}; Handled in {}; R - {}; {}".format(
                                                              stats_format.byte_count(
                                                                  block_message_length
                                                              ),
                                                              stats_format.timespan(
                                                                  block_message_queue_time,
                                                                  time.time()
                                                              ),
                                                              stats_format.duration(handling_time),
                                                              relay_desc,
                                                              block_message.extra_stats_data()
                                                          ))
        else:
            super(AbstractGatewayBlockchainConnection, self).advance_bytes_written_to_socket(bytes_sent)
Beispiel #3
0
 def _holding_timeout(self, block_hash, hold):
     block_stats.add_block_event_by_block_hash(
         block_hash,
         BlockStatEventType.BLOCK_HOLD_TIMED_OUT,
         network_num=hold.connection.network_num,
         peers=[hold.connection])
     self._process_and_broadcast_block(hold.block_message, hold.connection)
Beispiel #4
0
    def _process_ready_new_blocks(self):
        while self._ready_new_blocks:
            ready_block_hash = self._ready_new_blocks.pop()
            pending_new_block = self.pending_new_block_parts.contents[
                ready_block_hash]
            last_known_total_difficulty = self.node.try_calculate_total_difficulty(
                ready_block_hash, pending_new_block)
            new_block_msg = InternalEthBlockInfo.from_new_block_parts(
                pending_new_block, last_known_total_difficulty)
            self.pending_new_block_parts.remove_item(ready_block_hash)

            if self.is_valid_block_timestamp(new_block_msg):
                block_stats.add_block_event_by_block_hash(
                    ready_block_hash,
                    BlockStatEventType.BLOCK_RECEIVED_FROM_BLOCKCHAIN_NODE,
                    network_num=self.connection.network_num,
                    more_info="Protocol: {}, Network: {}. {}".format(
                        self.node.opts.blockchain_protocol,
                        self.node.opts.blockchain_network,
                        new_block_msg.extra_stats_data()),
                    block_height=new_block_msg.block_number(),
                )
                self.node.block_queuing_service.mark_block_seen_by_blockchain_node(
                    ready_block_hash, new_block_msg)
                self.node.block_processing_service.queue_block_for_processing(
                    new_block_msg, self.connection)
    def send_block_to_node(self,
                           block_hash: Sha256Hash,
                           block_msg: Optional[TBlockMessage] = None) -> None:
        if not self.node.should_process_block_hash(block_hash):
            return

        if block_msg is None:
            block_msg = self._blocks[block_hash]

        logger.info("Forwarding block {} to blockchain node.", block_hash)

        assert block_msg is not None
        self.node.send_msg_to_node(block_msg)
        (
            handling_time,
            relay_desc,
        ) = self.node.track_block_from_bdn_handling_ended(block_hash)

        # if tracking detailed send info, log this event only after all
        # bytes written to sockets
        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,
                more_info="{} bytes; Handled in {}; R - {}; {}".format(
                    len(block_msg.rawbytes()),
                    stats_format.duration(handling_time),
                    relay_desc,
                    block_msg.extra_stats_data(),
                ),
            )
Beispiel #6
0
    def record_block_receipt(self, cipher_hash, connection):
        """
        Records a receipt of a block hash. Releases key if threshold reached.
        :param cipher_hash encrypted block ObjectHash
        :param connection posting block received receipt
        """
        if cipher_hash in self._receipt_tracker:
            self._receipt_tracker[cipher_hash] += 1
            block_stats.add_block_event_by_block_hash(
                cipher_hash,
                BlockStatEventType.ENC_BLOCK_RECEIVED_BLOCK_RECEIPT,
                network_num=self._node.network_num,
                peers=[connection],
                more_info="{}, {} receipts".format(
                    stats_format.connection(connection),
                    self._receipt_tracker[cipher_hash]))

            if self._are_enough_receipts_received(cipher_hash):
                logger.debug(
                    "Received enough block receipt messages. Releasing key for block with hash: {}",
                    convert.bytes_to_hex(cipher_hash.binary))
                self._send_key(cipher_hash)
                self._node.alarm_queue.unregister_alarm(
                    self._alarms[cipher_hash])
                del self._receipt_tracker[cipher_hash]
                del self._alarms[cipher_hash]
Beispiel #7
0
    def _propagate_block_to_gateway_peers(self, cipher_hash, bx_block):
        """
        Propagates unencrypted bx_block to all gateway peers for encryption and sending to bloXroute.
        Also sends keys to bloXroute in case this was user error (e.g. no gateway peers).
        Called after a timeout. This invalidates all future bx_block receipts.
        """
        bx_block_hash = crypto.double_sha256(bx_block)
        hex_bx_block_hash = convert.bytes_to_hex(bx_block_hash)

        logger.debug("Did not receive enough receipts for: {}. Propagating compressed block to other gateways: {}",
                     cipher_hash, hex_bx_block_hash)
        self._send_key(cipher_hash)

        request = BlockPropagationRequestMessage(bx_block)
        conns = self._node.broadcast(request, None, connection_types=(ConnectionType.GATEWAY,))
        block_stats.add_block_event_by_block_hash(cipher_hash,
                                                  BlockStatEventType.ENC_BLOCK_PROPAGATION_NEEDED,
                                                  network_num=self._node.network_num,
                                                  compressed_block_hash=hex_bx_block_hash,
                                                  peers=conns,
                                                  more_info="Peers: {}, {} receipts".format(
                                                      stats_format.connections(conns),
                                                      self._receipt_tracker[cipher_hash]))

        del self._receipt_tracker[cipher_hash]
        del self._alarms[cipher_hash]
        return constants.CANCEL_ALARMS
Beispiel #8
0
    def _propagate_unencrypted_block_to_network(self, bx_block, connection,
                                                block_info):
        if block_info is None:
            raise ValueError(
                "Block info is required to propagate unencrypted block")

        broadcast_message = BroadcastMessage(block_info.block_hash,
                                             self._node.network_num,
                                             is_encrypted=False,
                                             blob=bx_block)
        conns = self._node.broadcast(
            broadcast_message,
            connection,
            connection_types=[ConnectionType.RELAY_BLOCK])
        handling_duration = self._node.track_block_from_node_handling_ended(
            block_info.block_hash)
        block_stats.add_block_event_by_block_hash(
            block_info.block_hash,
            BlockStatEventType.ENC_BLOCK_SENT_FROM_GATEWAY_TO_NETWORK,
            network_num=self._node.network_num,
            requested_by_peer=False,
            peers=conns,
            more_info="Peers: {}; Unencrypted; {}; Handled in {}".format(
                stats_format.connections(conns),
                self._format_block_info_stats(block_info),
                stats_format.duration(handling_duration)))
        logger.info("Propagating block {} to the BDN.", block_info.block_hash)
        return broadcast_message
Beispiel #9
0
    def send_block_to_node(self,
                           block_hash: Sha256Hash,
                           block_msg: Optional[TBlockMessage] = None) -> None:
        if not self.node.should_process_block_hash(block_hash):
            return

        if block_msg is None:
            block_msg = self.node.block_storage[block_hash]

        self.connection.log_info("Forwarding block {} to node", block_hash)

        assert block_msg is not None
        self.connection.enqueue_msg(block_msg)

        # TODO: revisit this metric for multi-node gateway
        (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,
            more_info="{} bytes; Handled in {}; R - {}; {}".format(
                len(block_msg.rawbytes()),
                stats_format.duration(handling_time),
                relay_desc,
                block_msg.extra_stats_data(),
            ),
        )
Beispiel #10
0
    def _propagate_encrypted_block_to_network(self, bx_block, connection,
                                              block_info):

        if block_info is None or block_info.block_hash is None:
            block_hash = b"Unknown"
            requested_by_peer = True
        else:
            block_hash = block_info.block_hash
            requested_by_peer = False

        encrypt_start_datetime = datetime.datetime.utcnow()
        encrypt_start_timestamp = time.time()
        encrypted_block, raw_cipher_hash = self._node.in_progress_blocks.encrypt_and_add_payload(
            bx_block)

        compressed_size = len(bx_block)
        encrypted_size = len(encrypted_block)

        encryption_details = "Encryption: {}; Size change: {}->{}bytes, {}".format(
            stats_format.timespan(encrypt_start_timestamp, time.time()),
            compressed_size, encrypted_size,
            stats_format.ratio(encrypted_size, compressed_size))

        block_stats.add_block_event_by_block_hash(
            block_hash,
            BlockStatEventType.BLOCK_ENCRYPTED,
            start_date_time=encrypt_start_datetime,
            end_date_time=datetime.datetime.utcnow(),
            network_num=self._node.network_num,
            matching_block_hash=convert.bytes_to_hex(raw_cipher_hash),
            matching_block_type=StatBlockType.ENCRYPTED.value,
            more_info=encryption_details)

        cipher_hash = Sha256Hash(raw_cipher_hash)
        broadcast_message = BroadcastMessage(cipher_hash,
                                             self._node.network_num,
                                             is_encrypted=True,
                                             blob=encrypted_block)

        conns = self._node.broadcast(
            broadcast_message,
            connection,
            connection_types=[ConnectionType.RELAY_BLOCK])

        handling_duration = self._node.track_block_from_node_handling_ended(
            block_hash)
        block_stats.add_block_event_by_block_hash(
            cipher_hash,
            BlockStatEventType.ENC_BLOCK_SENT_FROM_GATEWAY_TO_NETWORK,
            network_num=self._node.network_num,
            requested_by_peer=requested_by_peer,
            peers=conns,
            more_info="Peers: {}; {}; {}; Requested by peer: {}; Handled in {}"
            .format(stats_format.connections(conns), encryption_details,
                    self._format_block_info_stats(block_info),
                    requested_by_peer,
                    stats_format.duration(handling_duration)))
        self.register_for_block_receipts(cipher_hash, bx_block)
        return broadcast_message
 def can_add_block_to_queuing_service(self, block_hash: Sha256Hash) -> bool:
     if block_hash in self._blocks_seen_by_blockchain_node:
         block_stats.add_block_event_by_block_hash(
             block_hash,
             BlockStatEventType.BLOCK_IGNORE_SEEN_BY_BLOCKCHAIN_NODE,
             self.node.network_num,
         )
         return False
     return True
Beispiel #12
0
    def msg_new_block_hashes(self, msg: NewBlockHashesEthProtocolMessage):
        if not self.node.should_process_block_hash(msg.block_hash()):
            return

        block_hash_number_pairs = []
        for block_hash, block_number in msg.get_block_hash_number_pairs():
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_ANNOUNCED_BY_BLOCKCHAIN_NODE,
                network_num=self.connection.network_num,
                more_info="Protocol: {}, Network: {}. {}".format(
                    self.node.opts.blockchain_protocol,
                    self.node.opts.blockchain_network, msg.extra_stats_data()),
                block_height=block_number,
            )
            gateway_bdn_performance_stats_service.log_block_message_from_blockchain_node(
                False)

            if block_hash in self.node.blocks_seen.contents:
                self.node.on_block_seen_by_blockchain_node(
                    block_hash, block_number=block_number)
                block_stats.add_block_event_by_block_hash(
                    block_hash,
                    BlockStatEventType.
                    BLOCK_RECEIVED_FROM_BLOCKCHAIN_NODE_IGNORE_SEEN,
                    network_num=self.connection.network_num,
                    block_height=block_number,
                )
                self.connection.log_info(
                    "Ignoring duplicate block {} from local blockchain node.",
                    block_hash)
                continue

            recovery_cancelled = self.node.on_block_seen_by_blockchain_node(
                block_hash, block_number=block_number)
            if recovery_cancelled:
                continue

            self.node.track_block_from_node_handling_started(block_hash)
            block_hash_number_pairs.append((block_hash, block_number))

            self.connection.log_info(
                "Fetching block {} from local Ethereum node.", block_hash)

        if not block_hash_number_pairs:
            return

        for block_hash, block_number in block_hash_number_pairs:
            # pyre-fixme[6]: Expected `memoryview` for 1st param but got `None`.
            self.pending_new_block_parts.add(
                block_hash, NewBlockParts(None, None, block_number))
            self.connection.enqueue_msg(
                GetBlockHeadersEthProtocolMessage(None, block_hash.binary, 1,
                                                  0, False))

        self.request_block_body(
            [block_hash for block_hash, _ in block_hash_number_pairs])
Beispiel #13
0
 def msg_block(self, msg: BlockOntMessage) -> None:
     block_stats.add_block_event_by_block_hash(
         msg.block_hash(),
         BlockStatEventType.REMOTE_BLOCK_RECEIVED_BY_GATEWAY,
         network_num=self.connection.network_num,
         more_info="Protocol: {}, Network: {}".format(
             self.connection.node.opts.blockchain_protocol,
             self.connection.node.opts.blockchain_network))
     return self.msg_proxy_response(msg)
Beispiel #14
0
 def msg_get_data(self, msg: GetDataOntMessage) -> None:
     inventory_type, item_hash = msg.inv_type()
     if inventory_type == InventoryOntType.MSG_BLOCK.value:
         block_stats.add_block_event_by_block_hash(
             item_hash,
             BlockStatEventType.REMOTE_BLOCK_REQUESTED_BY_GATEWAY,
             network_num=self.connection.network_num,
             more_info="Protocol: {}, Network: {}".format(
                 self.node.opts.blockchain_protocol,
                 self.node.opts.blockchain_network))
     self.node.block_queuing_service.send_block_to_nodes(item_hash)
Beispiel #15
0
    def process_msg_block(self,
                          msg: AbstractBlockMessage,
                          block_number: Optional[int] = None) -> None:
        block_hash = msg.block_hash()
        # if gateway is still syncing, skip this process
        if not self.node.should_process_block_hash(block_hash):
            return

        self.node.block_cleanup_service.on_new_block_received(
            block_hash, msg.prev_block_hash())
        block_stats.add_block_event_by_block_hash(
            block_hash,
            BlockStatEventType.BLOCK_RECEIVED_FROM_BLOCKCHAIN_NODE,
            network_num=self.connection.network_num,
            more_info="Protocol: {}, Network: {}".format(
                self.node.opts.blockchain_protocol,
                self.node.opts.blockchain_network, msg.extra_stats_data()))
        gateway_bdn_performance_stats_service.log_block_message_from_blockchain_node(
            self.connection.endpoint, True)
        if block_hash in self.node.blocks_seen.contents:
            self.node.on_block_seen_by_blockchain_node(
                block_hash, self.connection, msg, block_number=block_number)
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.
                BLOCK_RECEIVED_FROM_BLOCKCHAIN_NODE_IGNORE_SEEN,
                network_num=self.connection.network_num)
            self.connection.log_info(
                "Discarding duplicate block {} from local blockchain node.",
                block_hash)
            self.node.log_blocks_network_content(self.node.network_num, msg)
            return

        if not self.is_valid_block_timestamp(msg):
            return

        gateway_bdn_performance_stats_service.log_block_from_blockchain_node(
            self.connection.endpoint)

        canceled_recovery = self.node.on_block_seen_by_blockchain_node(
            block_hash, self.connection, msg)
        if canceled_recovery:
            return

        self.node.track_block_from_node_handling_started(block_hash)
        self.node.on_block_seen_by_blockchain_node(block_hash,
                                                   self.connection,
                                                   msg,
                                                   block_number=block_number)
        self.node.block_processing_service.queue_block_for_processing(
            msg, self.connection)
        self.node.block_queuing_service_manager.push(
            block_hash, msg, node_received_from=self.connection)
        return
 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)
Beispiel #17
0
    def try_send_header_to_node(self, block_hash: Sha256Hash) -> bool:
        if not self.node.block_queuing_service_manager.is_in_common_block_storage(block_hash):
            return False
        block_message = cast(TBlockMessage, self.node.block_storage[block_hash])

        header_msg = self.build_block_header_message(block_hash, block_message)
        self.connection.enqueue_msg(header_msg)
        block_stats.add_block_event_by_block_hash(
            block_hash,
            BlockStatEventType.BLOCK_HEADER_SENT_TO_BLOCKCHAIN_NODE,
            network_num=self.node.network_num,
        )
        return True
Beispiel #18
0
 def log_requested_remote_blocks(self,
                                 block_hashes: List[Sha256Hash]) -> None:
     if self._skip_remote_block_requests_stats_count > 0:
         self._skip_remote_block_requests_stats_count -= 1
     else:
         for block_hash in block_hashes:
             block_stats.add_block_event_by_block_hash(
                 block_hash,
                 BlockStatEventType.REMOTE_BLOCK_REQUESTED_BY_GATEWAY,
                 network_num=self.network_num,
                 more_info=f"Protocol: {self.opts.blockchain_protocol}, "
                 f"Network: {self.opts.blockchain_network}")
         self._requested_remote_blocks_queue.append(block_hashes)
    def msg_compressed_block_txs(self, msg: CompressedBlockTxsMessage):
        start_time = time.time()

        self.msg_txs(msg.to_txs_message(),
                     RecoveredTxsSource.COMPRESSED_BLOCK_TXS_RECEIVED)

        block_stats.add_block_event_by_block_hash(
            msg.block_hash(),
            BlockStatEventType.ENC_BLOCK_COMPRESSED_TXS_RECEIVED_BY_GATEWAY,
            network_num=msg.network_num(),
            peers=[self],
            more_info="{}. processing time: {}".format(
                stats_format.connection(self),
                stats_format.timespan(start_time, time.time())))
Beispiel #20
0
 def _send_key(self, cipher_hash):
     key = self._node.in_progress_blocks.get_encryption_key(bytes(cipher_hash.binary))
     key_message = KeyMessage(cipher_hash, self._node.network_num, key=key)
     conns = self._node.broadcast(
         key_message,
         None,
         connection_types=(ConnectionType.RELAY_BLOCK, ConnectionType.GATEWAY)
     )
     block_stats.add_block_event_by_block_hash(
         cipher_hash,
         BlockStatEventType.ENC_BLOCK_KEY_SENT_FROM_GATEWAY_TO_NETWORK,
         network_num=self._node.network_num,
         peers=conns,
     )
    def _process_ready_new_blocks(self):
        while self._ready_new_blocks:
            ready_block_hash = self._ready_new_blocks.pop()
            pending_new_block = self.pending_new_block_parts.contents[
                ready_block_hash]
            self.pending_new_block_parts.remove_item(ready_block_hash)

            if ready_block_hash in self.node.blocks_seen.contents:
                self.node.on_block_seen_by_blockchain_node(
                    ready_block_hash,
                    self.connection,
                    block_number=pending_new_block.block_number)
                self.connection.log_info(
                    "Discarding already seen block {} received in block bodies msg from local blockchain node.",
                    ready_block_hash)
                return

            last_known_total_difficulty = self.node.try_calculate_total_difficulty(
                ready_block_hash, pending_new_block)
            new_block_msg = InternalEthBlockInfo.from_new_block_parts(
                pending_new_block, last_known_total_difficulty)

            if self.is_valid_block_timestamp(new_block_msg):
                block_stats.add_block_event_by_block_hash(
                    ready_block_hash,
                    BlockStatEventType.BLOCK_RECEIVED_FROM_BLOCKCHAIN_NODE,
                    network_num=self.connection.network_num,
                    more_info="Protocol: {}, Network: {}. {}".format(
                        self.node.opts.blockchain_protocol,
                        self.node.opts.blockchain_network,
                        new_block_msg.extra_stats_data()),
                    block_height=new_block_msg.block_number(),
                )
                gateway_bdn_performance_stats_service.log_block_from_blockchain_node(
                    self.connection.endpoint)

                canceled_recovery = self.node.on_block_seen_by_blockchain_node(
                    ready_block_hash, self.connection, new_block_msg,
                    pending_new_block.block_number)
                if canceled_recovery:
                    return

                self.node.block_queuing_service_manager.push(
                    ready_block_hash,
                    new_block_msg,
                    node_received_from=self.connection)
                self.node.block_processing_service.queue_block_for_processing(
                    new_block_msg, self.connection)
    def try_send_header_to_node(self, block_hash: Sha256Hash) -> bool:
        if block_hash not in self._blocks:
            return False

        block_message = self._blocks[block_hash]
        if block_message is None:
            return False

        header_msg = self.build_block_header_message(block_hash, block_message)
        self.node.send_msg_to_node(header_msg)
        block_stats.add_block_event_by_block_hash(
            block_hash,
            BlockStatEventType.BLOCK_HEADER_SENT_TO_BLOCKCHAIN_NODE,
            network_num=self.node.network_num,
        )
        return True
    def cancel_hold_timeout(self, block_hash, connection):
        """
        Lifts hold on block hash and cancels timeout.
        :param block_hash: ObjectHash
        :param connection: connection cancelling hold
        """
        if block_hash in self._holds.contents:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_HOLD_LIFTED,
                network_num=connection.network_num,
                more_info=stats_format.connection(connection))

            hold = self._holds.contents[block_hash]
            if hold.alarm is not None:
                self._node.alarm_queue.unregister_alarm(hold.alarm)
            del self._holds.contents[block_hash]
Beispiel #24
0
    def _check_for_sent_or_queued_forked_block(self, block_hash: Sha256Hash,
                                               block_number: int) -> bool:
        """
        Returns True if a block has already been queued or sent at the same height.
        """

        is_duplicate = False
        more_info = ""
        for queued_block_hash, timestamp in self._block_queue:
            if (not self._blocks_waiting_for_recovery[queued_block_hash]
                    and queued_block_hash in self._height_by_block_hash):
                if block_number == self._height_by_block_hash[
                        queued_block_hash]:
                    self.connection.log_info(
                        "In queuing service, fork detected at height {}. Setting aside block {} in favor of {}.",
                        block_number, block_hash, queued_block_hash)
                    is_duplicate = True
                    more_info = "already queued"

        if block_number in self.sent_block_at_height:
            self.connection.log_info(
                "In queuing service, fork detected at height {}. "
                "Setting aside block {} in favor of already sent {}.",
                block_number, block_hash,
                self.sent_block_at_height[block_number])
            is_duplicate = True
            more_info = "already sent"

        if block_number in self.accepted_block_hash_at_height:
            self.connection.log_info(
                "In queuing service, fork detected at height {}. "
                "Setting aside block {} in favor of already accepted {}.",
                block_number, block_hash,
                self.accepted_block_hash_at_height[block_number])
            is_duplicate = True
            more_info = "already accepted"

        if is_duplicate:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_IGNORE_DUPLICATE_HEIGHT,
                self.node.network_num,
                more_info=more_info)

        return is_duplicate
    def queue_block_for_processing(self, block_message, connection):
        """
        Queues up block for processing on timeout if hold message received.
        If no hold exists, compress and broadcast block immediately.
        :param block_message: block message to process
        :param connection: receiving connection (AbstractBlockchainConnection)
        """

        block_hash = block_message.block_hash()
        connection.log_info("Processing block {} from local blockchain node.",
                            block_hash)

        if block_hash in self._holds.contents:
            hold: BlockHold = self._holds.contents[block_hash]
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_HOLD_HELD_BLOCK,
                network_num=connection.network_num,
                more_info=stats_format.connection(hold.holding_connection))
            if hold.alarm is None:
                hold.alarm = self._node.alarm_queue.register_alarm(
                    self._node.opts.blockchain_block_hold_timeout_s,
                    self._holding_timeout, block_hash, hold)
                hold.block_message = block_message
                hold.connection = connection
        else:
            # Broadcast BlockHoldingMessage through relays and gateways
            conns = self._node.broadcast(BlockHoldingMessage(
                block_hash, self._node.network_num),
                                         broadcasting_conn=connection,
                                         prepend_to_queue=True,
                                         connection_types=[
                                             ConnectionType.RELAY_BLOCK,
                                             ConnectionType.GATEWAY
                                         ])
            if len(conns) > 0:
                block_stats.add_block_event_by_block_hash(
                    block_hash,
                    BlockStatEventType.BLOCK_HOLD_SENT_BY_GATEWAY_TO_PEERS,
                    network_num=self._node.network_num,
                    more_info=stats_format.connections(conns))
            self._process_and_broadcast_block(block_message, connection)
Beispiel #26
0
    def msg_get_data(self, msg: GetDataBtcMessage) -> None:
        """
        Handle GETDATA message from Bitcoin node.
        :param msg: GETDATA message
        """

        for inv_type, object_hash in msg:
            if InventoryType.is_block(inv_type):
                block_stats.add_block_event_by_block_hash(
                    object_hash,
                    BlockStatEventType.REMOTE_BLOCK_REQUESTED_BY_GATEWAY,
                    network_num=self.connection.network_num,
                    more_info="Protocol: {}, Network: {}".format(
                        self.node.opts.blockchain_protocol,
                        self.node.opts.blockchain_network))
            inv_msg = InvBtcMessage(magic=self.magic,
                                    inv_vects=[(InventoryType.MSG_BLOCK,
                                                object_hash)])
            self.node.send_msg_to_node(inv_msg)
        return self.msg_proxy_request(msg)
Beispiel #27
0
    def log_received_remote_blocks(self, blocks_count: int) -> None:
        if len(self._requested_remote_blocks_queue) > 0:
            expected_blocks = self._requested_remote_blocks_queue.pop()

            if len(expected_blocks) != blocks_count:
                logger.warning(log_messages.BLOCK_COUNT_MISMATCH,
                    blocks_count, len(expected_blocks))
                self._skip_remote_block_requests_stats_count = len(self._requested_remote_blocks_queue) * 2
                self._requested_remote_blocks_queue.clear()
                return

            for block_hash in expected_blocks:
                block_stats.add_block_event_by_block_hash(block_hash,
                                                          BlockStatEventType.REMOTE_BLOCK_RECEIVED_BY_GATEWAY,
                                                          network_num=self.network_num,
                                                          more_info="Protocol: {}, Network: {}".format(
                                                              self.opts.blockchain_protocol,
                                                              self.opts.blockchain_network))
        else:
            logger.warning(log_messages.UNEXPECTED_BLOCKS)
    def start_transaction_recovery(
            self,
            unknown_sids: Iterable[int],
            unknown_hashes: Iterable[Sha256Hash],
            block_hash: Sha256Hash,
            connection: Optional[AbstractRelayConnection] = None):
        all_unknown_sids = []
        all_unknown_sids.extend(unknown_sids)
        tx_service = self._node.get_tx_service()

        # retrieving sids of txs with unknown contents
        for tx_hash in unknown_hashes:
            tx_sid = tx_service.get_short_id(tx_hash)
            all_unknown_sids.append(tx_sid)

        get_txs_message = GetTxsMessage(short_ids=all_unknown_sids)
        self._node.broadcast(
            get_txs_message,
            connection_types=[ConnectionType.RELAY_TRANSACTION])

        if connection is not None:
            tx_stats.add_txs_by_short_ids_event(
                all_unknown_sids,
                TransactionStatEventType.
                TX_UNKNOWN_SHORT_IDS_REQUESTED_BY_GATEWAY_FROM_RELAY,
                network_num=self._node.network_num,
                peer=connection.peer_desc,
                block_hash=convert.bytes_to_hex(block_hash.binary))
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_STARTED,
                network_num=connection.network_num,
                request_hash=convert.bytes_to_hex(
                    crypto.double_sha256(get_txs_message.rawbytes())))
        else:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_REPEATED,
                network_num=self._node.network_num,
                request_hash=convert.bytes_to_hex(
                    crypto.double_sha256(get_txs_message.rawbytes())))
Beispiel #29
0
    def msg_block(self, msg: AbstractBlockMessage):
        """
        Handle a block message. Sends to node for encryption, then broadcasts.
        """
        block_hash = msg.block_hash()
        node = self.connection.node
        # if gateway is still syncing, skip this process
        if not node.should_process_block_hash(block_hash):
            return

        node.block_cleanup_service.on_new_block_received(block_hash, msg.prev_block_hash())
        block_stats.add_block_event_by_block_hash(block_hash, BlockStatEventType.BLOCK_RECEIVED_FROM_BLOCKCHAIN_NODE,
                                                  network_num=self.connection.network_num,
                                                  more_info="Protocol: {}, Network: {}".format(
                                                      node.opts.blockchain_protocol,
                                                      node.opts.blockchain_network,
                                                      msg.extra_stats_data()
                                                  )
                                                  )
        if block_hash in self.connection.node.blocks_seen.contents:
            node.on_block_seen_by_blockchain_node(block_hash)
            block_stats.add_block_event_by_block_hash(block_hash,
                                                      BlockStatEventType.BLOCK_RECEIVED_FROM_BLOCKCHAIN_NODE_IGNORE_SEEN,
                                                      network_num=self.connection.network_num)
            self.connection.log_info(
                "Discarding duplicate block {} from local blockchain node.",
                block_hash
            )
            return

        if not self.is_valid_block_timestamp(msg):
            return

        node.track_block_from_node_handling_started(block_hash)
        node.on_block_seen_by_blockchain_node(block_hash, msg)
        node.block_processing_service.queue_block_for_processing(msg, self.connection)
        gateway_bdn_performance_stats_service.log_block_from_blockchain_node()
        node.block_queuing_service.store_block_data(block_hash, msg)
        return
Beispiel #30
0
    def start_transaction_recovery(
            self,
            unknown_sids: Iterable[int],
            unknown_hashes: Iterable[Sha256Hash],
            block_hash: Sha256Hash,
            connection: Optional[AbstractRelayConnection] = None) -> None:
        all_unknown_sids = []
        all_unknown_sids.extend(unknown_sids)
        tx_service = self._node.get_tx_service()

        # retrieving sids of txs with unknown contents
        for tx_hash in unknown_hashes:
            transaction_key = tx_service.get_transaction_key(tx_hash)
            tx_sid = tx_service.get_short_id_by_key(transaction_key)
            all_unknown_sids.append(tx_sid)

        if not self._node.opts.request_recovery:
            if connection is not None:
                network_num = connection.network_num
            else:
                network_num = self._node.network_num
            # log recovery started to match with recovery completing
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_STARTED,
                network_num=network_num,
                txs_count=len(all_unknown_sids),
                more_info="recovery from relay is disabled",
            )
            return

        get_txs_message = GetTxsMessage(short_ids=all_unknown_sids)
        self._node.broadcast(
            get_txs_message,
            connection_types=(ConnectionType.RELAY_TRANSACTION, ))

        if connection is not None:
            tx_stats.add_txs_by_short_ids_event(
                all_unknown_sids,
                TransactionStatEventType.
                TX_UNKNOWN_SHORT_IDS_REQUESTED_BY_GATEWAY_FROM_RELAY,
                network_num=self._node.network_num,
                peers=[connection],
                block_hash=convert.bytes_to_hex(block_hash.binary))
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_STARTED,
                network_num=connection.network_num,
                txs_count=len(all_unknown_sids),
                request_hash=convert.bytes_to_hex(
                    crypto.double_sha256(get_txs_message.rawbytes())))
        else:
            block_stats.add_block_event_by_block_hash(
                block_hash,
                BlockStatEventType.BLOCK_RECOVERY_REPEATED,
                network_num=self._node.network_num,
                txs_count=len(all_unknown_sids),
                request_hash=convert.bytes_to_hex(
                    crypto.double_sha256(get_txs_message.rawbytes())))