def set_epoch_leader(self, leader_id): util.logger.debug( f"Set Epoch leader height({self.height}) leader_id({leader_id})") self.leader_id = leader_id self.__complain_vote = Vote( Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager)
def set_epoch_leader(self, leader_id, complained=False): util.logger.debug(f"Set Epoch leader height({self.height}) leader_id({leader_id})") self.leader_id = leader_id if complained and leader_id == ChannelProperty().peer_id: self.complained_result = self.complain_result() else: self.complained_result = None self.__complain_vote = Vote(Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager)
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 test_vote_init_from_peer_list(self): # GIVEN peer_manager = PeerManager(conf.LOOPCHAIN_DEFAULT_CHANNEL) self.__add_peer_to_peer_manager(peer_manager, 2) # WHEN vote = Vote("block_hash", peer_manager) logging.debug("votes: " + str(vote.votes)) # THEN self.assertTrue(vote.check_vote_init(peer_manager))
def test_vote_init_from_audience(self): # GIVEN peer_info1 = self.__make_peer_info("peerid-1", "groupid-1") peer_info2 = self.__make_peer_info("peerid-2", "groupid-2") audience = {peer_info1.peer_id: peer_info1, peer_info2.peer_id: peer_info2} # WHEN vote = Vote("block_hash", audience) logging.debug("votes: " + str(vote.votes)) # THEN self.assertTrue(vote.check_vote_init(audience))
def __init__(self, height: int, leader_id=None): util.logger.notice( f"New Epoch Start height({height}) leader_id({leader_id})") self.height = height self.leader_id = leader_id # TODO using Epoch in BlockManager instead using candidate_blocks directly. # But now! only collect leader complain votes. self.__candidate_blocks = None self.__complain_vote = Vote( Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager)
def test_vote_init_from_different_source(self): # GIVEN peer_info1 = self.__make_peer_info("peerid-1", "groupid-1") peer_info2 = self.__make_peer_info("peerid-2", "groupid-2") audience = {peer_info1.peer_id: peer_info1, peer_info2.peer_id: peer_info2} peer_manager = PeerManager(conf.LOOPCHAIN_DEFAULT_CHANNEL) self.__add_peer_to_peer_manager(peer_manager, 2) # WHEN vote = Vote("block_hash", audience) logging.debug("votes: " + str(vote.votes)) # THEN self.assertTrue(vote.check_vote_init(peer_manager))
def __init__(self, block_manager, leader_id=None): if block_manager.get_blockchain().last_block: self.height = block_manager.get_blockchain().last_block.header.height + 1 else: self.height = 1 self.leader_id = leader_id self.__block_manager = block_manager self.__blockchain = self.__block_manager.get_blockchain() util.logger.debug(f"New Epoch Start height({self.height }) leader_id({leader_id})") # TODO using Epoch in BlockManager instead using candidate_blocks directly. # But now! only collect leader complain votes. self.__candidate_blocks = None self.__complain_vote = Vote(Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager) self.complained_result = None
def __write_block_data(self, block: Block, vote: Vote = None): # a condition for the exception case of genesis block. next_total_tx = self.__total_tx if block.header.height > 0: next_total_tx += len(block.body.transactions) bit_length = next_total_tx.bit_length() byte_length = (bit_length + 7) // 8 next_total_tx_bytes = next_total_tx.to_bytes(byte_length, byteorder='big') block_version = self.__block_versioner.get_version(block.header.height) block_serializer = BlockSerializer.new(block_version, self.tx_versioner) block_serialized = json.dumps(block_serializer.serialize(block)) block_hash_encoded = block.header.hash.hex().encode(encoding='UTF-8') batch = leveldb.WriteBatch() batch.Put(block_hash_encoded, block_serialized.encode("utf-8")) batch.Put(BlockChain.LAST_BLOCK_KEY, block_hash_encoded) batch.Put(BlockChain.TRANSACTION_COUNT_KEY, next_total_tx_bytes) batch.Put( BlockChain.BLOCK_HEIGHT_KEY + block.header.height.to_bytes( conf.BLOCK_HEIGHT_BYTES_LEN, byteorder='big'), block_hash_encoded) if vote: batch.Put(BlockChain.BLOCK_INFO_KEY + block_hash_encoded, Vote.save_to(vote)) self.__confirmed_block_db.Write(batch) return next_total_tx
class Epoch: COMPLAIN_VOTE_HASH = "complain_vote_hash_for_reuse_Vote_class" def __init__(self, height: int, leader_id=None): util.logger.notice( f"New Epoch Start height({height}) leader_id({leader_id})") self.height = height self.leader_id = leader_id # TODO using Epoch in BlockManager instead using candidate_blocks directly. # But now! only collect leader complain votes. self.__candidate_blocks = None self.__complain_vote = Vote( Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager) @staticmethod def new_epoch(height: int, leader_id=None): leader_id = leader_id or ObjectManager( ).channel_service.block_manager.epoch.leader_id return Epoch(height, leader_id) def set_epoch_leader(self, leader_id): util.logger.notice( f"Set Epoch leader height({self.height}) leader_id({leader_id})") self.leader_id = leader_id def add_complain(self, complained_leader_id, new_leader_id, block_height, peer_id, group_id): util.logger.notice( f"add_complain complain_leader_id({complained_leader_id}), " f"new_leader_id({new_leader_id}), " f"block_height({block_height}), " f"peer_id({peer_id})") self.__complain_vote.add_vote(group_id, peer_id, new_leader_id) def complain_result(self) -> str or None: """return new leader id when complete complain leader. :return: new leader id or None """ vote_result = self.__complain_vote.get_result( Epoch.COMPLAIN_VOTE_HASH, conf.LEADER_COMPLAIN_RATIO) util.logger.notice(f"complain_result vote_result({vote_result})") return vote_result
def new_round(self, new_leader_id, round_=None): self.set_epoch_leader(new_leader_id, True) if round_ is None: self.round += 1 else: self.round = round_ logging.debug(f"new round {round_}, {self.round}") self.__complain_vote[self.round] = Vote(Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager)
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 __init__(self, block_hash: Hash32): """Recommend use factory methods(from_*) instead direct this. """ if ObjectManager().channel_service: audience = ObjectManager().channel_service.peer_manager else: audience = None self.start_time = util.get_time_stamp() # timestamp self.hash = block_hash self.vote = Vote(block_hash.hex(), audience) self.__block = None
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))
class Epoch: COMPLAIN_VOTE_HASH = "complain_vote_hash_for_reuse_Vote_class" def __init__(self, block_manager, leader_id=None): if block_manager.get_blockchain().last_block: self.height = block_manager.get_blockchain().last_block.header.height + 1 else: self.height = 1 self.leader_id = leader_id self.__block_manager = block_manager self.__blockchain = self.__block_manager.get_blockchain() util.logger.debug(f"New Epoch Start height({self.height }) leader_id({leader_id})") # TODO using Epoch in BlockManager instead using candidate_blocks directly. # But now! only collect leader complain votes. self.__candidate_blocks = None self.__complain_vote = Vote(Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager) self.complained_result = None @staticmethod def new_epoch(leader_id=None): block_manager = ObjectManager().channel_service.block_manager leader_id = leader_id or ObjectManager().channel_service.block_manager.epoch.leader_id return Epoch(block_manager, leader_id) def set_epoch_leader(self, leader_id, complained=False): util.logger.debug(f"Set Epoch leader height({self.height}) leader_id({leader_id})") self.leader_id = leader_id if complained and leader_id == ChannelProperty().peer_id: self.complained_result = self.complain_result() else: self.complained_result = None self.__complain_vote = Vote(Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager) def add_complain(self, complained_leader_id, new_leader_id, block_height, peer_id, group_id): util.logger.debug(f"add_complain complain_leader_id({complained_leader_id}), " f"new_leader_id({new_leader_id}), " f"block_height({block_height}), " f"peer_id({peer_id})") self.__complain_vote.add_vote(peer_id, new_leader_id) def complain_result(self) -> str: """return new leader id when complete complain leader. :return: new leader id or None """ vote_result = self.__complain_vote.get_result(Epoch.COMPLAIN_VOTE_HASH, conf.LEADER_COMPLAIN_RATIO) util.logger.debug(f"complain_result vote_result({vote_result})") return vote_result def pop_complained_candidate_leader(self): voters = self.__complain_vote.get_voters() if ChannelProperty().peer_id not in voters: # Processing to complain leader return None # Complained by myself but not completed. # I want to pop candidate leader with this method but this method can't pop, just get but will be pop # self.__complain_vote = Vote(Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager) peer_order_list = ObjectManager().channel_service.peer_manager.peer_order_list[conf.ALL_GROUP_ID] peer_order_len = len(peer_order_list) start_order = 1 # ObjectManager().channel_service.peer_manager.get_peer(self.leader_id).order for i in range(peer_order_len): index = (i + start_order) % (peer_order_len + 1) try: peer_id = peer_order_list[index] except KeyError: peer_id = None if peer_id in voters: util.logger.info(f"set epoch new leader id({peer_id}), voters length={len(voters)}") return peer_id return None def _check_unconfirmed_block(self): blockchain = self.__block_manager.get_blockchain() # util.logger.debug(f"-------------------_check_unconfirmed_block, " # f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})") if blockchain.last_unconfirmed_block: vote = self.__block_manager.candidate_blocks.get_vote(blockchain.last_unconfirmed_block.header.hash) # util.logger.debug(f"-------------------_check_unconfirmed_block, " # f"last_unconfirmed_block({self._blockchain.last_unconfirmed_block.header.hash}), " # f"vote({vote.votes})") vote_result = vote.get_result(blockchain.last_unconfirmed_block.header.hash.hex(), conf.VOTING_RATIO) if not vote_result: util.logger.debug(f"last_unconfirmed_block({blockchain.last_unconfirmed_block.header.hash}), " f"vote result({vote_result})") def __add_tx_to_block(self, block_builder): tx_queue = self.__block_manager.get_tx_queue() block_tx_size = 0 tx_versioner = self.__blockchain.tx_versioner while tx_queue: if block_tx_size >= conf.MAX_TX_SIZE_IN_BLOCK: logging.debug(f"consensus_base total size({block_builder.size()}) " f"count({len(block_builder.transactions)}) " f"_txQueue size ({len(tx_queue)})") break tx: 'Transaction' = tx_queue.get_item_in_status( TransactionStatusInQueue.normal, TransactionStatusInQueue.added_to_block ) if tx is None: break tv = TransactionVerifier.new(tx.version, tx_versioner) try: tv.verify(tx, self.__blockchain) except Exception as e: logging.warning(f"tx hash invalid.\n" f"tx: {tx}\n" f"exception: {e}") traceback.print_exc() else: block_builder.transactions[tx.hash] = tx block_tx_size += tx.size(tx_versioner) def makeup_block(self, complained_result: str): # self._check_unconfirmed_block( 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) if not complained_result: self.__add_tx_to_block(block_builder) return block_builder
class Epoch: COMPLAIN_VOTE_HASH = "complain_vote_hash_for_reuse_Vote_class" def __init__(self, block_manager, leader_id=None): if block_manager.get_blockchain().last_block: self.height = block_manager.get_blockchain( ).last_block.header.height + 1 else: self.height = 1 self.leader_id = leader_id self.prev_leader_id = None self.__block_manager = block_manager self.__blockchain = self.__block_manager.get_blockchain() util.logger.debug( f"New Epoch Start height({self.height }) leader_id({leader_id})") # TODO using Epoch in BlockManager instead using candidate_blocks directly. # But now! only collect leader complain votes. self.__candidate_blocks = None self.__complain_vote = Vote( Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager) @staticmethod def new_epoch(leader_id=None): block_manager = ObjectManager().channel_service.block_manager leader_id = leader_id or ObjectManager( ).channel_service.block_manager.epoch.leader_id return Epoch(block_manager, leader_id) def set_epoch_leader(self, leader_id): util.logger.debug( f"Set Epoch leader height({self.height}) leader_id({leader_id})") self.leader_id = leader_id self.__complain_vote = Vote( Epoch.COMPLAIN_VOTE_HASH, ObjectManager().channel_service.peer_manager) def add_complain(self, complained_leader_id, new_leader_id, block_height, peer_id, group_id): util.logger.debug( f"add_complain complain_leader_id({complained_leader_id}), " f"new_leader_id({new_leader_id}), " f"block_height({block_height}), " f"peer_id({peer_id})") self.__complain_vote.add_vote(group_id, peer_id, new_leader_id) def complain_result(self) -> str: """return new leader id when complete complain leader. :return: new leader id or None """ vote_result = self.__complain_vote.get_result( Epoch.COMPLAIN_VOTE_HASH, conf.LEADER_COMPLAIN_RATIO) util.logger.debug(f"complain_result vote_result({vote_result})") return vote_result def _check_unconfirmed_block(self): blockchain = self.__block_manager.get_blockchain() # util.logger.debug(f"-------------------_check_unconfirmed_block, " # f"candidate_blocks({len(self._block_manager.candidate_blocks.blocks)})") if blockchain.last_unconfirmed_block: vote = self.__block_manager.candidate_blocks.get_vote( blockchain.last_unconfirmed_block.header.hash) # util.logger.debug(f"-------------------_check_unconfirmed_block, " # f"last_unconfirmed_block({self._blockchain.last_unconfirmed_block.header.hash}), " # f"vote({vote.votes})") vote_result = vote.get_result( blockchain.last_unconfirmed_block.header.hash.hex(), conf.VOTING_RATIO) if not vote_result: util.logger.debug( f"last_unconfirmed_block({blockchain.last_unconfirmed_block.header.hash}), " f"vote result({vote_result})") def __add_tx_to_block(self, block_builder): tx_queue = self.__block_manager.get_tx_queue() block_tx_size = 0 tx_versioner = self.__blockchain.tx_versioner while tx_queue: if block_tx_size >= conf.MAX_TX_SIZE_IN_BLOCK: logging.debug( f"consensus_base total size({block_builder.size()}) " f"count({len(block_builder.transactions)}) " f"_txQueue size ({len(tx_queue)})") break tx: 'Transaction' = tx_queue.get_item_in_status( TransactionStatusInQueue.normal, TransactionStatusInQueue.added_to_block) if tx is None: break tv = TransactionVerifier.new(tx.version, tx_versioner) try: tv.verify(tx, self.__blockchain) except Exception as e: logging.warning(f"tx hash invalid.\n" f"tx: {tx}\n" f"exception: {e}") traceback.print_exc() else: block_builder.transactions[tx.hash] = tx block_tx_size += tx.size(tx_versioner) def makeup_block(self, complained: str): # self._check_unconfirmed_block( 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) if not complained: self.__add_tx_to_block(block_builder) return block_builder