def update_sub_services_properties(self, **properties): logging.info(f"properties {properties}") stub = StubCollection().channel_tx_creator_stubs[ ChannelProperty().name] asyncio.run_coroutine_threadsafe( stub.async_task().update_properties(properties), self.__loop_for_sub_services) stub = StubCollection().channel_tx_receiver_stubs[ ChannelProperty().name] asyncio.run_coroutine_threadsafe( stub.async_task().update_properties(properties), self.__loop_for_sub_services)
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 = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_tx(request.tx_hash), self.peer_service.inner_service.loop).result() 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)
def __handler_get_tx_by_address(self, request, context): """Get Transaction by address :param request: :param context: :return: """ params = json.loads(request.meta) address = params.pop('address', None) index = params.pop('index', None) if address is None or index is None: # or params: return loopchain_pb2.Message( code=message_code.Response.fail_illegal_params) channel_stub = StubCollection().channel_stubs[request.channel] future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_tx_by_address(address, index), self.peer_service.inner_service.loop) tx_list, next_index = future.result() tx_list_dumped = json.dumps(tx_list).encode( encoding=conf.PEER_DATA_ENCODING) return loopchain_pb2.Message(code=message_code.Response.success, meta=str(next_index), object=tx_list_dumped)
def GetBlock(self, request, context): """Block 정보를 조회한다. :param request: loopchain.proto 의 GetBlockRequest 참고 request.block_hash: 조회할 block 의 hash 값, "" 로 조회하면 마지막 block 의 hash 값을 리턴한다. request.block_data_filter: block 정보 중 조회하고 싶은 key 값 목록 "key1, key2, key3" 형식의 string request.tx_data_filter: block 에 포함된 transaction(tx) 중 조회하고 싶은 key 값 목록 "key1, key2, key3" 형식의 string :param context: :return: loopchain.proto 의 GetBlockReply 참고, block_hash, block 정보 json, block 에 포함된 tx 정보의 json 리스트를 받는다. 포함되는 정보는 param 의 filter 에 따른다. """ channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel channel_stub = StubCollection().channel_stubs[channel_name] future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_block( block_height=request.block_height, block_hash=request.block_hash, block_data_filter=request.block_data_filter, tx_data_filter=request.tx_data_filter ), self.peer_service.inner_service.loop ) response_code, block_hash, confirm_info, block_data_json, tx_data_json_list = future.result() return loopchain_pb2.GetBlockReply(response_code=response_code, block_hash=block_hash, block_data_json=block_data_json, confirm_info=confirm_info, tx_data_json=tx_data_json_list)
def UnSubscribe(self, request, context): """BlockGenerator 의 broadcast 채널에서 Peer 를 제외한다. :param request: :param context: :return: """ channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel channel_stub = StubCollection().channel_stubs[channel_name] peer_list = [target['peer_target'] for target in self.peer_service.channel_infos[channel_name]["peers"]] if (request.peer_target in peer_list and conf.ENABLE_CHANNEL_AUTH) or \ (request.node_type == loopchain_pb2.CommunityNode and not conf.ENABLE_CHANNEL_AUTH): asyncio.run_coroutine_threadsafe( channel_stub.async_task().remove_audience(peer_target=request.peer_target), self.peer_service.inner_service.loop ) util.logger.spam(f"peer_outer_service::Unsubscribe remove_audience target({request.peer_target}) " f"in channel({request.channel})") else: logging.error(f"This target({request.peer_target}), {request.node_type} failed to unsubscribe.") return loopchain_pb2.CommonReply(response_code=message_code.get_response_code(message_code.Response.fail), message=message_code.get_response_msg("Unknown type peer")) return loopchain_pb2.CommonReply(response_code=message_code.get_response_code(message_code.Response.success), message=message_code.get_response_msg(message_code.Response.success))
def __handler_status(self, request, context): util.logger.debug(f"peer_outer_service:handler_status ({request.message})") if request.message == "get_stub_manager_to_server": # this case is check only gRPC available return loopchain_pb2.Message(code=message_code.Response.success) channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel channel_stub = StubCollection().channel_stubs[channel_name] if request.message == "check peer status by rs": channel_stub.sync_task().reset_timer(TimerService.TIMER_KEY_CONNECT_PEER) callback = partial(self.__status_update, request.channel) future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_status(), self.peer_service.inner_service.loop ) future.add_done_callback(callback) status = self.__get_status_peer_type_data(request.channel) if status is None: return loopchain_pb2.Message(code=message_code.Response.fail) meta = json.loads(request.meta) if request.meta else {} if meta.get("highest_block_height", None) and meta["highest_block_height"] > status["block_height"]: util.logger.spam(f"(peer_outer_service.py:__handler_status) there is difference of height !") channel_stub.sync_task().block_height_sync() status_json = json.dumps(status) return loopchain_pb2.Message(code=message_code.Response.success, meta=status_json)
def ComplainLeader(self, request: ComplainLeaderRequest, context): channel = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel utils.logger.info(f"ComplainLeader {request.complain_vote}") channel_stub = StubCollection().channel_stubs[channel] asyncio.run_coroutine_threadsafe( channel_stub.async_task().complain_leader(vote_dumped=request.complain_vote), self.peer_service.inner_service.loop ) return loopchain_pb2.CommonReply(response_code=message_code.Response.success, message="success")
def VoteUnconfirmedBlock(self, request, context): channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel utils.logger.debug(f"VoteUnconfirmedBlock vote({request.vote})") channel_stub = StubCollection().channel_stubs[channel_name] asyncio.run_coroutine_threadsafe( channel_stub.async_task().vote_unconfirmed_block(request.vote), self.peer_service.inner_service.loop ) return loopchain_pb2.CommonReply(response_code=message_code.Response.success, message="success")
def __handler_peer_list(self, request, context): channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel channel_stub = StubCollection().channel_stubs[channel_name] future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_peer_list(), self.peer_service.inner_service.loop ) all_group_peer_list_str, peer_list_str = future.result() message = "All Group Peers count: " + all_group_peer_list_str return loopchain_pb2.Message( code=message_code.Response.success, message=message, meta=peer_list_str)
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 util.logger.debug(f"peer_outer_service::AnnounceUnconfirmedBlock channel({channel_name})") channel_stub = StubCollection().channel_stubs[channel_name] asyncio.run_coroutine_threadsafe( channel_stub.async_task().announce_unconfirmed_block(request.block), self.peer_service.inner_service.loop ) return loopchain_pb2.CommonReply(response_code=message_code.Response.success, message="success")
def __get_status_from_cache(self, channel: str): if channel in self.peer_service.status_cache: if channel in self.__status_cache_update_time: if util.datetime_diff_in_mins( self.__status_cache_update_time[channel]) \ > conf.ALLOW_STATUS_CACHE_LAST_UPDATE_IN_MINUTES: return None status_data = self.peer_service.status_cache[channel] else: channel_stub = StubCollection().channel_stubs[channel] status_data = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_status(), self.peer_service.inner_service.loop ).result() self.peer_service.status_cache[channel] = status_data return status_data
def __get_status_cache(self, channel_name, time_in_seconds): utils.logger.spam(f"__get_status_cache in seconds({time_in_seconds})") try: channel_stub = StubCollection().channel_stubs[channel_name] except KeyError: raise ChannelStatusError(f"Invalid channel({channel_name})") if self.__status_cache is None: self.__status_cache = channel_stub.sync_task().get_status() else: future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_status(), self.peer_service.inner_service.loop) future.add_done_callback(self.__set_status_cache) return self.__status_cache
def GetPrecommitBlock(self, request, context): """Return the precommit bock. :param request: :param context: :return: loopchain.proto 의 PrecommitBlockReply 참고, """ channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel channel_stub = StubCollection().channel_stubs[channel_name] future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_precommit_block(last_block_height=request.last_block_height), self.peer_service.inner_service.loop ) response_code, response_message, block = future.result() return loopchain_pb2.PrecommitBlockReply( response_code=response_code, response_message=response_message, block=block)
def GetInvokeResult(self, request, context): """get invoke result by tx_hash :param request: request.tx_hash = tx_hash :param context: :return: verify result """ channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel logging.debug(f"peer_outer_service:GetInvokeResult in channel({channel_name})") channel_stub = StubCollection().channel_stubs[channel_name] future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_invoke_result(request.tx_hash), self.peer_service.inner_service.loop ) response_code, result = future.result() return loopchain_pb2.GetInvokeResultReply(response_code=response_code, result=result)
def ComplainLeader(self, request: ComplainLeaderRequest, context): channel = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel util.logger.notice(f"ComplainLeader " f"height({request.block_height}) complained_peer({request.complained_leader_id})") channel_stub = StubCollection().channel_stubs[channel] asyncio.run_coroutine_threadsafe( channel_stub.async_task().complain_leader( complained_leader_id=request.complained_leader_id, new_leader_id=request.new_leader_id, block_height=request.block_height, peer_id=request.peer_id, group_id=request.group_id ), self.peer_service.inner_service.loop ) return loopchain_pb2.CommonReply(response_code=message_code.Response.success, message="success")
def AnnounceUnconfirmedBlock(self, request, context): """Send the UnconfirmedBlock includes collected transactions to reps and request to verify it. :param request: :param context: :return: """ channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel channel_stub = StubCollection().channel_stubs[channel_name] try: round_ = request.round_ except AttributeError: round_ = 0 asyncio.run_coroutine_threadsafe( channel_stub.async_task().announce_unconfirmed_block(request.block, round_), self.peer_service.inner_service.loop ) return loopchain_pb2.CommonReply(response_code=message_code.Response.success, message="success")
def CreateTx(self, request, context): """make tx by client request and broadcast it to the network :param request: :param context: :return: """ channel_name = request.channel or conf.LOOPCHAIN_DEFAULT_CHANNEL logging.info(f"peer_outer_service::CreateTx request({request.data}), channel({channel_name})") channel_stub = StubCollection().channel_stubs[channel_name] result_hash = asyncio.run_coroutine_threadsafe( channel_stub.async_task().create_tx(request.data), self.peer_service.inner_service.loop ).result() return loopchain_pb2.CreateTxReply( response_code=message_code.Response.success, tx_hash=result_hash, more_info='')
def AnnounceNewPeer(self, request, context): """RadioStation에서 Broadcasting 으로 신규 피어정보를 받아온다 :param request: PeerRequest :param context: :return: """ # RadioStation To Peer # prevent to show certificate content # logging.info('Here Comes new peer: ' + str(request)) channel = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel logging.debug(f"peer outer service::AnnounceNewPeer channel({channel})") if request.peer_object: channel_stub = StubCollection().channel_stubs[channel] asyncio.run_coroutine_threadsafe( channel_stub.async_task().announce_new_peer(request.peer_object, request.peer_target), self.peer_service.inner_service.loop ) return loopchain_pb2.CommonReply(response_code=0, message="success")
def BlockSync(self, request, context): # Peer To Peer channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel logging.info(f"BlockSync request hash({request.block_hash}) " f"request height({request.block_height}) channel({channel_name})") channel_stub = StubCollection().channel_stubs[channel_name] future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().block_sync(request.block_hash, request.block_height), self.peer_service.inner_service.loop ) response_code, block_height, max_block_height, unconfirmed_block_height, confirm_info, block_dumped = \ future.result() return loopchain_pb2.BlockSyncReply( response_code=response_code, block_height=block_height, max_block_height=max_block_height, confirm_info=confirm_info, block=block_dumped, unconfirmed_block_height=unconfirmed_block_height)
def Stop(self, request, context): """Peer를 중지시킨다 :param request: 중지요청 :param context: :return: 중지결과 """ if request is not None: logging.info('Peer will stop... by: ' + request.reason) try: for channel_name in self.peer_service.channel_infos: channel_stub = StubCollection().channel_stubs[channel_name] asyncio.run_coroutine_threadsafe(channel_stub.async_task().stop(), self.peer_service.inner_service.loop) self.peer_service.p2p_server_stop() except Exception as e: logging.debug("Score Service Already stop by other reason. %s", e) return loopchain_pb2.StopReply(status="0")
def __get_status_cache(self, channel_name, time_in_seconds): """Cache status data. :param channel_name: :param time_in_seconds: An essential parameter for the `LRU cache` even if not used. :return: """ try: channel_stub = StubCollection().channel_stubs[channel_name] except KeyError: raise ChannelStatusError(f"Invalid channel({channel_name})") if self.__status_cache is None: self.__status_cache = channel_stub.sync_task().get_status() else: future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_status(), self.peer_service.inner_service.loop) future.add_done_callback(self.__set_status_cache) return self.__status_cache
def Subscribe(self, request, context): """BlockGenerator 가 broadcast(unconfirmed or confirmed block) 하는 채널에 Peer 를 등록한다. :param request: :param context: :return: """ channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel if not request.peer_id or not request.peer_target: return loopchain_pb2.CommonReply( response_code=message_code.get_response_code(message_code.Response.fail_wrong_subscribe_info), message=message_code.get_response_msg(message_code.Response.fail_wrong_subscribe_info) ) try: channel_stub = StubCollection().channel_stubs[channel_name] except KeyError: return loopchain_pb2.CommonReply(response_code=message_code.get_response_code(message_code.Response.fail), message=f"There is no channel_stubs for channel({channel_name}).") peer_list = [target['peer_target'] for target in self.peer_service.channel_infos[channel_name]["peers"]] if (request.peer_target in peer_list and conf.ENABLE_CHANNEL_AUTH) or \ (request.node_type == loopchain_pb2.CommunityNode and not conf.ENABLE_CHANNEL_AUTH): asyncio.run_coroutine_threadsafe( channel_stub.async_task().add_audience(peer_target=request.peer_target), self.peer_service.inner_service.loop ) util.logger.debug(f"peer_outer_service::Subscribe add_audience " f"target({request.peer_target}) in channel({request.channel}), " f"order({request.peer_order})") else: logging.error(f"This target({request.peer_target}, {request.node_type}) failed to subscribe.") return loopchain_pb2.CommonReply(response_code=message_code.get_response_code(message_code.Response.fail), message=message_code.get_response_msg("Unknown type peer")) return loopchain_pb2.CommonReply(response_code=message_code.get_response_code(message_code.Response.success), message=message_code.get_response_msg(message_code.Response.success))
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)
def GetStatus(self, request, context): """Peer 의 현재 상태를 요청한다. :param request: :param context: :return: """ channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL if request.channel == '' else request.channel logging.debug("Peer GetStatus : %s", request) try: channel_stub = StubCollection().channel_stubs[channel_name] callback = partial(self.__status_update, channel_name) future = asyncio.run_coroutine_threadsafe( channel_stub.async_task().get_status(), self.peer_service.inner_service.loop) future.add_done_callback(callback) except BaseException as e: logging.error(f"Peer GetStatus Exception : {e}") status_data = self.__get_status_data(channel_name) if status_data is None: raise ChannelStatusError(f"Fail get status data from channel({channel_name})") status_data = copy.deepcopy(status_data) stubs = { "peer": StubCollection().peer_stub, "channel": StubCollection().channel_stubs.get(channel_name), "score": StubCollection().icon_score_stubs.get(channel_name) if util.channel_use_icx(channel_name) else StubCollection().score_stubs.get(channel_name) } mq_status_data = {} mq_down = False for key, stub in stubs.items(): message_count = -1 message_error = None try: mq_info = stub.sync_info().queue_info() message_count = mq_info.method.message_count except AttributeError: message_error = "Stub is not initialized." except Exception as e: message_error = f"{type(e).__name__}, {e}" mq_status_data[key] = {} mq_status_data[key]["message_count"] = message_count if message_error: mq_status_data[key]["error"] = message_error mq_down = True status_data["mq"] = mq_status_data if mq_down: reason = status_code.get_status_reason(status_code.Service.mq_down) status_data["status"] = "Service is offline: " + reason return loopchain_pb2.StatusReply( status=json.dumps(status_data), block_height=status_data["block_height"], total_tx=status_data["total_tx"], is_leader_complaining=status_data['leader_complaint'], peer_id=status_data['peer_id'])