Пример #1
0
    def GetLastBlockHash(self, request, context):
        """ 마지막 블럭 조회

        :param request: 블럭요청
        :param context:
        :return: 마지막 블럭
        """
        channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel
        # Peer To Client
        block_manager = self.peer_service.channel_manager.get_block_manager(
            channel_name)
        last_block = block_manager.get_blockchain().last_block
        response_code, response_msg = message_code.get_response(
            message_code.Response.fail)
        block_hash = None

        if last_block is not None:
            response_code, response_msg = message_code.get_response(
                message_code.Response.success)
            block_hash = last_block.block_hash

        return loopchain_pb2.BlockReply(
            response_code=response_code,
            message=(response_msg +
                     (" This is for block height sync",
                      " This is for Test Validation"
                      )[block_manager.peer_type == loopchain_pb2.PEER]),
            block_hash=block_hash)
Пример #2
0
    def add_unconfirm_block(self, block_unloaded, channel_name=None):
        if channel_name is None:
            channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL

        block = pickle.loads(block_unloaded)
        block_hash = block.block_hash

        response_code, response_msg = message_code.get_response(
            message_code.Response.fail_validate_block)

        # block 검증
        block_is_validated = False
        try:
            block_is_validated = Block.validate(block)
        except Exception as e:
            logging.error(e)

        if block_is_validated:
            # broadcast 를 받으면 받은 블럭을 검증한 후 검증되면 자신의 blockchain 의 unconfirmed block 으로 등록해 둔다.
            confirmed, reason = \
                self.__channel_manager.get_block_manager(channel_name).get_blockchain().add_unconfirm_block(block)

            if confirmed:
                response_code, response_msg = message_code.get_response(
                    message_code.Response.success_validate_block)
            elif reason == "block_height":
                # Announce 되는 블럭과 자신의 height 가 다르면 Block Height Sync 를 다시 시도한다.
                self.__channel_manager.get_block_manager(
                    channel_name).block_height_sync()

        return response_code, response_msg, block_hash
Пример #3
0
    def AnnounceConfirmedBlock(self, request, context):
        """Block Generator 가 announce 하는 인증된 블록의 대한 hash 를 전달받는다.
        :param request: BlockAnnounce of loopchain.proto
        :param context: gRPC parameter
        :return: CommonReply of loopchain.proto
        """
        # Peer To BlockGenerator
        logging.debug("AnnounceConfirmedBlock block hash: " +
                      request.block_hash)
        response_code, response_msg = message_code.get_response(
            message_code.Response.fail_announce_block)

        confirmed_block = pickle.loads(request.block)

        logging.debug(f"block \n"
                      f"peer_id({confirmed_block.peer_id})\n"
                      f"made_block_count({confirmed_block.made_block_count})\n"
                      f"is_divided_block({confirmed_block.is_divided_block})")

        if len(request.block) > 0:
            logging.warning(
                "AnnounceConfirmedBlock without Consensus ===================="
            )
            # 아래의 return 값을 확인하지 않아도 예외인 경우 아래 except 에서 확인된다.
            self.peer_service.add_unconfirm_block(request.block)

        try:
            self.peer_service.block_manager.confirm_block(request.block_hash)
            response_code, response_msg = message_code.get_response(
                message_code.Response.success_announce_block)
        except (BlockchainError, BlockInValidError, BlockError) as e:
            logging.error("AnnounceConfirmedBlock: " + str(e))

        return loopchain_pb2.CommonReply(response_code=response_code,
                                         message=response_msg)
Пример #4
0
    def GetTx(self, request, context):
        """get transaction

        :param request: tx_hash
        :param context:channel_loopchain_default
        :return:
        """
        channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel

        channel_stub = StubCollection().channel_stubs[channel_name]
        tx = channel_stub.sync_task().get_tx(request.tx_hash)

        response_code, response_msg = message_code.get_response(message_code.Response.fail)
        response_meta = ""
        response_data = ""
        response_sign = b''
        response_public_key = b''

        if tx is not None:
            response_code, response_msg = message_code.get_response(message_code.Response.success)
            response_meta = json.dumps(tx.meta)
            response_data = tx.get_data().decode(conf.PEER_DATA_ENCODING)
            response_sign = tx.signature
            response_public_key = tx.public_key

        return loopchain_pb2.GetTxReply(response_code=response_code,
                                        meta=response_meta,
                                        data=response_data,
                                        signature=response_sign,
                                        public_key=response_public_key,
                                        more_info=response_msg)
