Ejemplo n.º 1
0
    def add_complain(self, vote: LeaderVote):
        util.logger.spam(f"add_complain vote({vote})")

        if not self.epoch:
            util.logger.debug(f"Epoch is not initialized.")
            return

        if self.epoch.height == vote.block_height:
            if self.epoch.round == vote.round_:
                self.epoch.add_complain(vote)
            elif self.epoch.round > vote.round_:
                if vote.new_leader != ExternalAddress.empty():
                    self.__send_fail_leader_vote(vote)
                else:
                    return
            else:
                # TODO: do round sync
                return

            elected_leader = self.epoch.complain_result()
            if elected_leader:
                if elected_leader == ExternalAddress.empty().hex_xx(
                ) and vote.round_ == self.epoch.round:
                    util.logger.warning(
                        f"Fail to elect the next leader on {self.epoch.round} round."
                    )
                    elected_leader = self.blockchain.get_next_rep_in_reps(
                        ExternalAddress.fromhex(self.epoch.leader_id),
                        self.epoch.reps).hex_hx()
                if self.epoch.round == vote.round_:
                    self.__channel_service.reset_leader(elected_leader,
                                                        complained=True)
        elif self.epoch.height < vote.block_height:
            self.__channel_service.state_machine.block_sync()
Ejemplo n.º 2
0
    def test_leader_votes_completed_with_out_of_round(self):
        ratio = 0.51
        old_leader = self.reps[0]
        next_leader = self.reps[1]
        by_higher_rounder = ExternalAddress.empty()

        leader_votes = LeaderVotes(self.reps, ratio, 0, 0, old_leader)
        for i, (rep,
                signer) in enumerate(zip(self.reps[:26], self.signers[:26])):
            leader_vote = LeaderVote.new(signer, 0, 0, 0, old_leader,
                                         next_leader)
            leader_votes.add_vote(leader_vote)

        leader_votes.get_summary()
        print(f"leader_votes.is_completed(): {leader_votes.is_completed()}")
        print(f"leader_votes.get_result(): {leader_votes.get_result()}")
        self.assertEqual(leader_votes.is_completed(), False)
        self.assertEqual(leader_votes.get_result(), None)

        for i, (rep,
                signer) in enumerate(zip(self.reps[26:55],
                                         self.signers[26:55])):
            leader_vote = LeaderVote.new(signer, 0, 0, 0, old_leader,
                                         by_higher_rounder)
            leader_votes.add_vote(leader_vote)

        leader_votes.get_summary()
        print(f"leader_votes.is_completed(): {leader_votes.is_completed()}")
        print(f"leader_votes.get_result(): {leader_votes.get_result()}")
        self.assertEqual(leader_votes.is_completed(), True)
        self.assertEqual(leader_votes.get_result(), next_leader)
Ejemplo n.º 3
0
    def __send_fail_leader_vote(self, leader_vote: LeaderVote):
        version = self.blockchain.block_versioner.get_version(
            leader_vote.block_height)
        fail_vote = Vote.get_leader_vote_class(version).new(
            signer=ChannelProperty().peer_auth,
            block_height=leader_vote.block_height,
            round_=leader_vote.round,
            old_leader=leader_vote.old_leader,
            new_leader=ExternalAddress.empty(),
            timestamp=util.get_time_stamp())

        fail_vote_dumped = json.dumps(fail_vote.serialize())
        request = loopchain_pb2.ComplainLeaderRequest(
            complain_vote=fail_vote_dumped, channel=self.channel_name)

        reps_hash = self.blockchain.last_block.header.revealed_next_reps_hash or ChannelProperty(
        ).crep_root_hash
        rep_id = leader_vote.rep.hex_hx()
        target = self.blockchain.find_preps_targets_by_roothash(
            reps_hash)[rep_id]

        util.logger.debug(f"fail leader complain "
                          f"complained_leader_id({leader_vote.old_leader}), "
                          f"new_leader_id({ExternalAddress.empty()}),"
                          f"round({leader_vote.round}),"
                          f"target({target})")

        self.__channel_service.broadcast_scheduler.schedule_send_failed_leader_complain(
            "ComplainLeader", request, target=target)
Ejemplo n.º 4
0
    def is_unrecorded(self) -> bool:
        """Return is unrecorded block

        :return: bool
        """
        return (self.next_leader == ExternalAddress.empty() and
                self.reps_hash == self.next_reps_hash == Hash32.empty())
Ejemplo n.º 5
0
    def test_prep_changed_by_term_end_if_next_leader_is_empty(self, header_factory):
        header = header_factory(next_leader=ExternalAddress.empty(),
                                reps_hash=Hash32(os.urandom(Hash32.size)),
                                next_reps_hash=Hash32(os.urandom(Hash32.size)))

        assert header.prep_changed
        assert header.prep_changed_reason is NextRepsChangeReason.TermEnd
