コード例 #1
0
    def leader_complain(self):
        complained_leader_id, new_leader_id = self.get_leader_ids_for_complaint()
        version = self.blockchain.block_versioner.get_version(self.epoch.height)
        leader_vote = Vote.get_leader_vote_class(version).new(
            signer=ChannelProperty().peer_auth,
            block_height=self.epoch.height,
            round_=self.epoch.round,
            old_leader=ExternalAddress.fromhex_address(complained_leader_id),
            new_leader=ExternalAddress.fromhex_address(new_leader_id),
            timestamp=util.get_time_stamp()
        )
        util.logger.info(
            f"LeaderVote : old_leader({complained_leader_id}), new_leader({new_leader_id}), round({self.epoch.round})")
        self.add_complain(leader_vote)

        leader_vote_serialized = leader_vote.serialize()
        leader_vote_dumped = json.dumps(leader_vote_serialized)

        complain_kwargs = {
            "complain_vote": leader_vote_dumped,
            "channel": self.channel_name
        }

        if conf.RECOVERY_MODE:
            complain_kwargs["from_recovery"] = True

        request = loopchain_pb2.ComplainLeaderRequest(**complain_kwargs)

        util.logger.debug(
            f"complained_leader_id({complained_leader_id}), "
            f"new_leader_id({new_leader_id})")

        reps_hash = self.blockchain.get_next_reps_hash_by_header(self.blockchain.last_block.header)
        self.__channel_service.broadcast_scheduler.schedule_broadcast("ComplainLeader",
                                                                      request,
                                                                      reps_hash=reps_hash)
コード例 #2
0
    def create_tx(self, data):
        tx = Transaction()
        score_id = ""
        score_version = ""

        try:
            score_info = self._channel_service.score_info
            score_id = score_info[message_code.MetaParams.ScoreInfo.score_id]
            score_version = score_info[
                message_code.MetaParams.ScoreInfo.score_version]
        except KeyError as e:
            logging.debug(f"CreateTX : load score info fail\n" f"cause : {e}")

        send_tx_type = self._channel_service.get_channel_option(
        )["send_tx_type"]
        tx.init_meta(ChannelProperty().peer_id, score_id, score_version,
                     ChannelProperty().name, send_tx_type)
        tx.put_data(data)
        tx.sign_hash(ChannelProperty().peer_auth)

        self._channel_service.broadcast_scheduler.schedule_job(
            BroadcastCommand.CREATE_TX, tx)

        try:
            data_log = json.loads(data)
        except Exception as e:
            data_log = {'tx_hash': tx.tx_hash}

        util.apm_event(
            ChannelProperty().peer_id, {
                'event_type': 'CreateTx',
                'peer_id': ChannelProperty().peer_id,
                'peer_name': conf.PEER_NAME,
                'channel_name': ChannelProperty().name,
                'tx_hash': tx.tx_hash,
                'data': data_log
            })

        return tx.tx_hash
コード例 #3
0
 def update_sub_services_properties(self, **properties):
     stub = StubCollection().channel_tx_creator_stubs[ChannelProperty().name]
     asyncio.run_coroutine_threadsafe(stub.async_task().update_properties(properties), self.loop)
     stub = StubCollection().channel_tx_receiver_stubs[ChannelProperty().name]
     asyncio.run_coroutine_threadsafe(stub.async_task().update_properties(properties), self.loop)