Пример #5
0
    def add_unconfirm_block(self, block_unloaded):
        block = pickle.loads(block_unloaded)
        block_hash = block.block_hash

        response_code, response_msg = message_code.get_response(
            message_code.Response.fail_validate_block)

        # block 검증
        block_is_validated = False
        try:
            block_is_validated = block.validate()
        except (BlockInValidError, BlockError, TransactionInValidError) as e:
            logging.error(e)

        if block_is_validated:
            # broadcast 를 받으면 받은 블럭을 검증한 후 검증되면 자신의 blockchain 의 unconfirmed block 으로 등록해 둔다.
            confirmed, reason = self.__block_manager.get_blockchain(
            ).add_unconfirm_block(block)
            if confirmed:
                response_code, response_msg = message_code.get_response(
                    message_code.Response.success_validate_block)
            elif reason == "block_height":
                # Announce 되는 블럭과 자신의 height 가 다르면 Block Height Sync 를 다시 시도한다.
                self.block_height_sync(self.__stub_to_blockgenerator)

        return response_code, response_msg, block_hash
Пример #6
0
    def GetLastBlockHash(self, request, context):
        """ 마지막 블럭 조회

        :param request: 블럭요청
        :param context:
        :return: 마지막 블럭
        """
        # Peer To Client
        last_block = self.peer_service.block_manager.get_blockchain(
        ).last_block
        response_code, response_msg = message_code.get_response(
            message_code.Response.fail)
        block_hash = None

        if last_block is not None:
            response_code, response_msg = message_code.get_response(
                message_code.Response.success)
            block_hash = last_block.block_hash

        return loopchain_pb2.BlockReply(
            response_code=response_code,
            message=(response_msg +
                     (" This is for block height sync",
                      " This is for Test Validation"
                      )[self.peer_service.peer_type == loopchain_pb2.PEER]),
            block_hash=block_hash)
Пример #7
0
    def GetTx(self, request, context):
        """ 트랜잭션을 가져옵니다.

        :param request: tx_hash
        :param context:
        :return:
        """
        tx = self.peer_service.block_manager.get_tx(request.tx_hash)

        # TODO 지금은 일반적인 fail 메시지로만 처리한다. 상세화 여지 있음, 필요시 추가 가능 (by winDy)
        response_code, response_msg = message_code.get_response(
            message_code.Response.fail)
        response_meta = ""
        response_data = ""

        if tx is not None:
            response_code, response_msg = message_code.get_response(
                message_code.Response.success)
            response_meta = json.dumps(tx.get_meta())
            response_data = tx.get_data().decode(conf.PEER_DATA_ENCODING)

        return loopchain_pb2.GetTxReply(response_code=response_code,
                                        meta=response_meta,
                                        data=response_data,
                                        more_info=response_msg)
Пример #8
0
    def GetTx(self, request, context):
        """get transaction

        :param request: tx_hash
        :param context:channel_loopchain_default
        :return:
        """
        channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel
        # logging.debug(f"peer_outer_service::GetTx channel({channel_name})")
        tx = self.peer_service.channel_manager.get_block_manager(
            channel_name).get_tx(request.tx_hash)

        # TODO 지금은 일반적인 fail 메시지로만 처리한다. 상세화 여지 있음, 필요시 추가 가능 (by winDy)
        response_code, response_msg = message_code.get_response(
            message_code.Response.fail)
        response_meta = ""
        response_data = ""
        response_sign = b''
        response_public_key = b''

        if tx is not None:
            response_code, response_msg = message_code.get_response(
                message_code.Response.success)
            response_meta = json.dumps(tx.meta)
            response_data = tx.get_data().decode(conf.PEER_DATA_ENCODING)
            response_sign = tx.signature
            response_public_key = tx.public_key

        return loopchain_pb2.GetTxReply(response_code=response_code,
                                        meta=response_meta,
                                        data=response_data,
                                        signature=response_sign,
                                        public_key=response_public_key,
                                        more_info=response_msg)
