Exemple #1
0
    def __init__(self, blockchain_db=None):
        # block db has [ block_hash - block | block_height - block_hash | BlockChain.LAST_BLOCK_KEY - block_hash ]
        self.__confirmed_block_db = blockchain_db

        if self.__confirmed_block_db is None:
            try:
                self.__confirmed_block_db = leveldb.LevelDB(conf.DEFAULT_LEVEL_DB_PATH)
            except leveldb.LevelDBError:
                raise leveldb.LevelDBError("Fail To Create Level DB(path): " + conf.DEFAULT_LEVEL_DB_PATH)

        # level DB에서 블럭을 읽어 들이며, 만약 levelDB에 블럭이 없을 경우 제네시스 블럭을 만든다
        try:
            last_block_key = self.__confirmed_block_db.Get(BlockChain.LAST_BLOCK_KEY, True)
        except KeyError:
            last_block_key = None
        logging.debug("LAST BLOCK KEY : %s", last_block_key)

        if last_block_key:
            # DB에서 마지막 블럭을 가져와서 last_block 에 바인딩
            self.last_block = Block()
            block_dump = self.__confirmed_block_db.Get(last_block_key)
            self.last_block.deserialize_block(block_dump)
            logging.debug("restore from last block hash(" + str(self.last_block.block_hash) + ")")
            logging.debug("restore from last block height(" + str(self.last_block.height) + ")")
        else:
            # 제네시스 블럭 생성
            self.__add_genesisblock()

        # 블럭의 높이는 마지막 블럭의 높이와 같음
        self.block_height = self.last_block.height

        # made block count as a leader
        self.__made_block_count = 0
Exemple #2
0
    def confirm_block(self, confirmed_block_hash):
        """인증완료후 Block을 Confirm해 줍니다.
        :param confirmed_block_hash: 인증된 블럭의 hash
        :return: Block 에 포함된 tx 의 갯수를 리턴한다.
        """
        logging.debug("BlockChain::confirm_block")

        try:
            unconfirmed_block_byte = self.__confirmed_block_db.Get(
                BlockChain.UNCONFIRM_BLOCK_KEY)
        except KeyError:
            except_msg = "there is no unconfirmed block in this peer by: " + confirmed_block_hash
            logging.warning(except_msg)
            raise BlockchainError(except_msg)

        unconfirmed_block = Block()
        unconfirmed_block.deserialize_block(unconfirmed_block_byte)
        if unconfirmed_block.block_hash != confirmed_block_hash:
            logging.warning(
                "It's not possible to add block while check block hash is fail-"
            )
            raise BlockchainError('확인하는 블럭 해쉬 값이 다릅니다.')

        logging.debug("unconfirmed_block.block_hash: " +
                      unconfirmed_block.block_hash)
        logging.debug("confirmed_block_hash: " + confirmed_block_hash)
        logging.debug("unconfirmed_block.prev_block_hash: " +
                      unconfirmed_block.prev_block_hash)

        unconfirmed_block.block_type = BlockType.confirmed
        # Block Validate and save Block
        self.add_block(unconfirmed_block)
        self.__confirmed_block_db.Delete(BlockChain.UNCONFIRM_BLOCK_KEY)

        return unconfirmed_block.confirmed_transaction_list.__len__()
Exemple #3
0
    def init_block_chain(self, is_leader=False):
        # level DB에서 블럭을 읽어 들이며, 만약 levelDB에 블럭이 없을 경우 제네시스 블럭을 만든다
        try:
            last_block_key = self.__confirmed_block_db.Get(
                BlockChain.LAST_BLOCK_KEY, True)
        except KeyError:
            last_block_key = None
        logging.debug("LAST BLOCK KEY : %s", last_block_key)

        if last_block_key:
            # DB에서 마지막 블럭을 가져와서 last_block 에 바인딩
            self.__last_block = Block(channel_name=self.__channel_name)
            block_dump = self.__confirmed_block_db.Get(last_block_key)
            self.__last_block.deserialize_block(block_dump)
            logging.debug("restore from last block hash(" +
                          str(self.__last_block.block_hash) + ")")
            logging.debug("restore from last block height(" +
                          str(self.__last_block.height) + ")")

        # 블럭의 높이는 마지막 블럭의 높이와 같음
        if self.__last_block is None:
            self.__block_height = -1
        else:
            self.__block_height = self.__last_block.height
        logging.debug(f"ENGINE-303 init_block_chain: {self.__block_height}")
