def add_unconfirmed_block(self, unconfirmed_block: Block, round_: int): """ :param unconfirmed_block: :param round_: :return: """ self.__validate_epoch_of_unconfirmed_block(unconfirmed_block, round_) self.__validate_duplication_of_unconfirmed_block(unconfirmed_block) last_unconfirmed_block: Block = self.blockchain.last_unconfirmed_block # TODO After the v0.4 update, remove this version parsing. if parse_version( unconfirmed_block.header.version) >= parse_version("0.4"): ratio = conf.VOTING_RATIO else: ratio = conf.LEADER_COMPLAIN_RATIO if unconfirmed_block.header.reps_hash: reps = self.blockchain.find_preps_addresses_by_roothash( unconfirmed_block.header.reps_hash) leader_votes = LeaderVotes(reps, ratio, unconfirmed_block.header.height, None, unconfirmed_block.body.leader_votes) need_to_confirm = leader_votes.get_result() is None elif unconfirmed_block.body.confirm_prev_block: need_to_confirm = True else: need_to_confirm = False try: if need_to_confirm: self.blockchain.confirm_prev_block(unconfirmed_block) if unconfirmed_block.header.is_unrecorded: self.blockchain.last_unconfirmed_block = None raise UnrecordedBlock("It's an unnecessary block to vote.") elif last_unconfirmed_block is None: if self.blockchain.last_block.header.hash != unconfirmed_block.header.prev_hash: raise BlockchainError( f"last block is not previous block. block={unconfirmed_block}" ) self.blockchain.last_unconfirmed_block = unconfirmed_block except BlockchainError as e: util.logger.warning( f"BlockchainError while confirm_block({e}), retry block_height_sync" ) self.__channel_service.state_machine.block_sync() raise InvalidUnconfirmedBlock(e)
def test_leader_invalid_vote(self): ratio = 0.67 old_leader = self.reps[0] new_leader = self.reps[1] leader_votes = LeaderVotes(self.reps, ratio, 0, 0, old_leader) invalid_leader_vote = LeaderVote.new(self.signers[0], 0, 1, 0, old_leader, new_leader) self.assertRaises(RuntimeError, leader_votes.add_vote, invalid_leader_vote) invalid_leader_vote = LeaderVote.new(self.signers[0], 0, 0, 1, old_leader, new_leader) self.assertRaises(RuntimeError, leader_votes.add_vote, invalid_leader_vote) invalid_leader_vote = LeaderVote.new(self.signers[0], 0, 0, 0, new_leader, new_leader) self.assertRaises(RuntimeError, leader_votes.add_vote, invalid_leader_vote) invalid_leader_vote = LeaderVote(rep=self.reps[0], timestamp=0, signature=Signature(os.urandom(65)), block_height=0, round_=0, new_leader=new_leader, old_leader=old_leader) self.assertRaises(RuntimeError, leader_votes.add_vote, invalid_leader_vote) leader_vote = LeaderVote.new(self.signers[0], 0, 0, 0, old_leader, new_leader) leader_votes.add_vote(leader_vote) duplicate_leader_vote = LeaderVote.new(self.signers[0], 0, 0, 0, old_leader, self.reps[2]) self.assertRaises(votes.VoteDuplicateError, leader_votes.add_vote, duplicate_leader_vote)
def new_votes(self): self.reps_hash = self.__blockchain.last_block.header.revealed_next_reps_hash or \ ObjectManager().channel_service.peer_manager.prepared_reps_hash self.reps = self.__blockchain.find_preps_addresses_by_roothash(self.reps_hash) # TODO After the v0.4 update, remove this version parsing. if parse_version(self.__blockchain.last_block.header.version) >= parse_version("0.4"): ratio = conf.VOTING_RATIO else: ratio = conf.LEADER_COMPLAIN_RATIO leader_votes = LeaderVotes(self.reps, ratio, self.height, self.round, ExternalAddress.fromhex_address(self.leader_id)) self.complain_votes[self.round] = leader_votes
def makeup_block(self, complain_votes: LeaderVotes, prev_votes, new_term: bool = False, skip_add_tx: bool = False): last_block = self.__blockchain.last_unconfirmed_block or self.__blockchain.last_block block_height = last_block.header.height + 1 block_version = self.__blockchain.block_versioner.get_version(block_height) block_builder = BlockBuilder.new(block_version, self.__blockchain.tx_versioner) block_builder.fixed_timestamp = int(time.time() * 1_000_000) block_builder.prev_votes = prev_votes if complain_votes and complain_votes.get_result(): block_builder.leader_votes = complain_votes.votes if new_term: block_builder.next_leader = None block_builder.reps = None elif skip_add_tx: utils.logger.debug(f"skip_add_tx for block height({self.height})") else: self.__add_tx_to_block(block_builder) return block_builder
def test_leader_votes(self): ratio = 0.67 old_leader = self.reps[0] new_leaders = [self.reps[1], self.reps[2], self.reps[3], self.reps[4]] leader_votes = LeaderVotes(self.reps, ratio, 0, 0, old_leader) for i, (rep, signer) in enumerate(zip(self.reps, self.signers)): mod = i % 10 if mod < 1: new_leader = new_leaders[1] elif mod < 2: new_leader = new_leaders[2] elif mod < 3: new_leader = new_leaders[3] else: new_leader = new_leaders[0] leader_vote = LeaderVote.new(signer, 0, 0, 0, old_leader, new_leader) leader_votes.add_vote(leader_vote) logging.info(leader_votes) self.assertEqual(leader_votes.is_completed(), True) self.assertEqual(leader_votes.get_result(), new_leaders[0])
def test_leader_votes_completed(self): ratio = 0.67 old_leader = self.reps[0] new_leaders = [self.reps[1], self.reps[2]] leader_votes = LeaderVotes(self.reps, ratio, 0, 0, old_leader) for i, (rep, signer) in enumerate(zip(self.reps[:25], self.signers[:25])): new_leader = new_leaders[0] leader_vote = LeaderVote.new(signer, 0, 0, 0, old_leader, new_leader) leader_votes.add_vote(leader_vote) self.assertEqual(leader_votes.is_completed(), False) self.assertEqual(leader_votes.get_result(), None) for i, (rep, signer) in enumerate(zip(self.reps[25:50], self.signers[25:50])): new_leader = new_leaders[1] leader_vote = LeaderVote.new(signer, 0, 0, 0, old_leader, new_leader) leader_votes.add_vote(leader_vote) self.assertEqual(leader_votes.is_completed(), False) self.assertEqual(leader_votes.get_result(), None) for i, (rep, signer) in enumerate(zip(self.reps[50:75], self.signers[50:75])): new_leader = new_leaders[0] leader_vote = LeaderVote.new(signer, 0, 0, 0, old_leader, new_leader) leader_votes.add_vote(leader_vote) self.assertEqual(leader_votes.is_completed(), False) self.assertEqual(leader_votes.get_result(), None) for i, (rep, signer) in enumerate(zip(self.reps[75:90], self.signers[75:90])): new_leader = new_leaders[1] leader_vote = LeaderVote.new(signer, 0, 0, 0, old_leader, new_leader) leader_votes.add_vote(leader_vote) self.assertEqual(leader_votes.is_completed(), True) self.assertEqual(leader_votes.get_result(), None)
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)