Пример #9
0
    def vote_unconfirmed_block(self, block_hash, is_validated):
        logging.debug(
            f"block_manager:vote_unconfirmed_block ({self.channel_name}/{is_validated})"
        )

        if is_validated:
            vote_code, message = message_code.get_response(
                message_code.Response.success_validate_block)
        else:
            vote_code, message = message_code.get_response(
                message_code.Response.fail_validate_block)

        block_vote = loopchain_pb2.BlockVote(
            vote_code=vote_code,
            channel=self.channel_name,
            message=message,
            block_hash=block_hash,
            peer_id=self.__peer_id,
            group_id=ChannelProperty().group_id)

        self.candidate_blocks.add_vote(block_hash,
                                       ChannelProperty().group_id,
                                       ChannelProperty().peer_id, is_validated)
        self.__channel_service.broadcast_scheduler.schedule_broadcast(
            "VoteUnconfirmedBlock", block_vote)
Пример #10
0
    def AnnounceUnconfirmedBlock(self, request, context):
        """수집된 tx 로 생성한 Block 을 각 peer 에 전송하여 검증을 요청한다.

        :param request:
        :param context:
        :return:
        """
        channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel
        logging.debug(
            f"peer_outer_service::AnnounceUnconfirmedBlock channel({channel_name})"
        )
        unconfirmed_block = pickle.loads(request.block)

        logging.warning("Black Peer makes Fail validate Message by intention!")
        vote_code, message = message_code.get_response(
            message_code.Response.fail_validate_block)

        block_vote = loopchain_pb2.BlockVote(
            vote_code=vote_code,
            channel=channel_name,
            message=message,
            block_hash=unconfirmed_block.block_hash,
            peer_id=ObjectManager().peer_service.peer_id,
            group_id=ObjectManager().peer_service.group_id)

        self.peer_service.common_service.broadcast("VoteUnconfirmedBlock",
                                                   block_vote)

        return loopchain_pb2.CommonReply(
            response_code=message_code.Response.success, message="success")
Пример #11
0
    def AnnounceUnconfirmedBlock(self, request, context):
        """수집된 tx 로 생성한 Block 을 각 peer 에 전송하여 검증을 요청한다.

        :param request:
        :param context:
        :return:
        """
        # self.__block_manager.add_unconfirmed_block(request.block)

        unconfirmed_block = pickle.loads(request.block)

        logging.warning("Black Peer makes Fail validate Message by intention!")
        vote_code, message = message_code.get_response(
            message_code.Response.fail_validate_block)
        self.peer_service.stub_to_blockgenerator.call(
            "VoteUnconfirmedBlock",
            loopchain_pb2.BlockVote(
                vote_code=vote_code,
                message=message,
                block_hash=unconfirmed_block.block_hash,
                peer_id=ObjectManager().peer_service.peer_id,
                group_id=ObjectManager().peer_service.group_id))

        return loopchain_pb2.CommonReply(
            response_code=message_code.Response.success, message="success")
Пример #12
0
    def vote_unconfirmed_block(self, block_hash, is_validated, channel):
        logging.debug(f"vote_unconfirmed_block ({channel})")

        if is_validated:
            vote_code, message = message_code.get_response(message_code.Response.success_validate_block)
        else:
            vote_code, message = message_code.get_response(message_code.Response.fail_validate_block)

        block_vote = loopchain_pb2.BlockVote(
            vote_code=vote_code,
            channel=channel,
            message=message,
            block_hash=block_hash,
            peer_id=self.__peer_id,
            group_id=ObjectManager().peer_service.group_id)

        self.broadcast("VoteUnconfirmedBlock", block_vote)
Пример #13
0
    def vote_unconfirmed_block(self, block_hash, is_validated):
        logging.debug("vote_unconfirmed_block ....")
        if is_validated:
            vote_code, message = message_code.get_response(message_code.Response.success_validate_block)
        else:
            vote_code, message = message_code.get_response(message_code.Response.fail_validate_block)

        if self.__stub_to_blockgenerator is not None:
            self.__stub_to_blockgenerator.call(
                "VoteUnconfirmedBlock",
                loopchain_pb2.BlockVote(
                    vote_code=vote_code,
                    message=message,
                    block_hash=block_hash,
                    peer_id=ObjectManager().peer_service.peer_id,
                    group_id=ObjectManager().peer_service.group_id)
            )
        else:
            logging.error("No block generator stub!")