Exemple #4
0
    def rebuild_blocks(self):
        # Genesis block 까지 순회하며 Block 정보를 복원한다.
        logging.info("re-build blocks from DB....")

        block = Block()
        prev_block_hash = self.last_block.block_hash
        total_tx = 0

        while prev_block_hash != "":
            block_dump = self.__confirmed_block_db.Get(
                prev_block_hash.encode(encoding='UTF-8'))
            block.deserialize_block(block_dump)

            # Rebuild Block 코드 구간. 현재는 total_tx 만 구하고 있음
            # TODO 향후에도 rebuild_blocks가 total_tx만 구하는 경우 이 로직은 제거 하고 total_tx 는 다른 방식으로 구하도록 한다.
            # logging.debug("re-build block: " + str(block.block_hash))
            total_tx += block.confirmed_transaction_list.__len__()

            prev_block_hash = block.prev_block_hash

        logging.info("rebuilt blocks, total_tx: " + str(total_tx))
        logging.info("block hash(" + self.last_block.block_hash +
                     ") and height(" + str(self.last_block.height) + ")")

        return total_tx
Exemple #5
0
    def __find_block_by_key(self, key):
        block = Block()

        try:
            block_bytes = self.__confirmed_block_db.Get(key)
            block.deserialize_block(block_bytes)
        except KeyError:
            block = None

        return block
Exemple #6
0
    def __find_block_by_key(self, key):

        try:
            block_bytes = self.__confirmed_block_db.Get(key)
            block = Block(channel_name=self.__channel_name)
            block.deserialize_block(block_bytes)
        except KeyError as e:
            block = None
            logging.error(f"__find_block_by_key::KeyError block_hash({key}) error({e})")

        return block
Exemple #7
0
    def __add_genesisblock(self):
        """제네시스 블럭을 추가 합니다.

        :return:
        """
        logging.info("Make Genesis Block....")
        block = Block(channel_name=self.__channel_name)
        block.block_status = BlockStatus.confirmed
        block.generate_block()
        # 제네시스 블럭을 추가 합니다.
        self.add_block(block)
Exemple #8
0
 def __add_genesisblock(self):
     """
     제네시스 블럭을 추가 합니다.
     :return:
     """
     logging.info("Make Genesis Block....")
     block = Block()
     block.block_type = BlockType.confirmed
     block.generate_block()
     # 제네시스 블럭을 추가 합니다.
     self.add_block(block)
    def generate_test_block(self):
        """
        임시 블럭 생성하는 메소드
        :return: 임시 블럭
        """

        block = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)
        for x in range(0, 10):
            tx = test_util.create_basic_tx(self.__peer_id, self.peer_auth)
            self.assertNotEqual(tx.tx_hash, "", "트랜잭션 생성 실패")
            self.assertTrue(block.put_transaction(tx), "Block에 트랜잭션 추가 실패")

        return block
 def test_serialize_and_deserialize(self):
     """
     블럭 serialize and deserialize 테스트
     """
     block = self.__generate_block()
     test_dmp = block.serialize_block()
     block2 = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)
     block2.deserialize_block(test_dmp)
     logging.debug("serialize block hash : %s , deserialize block hash %s",
                   block.merkle_tree_root_hash,
                   block2.merkle_tree_root_hash)
     self.assertEqual(block.merkle_tree_root_hash,
                      block2.merkle_tree_root_hash, "블럭이 같지 않습니다 ")
Exemple #11
0
 def test_serialize_and_deserialize(self):
     """
     블럭 serialize and deserialize 테스트
     """
     block = self.generate_block()
     test_dmp = block.serialize_block()
     block2 = Block()
     block2.deserialize_block(test_dmp)
     logging.debug("serialize block hash : %s , deserialize block hash %s",
                   block.merkle_tree_root_hash,
                   block2.merkle_tree_root_hash)
     self.assertEqual(block.merkle_tree_root_hash,
                      block2.merkle_tree_root_hash, "블럭이 같지 않습니다 ")
