Example #1
0
    def remove_broken_block(self, block_hash):
        """실패한 block 을 candidate blocks 에서 제외 한다.

        :return: 실패한 block Object
        """

        remove_block = self.__unconfirmed_blocks.pop(block_hash)[1]

        # refresh next_block's prev_hash
        prev_block: Block = None
        curr_block: Block = None  # It's for type hint
        for curr_block in self.__unconfirmed_blocks.values():
            if curr_block.prev_block_hash == block_hash:
                if prev_block:
                    curr_block.prev_block_hash = prev_block.block_hash
                else:
                    block_manager = ObjectManager(
                    ).channel_service.block_manager
                    block_chain = block_manager.get_blockchain()
                    curr_block.prev_block_hash = block_chain.last_block.block_hash
                break
            prev_block = curr_block

        return remove_block
    def block_height_sync(self, target_peer_stub=None):
        """block height sync with other peers
        """

        if self.__block_height_sync_lock is True:
            # ***** 이 보정 프로세스는 AnnounceConfirmBlock 메시지를 받았을때 블럭 Height 차이로 Peer 가 처리하지 못한 경우에도 진행한다.
            # 따라서 이미 sync 가 진행 중일때의 요청은 무시한다.
            logging.warning("block height sync is already running...")
            return

        peer_target = ObjectManager().peer_service.peer_target
        peer_manager = ObjectManager(
        ).peer_service.channel_manager.get_peer_manager(self.__channel_name)
        block_manager = ObjectManager(
        ).peer_service.channel_manager.get_block_manager(self.__channel_name)

        self.__block_height_sync_lock = True
        if target_peer_stub is None:
            target_peer_stub = peer_manager.get_leader_stub_manager()

        ### Block Height 보정 작업, Peer의 데이타 동기화 Process ###
        ### Love&Hate Algorithm ###
        logging.info("try block height sync...with love&hate")

        # Make Peer Stub List [peer_stub, ...] and get max_height of network
        max_height = 0
        peer_stubs = []
        target_list = list(peer_manager.get_IP_of_peers_in_group())
        for peer_target_each in target_list:
            target = ":".join(peer_target_each.split(":")[1:])
            if target != peer_target:
                logging.debug(f"try to target({target})")
                channel = grpc.insecure_channel(target)
                stub = loopchain_pb2_grpc.PeerServiceStub(channel)
                try:
                    response = stub.GetStatus(
                        loopchain_pb2.StatusRequest(
                            request="", channel=self.__channel_name))
                    if response.block_height > max_height:
                        # Add peer as higher than this
                        max_height = response.block_height
                        peer_stubs.append(stub)
                except Exception as e:
                    logging.warning("Already bad.... I don't love you" +
                                    str(e))

        if len(peer_stubs) == 0:
            util.logger.warning(
                f"peer_service:block_height_sync there is no other peer to height sync!"
            )
            self.__block_height_sync_lock = False
            return

        my_height = block_manager.get_blockchain().block_height

        if max_height > my_height:  # 자기가 가장 높은 블럭일때 처리 필요 TODO
            logging.info(
                f"You need block height sync to: {max_height} yours: {my_height}"
            )
            # 자기(현재 Peer)와 다르면 Peer 목록을 순회하며 마지막 block 에서 자기 Height Block 까지 역순으로 요청한다.
            # (blockchain 의 block 탐색 로직 때문에 height 순으로 조회는 비효율적이다.)

            preload_blocks = {}  # height : block dictionary

            # Target Peer 의 마지막 block hash 부터 시작한다.
            response = target_peer_stub.call(
                "GetLastBlockHash",
                loopchain_pb2.StatusRequest(request="",
                                            channel=self.__channel_name))
            logging.debug(response)
            request_hash = response.block_hash

            max_try = max_height - my_height
            while block_manager.get_blockchain().last_block.block_hash \
                    != request_hash and max_try > 0:

                for peer_stub in peer_stubs:
                    response = None
                    try:
                        # 이때 요청 받은 Peer 는 해당 Block 과 함께 자신의 현재 Height 를 같이 보내준다.
                        # TODO target peer 의 마지막 block 보다 높은 Peer 가 있으면 현재 target height 까지 완료 후
                        # TODO Height Sync 를 다시 한다.
                        response = peer_stub.BlockSync(
                            loopchain_pb2.BlockSyncRequest(
                                block_hash=request_hash,
                                channel=self.__channel_name),
                            conf.GRPC_TIMEOUT)
                    except Exception as e:
                        logging.warning("There is a bad peer, I hate you: " +
                                        str(e))

                    if response is not None and response.response_code == message_code.Response.success:
                        util.logger.spam(
                            f"response block_height({response.block_height})")
                        dump = response.block
                        block = pickle.loads(dump)

                        # 마지막 블럭에서 역순으로 블럭을 구한다.
                        request_hash = block.prev_block_hash

                        # add block to preload_blocks
                        logging.debug("Add preload_blocks Height: " +
                                      str(block.height))
                        preload_blocks[block.height] = block

                        if response.max_block_height > max_height:
                            max_height = response.max_block_height

                        if (my_height + 1) == block.height:
                            max_try = 0  # 더이상 요청을 진행하지 않는다.
                            logging.info("Block Height Sync Complete.")
                            break
                        max_try -= 1
                    else:
                        # 이 반복 요청중 응답 하지 않은 Peer 는 반복중에 다시 요청하지 않는다.
                        # (TODO: 향후 Bad에 대한 리포트 전략은 별도로 작업한다.)
                        peer_stubs.remove(peer_stub)
                        logging.warning(
                            "Make this peer to bad (error above or no response): "
                            + str(peer_stub))

            if preload_blocks.__len__() > 0:
                while my_height < max_height:
                    add_height = my_height + 1
                    logging.debug("try add block height: " + str(add_height))
                    try:
                        block_manager.add_block(preload_blocks[add_height])
                        my_height = add_height
                    except KeyError as e:
                        logging.error("fail block height sync: " + str(e))
                        break
                    except exception.BlockError as e:
                        logging.error(
                            "Block Error Clear all block and restart peer.")
                        block_manager.clear_all_blocks()
                        util.exit_and_msg(
                            "Block Error Clear all block and restart peer.")

            if my_height < max_height:
                # block height sync 가 완료되지 않았으면 다시 시도한다.
                logging.warning(
                    "fail block height sync in one time... try again...")
                self.__block_height_sync_lock = False
                self.block_height_sync(target_peer_stub)

        self.__block_height_sync_lock = False