Пример #14
0
    async def post(self, request):
        request_body_json = request.json
        result, verify_message = self.__validate_query_request(
            request_body_json)
        query_data = dict()

        if result is False:
            logging.warning(verify_message)
            message = message_code.get_response(
                message_code.Response.fail_validate_params)
            query_data['response_code'] = str(message[0].value)
            query_data['response'] = message[1] + '. ' + verify_message
        else:
            request_body_dump = json.dumps(request_body_json)
            channel = get_channel_name_from_json(request.json)

            try:
                grpc_response = ServerComponents().query(
                    request_body_dump, channel)
                if grpc_response is None:
                    query_data['response'] = str(grpc_response)
                    query_data['response_code'] = str(
                        message_code.Response.not_treat_message_code)
                else:
                    logging.debug(f"query result : {grpc_response}")
                    query_data['response_code'] = str(
                        grpc_response.response_code)
                    try:
                        query_data['response'] = json.loads(
                            grpc_response.response)

                    except json.JSONDecodeError as e:
                        logging.warning(
                            "your response is not json, your response(" +
                            str(grpc_response.response) + ")")
                        query_data['response'] = grpc_response.response

            except _Rendezvous as e:
                logging.error(f'Execute Query Error : {e}')
                if e.code() == grpc.StatusCode.DEADLINE_EXCEEDED:
                    logging.debug("gRPC timeout !!!")
                    query_data['response_code'] = str(
                        message_code.Response.timeout_exceed)

        return response.json(query_data)
Пример #15
0
    def GetLastBlockHash(self, request, context):
        """ 마지막 블럭 조회

        :param request: 블럭요청
        :param context:
        :return: 마지막 블럭
        """
        channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel
        # Peer To Client
        channel_stub = StubCollection().channel_stubs[channel_name]
        response_code, block_hash, block_data_json, tx_data_json_list = \
            channel_stub.sync_task().get_block(
                block_height=-1,
                block_hash='',
                block_data_filter='block_hash',
                tx_data_filter='')

        response_code, response_msg = message_code.get_response(response_code)
        return loopchain_pb2.BlockReply(response_code=response_code,
                                        message=response_msg,
                                        block_hash=block_hash)
Пример #16
0
    def GetLastBlockHash(self, request, context):
        """ 마지막 블럭 조회

        :param request: 블럭요청
        :param context:
        :return: 마지막 블럭
        """
        channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel
        # Peer To Client
        channel_stub = StubCollection().channel_stubs[channel_name]
        future = asyncio.run_coroutine_threadsafe(
            channel_stub.async_task().get_block(
                block_height=-1,
                block_hash='',
                block_data_filter='block_hash',
                tx_data_filter=''
            ), self.peer_service.inner_service.loop
        )
        response_code, block_hash, _, block_data_json, tx_data_json_list = future.result()
        response_code, response_msg = message_code.get_response(response_code)

        return loopchain_pb2.BlockReply(response_code=response_code,
                                        message=response_msg,
                                        block_hash=block_hash)