コード例 #4
0
    async def consensus(self):
        util.logger.debug(
            f"-------------------consensus "
            f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})"
        )
        async with self.__lock:
            if self._block_manager.epoch.leader_id != ChannelProperty(
            ).peer_id:
                util.logger.warning(
                    f"This peer is not leader. epoch leader={self._block_manager.epoch.leader_id}"
                )
                return

            self._vote_queue = asyncio.Queue(loop=self._loop)

            complained_result = self._block_manager.epoch.complained_result
            block_builder = self._block_manager.epoch.makeup_block(
                complained_result)
            vote_result = None
            last_unconfirmed_block = self._blockchain.last_unconfirmed_block
            next_leader = ExternalAddress.fromhex(ChannelProperty().peer_id)

            if complained_result:
                util.logger.spam("consensus block_builder.complained")
                """
                confirm_info = self._blockchain.find_confirm_info_by_hash(self._blockchain.last_block.header.hash)
                if not confirm_info and self._blockchain.last_block.header.height > 0:
                    util.logger.spam("Can't make a block as a leader, this peer will be complained too.")
                    return
                """

                self._made_block_count += 1
            elif len(block_builder.transactions) > 0:
                util.logger.spam(
                    f"consensus len(block_builder.transactions) > 0")
                if last_unconfirmed_block:
                    if (len(last_unconfirmed_block.body.transactions) > 0
                            or last_unconfirmed_block.header.complained) or (
                                len(last_unconfirmed_block.body.transactions)
                                == 0 and
                                last_unconfirmed_block.header.peer_id.hex_hx()
                                != ChannelProperty().peer_id):
                        vote = self._block_manager.candidate_blocks.get_vote(
                            last_unconfirmed_block.header.hash)
                        vote_result = await self._wait_for_voting(
                            last_unconfirmed_block)
                        if not vote_result:
                            return self.__block_generation_timer.call()

                        self.__add_block(last_unconfirmed_block, vote)
                        self._block_manager.epoch = Epoch.new_epoch(
                            ChannelProperty().peer_id)

                        next_leader = last_unconfirmed_block.header.next_leader
            else:
                if (last_unconfirmed_block) and (
                        len(last_unconfirmed_block.body.transactions) > 0
                        or last_unconfirmed_block.header.complained):
                    vote = self._block_manager.candidate_blocks.get_vote(
                        last_unconfirmed_block.header.hash)
                    vote_result = await self._wait_for_voting(
                        last_unconfirmed_block)
                    if not vote_result:
                        return self.__block_generation_timer.call()

                    self.__add_block(last_unconfirmed_block, vote)

                    peer_manager = ObjectManager().channel_service.peer_manager
                    next_leader = ExternalAddress.fromhex(
                        peer_manager.get_next_leader_peer(
                            current_leader_peer_id=ChannelProperty().peer_id).
                        peer_id)
                else:
                    return self.__block_generation_timer.call()

            candidate_block = self.__build_candidate_block(
                block_builder, next_leader, vote_result)
            candidate_block, invoke_results = ObjectManager(
            ).channel_service.score_invoke(candidate_block)
            self._block_manager.set_invoke_results(
                candidate_block.header.hash.hex(), invoke_results)

            util.logger.spam(f"candidate block : {candidate_block.header}")

            self._block_manager.vote_unconfirmed_block(
                candidate_block.header.hash, True)
            self._block_manager.candidate_blocks.add_block(candidate_block)
            self._blockchain.last_unconfirmed_block = candidate_block

            broadcast_func = partial(
                self._block_manager.broadcast_send_unconfirmed_block,
                candidate_block)
            self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func)
            if await self._wait_for_voting(candidate_block) is None:
                return

            if len(candidate_block.body.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \
                    next_leader.hex_hx() != ChannelProperty().peer_id:
                util.logger.spam(f"-------------------turn_to_peer "
                                 f"next_leader({next_leader.hex_hx()}) "
                                 f"peer_id({ChannelProperty().peer_id})")
                ObjectManager().channel_service.reset_leader(
                    next_leader.hex_hx())
            else:
                self._block_manager.epoch = Epoch.new_epoch(
                    next_leader.hex_hx())
                if not conf.ALLOW_MAKE_EMPTY_BLOCK:
                    self.__block_generation_timer.call_instantly()
                else:
                    self.__block_generation_timer.call()
コード例 #5
0
 def add_zero_limit_control(self):
     stub = StubCollection().channel_tx_creator_stubs[ChannelProperty().name]
     stub.sync_task().add_zero_limit_control()
コード例 #6
0
 def get_channel_option(self) -> dict:
     return conf.CHANNEL_OPTION[ChannelProperty().name]
コード例 #7
0
    async def consensus(self):
        util.logger.debug(
            f"-------------------consensus "
            f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})"
        )
        async with self.__lock:
            if self._block_manager.epoch.leader_id != ChannelProperty(
            ).peer_id:
                util.logger.warning(
                    f"This peer is not leader. epoch leader={self._block_manager.epoch.leader_id}"
                )
                return

            self._vote_queue = asyncio.Queue(loop=self._loop)

            complained_result = self._block_manager.epoch.complained_result
            block_builder = self._block_manager.epoch.makeup_block(
                complained_result)
            vote_result = None
            last_unconfirmed_block = self._blockchain.last_unconfirmed_block
            next_leader = ExternalAddress.fromhex(ChannelProperty().peer_id)

            need_next_call = False
            try:
                if complained_result:
                    util.logger.spam("consensus block_builder.complained")
                    """
                    confirm_info = self._blockchain.find_confirm_info_by_hash(self._blockchain.last_block.header.hash)
                    if not confirm_info and self._blockchain.last_block.header.height > 0:
                        util.logger.spam("Can't make a block as a leader, this peer will be complained too.")
                        return
                    """
                    self._made_block_count += 1
                elif self.made_block_count >= (conf.MAX_MADE_BLOCK_COUNT - 1):
                    if last_unconfirmed_block:
                        await self.__add_block(last_unconfirmed_block)
                        peer_manager = ObjectManager(
                        ).channel_service.peer_manager
                        next_leader = ExternalAddress.fromhex(
                            peer_manager.get_next_leader_peer(
                                current_leader_peer_id=ChannelProperty(
                                ).peer_id).peer_id)
                    else:
                        util.logger.info(
                            f"This leader already made {self.made_block_count} blocks. "
                            f"MAX_MADE_BLOCK_COUNT is {conf.MAX_MADE_BLOCK_COUNT} "
                            f"There is no more right. Consensus loop will return."
                        )
                        return
                elif len(block_builder.transactions
                         ) > 0 or conf.ALLOW_MAKE_EMPTY_BLOCK:
                    if last_unconfirmed_block:
                        next_leader = await self.__add_block_and_new_epoch(
                            block_builder, last_unconfirmed_block)
                elif len(block_builder.transactions) == 0 and (
                        last_unconfirmed_block
                        and len(last_unconfirmed_block.body.transactions) > 0):
                    next_leader = await self.__add_block_and_new_epoch(
                        block_builder, last_unconfirmed_block)
                else:
                    need_next_call = True
            except NotEnoughVotes:
                need_next_call = True
            finally:
                if need_next_call:
                    return self.__block_generation_timer.call()

            candidate_block = self.__build_candidate_block(
                block_builder, next_leader, vote_result)
            candidate_block, invoke_results = ObjectManager(
            ).channel_service.score_invoke(candidate_block)
            self._block_manager.set_invoke_results(
                candidate_block.header.hash.hex(), invoke_results)

            util.logger.spam(f"candidate block : {candidate_block.header}")

            self._block_manager.vote_unconfirmed_block(
                candidate_block.header.hash, True)
            self._block_manager.candidate_blocks.add_block(candidate_block)
            self._blockchain.last_unconfirmed_block = candidate_block

            broadcast_func = partial(
                self._block_manager.broadcast_send_unconfirmed_block,
                candidate_block)
            self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func)
            if await self._wait_for_voting(candidate_block) is None:
                return

            if next_leader.hex_hx() != ChannelProperty().peer_id:
                util.logger.spam(f"-------------------turn_to_peer "
                                 f"next_leader({next_leader.hex_hx()}) "
                                 f"peer_id({ChannelProperty().peer_id})")
                ObjectManager().channel_service.reset_leader(
                    next_leader.hex_hx())
                ObjectManager().channel_service.turn_on_leader_complain_timer()
            else:
                self._block_manager.epoch = Epoch.new_epoch(
                    next_leader.hex_hx())
                if not conf.ALLOW_MAKE_EMPTY_BLOCK:
                    self.__block_generation_timer.call_instantly()
                else:
                    self.__block_generation_timer.call()