Exemple #12
0
    def generate_test_block(self):
        """
        임시 블럭 생성하는 메소드
        :return: 임시 블럭
        """

        block = Block()
        for x in range(0, 10):
            tx = Transaction()
            hashed_value = tx.put_data("{args:[]}")
            self.assertNotEqual(hashed_value, "", "트랜잭션 생성 실패")
            self.assertTrue(block.put_transaction(tx), "Block에 트랜잭션 추가 실패")

        return block
Exemple #13
0
    def _rebuild_transaction_count_from_blocks(self):
        total_tx = 0
        block_hash = self.__last_block.block_hash
        while block_hash != "":
            block_dump = self.__confirmed_block_db.Get(block_hash.encode(encoding='UTF-8'))
            block = Block(channel_name=self.__channel_name)
            block.deserialize_block(block_dump)

            # Count only normal block`s tx count, not genesis block`s
            if block.height > 0:
                total_tx += block.confirmed_tx_len

            block_hash = block.prev_block_hash
        return total_tx
Exemple #14
0
    def __init__(self, blockchain_db=None, channel_name=None):
        if channel_name is None:
            channel_name = conf.LOOPCHAIN_DEFAULT_CHANNEL
        self.__block_height = 0
        self.__last_block = None
        self.__channel_name = channel_name

        self.__peer_id = None
        if ObjectManager().peer_service is not None:
            self.__peer_id = ObjectManager().peer_service.peer_id

        # block db has [ block_hash - block | block_height - block_hash | BlockChain.LAST_BLOCK_KEY - block_hash ]
        self.__confirmed_block_db = blockchain_db
        # logging.debug(f"BlockChain::init confirmed_block_db({self.__confirmed_block_db})")

        if self.__confirmed_block_db is None:
            try:
                self.__confirmed_block_db = leveldb.LevelDB(
                    conf.DEFAULT_LEVEL_DB_PATH)
            except leveldb.LevelDBError:
                raise leveldb.LevelDBError("Fail To Create Level DB(path): " +
                                           conf.DEFAULT_LEVEL_DB_PATH)

        # level DB에서 블럭을 읽어 들이며, 만약 levelDB에 블럭이 없을 경우 제네시스 블럭을 만든다
        try:
            last_block_key = self.__confirmed_block_db.Get(
                BlockChain.LAST_BLOCK_KEY, True)
        except KeyError:
            last_block_key = None
        logging.debug("LAST BLOCK KEY : %s", last_block_key)

        if last_block_key:
            # DB에서 마지막 블럭을 가져와서 last_block 에 바인딩
            self.__last_block = Block(channel_name=self.__channel_name)
            block_dump = self.__confirmed_block_db.Get(last_block_key)
            self.__last_block.deserialize_block(block_dump)
            logging.debug("restore from last block hash(" +
                          str(self.__last_block.block_hash) + ")")
            logging.debug("restore from last block height(" +
                          str(self.__last_block.height) + ")")
        else:
            # 제네시스 블럭 생성
            self.__add_genesisblock()

        # 블럭의 높이는 마지막 블럭의 높이와 같음
        self.__block_height = self.__last_block.height

        # made block count as a leader
        self.__made_block_count = 0
Exemple #15
0
 def test_put_transaction(self):
     """
     Block 에 여러 개 transaction 들을 넣는 것을 test.
     """
     block = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)
     tx_list = []
     tx_size = 10
     for x in range(0, tx_size):
         tx = test_util.create_basic_tx(self.__peer_id, self.peer_auth)
         tx2 = test_util.create_basic_tx(self.__peer_id, self.peer_auth)
         tx_list.append(tx2)
         self.assertNotEqual(tx.tx_hash, "", "트랜잭션 생성 실패")
         self.assertTrue(block.put_transaction(tx), "Block에 트랜잭션 추가 실패")
     self.assertTrue(block.put_transaction(tx_list), "Block에 여러 트랜잭션 추가 실패")
     self.assertEqual(block.confirmed_tx_len, tx_size * 2, "트랜잭션 사이즈 확인 실패")