Пример #17
0
    def ConnectPeer(self, request: loopchain_pb2.ConnectPeerRequest, context):
        """RadioStation 에 접속한다. 응답으로 기존의 접속된 Peer 목록을 받는다.

        :param request: PeerRequest
        :param context:
        :return: ConnectPeerReply
        """
        logging.info(f"Trying to connect peer: {request.peer_id}")

        if conf.ENABLE_RADIOSTATION_HEARTBEAT:
            timer_key = f"{TimerService.TIMER_KEY_RS_HEARTBEAT}_{request.channel}"
            if timer_key not in ObjectManager(
            ).rs_service.timer_service.timer_list.keys():
                ObjectManager().rs_service.timer_service.add_timer(
                    timer_key,
                    Timer(
                        target=timer_key,
                        duration=conf.SLEEP_SECONDS_IN_RADIOSTATION_HEARTBEAT,
                        is_repeat=True,
                        callback=ObjectManager().rs_service.check_peer_status,
                        callback_kwargs={"channel": request.channel}))

        if conf.ENABLE_CHANNEL_AUTH:
            if request.peer_target not in ObjectManager(
            ).rs_service.admin_manager.get_peer_list_by_channel(
                    request.channel):
                status, reason = message_code.get_response(
                    message_code.Response.fail_invalid_peer_target)
                return loopchain_pb2.ConnectPeerReply(status=status,
                                                      peer_list=b'',
                                                      more_info=reason)

        channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if not request.channel else request.channel
        logging.debug(f"ConnectPeer channel_name({channel_name})")
        logging.debug(f"Connect Peer "
                      f"\nPeer_id : {request.peer_id}"
                      f"\nPeer_target : {request.peer_target}"
                      f"\nChannel : {request.channel}")

        peer = PeerInfo(request.peer_id,
                        request.group_id,
                        request.peer_target,
                        PeerStatus.unknown,
                        cert=request.cert)

        util.logger.spam(f"service::ConnectPeer try add_peer")

        # when first peer ConnectPeer to RS,
        # RS need rebuild peer list from db.
        # For prevent leader split by RS.
        with self.__load_peer_manager_lock:
            peer_manager = ObjectManager().rs_service.channel_manager.\
                get_peer_manager(channel_name)
            util.logger.spam(f"before load peer_manager "
                             f"peer_count({peer_manager.get_peer_count()})")

            if peer_manager.get_peer_count() == 0:
                util.logger.spam(f"try load peer_manager from db")
                peer_manager = ObjectManager().rs_service.admin_manager.\
                    load_peer_manager(channel_name)
                ObjectManager().rs_service.channel_manager.\
                    set_peer_manager(channel_name, peer_manager)

        util.logger.spam(f"after load peer_manager "
                         f"peer_count({peer_manager.get_peer_count()})")

        peer_order = peer_manager.add_peer(peer)

        peer_list_dump = b''
        status, reason = message_code.get_response(message_code.Response.fail)

        if peer_order > 0:
            try:
                peer_list_dump = peer_manager.dump()
                status, reason = message_code.get_response(
                    message_code.Response.success)

            except pickle.PicklingError as e:
                logging.warning("fail peer_list dump")
                reason += " " + str(e)

        # save current peer_manager after ConnectPeer from new peer.
        ObjectManager().rs_service.admin_manager.save_peer_manager(
            channel_name, peer_manager)

        return loopchain_pb2.ConnectPeerReply(status=status,
                                              peer_list=peer_list_dump,
                                              channels=None,
                                              more_info=reason)
