Esempio n. 1
0
 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)
Esempio n. 2
0
 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)
Esempio n. 3
0
    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))
Esempio n. 4
0
    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))
Esempio n. 5
0
    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))
Esempio n. 6
0
    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)
Esempio n. 7
0
    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))
Esempio n. 8
0
    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
Esempio n. 9
0
    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
Esempio n. 10
0
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
Esempio n. 11
0
    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)
Esempio n. 12
0
    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)
Esempio n. 13
0
    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
Esempio n. 14
0
    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))
Esempio n. 15
0
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
Esempio n. 16
0
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