Exemple #16
0
    def put_precommit_block(self, precommit_block: Block):
        # write precommit block to DB
        logging.debug(
            f"blockchain:put_precommit_block ({self.__channel_name}), hash ({precommit_block.block_hash})"
        )
        if self.__last_block.height < precommit_block.height:
            self.__precommit_tx(precommit_block)
            util.logger.spam(
                f"blockchain:put_precommit_block:confirmed_transaction_list")
            # for tx in precommit_block.confirmed_transaction_list:
            #     tx_hash = tx.tx_hash
            #     util.logger.spam(f"blockchain.py:put_precommit_block:{tx_hash}")

            results = self.__confirmed_block_db.Put(
                BlockChain.PRECOMMIT_BLOCK_KEY,
                precommit_block.serialize_block())

            util.logger.spam(f"result of to write to db ({results})")
            logging.info(
                f"ADD BLOCK PRECOMMIT HEIGHT : {precommit_block.height} , "
                f"HASH : {precommit_block.block_hash}, CHANNEL : {self.__channel_name}"
            )
        else:
            results = None
            logging.debug(
                f"blockchain:put_precommit_block::this precommit block is not validate. "
                f"the height of precommit block must be bigger than the last block."
                f"(last block:{self.__last_block.height}/precommit block:{precommit_block.height})"
            )

        return results
    def score_invoke(self, _block: Block) -> dict or None:
        if conf.USE_EXTERNAL_SCORE:
            method = "icx_sendTransaction"
            transactions = []
            for tx in _block.confirmed_transaction_list:
                data = tx.icx_origin_data
                transaction = {"method": method, "params": data}
                transactions.append(transaction)

            request = {
                'block': {
                    'blockHeight': _block.height,
                    'blockHash': _block.block_hash,
                    'prevBlockHash': _block.prev_block_hash,
                    'timestamp': _block.time_stamp
                },
                'transactions': transactions
            }
            request = convert_params(request, ParamType.invoke)
            stub = StubCollection().icon_score_stubs[ChannelProperty().name]
            response = stub.sync_task().invoke(request)
            response_to_json_query(response)
            _block.commit_state[
                ChannelProperty().name] = response['stateRootHash']
            return response["txResults"]
        else:
            stub = StubCollection().score_stubs[ChannelProperty().name]
            response = stub.sync_task().score_invoke(_block)

            if response.code == message_code.Response.success:
                commit_state = pickle.loads(response.object)
                _block.commit_state = commit_state
                return json.loads(response.meta)

        return None
    def __generate_block_data(self) -> Block:
        """ block data generate
        :return: unsigned block
        """
        genesis = Block(channel_name="test_channel")
        genesis.generate_block()
        # Block 생성
        block = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)
        # Transaction(s) 추가
        for x in range(0, 10):
            block.put_transaction(
                test_util.create_basic_tx(self.__peer_id, self.__peer_auth))

        # Hash 생성 이 작업까지 끝내고 나서 Block을 peer에 보낸다
        block.generate_block(genesis)
        return block
Exemple #19
0
    def __add_genesisblock(self, tx_info: dict = None):
        """제네시스 블럭을 추가 합니다.

        :param tx_info: Transaction data for making genesis block from an initial file
        :return:
        """
        logging.info("Make Genesis Block....")
        block = Block(channel_name=self.__channel_name)
        block.block_status = BlockStatus.confirmed
        if tx_info:
            keys = tx_info.keys()
            if "accounts" in keys and "message" in keys:
                genesis_validator = get_genesis_tx_validator(
                    self.__channel_name)
                is_valid, tx = genesis_validator.init_genesis_tx(tx_info)
                if is_valid:
                    block.put_genesis_transaction(tx)
                else:
                    raise TransactionInvalidError(
                        message=f"The Genesis block data is invalid."
                        f"That's why Genesis block couldn't be generated.")

        block.generate_block()
        # 제네시스 블럭을 추가 합니다.
        self.add_block(block)