Пример #18
0
    def ConnectPeer(self, request, context):
        """RadioStation 에 접속한다. 응답으로 기존의 접속된 Peer 목록을 받는다.

        :param request: PeerRequest
        :param context:
        :return: PeerReply
        """
        logging.info("Trying to connect peer: " + request.peer_id)

        res, info = self._rs.validate_group_id(request.group_id)
        if res < 0:  # 잘못된 입력이 들어오면 빈 list를 보내준다.
            return loopchain_pb2.PeerReply(status=message_code.Response.fail,
                                           peer_list=b'',
                                           more_info=info)

        logging.debug("Connect Peer " + "\nPeer_id : " + request.peer_id +
                      "\nGroup_id : " + request.group_id + "\nPeer_target : " +
                      request.peer_target)

        auth = ""
        token = ""
        logging.info("CA SECURITY_MODE : " + str(self.__ca.is_secure))
        if self.__ca.is_secure:
            logging.debug("RadioStation is secure mode")
            if request.token is None or request.token is "":
                info = "Peer Token is None"
                return loopchain_pb2.PeerReply(
                    status=message_code.Response.fail,
                    peer_list=b'',
                    more_info=info)

            else:
                # TOKEN : "00", CERT : "01", SIGN : "02"
                tag = request.token[:2]
                data = request.token[2:]
                logging.debug("TOKEN TYPE : %s", tag)

                if tag == conf.TOKEN_TYPE_TOKEN:
                    peer = self.__peer_manager.get_peer(
                        request.peer_id, request.group_id)
                    if peer is None:
                        info = "Invalid Peer_ID[" + request.peer_id + "], Group_ID[" + request.group_id + "%s]"
                        return loopchain_pb2.PeerReply(
                            status=message_code.Response.fail,
                            peer_list=b'',
                            more_info=info)
                    else:
                        peer_type = request.peer_type
                        if peer_type is loopchain_pb2.BLOCK_GENERATOR:
                            peer_type = loopchain_pb2.PEER

                        if self.__ca.verify_peer_token(peer_token=data,
                                                       peer=peer,
                                                       peer_type=peer_type):
                            auth = peer.auth
                            token = request.token
                        else:
                            info = "Invalid TOKEN"
                            return loopchain_pb2.PeerReply(
                                status=message_code.Response.fail,
                                peer_list=b'',
                                more_info=info)

                elif tag == conf.TOKEN_TYPE_CERT:
                    # TODO: 인증서와 Peer_ID가 연결될 수 있는 장치가 필요(인증서 내부 정보 활용)
                    if self.__ca.verify_certificate_der(bytes.fromhex(data)):
                        rand_key = secrets.token_bytes(16).hex()
                        self._rs.auth[request.peer_id] = {
                            'rand_key': rand_key,
                            'auth': data
                        }
                        return loopchain_pb2.PeerReply(
                            status=message_code.Response.success,
                            peer_list=b'',
                            more_info=rand_key)
                    else:
                        info = "Invalid Peer Certificate"
                        return loopchain_pb2.PeerReply(
                            status=message_code.Response.fail,
                            peer_list=b'',
                            more_info=info)

                elif tag == conf.TOKEN_TYPE_SIGN:
                    try:
                        peer_auth = self._rs.auth[request.peer_id]
                        rand_key = peer_auth['rand_key']
                        auth = peer_auth['auth']
                    except KeyError:
                        info = "No Peer Info"
                        return loopchain_pb2.PeerReply(
                            status=message_code.Response.fail,
                            peer_list=b'',
                            more_info=info)
                    logging.debug("Get Rand_key: %s, auth: %s", rand_key, auth)

                    new_token = self.__ca.generate_peer_token(
                        peer_sign=bytes.fromhex(data),
                        peer_cert=bytes.fromhex(auth),
                        peer_id=request.peer_id,
                        peer_target=request.peer_target,
                        group_id=request.group_id,
                        peer_type=request.peer_type,
                        rand_key=bytes.fromhex(rand_key),
                        token_interval=conf.TOKEN_INTERVAL)

                    if new_token is not None:
                        self._rs.auth[request.peer_id] = {}

                    token = conf.TOKEN_TYPE_TOKEN + new_token

                else:
                    info = "Unknown token type(" + tag + ")"
                    return loopchain_pb2.PeerReply(
                        status=message_code.Response.fail,
                        peer_list=b'',
                        more_info=info)

        peer = self.__peer_manager.make_peer(request.peer_id,
                                             request.group_id,
                                             request.peer_target,
                                             PeerStatus.connected,
                                             peer_auth=auth,
                                             peer_token=token)

        peer_order = self.__peer_manager.add_peer_object(peer)
        self.__common_service.save_peer_list(self.__peer_manager)

        if peer_order > 0:
            # 접속 완료
            try:
                peer_dump = pickle.dumps(peer)
            except pickle.PicklingError as e:
                logging.warning("Fail Peer Dump: " + str(e))
                peer_dump = b''

            request.peer_object = peer_dump
            request.peer_order = peer_order
            logging.debug("Connect Peer: " + str(request))

            # self.__broadcast_new_peer(request)
            # TODO RS subsribe 를 이용하는 경우, RS 가 재시작시 peer broadcast 가 전체로 되지 않는 문제가 있다.
            # peer_list 를 이용하여 broadcast 하는 구조가되면 RS 혹은 Leader 에 대한 Subscribe 구조는 유효하지 않다.
            # 하지만 broadcast process 는 peer_list broadcast 인 경우 사용되어지지 않는다. peer_list 에서 broadcast 하는 동안
            # block 되는 구조. broadcast Process 를 peer_list 를 이용한 broadcast 에서도 활용할 수 있게 하거나.
            # RS 혹은 Leader 가 재시작 후에 Subscribe 정보를 복원하게 하거나.
            # 혹은 peer_list 가 broadcast 하여도 성능상(동시성에 있어) 문제가 없는지 보증하여야 한다. TODO TODO TODO
            self.__peer_manager.announce_new_peer(request)

            logging.debug("get_IP_of_peers_in_group: " +
                          str(self.__peer_manager.get_IP_of_peers_in_group()))

            try:
                peer_list_dump = self.__peer_manager.dump()
                status, reason = message_code.get_response(
                    message_code.Response.success)
                # TODO 현재는 reason 을 token return 으로 사용하고 있음, 이 용도로 reason 을 사용할 때에는 token 만 담겨있어야 함
                # 추후 메시지를 확장할 때 리팩토링 할 것
                reason = token
            except pickle.PicklingError as e:
                logging.warning("fail peer_list dump")
                peer_list_dump = b''
                status, reason = message_code.get_response(
                    message_code.Response.fail)
                reason += " " + str(e)

            return loopchain_pb2.PeerReply(status=status,
                                           peer_list=peer_list_dump,
                                           more_info=reason)
