def test_add_vote_fail_before_add_peer(self): # GIVEN peer_manager = PeerManager(conf.LOOPCHAIN_DEFAULT_CHANNEL) self.__add_peer_to_peer_manager(peer_manager, 3) peer_manager.add_peer( PeerInfo("peerid-4", "groupid-3", "peerid-4_target", cert=self.__cert)) peer_manager.add_peer( PeerInfo("peerid-5", "groupid-3", "peerid-5_target", cert=self.__cert)) vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # WHEN vote.add_vote("groupid-1", "peerid-1", None) vote.add_vote("groupid-3", "peerid-4", None) ret1 = vote.add_vote("groupid-4", "peerid-1", None) ret2 = vote.add_vote("groupid-1", "peerid-9", None) self.assertFalse(ret1) self.assertFalse(ret2) # THEN ret = vote.get_result_detail("block_hash", 0.51) self.assertEqual(ret[5], 5)
def test_fail_vote(self): # GIVEN peer_manager = PeerManager(conf.LOOPCHAIN_DEFAULT_CHANNEL) self.__add_peer_to_peer_manager(peer_manager, 3) peer_manager.add_peer( PeerInfo("peerid-4", "groupid-3", "peerid-4_target", cert=self.__cert)) peer_manager.add_peer( PeerInfo("peerid-5", "groupid-3", "peerid-5_target", cert=self.__cert)) vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # WHEN vote.add_vote("groupid-1", "peerid-1", conf.TEST_FAIL_VOTE_SIGN) vote.add_vote("groupid-3", "peerid-4", conf.TEST_FAIL_VOTE_SIGN) vote.add_vote("groupid-3", "peerid-5", conf.TEST_FAIL_VOTE_SIGN) vote.get_result("block_hash", 0.51) # THEN self.assertTrue(vote.is_failed_vote("block_hash", 0.51))
def announce_new_peer(self, peer_info_dumped, peer_target) -> None: try: peer_info = PeerInfo.load(peer_info_dumped) except Exception as e: traceback.print_exc() logging.error( f"Invalid peer info. peer_target={peer_target}, exception={e}") return logging.debug("Add New Peer: " + str(peer_info.peer_id)) peer_manager = self._channel_service.peer_manager peer_manager.add_peer(peer_info) # broadcast the new peer to the others for adding an audience self._channel_service.broadcast_scheduler.schedule_job( BroadcastCommand.SUBSCRIBE, peer_target) logging.debug("Try save peer list...") # self._channel_service.save_peer_manager(peer_manager) self._channel_service.show_peers() if conf.CONSENSUS_ALGORITHM == conf.ConsensusAlgorithm.lft: quorum, complain_quorum = peer_manager.get_quorum() self._channel_service.consensus.set_quorum( quorum=quorum, complain_quorum=complain_quorum)
def test_add_vote(self): # GIVEN peer_manager = PeerManager(conf.LOOPCHAIN_DEFAULT_CHANNEL) self.__add_peer_to_peer_manager(peer_manager, 3) peer_manager.add_peer(PeerInfo("peerid-4", "groupid-3", "peerid-4_target")) peer_manager.add_peer(PeerInfo("peerid-5", "groupid-3", "peerid-5_target")) vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # WHEN vote.add_vote("peerid-1", None) self.assertFalse(vote.get_result("block_hash", 0.51)) # THEN vote.add_vote("peerid-2", None) self.assertTrue(vote.get_result("block_hash", 0.51))
def __add_peer_to_peer_manager(self, peer_manager: PeerManager, number_of_peer): for i in range(1, number_of_peer + 1): number = str(i) peer_data = PeerInfo("peerid-" + number, "groupid-" + number, "peerid-" + number + "_target", cert=self.__cert) peer_manager.add_peer(peer_data)
def get_leader_object(self): mock_info = PeerInfo(peer_id="peer_id", group_id='a', target="192.0.0.1:1234", status=PeerStatus.unknown, cert=self.__peer_auth.peer_cert, order=0) mock_peer_object = PeerObject(list(conf.CHANNEL_OPTION)[0], mock_info) return mock_peer_object
def add_peer(self, peer_info: Union[PeerInfo, dict]): """add_peer to peer_manager :param peer_info: PeerInfo, dict :return: create_peer_order """ if isinstance(peer_info, dict): peer_info = PeerInfo(peer_info["id"], peer_info["id"], peer_info["peer_target"], order=peer_info["order"]) logging.debug(f"add peer id: {peer_info.peer_id}") # If exist same peer_target in peer_list, delete exist one. # this is temporary rule that will be argued. # if peer_info.peer_id not in self.peer_list[conf.ALL_GROUP_ID].keys(): # exist_peer = self.__get_peer_by_target(peer_info.target) # if exist_peer is not None: # self.remove_peer(exist_peer.peer_id, exist_peer.group_id) self.__init_peer_group(peer_info.group_id) util.logger.spam(f"peer_manager::add_peer try make PeerObject") peer_object = PeerObject(self.__channel_name, peer_info) # add_peer logic must be atomic with self.__add_peer_lock: if peer_info.order <= 0: if peer_info.peer_id in self.peer_list[peer_info.group_id]: peer_info.order = self.peer_list[peer_info.group_id][peer_info.peer_id].order else: peer_info.order = self.__make_peer_order(peer_info) logging.debug(f"new peer order {peer_info.peer_id} : {peer_info.order}") # set to leader peer if (self.peer_leader[peer_info.group_id] == 0) or (len(self.peer_list[peer_info.group_id]) == 0): logging.debug("Set Group Leader Peer: " + str(peer_info.order)) self.peer_leader[peer_info.group_id] = peer_info.order if (self.peer_leader[conf.ALL_GROUP_ID] == 0) or (len(self.peer_list[conf.ALL_GROUP_ID]) == 0): logging.debug("Set ALL Leader Peer: " + str(peer_info.order)) self.peer_leader[conf.ALL_GROUP_ID] = peer_info.order self.peer_list[peer_info.group_id][peer_info.peer_id] = peer_info self.peer_list[conf.ALL_GROUP_ID][peer_info.peer_id] = peer_info self.peer_order_list[peer_info.group_id][peer_info.order] = peer_info.peer_id self.peer_order_list[conf.ALL_GROUP_ID][peer_info.order] = peer_info.peer_id self.__peer_object_list[peer_info.group_id][peer_info.peer_id] = peer_object self.__peer_object_list[conf.ALL_GROUP_ID][peer_info.peer_id] = peer_object return peer_info.order
def leader_complain_to_rs(self, group_id, is_announce_new_peer=True) -> PeerInfo: """When fail to find a leader, Ask to RS. RS check leader and notify leader info or set new leader first peer who complained :param group_id: :param is_announce_new_peer: :return: """ logging.debug(f"peer_manager:leader_complain_to_rs") if ObjectManager().peer_service.stub_to_radiostation is None: return None peer_self = self.get_peer(ObjectManager().peer_service.peer_id, ObjectManager().peer_service.group_id) peer_self_dumped = peer_self.dump() response = ObjectManager().peer_service.stub_to_radiostation.call( "Request", loopchain_pb2.Message( code=message_code.Request.peer_complain_leader, message=group_id, object=peer_self_dumped)) if response is not None and response.code == message_code.Response.success: try: leader_peer = PeerInfo.load(response.object) except Exception as e: traceback.print_exc() logging.error( f"Invalid peer info. Check your Radio Station. exception={e}" ) return None self.set_leader_peer(leader_peer) else: leader_peer = None # logging.debug(str(leader_peer)) # self.set_leader_peer(leader_peer) # # if is_announce_new_peer: # self.announce_new_leader("", leader_peer.peer_id) return leader_peer
def setUp(self): test_util.print_testname(self._testMethodName) self.__peer_auth = test_util.create_peer_auth() peer_service_mock = Mock() peer_service_mock.peer_manager = Mock() mock_info = PeerInfo(peer_id=self.__peer_id, group_id='a', target="192.0.0.1:1234", status=PeerStatus.unknown, cert=self.__peer_auth.get_public_der(), order=0) mock_peer_object = PeerObject(mock_info) def get_leader_object(): return mock_peer_object peer_service_mock.peer_manager.get_leader_object = get_leader_object ObjectManager().peer_service = peer_service_mock
def deserialize(peer_list_data_serialized: dict) -> 'PeerListData': peer_info_list = dict() for group_id, peer_info_group_serialized in peer_list_data_serialized['peer_info_list'].items(): group = dict() for peer_id, peer_info_serialized in peer_info_group_serialized.items(): group[peer_id] = PeerInfo.deserialize(peer_info_serialized) peer_info_list[group_id] = group peer_order_list = dict() for group_id, order_list in peer_list_data_serialized['peer_order_list'].items(): converted_order_list = dict() for order, peer_id in order_list.items(): converted_order_list[int(order)] = peer_id peer_order_list[group_id] = converted_order_list peer_list_data = PeerListData() peer_list_data.__peer_info_list = peer_info_list peer_list_data.__peer_leader = peer_list_data_serialized['peer_leader'] peer_list_data.__peer_order_list = peer_order_list return peer_list_data
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)
async def get(self, request, request_type): # args = ServerComponents().parser.parse_args() args = request.raw_args channel = get_channel_name_from_args(args) logging.debug(f'channel name : {channel}') if request_type == self.__REQUEST_TYPE['PEER_LIST']: grpc_response = ServerComponents().get_peer_list(channel) peer_manager = PeerManager(channel) peer_list_data = PeerListData.load(grpc_response.peer_list) peer_manager.set_peer_list(peer_list_data) all_peer_list = [] connected_peer_list = [] if peer_manager.peer_list: leader_peer_id = "" # for set peer_type info to peer leader_peer = peer_manager.get_leader_peer(conf.ALL_GROUP_ID, is_peer=False) if leader_peer is not None: leader_peer_id = leader_peer.peer_id for peer_id in peer_manager.peer_list[conf.ALL_GROUP_ID]: peer_each: PeerInfo = peer_manager.peer_list[ conf.ALL_GROUP_ID][peer_id] peer_data = peer_each.serialize() if peer_each.peer_id == leader_peer_id: peer_data['peer_type'] = loopchain_pb2.BLOCK_GENERATOR else: peer_data['peer_type'] = loopchain_pb2.PEER all_peer_list.append(peer_data) if peer_each.status == PeerStatus.connected: connected_peer_list.append(peer_data) json_data = { 'registered_peer_count': len(all_peer_list), 'connected_peer_count': len(connected_peer_list), 'registered_peer_list': all_peer_list, 'connected_peer_list': connected_peer_list } result = { 'response_code': message_code.Response.success, 'data': json_data } elif request_type == self.__REQUEST_TYPE['PEER_STATUS_LIST']: grpc_response = ServerComponents().get_peer_list(channel) peer_manager = PeerManager(channel) peer_list_data = PeerListData.load(grpc_response.peer_list) peer_manager.set_peer_list(peer_list_data) registered_peer_count = 0 connected_peer_count = 0 all_peer_list = [] if peer_manager.peer_list: async_futures: List[grpc.Future] = [] for peer_id in peer_manager.peer_list[conf.ALL_GROUP_ID]: async_future = ServerComponents().get_peer_status_async( peer_id, conf.ALL_GROUP_ID, channel) async_futures.append(async_future) if async_futures: futures.as_completed(async_futures) for async_future, peer_id in zip( async_futures, peer_manager.peer_list[conf.ALL_GROUP_ID]): if async_future.exception(): logging.warning( f'RequestType({request_type}), exception({async_future.exception()})' ) continue grpc_response = async_future.result() if grpc_response is not None and grpc_response.status != "": peer_each = peer_manager.peer_list[ conf.ALL_GROUP_ID][peer_id] status_json = json.loads(grpc_response.status) status_json["order"] = peer_each.order all_peer_list.append(status_json) registered_peer_count = peer_manager.get_peer_count() connected_peer_count = peer_manager.get_connected_peer_count() json_data = { 'registered_peer_count': registered_peer_count, 'connected_peer_count': connected_peer_count, 'peer_status_list': all_peer_list } result = { 'response_code': message_code.Response.success, 'data': json_data } elif request_type == self.__REQUEST_TYPE['LEADER_PEER']: grpc_response = ServerComponents().get_leader_peer(channel) result = dict() result['response_code'] = grpc_response.code if grpc_response.code == message_code.Response.success: peer_info = PeerInfo.load(grpc_response.object) result['data'] = peer_info.serialize() else: result['message'] = message_code.get_response_msg( grpc_response.code) elif request_type == self.__REQUEST_TYPE['PEER_STATUS']: peer_id = args['peer_id'] group_id = args['group_id'] if peer_id is None or group_id is None: return self.__abort_if_arg_isnt_enough('peer_id, group_id') # logging.debug(f"try get_peer_status peer_id({peer_id}), group_id({group_id})") grpc_response = ServerComponents().get_peer_status( args['peer_id'], args['group_id'], channel) result = json.loads(grpc_response.status) else: return ServerComponents().abort_if_url_doesnt_exist( request_type, self.__REQUEST_TYPE) return response.json(result)
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)