Exemple #20
0
    def generate_block(self):
        """
        블럭 생성기
        :return: 임의생성블럭
        """
        genesis = Block()
        genesis.generate_block()
        # Block 생성
        block = Block()
        # Transaction(s) 추가
        for x in range(0, 10):
            tx = Transaction()
            tx.put_data("{args:[]}")
            block.put_transaction(tx)

        # Hash 생성 이 작업까지 끝내고 나서 Block을 peer에 보낸다
        block.generate_block(genesis)
        return block
    def test_validate_block(self):
        """ GIVEN correct block and invalid signature block
        WHEN validate two block
        THEN correct block validate return True, invalid block raise exception
        """
        # GIVEN
        # create correct block
        block = self.__generate_block()
        # WHEN THEN
        self.assertTrue(Block.validate(block), "Fail to validate block!")

        # GIVEN
        # create invalid block
        invalid_signature_block = self.__generate_invalid_block()

        # WHEN THEN
        with self.assertRaises(BlockInValidError):
            Block.validate(invalid_signature_block)
Exemple #22
0
 def test_put_transaction(self):
     """
     Block 에 여러 개 transaction 들을 넣는 것을 test.
     """
     block = Block()
     tx_list = []
     tx_size = 10
     for x in range(0, tx_size):
         tx = Transaction()
         tx2 = Transaction()
         hashed_value = tx.put_data("{args:[]}")
         tx2.put_data("{args:[]}")
         tx_list.append(tx2)
         self.assertNotEqual(hashed_value, "", "트랜잭션 생성 실패")
         self.assertTrue(block.put_transaction(tx), "Block에 트랜잭션 추가 실패")
     self.assertTrue(block.put_transaction(tx_list), "Block에 여러 트랜잭션 추가 실패")
     self.assertEqual(len(block.confirmed_transaction_list), tx_size * 2,
                      "트랜잭션 사이즈 확인 실패")
Exemple #23
0
    def test_block_hash_must_be_the_same_regardless_of_the_commit_state(self):
        # ENGINE-302
        # GIVEN
        block1 = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)
        block2 = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)

        # WHEN
        block1.commit_state = {"TEST": "TEST_VALUE1234"}
        block1.generate_block()
        block2.generate_block()
        util.logger.spam(f"block1 hash({block1.block_hash})")
        util.logger.spam(f"block1 hash({block2.block_hash})")

        # THEN
        self.assertEqual(block1.block_hash, block2.block_hash)
Exemple #24
0
    def test_block_prevent_tx_duplication(self):
        origin_send_tx_type = conf.CHANNEL_OPTION[
            conf.LOOPCHAIN_DEFAULT_CHANNEL]["send_tx_type"]
        conf.CHANNEL_OPTION[conf.LOOPCHAIN_DEFAULT_CHANNEL][
            "send_tx_type"] = conf.SendTxType.icx
        tx_validator.refresh_tx_validators()

        try:
            block = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)

            client = IcxWallet()
            client.to_address = 'hxebf3a409845cd09dcb5af31ed5be5e34e2af9433'
            client.value = 1

            hash_generator = get_tx_hash_generator(
                conf.LOOPCHAIN_DEFAULT_CHANNEL)
            validator = IconValidateStrategy(hash_generator)
            icx_origin = client.create_icx_origin()
            for i in range(10):
                tx = validator.restore(json.dumps(icx_origin), 'icx/score')
                block.put_transaction(tx)

            block.generate_block()
            self.assertEqual(block.confirmed_tx_len, 1)
        finally:
            conf.CHANNEL_OPTION[conf.LOOPCHAIN_DEFAULT_CHANNEL][
                "send_tx_type"] = origin_send_tx_type
            tx_validator.refresh_tx_validators()