Пример #19
0
    def ConnectPeer(self, request, context):
        """RadioStation 에 접속한다. 응답으로 기존의 접속된 Peer 목록을 받는다.

        :param request: PeerRequest
        :param context:
        :return: PeerReply
        """
        logging.info("Trying to connect peer: "+request.peer_id)

        res, info = self._rs.validate_group_id(request.group_id)
        if res < 0:  # send null list(b'') while wrong input.
            return loopchain_pb2.PeerReply(status=message_code.Response.fail, peer_list=b'', more_info=info)

        logging.debug("Connect Peer "
                      + "\nPeer_id : " + request.peer_id
                      + "\nGroup_id : " + request.group_id
                      + "\nPeer_target : " + request.peer_target)

        auth = ""
        token = ""
        logging.info("CA SECURITY_MODE : " + str(self.__ca.is_secure))
        if self.__ca.is_secure:
            logging.debug("RadioStation is secure mode")
            if request.token is None or request.token is "":
                info = "Peer Token is None"
                return loopchain_pb2.PeerReply(status=message_code.Response.fail, peer_list=b'', more_info=info)

            else:
                # TOKEN : "00", CERT : "01", SIGN : "02"
                tag = request.token[:2]
                data = request.token[2:]
                logging.debug("TOKEN TYPE : %s", tag)

                if tag == conf.TOKEN_TYPE_TOKEN:
                    peer = self.__peer_manager.get_peer(request.peer_id, request.group_id)
                    if peer is None:
                        info = "Invalid Peer_ID[" + request.peer_id + "], Group_ID[" + request.group_id + "%s]"
                        return loopchain_pb2.PeerReply(status=message_code.Response.fail, peer_list=b'', more_info=info)
                    else:
                        peer_type = request.peer_type
                        if peer_type is loopchain_pb2.BLOCK_GENERATOR:
                            peer_type = loopchain_pb2.PEER

                        if self.__ca.verify_peer_token(peer_token=data, peer=peer, peer_type=peer_type):
                            auth = peer.auth
                            token = request.token
                        else:
                            info = "Invalid TOKEN"
                            return loopchain_pb2.PeerReply(status=message_code.Response.fail, peer_list=b'', more_info=info)

                elif tag == conf.TOKEN_TYPE_CERT:
                    # TODO: 인증서와 Peer_ID가 연결될 수 있는 장치가 필요(인증서 내부 정보 활용)
                    if self.__ca.verify_certificate_der(bytes.fromhex(data)):
                        rand_key = secrets.token_bytes(16).hex()
                        self._rs.auth[request.peer_id] = {'rand_key': rand_key, 'auth': data}
                        return loopchain_pb2.PeerReply(status=message_code.Response.success, peer_list=b'', more_info=rand_key)
                    else:
                        info = "Invalid Peer Certificate"
                        return loopchain_pb2.PeerReply(status=message_code.Response.fail, peer_list=b'', more_info=info)

                elif tag == conf.TOKEN_TYPE_SIGN:
                    try:
                        peer_auth = self._rs.auth[request.peer_id]
                        rand_key = peer_auth['rand_key']
                        auth = peer_auth['auth']
                    except KeyError:
                        info = "No Peer Info"
                        return loopchain_pb2.PeerReply(status=message_code.Response.fail, peer_list=b'', more_info=info)
                    logging.debug("Get Rand_key: %s, auth: %s", rand_key, auth)

                    new_token = self.__ca.generate_peer_token(peer_sign=bytes.fromhex(data),
                                                              peer_cert=bytes.fromhex(auth),
                                                              peer_id=request.peer_id,
                                                              peer_target=request.peer_target,
                                                              group_id=request.group_id,
                                                              peer_type=request.peer_type,
                                                              rand_key=bytes.fromhex(rand_key),
                                                              token_interval=conf.TOKEN_INTERVAL)

                    if new_token is not None:
                        self._rs.auth[request.peer_id] = {}

                    token = conf.TOKEN_TYPE_TOKEN + new_token

                else:
                    info = "Unknown token type(" + tag + ")"
                    return loopchain_pb2.PeerReply(status=message_code.Response.fail, peer_list=b'', more_info=info)

        peer = self.__peer_manager.make_peer(request.peer_id,
                                             request.group_id,
                                             request.peer_target,
                                             PeerStatus.unknown,
                                             peer_auth=auth,
                                             peer_token=token)

        peer_order = self.__peer_manager.add_peer_object(peer)
        self.__common_service.save_peer_list(self.__peer_manager)

        peer_list_dump = b''
        status, reason = message_code.get_response(message_code.Response.fail)

        if peer_order > 0:
            try:
                peer_list_dump = self.__peer_manager.dump()
                status, reason = message_code.get_response(message_code.Response.success)
                # TODO 현재는 reason 을 token return 으로 사용하고 있음, 이 용도로 reason 을 사용할 때에는 token 만 담겨있어야 함
                # 추후 메시지를 확장할 때 리팩토링 할 것
                reason = token
            except pickle.PicklingError as e:
                logging.warning("fail peer_list dump")
                reason += " " + str(e)

        return loopchain_pb2.PeerReply(
            status=status,
            peer_list=peer_list_dump,
            more_info=reason
        )