Ejemplo n.º 6
0
    def verify_leader_votes(self, block: 'Block', prev_block: 'Block', reps: Sequence[ExternalAddress]):
        body: BlockBody = block.body
        if body.leader_votes:
            any_vote = next(vote for vote in body.leader_votes if vote)
            votes_class = v0_5.LeaderVotes if any_vote.version else v0_1a.LeaderVotes
            leader_votes = votes_class(
                reps, conf.VOTING_RATIO,
                block.header.height, any_vote.round, any_vote.old_leader, body.leader_votes)
            if leader_votes.get_result() == ExternalAddress.empty():
                if leader_votes.block_height != block.header.height:
                    exception = RuntimeError(f"Block({block.header.height}, {block.header.hash.hex()}, "
                                             f"Height({block.header.height}), "
                                             f"Expected({leader_votes.round}).")
                    self._handle_exception(exception)
            elif leader_votes.get_result() != block.header.peer_id:
                exception = RuntimeError(f"Block({block.header.height}, {block.header.hash.hex()}, "
                                         f"Leader({block.header.peer_id.hex_xx()}), "
                                         f"Expected({leader_votes.get_result()}).")
                self._handle_exception(exception)

            try:
                leader_votes.verify()
            except Exception as e:
                # FIXME : leader_votes.verify does not verify all votes when raising an exception.
                self._handle_exception(e)
        else:
            prev_block_header: BlockHeader = prev_block.header
            if prev_block_header.next_leader != block.header.peer_id and not prev_block_header.prep_changed:
                exception = RuntimeError(f"Block({block.header.height}, {block.header.hash.hex()}, "
                                         f"Leader({block.header.peer_id.hex_xx()}), "
                                         f"Expected({prev_block_header.next_leader.hex_xx()}).\n "
                                         f"LeaderVotes({body.leader_votes}")
                self._handle_exception(exception)
Ejemplo n.º 7
0
    def add_complain(self, vote: LeaderVote):
        util.logger.debug(f"vote({vote})")

        if not self.preps_contain(vote.rep):
            util.logger.debug(f"ignore vote from unknown prep: {vote.rep.hex_hx()}")
            return

        if not self.epoch:
            util.logger.debug(f"Epoch is not initialized.")
            return

        if self.epoch.height == vote.block_height:
            if self.epoch.round == vote.round:
                self.epoch.add_complain(vote)
                elected_leader = self.epoch.complain_result()
                if elected_leader:
                    self.__channel_service.reset_leader(elected_leader, complained=True)
            elif self.epoch.round > vote.round:
                if vote.new_leader != ExternalAddress.empty():
                    self.__send_fail_leader_vote(vote)
                else:
                    return
            else:
                # TODO: do round sync
                return
        elif self.epoch.height < vote.block_height:
            self.__channel_service.state_machine.block_sync()
Ejemplo n.º 8
0
 def __get_next_leader_by_block(self, block: Block) -> str:
     if block.header.next_leader is None:
         if block.header.peer_id:
             return block.header.peer_id.hex_hx()
         else:
             return ExternalAddress.empty().hex_hx()
     else:
         return block.header.next_leader.hex_hx()
Ejemplo n.º 9
0
 def deserialize(cls, votes_data: List[Dict], voting_ratio: float):
     if votes_data:
         votes = [LeaderVote.deserialize(vote_data) for vote_data in votes_data]
         reps = [vote.rep for vote in votes]
         votes_instance = cls(reps, voting_ratio, votes[0].block_height, votes[0].round_, votes[0].old_leader)
         for vote in votes:
             index = reps.index(vote.rep)
             votes_instance.votes[index] = vote
         return votes_instance
     else:
         return cls([], voting_ratio, -1, -1, ExternalAddress.empty())
Ejemplo n.º 10
0
    def prep_changed_reason(self) -> Optional[NextRepsChangeReason]:
        """Return prep changed reason

        :return: NextRepsChangeReason : NoChange, TermEnd, Penalty
        """
        if not self.prep_changed and not self.is_unrecorded:
            return NextRepsChangeReason.NoChange

        if self.next_leader == ExternalAddress.empty():
            return NextRepsChangeReason.TermEnd

        return NextRepsChangeReason.Penalty
Ejemplo n.º 11
0
 def _build_next_leader(self):
     if self.next_reps_change_reason is NextRepsChangeReason.TermEnd:
         return ExternalAddress.empty()
     elif self.next_reps_change_reason is NextRepsChangeReason.Penalty:
         if not self.is_max_made_block_count and self.peer_id in self.next_reps:
             next_index = self.reps.index(self.peer_id)
         else:
             curr_index = self.reps.index(self.peer_id)
             next_index = curr_index + 1
         next_index = next_index if next_index < len(self.next_reps) else 0
         return self.next_reps[next_index]
     else:
         return self.next_leader
Ejemplo n.º 12
0
 def is_failed(self, value: ExternalAddress, count: int) -> bool:
     return value == ExternalAddress.empty() and count >= len(
         self.reps) - self.quorum + 1
Ejemplo n.º 13
0
 def get_out_of_round(self):
     counter = Counter(vote.result() for vote in self.votes if vote)
     out_of_round = counter[ExternalAddress.empty()]
     return out_of_round
Ejemplo n.º 14
0
 def get_majority(self):
     counter = Counter(vote.result() for vote in self.votes if (vote and vote.result() != ExternalAddress.empty()))
     majorities = counter.most_common()
     return majorities
Ejemplo n.º 15
0
 def empty(cls, rep: ExternalAddress, block_height: int, round_: int,
           old_leader: ExternalAddress):
     return cls(rep, 0, Signature.empty(), block_height, round_, old_leader,
                ExternalAddress.empty())