Exemple #25
0
 def test_block_rebuild(self):
     """ GIVEN 1Block with 3tx, and conf remove failed tx when in block
     WHEN Block call verify_through_score_invoke
     THEN all order 3tx must removed in block
     """
     block = Block(conf.LOOPCHAIN_DEFAULT_CHANNEL)
     fail_tx_hash = None
     for i in range(3):
         tx = Transaction()
         tx.put_meta(Transaction.CHANNEL_KEY,
                     conf.LOOPCHAIN_DEFAULT_CHANNEL)
         tx.put_data("aaaaa")
         tx.sign_hash(self.peer_auth)
         block.put_transaction(tx)
         if i == 2:
             fail_tx_hash = tx.tx_hash
     verify, need_rebuild, invoke_results = block.verify_through_score_invoke(
         True)
     self.assertTrue(need_rebuild)
     logging.debug(f"fail tx hash : {fail_tx_hash}")
     self.assertEqual(block.confirmed_tx_len, 2)
     for i, tx in enumerate(block.confirmed_transaction_list):
         self.assertNotEqual(i, 2, "index 2 must be deleted")
         self.assertNotEqual(tx.tx_hash, fail_tx_hash)
 def test_transaction_merkle_tree_validate_block(self):
     """
     머클트리 검증
     """
     # 블럭을 검증 - 모든 머클트리의 내용 검증
     block = self.__generate_block()
     mk_result = True
     for tx in block.confirmed_transaction_list:
         # FIND tx index
         idx = block.confirmed_transaction_list.index(tx)
         sm_result = Block.merkle_path(block, idx)
         mk_result &= sm_result
         # logging.debug("Transaction %i th is %s (%s)", idx, sm_result, tx.get_tx_hash())
     # logging.debug("block mekletree : %s ", block.merkle_tree)
     self.assertTrue(mk_result, "머클트리검증 실패")
    async def announce_confirmed_block(self,
                                       serialized_block,
                                       commit_state="{}"):
        try:
            confirmed_block = Block(channel_name=ChannelProperty().name)
            confirmed_block.deserialize_block(serialized_block)
            util.logger.spam(
                f"channel_inner_service:announce_confirmed_block\n "
                f"hash({confirmed_block.block_hash}), block_type({confirmed_block.block_type}), "
                f"block height({confirmed_block.height}), "
                f"commit_state({commit_state})")
            try:
                confirmed_block.commit_state = ast.literal_eval(commit_state)
            except Exception as e:
                logging.warning(
                    f"channel_inner_service:announce_confirmed_block FAIL get commit_state_dict, "
                    f"error by : {e}")

            if self._channel_service.block_manager.get_blockchain(
            ).block_height < confirmed_block.height:
                self._channel_service.block_manager.add_confirmed_block(
                    confirmed_block)
            else:
                logging.debug(
                    f"channel_inner_service:announce_confirmed_block "
                    f"already synced block height({confirmed_block.height})")
            response_code = message_code.Response.success
            # stop subscribe timer
            if TimerService.TIMER_KEY_SUBSCRIBE in self._channel_service.timer_service.timer_list.keys(
            ):
                self._channel_service.timer_service.stop_timer(
                    TimerService.TIMER_KEY_SUBSCRIBE)
        except Exception as e:
            logging.error(f"announce confirmed block error : {e}")
            response_code = message_code.Response.fail
        return response_code
Exemple #28
0
    def generate_block(self):
        """임시 블럭 생성하는 메소드

        :return: 임시 블럭
        """
        block = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)
        for x in range(10):
            tx = test_util.create_basic_tx(self.__peer_id, self.__peer_auth)
            block.put_transaction(tx)
        block.generate_block(self.chain.last_block)
        return block
Exemple #29
0
    def __add_single_tx_block_blockchain_return_tx(self):
        last_block = self.chain.last_block
        block = Block(channel_name=conf.LOOPCHAIN_DEFAULT_CHANNEL)
        tx = test_util.create_basic_tx(self.__peer_id, self.peer_auth)
        block.put_transaction(tx)

        logging.debug("tx_hash: " + tx.tx_hash)

        block.generate_block(last_block)
        block.block_status = BlockStatus.confirmed

        # add_block to blockchain
        self.assertTrue(self.chain.add_block(block),
                        "Fail Add Block to BlockChain")
        return tx
Exemple #30
0
    def generate_block(self):
        """
        임시 블럭 생성하는 메소드
        :return: 임시 블럭
        """

        block = Block()
        for x in range(10):
            tx = Transaction()
            tx.put_data("{args:[]}")
            block.put_transaction(tx)
        block.generate_block(self.chain.last_block)
        return block