Пример #20
0
    def ConnectPeer(self, request: loopchain_pb2.ConnectPeerRequest, context):
        """RadioStation 에 접속한다. 응답으로 기존의 접속된 Peer 목록을 받는다.

        :param request: PeerRequest
        :param context:
        :return: ConnectPeerReply
        """
        logging.info("Trying to connect peer: " + request.peer_id)

        res, info = ObjectManager().rs_service.validate_group_id(
            request.group_id)
        if res < 0:  # send null list(b'') while wrong input.
            return loopchain_pb2.ConnectPeerReply(
                status=message_code.Response.fail,
                peer_list=b'',
                more_info=info)

        # TODO check peer's authorization for channel
        channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if not request.channel else request.channel
        logging.debug(f"ConnectPeer channel_name({channel_name})")

        # channels: list = ObjectManager().rs_service.channel_manager.authorized_channels(request.peer_id)
        # if channel_name not in channels:
        #     return loopchain_pb2.ConnectPeerReply(
        #         status=message_code.Response.fail,
        #         peer_list=b'',
        #         more_info=f"channel({channel_name}) is not authorized for peer_id({request.peer_id})")

        logging.debug("Connect Peer " + "\nPeer_id : " + request.peer_id +
                      "\nGroup_id : " + request.group_id + "\nPeer_target : " +
                      request.peer_target)

        peer = PeerInfo(request.peer_id,
                        request.group_id,
                        request.peer_target,
                        PeerStatus.unknown,
                        cert=request.cert)

        util.logger.spam(f"service::ConnectPeer try add_peer")
        peer_order = ObjectManager(
        ).rs_service.channel_manager.get_peer_manager(channel_name).add_peer(
            peer)

        peer_list_dump = b''
        status, reason = message_code.get_response(message_code.Response.fail)

        if peer_order > 0:
            try:
                peer_list_dump = ObjectManager(
                ).rs_service.channel_manager.get_peer_manager(
                    channel_name).dump()
                status, reason = message_code.get_response(
                    message_code.Response.success)

            except pickle.PicklingError as e:
                logging.warning("fail peer_list dump")
                reason += " " + str(e)

        return loopchain_pb2.ConnectPeerReply(status=status,
                                              peer_list=peer_list_dump,
                                              channels=None,
                                              more_info=reason)