コード例 #8
0
 def __init_consensus(self):
     consensus = Consensus(self, ChannelProperty().name)
     self.__consensus = consensus
     self.__block_manager.consensus = consensus
     consensus.register_subscriber(self.__block_manager)
コード例 #9
0
 def __init_node_subscriber(self):
     self.__node_subscriber = NodeSubscriber(
         channel=ChannelProperty().name,
         rs_target=ChannelProperty().rs_target
     )
コード例 #10
0
        async def _serve():
            await StubCollection().create_peer_stub()
            results = await StubCollection().peer_stub.async_task().get_channel_info_detail(ChannelProperty().name)

            await self.init(*results)

            self.__timer_service.start()
            logging.info(f'channel_service: init complete channel: {ChannelProperty().name}')
コード例 #11
0
 async def _select_node_type(self):
     if self._is_role_switched():
         new_node_type = self._get_node_type_by_peer_list()
         utils.logger.info(f"Role switching to new node type: {new_node_type.name}")
         ChannelProperty().node_type = new_node_type
     self.__inner_service.update_sub_services_properties(node_type=ChannelProperty().node_type.value)
コード例 #12
0
    async def consensus(self):
        util.logger.debug(
            f"-------------------consensus "
            f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})"
        )
        with self.__lock:
            complained_result = self._block_manager.epoch.complained_result
            block_builder = self._block_manager.epoch.makeup_block(
                complained_result)
            vote_result = None
            last_unconfirmed_block = self._blockchain.last_unconfirmed_block
            next_leader = ExternalAddress.fromhex(ChannelProperty().peer_id)

            if complained_result:
                util.logger.spam("consensus block_builder.complained")
                confirm_info = self._blockchain.find_confirm_info_by_hash(
                    self._blockchain.last_block.header.hash)
                if not confirm_info and self._blockchain.last_block.header.height > 0:
                    util.logger.spam(
                        "Can't make a block as a leader, this peer will be complained too."
                    )
                    return
                vote_result = True
                self._block_manager.epoch.set_epoch_leader(
                    ChannelProperty().peer_id)
                self._made_block_count += 1
            elif len(block_builder.transactions) > 0:
                util.logger.spam(
                    f"consensus len(block_builder.transactions) > 0")
                if last_unconfirmed_block:
                    if (len(last_unconfirmed_block.body.transactions) > 0
                            or last_unconfirmed_block.header.complained) or (
                                len(last_unconfirmed_block.body.transactions)
                                == 0 and
                                last_unconfirmed_block.header.peer_id.hex_hx()
                                != ChannelProperty().peer_id):
                        vote = self._block_manager.candidate_blocks.get_vote(
                            last_unconfirmed_block.header.hash)
                        vote_result = vote.get_result(
                            last_unconfirmed_block.header.hash.hex(),
                            conf.VOTING_RATIO)
                        if not vote_result:
                            return self.__block_generation_timer.call()

                        self.__add_block(last_unconfirmed_block, vote)

                        next_leader = last_unconfirmed_block.header.next_leader
            else:
                if (last_unconfirmed_block) and (
                        len(last_unconfirmed_block.body.transactions) > 0
                        or last_unconfirmed_block.header.complained):
                    vote = self._block_manager.candidate_blocks.get_vote(
                        last_unconfirmed_block.header.hash)
                    vote_result = vote.get_result(
                        last_unconfirmed_block.header.hash.hex(),
                        conf.VOTING_RATIO)
                    if not vote_result:
                        return self.__block_generation_timer.call()

                    self.__add_block(last_unconfirmed_block, vote)

                    peer_manager = ObjectManager().channel_service.peer_manager
                    next_leader = ExternalAddress.fromhex(
                        peer_manager.get_next_leader_peer(
                            current_leader_peer_id=ChannelProperty().peer_id).
                        peer_id)
                else:
                    return self.__block_generation_timer.call()

            last_block = self._blockchain.last_block
            block_builder.height = last_block.header.height + 1
            block_builder.prev_hash = last_block.header.hash
            block_builder.next_leader = next_leader
            block_builder.peer_private_key = ObjectManager(
            ).channel_service.peer_auth.private_key
            block_builder.confirm_prev_block = vote_result or (
                self._made_block_count > 0)

            candidate_block = block_builder.build()
            candidate_block, invoke_results = ObjectManager(
            ).channel_service.score_invoke(candidate_block)
            self._block_manager.set_invoke_results(
                candidate_block.header.hash.hex(), invoke_results)

            util.logger.spam(f"candidate block : {candidate_block.header}")
            block_verifier = BlockVerifier.new(candidate_block.header.version,
                                               self._blockchain.tx_versioner)
            block_verifier.verify(candidate_block, self._blockchain.last_block,
                                  self._blockchain)

            self._block_manager.vote_unconfirmed_block(
                candidate_block.header.hash, True)
            self._block_manager.candidate_blocks.add_block(candidate_block)

            self._blockchain.last_unconfirmed_block = candidate_block
            broadcast_func = partial(
                self._block_manager.broadcast_send_unconfirmed_block,
                candidate_block)

            # TODO Temporary ignore below line for developing leader complain
            self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func)

            if len(block_builder.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \
                    next_leader.hex_hx() != ChannelProperty().peer_id:
                util.logger.spam(f"-------------------turn_to_peer "
                                 f"next_leader({next_leader.hex_hx()}) "
                                 f"peer_id({ChannelProperty().peer_id})")
                await ObjectManager().channel_service.reset_leader(
                    next_leader.hex_hx())
            else:
                self.__block_generation_timer.call()
コード例 #13
0
    async def consensus(self):
        util.logger.debug(
            f"-------------------consensus "
            f"candidate_blocks({len(self._blockmanager.candidate_blocks.blocks)})"
        )
        with self.__lock:
            block_builder = self._makeup_block()
            vote_result = None

            if len(block_builder.transactions) > 0:
                # util.logger.debug(f"-------------------consensus logic-1")
                next_leader = ExternalAddress.fromhex(
                    ChannelProperty().peer_id)

                if self._blockchain.last_unconfirmed_block:
                    if (len(self._blockchain.last_unconfirmed_block.body.
                            transactions) > 0) or (
                                len(self._blockchain.last_unconfirmed_block.
                                    body.transactions) == 0 and
                                self._blockchain.last_unconfirmed_block.header.
                                peer_id.hex_hx() != ChannelProperty().peer_id):
                        # util.logger.debug(f"-------------------consensus logic-2")
                        vote = self._blockmanager.candidate_blocks.get_vote(
                            self._blockchain.last_unconfirmed_block.header.hash
                        )
                        vote_result = vote.get_result(
                            self._blockchain.last_unconfirmed_block.header.
                            hash.hex(), conf.VOTING_RATIO)
                        if not vote_result:
                            return self.__block_generation_timer.call()

                        self._blockmanager.add_block(
                            self._blockchain.last_unconfirmed_block, vote)
                        self._made_block_count += 1

                        next_leader = self._blockchain.last_unconfirmed_block.header.next_leader
            else:
                if self._blockchain.last_unconfirmed_block and len(
                        self._blockchain.last_unconfirmed_block.body.
                        transactions) > 0:
                    # util.logger.debug(f"-------------------consensus logic-3")
                    vote = self._blockmanager.candidate_blocks.get_vote(
                        self._blockchain.last_unconfirmed_block.header.hash)
                    vote_result = vote.get_result(
                        self._blockchain.last_unconfirmed_block.header.hash.
                        hex(), conf.VOTING_RATIO)
                    if not vote_result:
                        return self.__block_generation_timer.call()

                    self._blockmanager.add_block(
                        self._blockchain.last_unconfirmed_block, vote)
                    self._made_block_count += 1

                    peer_manager = ObjectManager().channel_service.peer_manager
                    next_leader = ExternalAddress.fromhex(
                        peer_manager.get_next_leader_peer().peer_id)
                else:
                    # util.logger.spam(f"tx count in block({len(block_builder.transactions)})")
                    return self.__block_generation_timer.call()

            last_block = self._blockchain.last_block
            block_builder.height = last_block.header.height + 1
            block_builder.prev_hash = last_block.header.hash
            block_builder.next_leader = next_leader
            block_builder.peer_private_key = ObjectManager(
            ).channel_service.peer_auth.peer_private_key
            block_builder.confirm_prev_block = vote_result or (
                self._made_block_count > 0)

            candidate_block = block_builder.build()
            candidate_block, invoke_results = ObjectManager(
            ).channel_service.score_invoke(candidate_block)
            self._blockmanager.set_invoke_results(
                candidate_block.header.hash.hex(), invoke_results)

            block_verifier = BlockVerifier.new(candidate_block.header.version,
                                               self._blockchain.tx_versioner)
            block_verifier.verify(candidate_block, self._blockchain.last_block,
                                  self._blockchain)

            logging.debug(f"candidate block : {candidate_block.header}")

            self._blockmanager.vote_unconfirmed_block(
                candidate_block.header.hash, True)
            self._blockmanager.candidate_blocks.add_block(candidate_block)

            self._blockchain.last_unconfirmed_block = candidate_block
            broadcast_func = partial(
                self._blockmanager.broadcast_send_unconfirmed_block,
                candidate_block)
            self.__start_broadcast_send_unconfirmed_block_timer(broadcast_func)

            if len(block_builder.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \
                    next_leader.hex() != ChannelProperty().peer_id:
                # util.logger.debug(f"-------------------turn_to_peer")
                ObjectManager().channel_service.state_machine.turn_to_peer()
            else:
                self.__block_generation_timer.call()
コード例 #14
0
    def __get_peer_stub_list(self) -> Tuple[int, int, List[Tuple]]:
        """It updates peer list for block manager refer to peer list on the loopchain network.
        This peer list is not same to the peer list of the loopchain network.

        :return max_height: a height of current blockchain
        :return unconfirmed_block_height: unconfirmed_block_height on the network
        :return peer_stubs: current peer list on the network (target, peer_stub)
        """
        max_height = -1  # current max height
        unconfirmed_block_height = -1
        peer_stubs = []  # peer stub list for block height synchronization

        if not ObjectManager().channel_service.is_support_node_function(
                conf.NodeFunction.Vote):
            rs_client = ObjectManager().channel_service.rs_client
            status_response = rs_client.call(RestMethod.Status)
            max_height = status_response['block_height']
            peer_stubs.append((rs_client.target, rs_client))
            return max_height, unconfirmed_block_height, peer_stubs

        # Make Peer Stub List [peer_stub, ...] and get max_height of network
        self.__block_height_sync_bad_targets = {
            k: v
            for k, v in self.__block_height_sync_bad_targets.items()
            if v > self.blockchain.block_height
        }
        util.logger.info(
            f"Bad Block Sync Peer : {self.__block_height_sync_bad_targets}")
        peer_target = ChannelProperty().peer_target
        my_height = self.blockchain.block_height

        if self.blockchain.last_block:
            reps_hash = self.blockchain.get_reps_hash_by_header(
                self.blockchain.last_block.header)
        else:
            reps_hash = ChannelProperty().crep_root_hash
        rep_targets = self.blockchain.find_preps_targets_by_roothash(reps_hash)
        target_list = list(rep_targets.values())
        for target in target_list:
            if target == peer_target:
                continue
            if target in self.__block_height_sync_bad_targets:
                continue
            util.logger.debug(f"try to target({target})")
            channel = GRPCHelper().create_client_channel(target)
            stub = loopchain_pb2_grpc.PeerServiceStub(channel)
            try:
                response = stub.GetStatus(
                    loopchain_pb2.StatusRequest(
                        request='block_sync',
                        channel=self.__channel_name,
                    ), conf.GRPC_TIMEOUT_SHORT)
                target_block_height = max(response.block_height,
                                          response.unconfirmed_block_height)

                if target_block_height > my_height:
                    peer_stubs.append((target, stub))
                    max_height = max(max_height, target_block_height)
                    unconfirmed_block_height = max(
                        unconfirmed_block_height,
                        response.unconfirmed_block_height)

            except Exception as e:
                util.logger.warning(
                    f"This peer has already been removed from the block height target node. {e}"
                )

        return max_height, unconfirmed_block_height, peer_stubs
コード例 #15
0
    async def __block_height_sync_channel(self):
        # leader 로 시작하지 않았는데 자신의 정보가 leader Peer 정보이면 block height sync 하여
        # 최종 블럭의 leader 를 찾는다.
        peer_manager = self.peer_manager
        peer_leader = peer_manager.get_leader_peer()
        self_peer_object = peer_manager.get_peer(ChannelProperty().peer_id)
        is_delay_announce_new_leader = False
        peer_old_leader = None

        if peer_leader:
            block_sync_target = peer_leader.target
            block_sync_target_stub = StubManager.get_stub_manager_to_server(
                block_sync_target,
                loopchain_pb2_grpc.PeerServiceStub,
                time_out_seconds=conf.CONNECTION_RETRY_TIMEOUT,
                ssl_auth_type=conf.GRPC_SSL_TYPE)
        else:
            block_sync_target = ChannelProperty().radio_station_target
            block_sync_target_stub = self.__radio_station_stub

        if block_sync_target != ChannelProperty().peer_target:
            if block_sync_target_stub is None:
                logging.warning(
                    "You maybe Older from this network... or No leader in this network!"
                )

                is_delay_announce_new_leader = True
                peer_old_leader = peer_leader
                peer_leader = self.peer_manager.leader_complain_to_rs(
                    conf.ALL_GROUP_ID, is_announce_new_peer=False)

                if peer_leader is not None and ChannelProperty(
                ).node_type == conf.NodeType.CommunityNode:
                    block_sync_target_stub = StubManager.get_stub_manager_to_server(
                        peer_leader.target,
                        loopchain_pb2_grpc.PeerServiceStub,
                        time_out_seconds=conf.CONNECTION_RETRY_TIMEOUT,
                        ssl_auth_type=conf.GRPC_SSL_TYPE)

            if self.is_support_node_function(conf.NodeFunction.Vote) and \
                    (not peer_leader or peer_leader.peer_id == ChannelProperty().peer_id):
                peer_leader = self_peer_object
                self.block_manager.set_peer_type(loopchain_pb2.BLOCK_GENERATOR)
            else:
                _, future = self.block_manager.block_height_sync(
                    block_sync_target_stub)
                await future

                if block_sync_target_stub is None:
                    util.exit_and_msg("Fail connect to leader!!")

                self.show_peers()

            if block_sync_target_stub is not None and self.is_support_node_function(
                    conf.NodeFunction.Vote):
                await self.__subscribe_call_to_stub_by_method(
                    block_sync_target_stub, loopchain_pb2.BLOCK_GENERATOR)

            if is_delay_announce_new_leader:
                self.peer_manager.announce_new_leader(
                    peer_old_leader.peer_id,
                    peer_leader.peer_id,
                    self_peer_id=ChannelProperty().peer_id)
コード例 #16
0
    def __subscribe_call_to_rs_stub(self, rs_rest_stub):
        response = {
            'response_code': message_code.Response.fail,
            'message':
            message_code.get_response_msg(message_code.Response.fail)
        }

        try:
            if conf.REST_SSL_TYPE == conf.SSLAuthType.none:
                peer_target = ChannelProperty().rest_target
            else:
                peer_target = f"https://{ChannelProperty().rest_target}"
            response = rs_rest_stub.call("Subscribe", {
                'channel': ChannelProperty().name,
                'peer_target': peer_target
            })

        except Exception as e:
            logging.warning(
                f"Due to Subscription fail to RadioStation(mother peer), "
                f"automatically retrying subscribe call")

        if response['response_code'] == message_code.Response.success:
            if TimerService.TIMER_KEY_SUBSCRIBE in self.__timer_service.timer_list.keys(
            ):
                self.__timer_service.stop_timer(
                    TimerService.TIMER_KEY_SUBSCRIBE)
                self.radio_station_stub.update_methods_version()
                logging.debug(
                    f"Subscription to RadioStation(mother peer) is successful."
                )

            if TimerService.TIMER_KEY_SHUTDOWN_WHEN_FAIL_SUBSCRIBE in self.__timer_service.timer_list.keys(
            ):
                self.__timer_service.stop_timer(
                    TimerService.TIMER_KEY_SHUTDOWN_WHEN_FAIL_SUBSCRIBE)

            # start next get_status timer
            timer_key = TimerService.TIMER_KEY_GET_LAST_BLOCK_KEEP_CITIZEN_SUBSCRIPTION
            if timer_key not in self.__timer_service.timer_list.keys():
                util.logger.spam(
                    f"add timer for check_block_height_call to radiostation..."
                )
                self.__timer_service.add_timer(
                    timer_key,
                    Timer(target=timer_key,
                          duration=conf.GET_LAST_BLOCK_TIMER,
                          is_repeat=True,
                          callback=self.__check_block_height_call_to_rs_stub,
                          callback_kwargs={"rs_rest_stub": rs_rest_stub}))
        else:
            timer_key = TimerService.TIMER_KEY_SHUTDOWN_WHEN_FAIL_SUBSCRIBE
            if timer_key not in self.__timer_service.timer_list.keys():
                error = f"Shutdown by Subscribe retry timeout({conf.SHUTDOWN_TIMER})"
                self.__timer_service.add_timer(
                    timer_key,
                    Timer(target=timer_key,
                          duration=conf.SHUTDOWN_TIMER,
                          callback=self.__shutdown_peer,
                          callback_kwargs={"message": error}))

        return response
コード例 #17
0
    async def consensus(self):
        start_time = time.time()
        empty_block: Block = None

        try:
            self._loop = asyncio.get_event_loop()
            self._vote_queue = asyncio.Queue(loop=self._loop)

            block_builder = self._makeup_block()

            if len(block_builder.transactions
                   ) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK:
                return

            peer_manager = ObjectManager().channel_service.peer_manager

            last_block = self._blockchain.last_block
            block_builder.height = last_block.header.height + 1
            block_builder.prev_hash = last_block.header.hash
            block_builder.next_leader = Address.fromhex(
                peer_manager.get_next_leader_peer().peer_id)
            block_builder.peer_private_key = ObjectManager(
            ).channel_service.peer_auth.peer_private_key
            block_builder.confirm_prev_block = (self._made_block_count > 0)

            candidate_block = block_builder.build()
            candidate_block, invoke_results = ObjectManager(
            ).channel_service.score_invoke(candidate_block)

            block_verifier = BlockVerifier.new("0.1a")
            block_verifier.verify(candidate_block, self._blockchain.last_block,
                                  self._blockchain)

            logging.info(
                f"candidate block height: {candidate_block.header.height}")
            logging.info(
                f"candidate block hash: {candidate_block.header.hash.hex()}")
            logging.info(
                f"candidate block next leader: {candidate_block.header.next_leader.hex()}"
            )
            logging.info(
                f"candidate block confirm_prev_block: {candidate_block.body.confirm_prev_block}"
            )

            vote = Vote(candidate_block.header.hash.hex(),
                        ObjectManager().channel_service.peer_manager)
            vote.add_vote(ChannelProperty().group_id,
                          ChannelProperty().peer_id, True)

            self._blockmanager.broadcast_send_unconfirmed_block(
                candidate_block)
            success = await self._wait_for_voting(candidate_block, vote)
            if not success:
                return

            self._blockmanager.set_invoke_results(
                candidate_block.header.hash.hex(), invoke_results)
            self._blockmanager.add_block(candidate_block)
            self._made_block_count += 1

            pending_tx = self._txQueue.get_item_in_status(
                TransactionStatusInQueue.normal,
                TransactionStatusInQueue.normal)
            if not pending_tx and not conf.ALLOW_MAKE_EMPTY_BLOCK:
                block_builder = BlockBuilder.new("0.1a")
                block_builder.prev_hash = candidate_block.header.hash
                block_builder.height = candidate_block.header.height + 1
                block_builder.next_leader = candidate_block.header.next_leader
                block_builder.peer_private_key = ObjectManager(
                ).channel_service.peer_auth.peer_private_key
                block_builder.confirm_prev_block = True
                empty_block = block_builder.build()

                self._blockmanager.broadcast_send_unconfirmed_block(
                    empty_block)

                ObjectManager().channel_service.state_machine.turn_to_peer()
        finally:
            if not empty_block:
                elapsed_time = time.time() - start_time
                delay_time = conf.INTERVAL_BLOCKGENERATION - elapsed_time
                self._start_consensus_timer(delay_time)
コード例 #18
0
 async def _init_rs_client(self):
     self.__rs_client = RestClient(channel=ChannelProperty().name)
     await self._init_rs_target(refresh_all=True)
コード例 #19
0
 def __init_consensus(self):
     consensus = Consensus(self, ChannelProperty().name)
     self.__consensus = consensus
     self.__block_manager.consensus = consensus
     consensus.multiple_register(self.__block_manager)
コード例 #20
0
    async def consensus(self):
        util.logger.debug(f"-------------------consensus-------------------")
        async with self.__lock:
            if self._block_manager.epoch.leader_id != ChannelProperty(
            ).peer_id:
                util.logger.warning(
                    f"This peer is not leader. epoch leader={self._block_manager.epoch.leader_id}"
                )

            self._vote_queue = asyncio.Queue(loop=self._loop)
            complain_votes = self.__get_complaint_votes()
            complained_result = self._block_manager.epoch.complained_result
            if complained_result:
                self._blockchain.last_unconfirmed_block = None
            else:
                self._block_manager.epoch.remove_duplicate_tx_when_turn_to_leader(
                )

            last_block_vote_list = await self.__get_votes(
                self._blockchain.latest_block.header.hash)
            if last_block_vote_list is None:
                return

            last_unconfirmed_block: Optional[
                Block] = self._blockchain.last_unconfirmed_block
            last_block_header = self._blockchain.last_block.header

            if last_block_header.prep_changed:
                new_term = last_unconfirmed_block is None
            else:
                new_term = False

            if last_unconfirmed_block and not last_block_vote_list and not new_term:
                return

            # unrecorded_block means the last block of term to add prep changed block.
            if last_unconfirmed_block and last_unconfirmed_block.header.prep_changed:
                first_leader_of_term = self._blockchain.find_preps_ids_by_roothash(
                    last_unconfirmed_block.header.revealed_next_reps_hash)[0]
                is_unrecorded_block = ChannelProperty(
                ).peer_address != first_leader_of_term
            else:
                is_unrecorded_block = False

            skip_add_tx = is_unrecorded_block or complained_result
            block_builder = self._block_manager.epoch.makeup_block(
                complain_votes, last_block_vote_list, new_term, skip_add_tx)
            need_next_call = False
            try:
                if complained_result or new_term:
                    util.logger.spam(
                        "consensus block_builder.complained or new term")
                    """
                    confirm_info = self._blockchain.find_confirm_info_by_hash(self._blockchain.last_block.header.hash)
                    if not confirm_info and self._blockchain.last_block.header.height > 0:
                        util.logger.spam("Can't make a block as a leader, this peer will be complained too.")
                        return
                    """
                    block_builder = self._makeup_new_block(
                        block_builder.version, complain_votes,
                        self._blockchain.last_block.header.hash)
                elif self._blockchain.my_made_block_count == (
                        conf.MAX_MADE_BLOCK_COUNT - 2):
                    # (conf.MAX_MADE_BLOCK_COUNT - 2) means if made_block_count is 8,
                    # but after __add_block, it becomes 9
                    # so next unconfirmed block height is 10 (last).
                    if last_unconfirmed_block:
                        await self.__add_block(last_unconfirmed_block)
                    else:
                        util.logger.info(
                            f"This leader already made "
                            f"{self._blockchain.my_made_block_count} blocks. "
                            f"MAX_MADE_BLOCK_COUNT is {conf.MAX_MADE_BLOCK_COUNT} "
                            f"There is no more right. Consensus loop will return."
                        )
                        return
                elif len(block_builder.transactions) == 0 and not conf.ALLOW_MAKE_EMPTY_BLOCK and \
                        (last_unconfirmed_block and len(last_unconfirmed_block.body.transactions) == 0):
                    need_next_call = True
                elif last_unconfirmed_block:
                    await self.__add_block(last_unconfirmed_block)
            except (NotEnoughVotes, InvalidBlock):
                need_next_call = True
            except ThereIsNoCandidateBlock:
                util.logger.warning(f"There is no candidate block.")
                return
            finally:
                if need_next_call:
                    return self.__block_generation_timer.call()

            util.logger.spam(
                f"self._block_manager.epoch.leader_id: {self._block_manager.epoch.leader_id}"
            )
            candidate_block = self.__build_candidate_block(block_builder)
            candidate_block, invoke_results = self._blockchain.score_invoke(
                candidate_block,
                self._blockchain.latest_block,
                is_block_editable=True,
                is_unrecorded_block=is_unrecorded_block)

            util.logger.spam(f"candidate block : {candidate_block.header}")
            self._block_manager.candidate_blocks.add_block(
                candidate_block,
                self._blockchain.find_preps_addresses_by_header(
                    candidate_block.header))
            self.__broadcast_block(candidate_block)

            if is_unrecorded_block:
                self._blockchain.last_unconfirmed_block = None
            else:
                self._block_manager.vote_unconfirmed_block(
                    candidate_block, self._block_manager.epoch.round, True)
                self._blockchain.last_unconfirmed_block = candidate_block
                try:
                    await self._wait_for_voting(candidate_block)
                except NotEnoughVotes:
                    return

            if not candidate_block.header.prep_changed:
                if (self._blockchain.made_block_count_reached_max(
                        self._blockchain.last_block)
                        or self._block_manager.epoch.leader_id !=
                        ChannelProperty().peer_id):
                    ObjectManager().channel_service.reset_leader(
                        self._block_manager.epoch.leader_id)

            self.__block_generation_timer.call()
コード例 #21
0
 def __init_broadcast_scheduler(self):
     scheduler = BroadcastSchedulerFactory.new(channel=ChannelProperty().name,
                                               self_target=ChannelProperty().peer_target)
     scheduler.start()
     self.__broadcast_scheduler = scheduler
コード例 #22
0
ファイル: channel_service.py プロジェクト: assays/loopchain
 def __get_role_switch_block_height(self):
     # Currently, only one way role switch is supported from Citizen to Rep
     if ChannelProperty().node_type != conf.NodeType.CitizenNode:
         return -1
     return self.get_channel_option().get('role_switch_block_height', -1)
コード例 #23
0
 async def __init_sub_services(self):
     self.__inner_service.init_sub_services()
     await StubCollection().create_channel_tx_creator_stub(ChannelProperty().name)
     await StubCollection().create_channel_tx_receiver_stub(ChannelProperty().name)
コード例 #24
0
 def _load_peers_from_file(peer_manager: 'PeerManager'):
     utils.logger.debug(f"load_peers_from_file")
     channel_info = utils.load_json_data(conf.CHANNEL_MANAGE_DATA_PATH)
     reps: list = channel_info[ChannelProperty().name].get("peers")
     for peer in reps:
         peer_manager.add_peer(peer)
コード例 #25
0
 def is_support_node_function(self, node_function):
     return conf.NodeType.is_support_node_function(node_function, ChannelProperty().node_type)
コード例 #26
0
 def __init_peer_auth(self):
     try:
         self.__peer_auth = Signer.from_channel(ChannelProperty().name)
     except Exception as e:
         logging.exception(f"peer auth init fail cause : {e}")
         util.exit_and_msg(f"peer auth init fail cause : {e}")
コード例 #27
0
ファイル: peer_manager.py プロジェクト: stjordanis/loopchain
    def announce_new_leader(self,
                            complained_leader_id,
                            new_leader_id,
                            is_broadcast=True,
                            self_peer_id=None):
        """Announce New Leader Id to Network

        :param complained_leader_id:
        :param new_leader_id:
        :param is_broadcast: False(notify to RS only), True(broadcast to network include RS)
        :param self_peer_id:
        :return:
        """
        util.logger.spam(
            f"peer_manager:announce_new_leader channel({self.__channel_name}), "
            f"complained_leader_id({complained_leader_id}), "
            f"new_leader_id({new_leader_id}), "
            f"is_broadcast({is_broadcast})")
        is_rs = ObjectManager().rs_service is not None

        announce_message = loopchain_pb2.ComplainLeaderRequest(
            complained_leader_id=complained_leader_id,
            channel=self.__channel_name,
            new_leader_id=new_leader_id,
            message="Announce New Leader",
            peer_id=ChannelProperty().peer_id,
            group_id=ChannelProperty().group_id)

        # new_leader_peer = self.get_peer(new_leader_id)

        # Announce New Leader to Radio station
        try:
            channel_service = ObjectManager().channel_service
            if channel_service:
                response = channel_service.radio_station_stub.call(
                    "AnnounceNewLeader", announce_message)
                if response.response_code == message_code.Response.fail_no_peer_info_in_rs:
                    util.logger.spam(
                        f"peer_manager:announce_new_leader fail no peer info in rs! is_broadcast({is_broadcast})"
                    )
                    announce_message.message = message_code.get_response_msg(
                        message_code.Response.fail_no_peer_info_in_rs)
                    ObjectManager().channel_service.connect_to_radio_station(
                        is_reconnect=True)

                    ObjectManager(
                    ).channel_service.broadcast_scheduler.schedule_broadcast(
                        "Request",
                        loopchain_pb2.Message(
                            code=message_code.Request.peer_reconnect_to_rs,
                            channel=self.__channel_name))
        except Exception as e:
            # logging.debug("in RS there is no peer_service....")
            is_rs = True

        if is_broadcast is True:
            for peer_id in list(self.peer_list[conf.ALL_GROUP_ID]):
                if new_leader_id == peer_id and is_rs is not True:
                    util.logger.spam(
                        f"Prevent reset leader loop in AnnounceNewLeader message"
                    )
                    continue

                peer_each = self.peer_list[conf.ALL_GROUP_ID][peer_id]
                stub_manager = self.get_peer_stub_manager(
                    peer_each, conf.ALL_GROUP_ID)
                try:
                    stub_manager.call_async("AnnounceNewLeader",
                                            announce_message,
                                            is_stub_reuse=True)
                except Exception as e:
                    logging.warning("gRPC Exception: " + str(e))
                    logging.debug("No response target: " +
                                  str(peer_each.target))
コード例 #28
0
ファイル: block_manager.py プロジェクト: stjordanis/loopchain
    def __get_peer_stub_list(self, target_peer_stub=None):
        """It updates peer list for block manager refer to peer list on the loopchain network.
        This peer list is not same to the peer list of the loopchain network.

        :return max_height: a height of current blockchain
        :return peer_stubs: current peer list on the loopchain network
        """
        peer_target = ChannelProperty().peer_target
        peer_manager = ObjectManager().channel_service.peer_manager

        # Make Peer Stub List [peer_stub, ...] and get max_height of network
        max_height = -1  # current max height
        peer_stubs = []  # peer stub list for block height synchronization

        if ObjectManager().channel_service.is_support_node_function(
                conf.NodeFunction.Vote):
            target_dict = peer_manager.get_IP_of_peers_dict()
            target_list = [
                peer_target for peer_id, peer_target in target_dict.items()
                if peer_id != ChannelProperty().peer_id
            ]
        else:
            target_list = [f"{target_peer_stub.target}"]

        for target in target_list:
            if target != peer_target:
                logging.debug(f"try to target({target})")
                channel = GRPCHelper().create_client_channel(target)
                stub = loopchain_pb2_grpc.PeerServiceStub(channel)
                try:
                    if ObjectManager(
                    ).channel_service.is_support_node_function(
                            conf.NodeFunction.Vote):
                        response = stub.GetStatus(
                            loopchain_pb2.StatusRequest(
                                request="",
                                channel=self.__channel_name,
                            ), conf.GRPC_TIMEOUT_SHORT)
                    else:
                        response = target_peer_stub.call("Status")
                        util.logger.spam('{/api/v1/status/peer} response: ' +
                                         response.text)
                        response.block_height = int(
                            json.loads(response.text)["block_height"])
                        response.unconfirmed_block_height = int(
                            json.loads(response.text).get(
                                "unconfirmed_block_height", -1))
                        stub.target = target

                    response.block_height = max(
                        response.block_height,
                        response.unconfirmed_block_height)

                    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(
                        f"This peer has already been removed from the block height target node. {e}"
                    )

        return max_height, peer_stubs
コード例 #29
0
    def _get_peer_stub_list(self) -> Tuple[int, int, List[Tuple[str, Any]]]:
        """It updates peer list for block manager refer to peer list on the loopchain network.
        This peer list is not same to the peer list of the loopchain network.

        :return max_height: a height of current blockchain
        :return unconfirmed_block_height: unconfirmed_block_height on the network
        :return peer_stubs: current peer list on the network (target, peer_stub)
        """
        max_height = -1  # current max height
        unconfirmed_block_height = -1
        peer_stubs = []  # peer stub list for block height synchronization

        rs_client: RestClient = self._channel_service.rs_client

        if not self._channel_service.is_support_node_function(
                conf.NodeFunction.Vote):
            status_response = rs_client.call(RestMethod.Status)
            max_height = status_response['block_height']
            peer_stubs.append((rs_client.target, rs_client))
            return max_height, unconfirmed_block_height, peer_stubs

        # Make Peer Stub List [peer_stub, ...] and get max_height of network
        self._block_height_sync_bad_targets = {
            k: v
            for k, v in self._block_height_sync_bad_targets.items()
            if v > self._blockchain.block_height
        }
        utils.logger.info(
            f"Bad Block Sync Peer : {self._block_height_sync_bad_targets}")
        peer_target = ChannelProperty().peer_target
        my_height = self._blockchain.block_height

        port_pattern = re.compile(r":([0-9]{2,5})$")

        def _converter(target) -> str:
            port = int(port_pattern.search(target).group(1))
            new_port = f":{port + conf.PORT_DIFF_REST_SERVICE_CONTAINER}"
            return port_pattern.sub(new_port, target)

        endpoints = {
            target: _converter(target)
            for target in self._block_manager.get_target_list()
        }

        for grpc_endpoint, rest_endpoint in endpoints.items():
            if grpc_endpoint == peer_target:
                continue
            if grpc_endpoint in self._block_height_sync_bad_targets:
                continue
            utils.logger.debug(
                f"try to grpc_endpoint({grpc_endpoint}), rest_endpoint({rest_endpoint})"
            )
            channel = GRPCHelper().create_client_channel(grpc_endpoint)
            stub = loopchain_pb2_grpc.PeerServiceStub(channel)
            try:
                client = RestClient(self._block_manager.channel_name,
                                    rest_endpoint)
                response: dict = client.call(RestMethod.Status,
                                             timeout=conf.REST_TIMEOUT)
                target_block_height = max(response["block_height"],
                                          response["unconfirmed_block_height"])

                recovery = response.get("recovery", {})
                # only recovery_mode node should be included in block sync when running by recovery_mode
                if conf.RECOVERY_MODE and not recovery.get("mode", False):
                    continue

                if target_block_height > my_height:
                    peer_stubs.append((grpc_endpoint, stub))
                    max_height = max(max_height, target_block_height)
                    unconfirmed_block_height = max(
                        unconfirmed_block_height,
                        response["unconfirmed_block_height"])

            except Exception as e:
                utils.logger.warning(
                    f"This peer has already been removed from the block height target node. {e!r}"
                )

        return max_height, unconfirmed_block_height, peer_stubs
コード例 #30
0
 async def update_dos_properties(self, properties):
     stub = StubCollection().channel_tx_creator_stubs[ChannelProperty().name]
     await stub.async_task().update_dos_properties(properties)