Ejemplo n.º 1
0
def _patch_service_engine(icon_service_engine, revision):
    # Mocks get_balance so, it returns always 100 icx
    # TODO : patch when use get_balance or transfer
    IconScoreContext.engine = ContextEngine(deploy=DeployEngine(),
                                            fee=FeeEngine(),
                                            icx=IcxEngine(),
                                            iiss=IISSEngine(),
                                            prep=PRepEngine(),
                                            issue=IssueEngine(),
                                            inv=INVEngine())

    db = icon_service_engine._icx_context_db
    IconScoreContext.storage = ContextStorage(deploy=DeployStorage(db),
                                              fee=FeeStorage(db),
                                              icx=IcxStorage(db),
                                              iiss=IISSStorage(db),
                                              prep=PRepStorage(db),
                                              issue=IssueStorage(db),
                                              meta=MetaDBStorage(db),
                                              rc=RewardCalcStorage(),
                                              inv=INVStorage(db))
    IconScoreContext.engine.inv._inv_container = generate_inv_container(
        False, revision)

    return icon_service_engine
Ejemplo n.º 2
0
    def setUp(self):
        db_path = os.path.join(PROJECT_ROOT_PATH, self._TEST_DB_PATH)
        score_path = os.path.join(PROJECT_ROOT_PATH, self._ROOT_SCORE_PATH)

        self.score_path = score_path
        self._tx_index = 0

        self.__ensure_dir(db_path)
        self._icx_db = ContextDatabaseFactory.create_by_name(ICON_DEX_DB_NAME)
        self._icx_db.address = ICX_ENGINE_ADDRESS
        self._icx_storage = IcxStorage(self._icx_db)
        self._score_deploy_engine = isde.Engine()
        self._deploy_storage = DeployStorage(self._icx_db)

        self._icon_score_mapper = IconScoreMapper()

        self.addr1 = create_address(AddressPrefix.EOA)
        self.addr2 = create_address(AddressPrefix.EOA)
        self.score_address = create_address(AddressPrefix.CONTRACT)
        self._score_deploy_engine.open()
        IconScoreContext.storage = ContextStorage(
            deploy=self._deploy_storage,
            fee=None,
            icx=None,
            iiss=None,
            prep=None,
            issue=None,
            rc=None,
            meta=None,
        )

        self.make_context()
Ejemplo n.º 3
0
def _patch_service_engine(icon_service_engine, revision):
    # Mocks get_balance so, it returns always 100 icx
    # TODO : patch when use get_balance or transfer
    IconScoreContext.engine = ContextEngine(
        deploy=DeployEngine(),
        fee=FeeEngine(),
        icx=IcxEngine(),
        iiss=IISSEngine(),
        prep=PRepEngine(),
        issue=IssueEngine()
    )

    db = icon_service_engine._icx_context_db
    IconScoreContext.storage = ContextStorage(
        deploy=DeployStorage(db),
        fee=FeeStorage(db),
        icx=IcxStorage(db),
        iiss=IISSStorage(db),
        prep=PRepStorage(db),
        issue=IssueStorage(db),
        meta=MetaDBStorage(db),
        rc=RewardCalcStorage()
    )

    # Patch revision
    def set_revision_to_context(context):
        context.revision = revision

    icon_service_engine._set_revision_to_context = \
        Mock(side_effect=set_revision_to_context)

    return icon_service_engine
Ejemplo n.º 4
0
    def setUp(self):
        context = IconScoreContext(IconScoreContextType.DIRECT)
        block = Mock(spec=Block)
        block.attach_mock(Mock(return_value=0), 'height')
        context.block = block

        self._sender = Address.from_data(AddressPrefix.EOA, os.urandom(20))
        self._score_address = Address.from_data(AddressPrefix.CONTRACT, os.urandom(20))

        context_db = create_context_db()

        self.deploy_storage = DeployStorage(context_db)
        deploy_info = IconScoreDeployInfo(self._score_address,
                                          DeployState.ACTIVE,
                                          self._sender,
                                          os.urandom(32),
                                          os.urandom(32))
        self.icx_storage = IcxStorage(context_db)
        self._icx_engine = IcxEngine()

        self.fee_storage = FeeStorage(context_db)
        patch_fee_storage(self.fee_storage)

        self.deploy_storage.put_deploy_info(context, deploy_info)
        context.storage = ContextStorage(deploy=self.deploy_storage, fee=self.fee_storage, icx=self.icx_storage,
                                         iiss=None, prep=None, issue=None, rc=None, meta=None)
        context.engine = ContextEngine(deploy=None, fee=None, icx=self._icx_engine, iiss=None, prep=None, issue=None)
        self._icx_engine.open(self.icx_storage)

        self.icx_storage._put_genesis_data_account(context,
                                                   CoinPartType.GENERAL,
                                                   self._sender,
                                                   100000000 * 10 ** 18)
        self.icx_storage._put_genesis_data_account(context, CoinPartType.TREASURY,
                                                   Address.from_data(AddressPrefix.EOA, os.urandom(20)), 0)

        self._engine = FeeEngine()
    def setUp(self):
        db_path: str = os.path.join(TEST_ROOT_PATH, self._TEST_DB_PATH)
        # score_root_path: str = os.path.join(TEST_ROOT_PATH, self._SCORE_ROOT_PATH)

        self._tx_index = 0
        self.__ensure_dir(db_path)

        self._icx_db = ContextDatabaseFactory.create_by_name('icx_db')
        self._icx_db.address = ICX_ENGINE_ADDRESS
        self._icx_storage = IcxStorage(self._icx_db)
        self._icon_deploy_storage = DeployStorage(self._icx_db)

        self._icon_score_mapper = IconScoreMapper()

        IconScoreContextUtil.validate_score_blacklist = Mock()
        IconScoreContextUtil.get_owner = Mock()
        IconScoreContextUtil.get_icon_score = Mock()
        IconScoreContextUtil.is_service_flag_on = Mock()

        self.from_address = create_address(AddressPrefix.EOA)

        self._engine = DeployEngine()
        IconScoreContext.storage = ContextStorage(
            deploy=self._icon_deploy_storage,
            fee=None,
            icx=None,
            iiss=None,
            prep=None,
            issue=None,
            rc=None,
            meta=None)
        self.make_context()
        self._icon_deploy_storage.open(self._context)
        self._engine.open(self._icon_deploy_storage)

        self._one_icx = 1 * 10**18
        self._one_icx_to_token = 1
 def setUp(self):
     self.storage = DeployStorage(Mock(spec=ContextDatabase))
class TestIconScoreDeployStorage(unittest.TestCase):
    def setUp(self):
        self.storage = DeployStorage(Mock(spec=ContextDatabase))

    def test_put_deploy_info_and_tx_params(self):
        self.storage.put_deploy_tx_params = Mock()
        self.storage.get_deploy_tx_params = Mock(return_value=bytes())
        context = Mock(spec=IconScoreContext)
        score_address = create_address(1)
        deploy_type = DeployType.INSTALL
        owner = create_address()
        tx_hash = create_tx_hash()
        deploy_data = {}
        with self.assertRaises(InvalidParamsException) as e:
            self.storage.put_deploy_info_and_tx_params(context, score_address,
                                                       deploy_type, owner,
                                                       tx_hash, deploy_data)
        self.assertEqual(e.exception.code, ExceptionCode.INVALID_PARAMETER)
        self.assertEqual(e.exception.message,
                         f'deploy_params already exists: {tx_hash}')
        self.storage.put_deploy_tx_params.assert_not_called()

        with patch('iconservice.deploy.storage.IconScoreDeployTXParams'
                   ) as MockTxParams:
            with patch('iconservice.deploy.storage.IconScoreDeployInfo'
                       ) as MockDeployInfos:
                self.storage.put_deploy_tx_params.reset_mock()
                self.storage.get_deploy_tx_params.reset_mock()
                self.storage.get_deploy_tx_params.return_value = None
                self.storage.get_deploy_info = Mock(return_value=None)
                self.storage.put_deploy_info = Mock()
                context = Mock(spec=IconScoreContext)
                score_address = create_address(1)
                deploy_type = DeployType.INSTALL
                owner = create_address()
                tx_hash = create_tx_hash()
                deploy_data = {}
                tx_params = IconScoreDeployTXParams(tx_hash, deploy_type,
                                                    score_address, deploy_data)
                MockTxParams.return_value = tx_params
                deploy_info = IconScoreDeployInfo(score_address,
                                                  DeployState.INACTIVE, owner,
                                                  ZERO_TX_HASH, tx_hash)
                MockDeployInfos.return_value = deploy_info

                self.storage.put_deploy_info_and_tx_params(
                    context, score_address, deploy_type, owner, tx_hash,
                    deploy_data)
                self.storage.get_deploy_tx_params.assert_called_once_with(
                    context, tx_hash)
                self.storage.put_deploy_tx_params.assert_called_once_with(
                    context, tx_params)
                self.storage.get_deploy_info.assert_called_once_with(
                    context, score_address)
                self.storage.put_deploy_info.assert_called_once_with(
                    context, deploy_info)

        with patch('iconservice.deploy.storage.IconScoreDeployTXParams'
                   ) as MockTxParams:
            self.storage.put_deploy_tx_params.reset_mock()
            self.storage.get_deploy_tx_params.reset_mock()
            self.storage.get_deploy_tx_params.return_value = None
            self.storage.put_deploy_info = Mock()
            context = Mock(spec=IconScoreContext)
            score_address = create_address(1)
            deploy_type = DeployType.INSTALL
            owner = create_address()
            tx_hash = create_tx_hash()
            deploy_data = {}
            deploy_info = IconScoreDeployInfo(score_address,
                                              DeployState.INACTIVE, owner,
                                              ZERO_TX_HASH, tx_hash)
            tx_params = IconScoreDeployTXParams(tx_hash, deploy_type,
                                                score_address, deploy_data)
            self.storage.get_deploy_info = Mock(return_value=deploy_info)
            MockTxParams.return_value = tx_params

            other_owner = create_address()

            with self.assertRaises(AccessDeniedException) as e:
                self.storage.put_deploy_info_and_tx_params(
                    context, score_address, deploy_type, other_owner, tx_hash,
                    deploy_data)
            self.assertEqual(e.exception.code, ExceptionCode.ACCESS_DENIED)
            self.assertEqual(
                e.exception.message,
                f'Invalid owner: {deploy_info.owner} != {other_owner}')

            self.storage.get_deploy_tx_params.assert_called_once_with(
                context, tx_hash)
            self.storage.put_deploy_tx_params.assert_called_once_with(
                context, tx_params)
            self.storage.get_deploy_info.assert_called_once_with(
                context, score_address)

        with patch('iconservice.deploy.storage.IconScoreDeployTXParams'
                   ) as MockTxParams:
            self.storage.put_deploy_tx_params.reset_mock()
            self.storage.get_deploy_tx_params.reset_mock()
            self.storage.get_deploy_tx_params.return_value = None
            self.storage.put_deploy_info = Mock()
            self.storage._db.delete = Mock()
            context = Mock(spec=IconScoreContext)
            context.revision = 0
            score_address = create_address(1)
            deploy_type = DeployType.INSTALL
            owner = create_address()
            tx_hash = create_tx_hash()
            deploy_data = {}
            already_tx_hash = create_tx_hash()
            deploy_info = IconScoreDeployInfo(score_address,
                                              DeployState.INACTIVE, owner,
                                              ZERO_TX_HASH, already_tx_hash)
            tx_params = IconScoreDeployTXParams(tx_hash, deploy_type,
                                                score_address, deploy_data)
            self.storage.get_deploy_info = Mock(
                return_value=deepcopy(deploy_info))
            self.storage._create_db_key = Mock(
                return_value=deploy_info.next_tx_hash)
            MockTxParams.return_value = tx_params

            self.storage.put_deploy_info_and_tx_params(context, score_address,
                                                       deploy_type, owner,
                                                       tx_hash, deploy_data)
            self.storage.get_deploy_tx_params.assert_called_once_with(
                context, tx_hash)
            self.storage.put_deploy_tx_params.assert_called_once_with(
                context, tx_params)
            self.storage.get_deploy_info.assert_called_once_with(
                context, score_address)
            self.storage._db.delete.assert_called_once_with(
                context, deploy_info.next_tx_hash)

            self.storage.put_deploy_info.assert_called_once()
            ret_context, ret_deployinfo = self.storage.put_deploy_info.call_args[
                0]
            self.assertEqual(ret_context, context)
            self.assertEqual(ret_deployinfo.next_tx_hash, tx_hash)

    def test_update_score_info(self):
        context = Mock(spec=IconScoreContext)
        score_address = create_address(1)
        tx_hash = create_tx_hash()

        self.storage.get_deploy_info = Mock(return_value=None)
        with self.assertRaises(InvalidParamsException) as e:
            self.storage.update_score_info(context, score_address, tx_hash)
        self.assertEqual(e.exception.code, ExceptionCode.INVALID_PARAMETER)
        self.assertEqual(e.exception.message,
                         f'deploy_info is None: {score_address}')
        self.storage.get_deploy_info.assert_called_once_with(
            context, score_address)

        current_tx_hash = create_tx_hash()
        next_tx_hash = create_tx_hash()
        deploy_info = IconScoreDeployInfo(score_address, DeployState.INACTIVE,
                                          create_address(), current_tx_hash,
                                          next_tx_hash)
        self.storage.get_deploy_info = Mock(return_value=deepcopy(deploy_info))
        with self.assertRaises(InvalidParamsException) as e:
            self.storage.update_score_info(context, score_address, tx_hash)
        self.assertEqual(e.exception.code, ExceptionCode.INVALID_PARAMETER)
        self.assertEqual(
            e.exception.message, f'Invalid update: '
            f'tx_hash({tx_hash}) != next_tx_hash({next_tx_hash})')
        self.storage.get_deploy_info.assert_called_once_with(
            context, score_address)

        owner = create_address()
        current_tx_hash = create_tx_hash()
        deploy_info = IconScoreDeployInfo(score_address, DeployState.INACTIVE,
                                          owner, current_tx_hash, tx_hash)
        self.storage.get_deploy_info = Mock(return_value=deepcopy(deploy_info))
        self.storage.put_deploy_info = Mock()
        self.storage.get_deploy_tx_params = Mock(return_value=None)
        with self.assertRaises(InvalidParamsException) as e:
            self.storage.update_score_info(context, score_address, tx_hash)
        self.assertEqual(e.exception.code, ExceptionCode.INVALID_PARAMETER)
        self.assertEqual(e.exception.message, f'tx_params is None: {tx_hash}')
        self.storage.get_deploy_info.assert_called_once_with(
            context, score_address)
        self.storage.put_deploy_info.assert_called_once()
        ret_context, ret_deploy_info = self.storage.put_deploy_info.call_args[
            0]
        self.assertEqual(ret_context, context)
        expected = IconScoreDeployInfo(score_address, DeployState.ACTIVE,
                                       owner, tx_hash, ZERO_TX_HASH)
        self.assertEqual(ret_deploy_info.to_bytes(), expected.to_bytes())

        self.storage.get_deploy_info = Mock(return_value=deepcopy(deploy_info))
        self.storage.put_deploy_info = Mock()
        self.storage.get_deploy_tx_params = Mock(return_value=Mock(
            spec=IconScoreDeployTXParams))
        self.storage.update_score_info(context, score_address, tx_hash)
        self.storage.get_deploy_info.assert_called_once_with(
            context, score_address)
        self.storage.put_deploy_info.assert_called_once()
        ret_context, ret_deploy_info = self.storage.put_deploy_info.call_args[
            0]
        self.assertEqual(ret_context, context)
        expected = IconScoreDeployInfo(score_address, DeployState.ACTIVE,
                                       owner, tx_hash, ZERO_TX_HASH)
        self.assertEqual(ret_deploy_info.to_bytes(), expected.to_bytes())
        self.storage.get_deploy_tx_params.assert_called_once_with(
            context, tx_hash)

    def test_put_deploy_info(self):
        context = Mock(spec=IconScoreContext)
        score_address = create_address(1)
        deploy_info = IconScoreDeployInfo(score_address, DeployState.INACTIVE,
                                          create_address(), ZERO_TX_HASH,
                                          create_tx_hash())
        self.storage._create_db_key = Mock(
            return_value=score_address.to_bytes())
        self.storage._db.put = Mock()

        self.storage.put_deploy_info(context, deploy_info)
        self.storage._db.put.assert_called_once_with(context,
                                                     score_address.to_bytes(),
                                                     deploy_info.to_bytes())

    def test_get_deploy_info(self):
        context = Mock(spec=IconScoreContext)

        score_address = create_address(1)
        self.storage._create_db_key = Mock(
            return_value=score_address.to_bytes())
        self.storage._db.get = Mock(return_value=None)
        self.assertEqual(None,
                         self.storage.get_deploy_info(context, score_address))

        score_address = create_address(1)
        deploy_info = IconScoreDeployInfo(score_address, DeployState.INACTIVE,
                                          create_address(), ZERO_TX_HASH,
                                          create_tx_hash())
        self.storage._create_db_key = Mock(
            return_value=score_address.to_bytes())
        self.storage._db.get = Mock(return_value=deploy_info.to_bytes())
        self.assertEqual(
            deploy_info.to_bytes(),
            self.storage.get_deploy_info(context, score_address).to_bytes())

    def test_put_deploy_tx_params(self):
        context = Mock(spec=IconScoreContext)
        tx_hash = create_tx_hash()
        tx_params = IconScoreDeployTXParams(tx_hash, DeployType.INSTALL,
                                            create_address(1), {})
        self.storage._create_db_key = Mock(return_value=tx_hash)
        self.storage._db.put = Mock()

        self.storage.put_deploy_tx_params(context, tx_params)
        self.storage._db.put.assert_called_once_with(context,
                                                     tx_params.tx_hash,
                                                     tx_params.to_bytes())

    def test_get_deploy_tx_params(self):
        context = Mock(spec=IconScoreContext)

        tx_hash = create_tx_hash()
        self.storage._create_db_key = Mock(return_value=tx_hash)
        self.storage._db.get = Mock(return_value=None)
        self.assertEqual(None,
                         self.storage.get_deploy_tx_params(context, tx_hash))

        tx_hash = create_tx_hash()
        tx_params = IconScoreDeployTXParams(tx_hash, DeployType.INSTALL,
                                            create_address(1), {})
        self.storage._create_db_key = Mock(return_value=tx_hash)
        self.storage._db.get = Mock(return_value=tx_params.to_bytes())

        self.assertEqual(
            tx_params.to_bytes(),
            self.storage.get_deploy_tx_params(context, tx_hash).to_bytes())

    def test_create_db_key(self):
        prefix = b'prefix'
        src_key = b'src_key'
        self.assertEqual(prefix + src_key,
                         self.storage._create_db_key(prefix, src_key))

        prefix = b''
        src_key = b''
        self.assertEqual(prefix + src_key,
                         self.storage._create_db_key(prefix, src_key))

    def test_get_tx_hashes_by_score_address(self):
        context = Mock(spec=IconScoreContext)
        score_address = create_address(1)

        self.storage.get_deploy_info = Mock(return_value=None)
        self.assertEqual((None, None),
                         self.storage.get_tx_hashes_by_score_address(
                             context, score_address))
        self.storage.get_deploy_info.assert_called_once_with(
            context, score_address)

        deploy_info = Mock(spec=IconScoreDeployInfo)
        deploy_info.attach_mock(Mock(), 'current_tx_hash')
        deploy_info.attach_mock(Mock(), 'next_tx_hash')
        self.storage.get_deploy_info = Mock(return_value=deploy_info)
        self.assertEqual(
            (deploy_info.current_tx_hash, deploy_info.next_tx_hash),
            self.storage.get_tx_hashes_by_score_address(
                context, score_address))
        self.storage.get_deploy_info.assert_called_once_with(
            context, score_address)

    def test_get_score_address_by_tx_hash(self):
        context = Mock(spec=IconScoreContext)
        tx_hash = create_tx_hash()

        self.storage.get_deploy_tx_params = Mock(return_value=None)
        self.assertIsNone(
            self.storage.get_score_address_by_tx_hash(context, tx_hash))
        self.storage.get_deploy_tx_params.assert_called_once_with(
            context, tx_hash)

        tx_params = Mock(spec=IconScoreDeployTXParams)
        self.storage.get_deploy_tx_params = Mock(return_value=tx_params)
        self.assertEqual(
            tx_params.score_address,
            self.storage.get_score_address_by_tx_hash(context, tx_hash))
        self.storage.get_deploy_tx_params.assert_called_once_with(
            context, tx_hash)
Ejemplo n.º 8
0
class TestFeeEngine(unittest.TestCase):

    def setUp(self):
        context = IconScoreContext(IconScoreContextType.DIRECT)
        block = Mock(spec=Block)
        block.attach_mock(Mock(return_value=0), 'height')
        context.block = block

        self._sender = Address.from_data(AddressPrefix.EOA, os.urandom(20))
        self._score_address = Address.from_data(AddressPrefix.CONTRACT, os.urandom(20))

        context_db = create_context_db()

        self.deploy_storage = DeployStorage(context_db)
        deploy_info = IconScoreDeployInfo(self._score_address,
                                          DeployState.ACTIVE,
                                          self._sender,
                                          os.urandom(32),
                                          os.urandom(32))
        self.icx_storage = IcxStorage(context_db)
        self._icx_engine = IcxEngine()

        self.fee_storage = FeeStorage(context_db)
        patch_fee_storage(self.fee_storage)

        self.deploy_storage.put_deploy_info(context, deploy_info)
        context.storage = ContextStorage(deploy=self.deploy_storage, fee=self.fee_storage, icx=self.icx_storage,
                                         iiss=None, prep=None, issue=None, rc=None, meta=None)
        context.engine = ContextEngine(deploy=None, fee=None, icx=self._icx_engine, iiss=None, prep=None, issue=None)
        self._icx_engine.open(self.icx_storage)

        self.icx_storage._put_genesis_data_account(context,
                                                   CoinPartType.GENERAL,
                                                   self._sender,
                                                   100000000 * 10 ** 18)
        self.icx_storage._put_genesis_data_account(context, CoinPartType.TREASURY,
                                                   Address.from_data(AddressPrefix.EOA, os.urandom(20)), 0)

        self._engine = FeeEngine()

    def tearDown(self):
        ContextContainer._clear_context()
        clear_inner_task()
        VirtualStepCalculator.calculate_virtual_step = calculate_virtual_step

    def get_context(self):
        context = IconScoreContext(IconScoreContextType.INVOKE)
        context.step_counter = Mock(spec=IconScoreStepCounter)
        context.step_counter.step_price = 10 ** 10
        context.tx = Mock(spec=Transaction)
        context.tx.to = self._score_address
        block = Mock(spec=Block)
        block.attach_mock(Mock(return_value=0), 'height')
        context.block = block
        context.storage = ContextStorage(deploy=self.deploy_storage,fee=self.fee_storage, icx=self.icx_storage,
                                         iiss=None, prep=None, issue=None, rc=None, meta=None)
        context.engine = ContextEngine(deploy=None, fee=None, icx=self._icx_engine, iiss=None, prep=None, issue=None)
        return context

    def _deposit_bulk(self, count):
        self.context = self.get_context()
        self.block_height = 0
        input_params = []

        for i in range(count):
            tx_hash = os.urandom(32)
            amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
            block_height = randrange(100, 10000)
            term = get_rand_term()

            before_sender_balance = self._icx_engine.get_balance(self.context, self._sender)
            self._engine.add_deposit(
                self.context, tx_hash, self._sender, self._score_address, amount, block_height, term)
            after_sender_balance = self._icx_engine.get_balance(self.context, self._sender)

            self.assertEqual(amount, before_sender_balance - after_sender_balance)
            input_params.append((tx_hash, amount, block_height, term))

        return input_params

    def test_deposit_fee(self):
        context = self.get_context()
        block_height = 0

        size = randrange(10, 100)
        input_param = self._deposit_bulk(size)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, block_height)
        self.assertEqual(size, len(deposit_info.deposits))

        for i in range(size):
            tx_hash, amount, block_height, term = input_param[i]
            deposit = deposit_info.deposits[i]
            self.assertEqual(tx_hash, deposit.id)
            self.assertEqual(self._sender, deposit.sender)
            self.assertEqual(self._score_address, deposit.score_address)
            self.assertEqual(amount, deposit.deposit_amount)
            self.assertEqual(block_height, deposit.created)
            self.assertEqual(block_height + term, deposit.expires)

    def test_deposit_append_and_delete(self):
        size = randrange(10, 100)
        deposit_list = self._deposit_bulk(size)

        for i in range(size):
            index = randrange(0, size)
            size -= 1
            withdrawal_deposit_id = deposit_list.pop(index)[0]
            self._engine.withdraw_deposit(self.context, self._sender, withdrawal_deposit_id, 1)

            deposit_info = self._engine.get_deposit_info(self.context, self._score_address, 1)
            for j in range(size):
                deposit = deposit_info.deposits[j]
                self.assertEqual(deposit.id, deposit_list[j][0])
                self.assertEqual(self._sender, deposit.sender)
                self.assertEqual(self._score_address, deposit.score_address)
                self.assertEqual(deposit.deposit_amount, deposit_list[j][1])
                self.assertEqual(deposit.created, deposit_list[j][2])
                self.assertEqual(deposit.expires, deposit_list[j][2] + deposit_list[j][3])

        input_param = self._deposit_bulk(100)
        deposit_info = self._engine.get_deposit_info(self.context, self._score_address, self.block_height)

        self.assertEqual(100, len(deposit_info.deposits))

        for i in range(size):
            tx_hash, amount, block_height, term = input_param[i]
            deposit = deposit_info.deposits[i]
            self.assertEqual(tx_hash, deposit.id)
            self.assertEqual(self._sender, deposit.sender)
            self.assertEqual(self._score_address, deposit.score_address)
            self.assertEqual(amount, deposit.deposit_amount)
            self.assertEqual(block_height, deposit.created)
            self.assertEqual(block_height + term, deposit.expires)

    def test_deposit_fee_invalid_param(self):
        context = self.get_context()

        tx_hash = os.urandom(32)
        amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
        block_height = randrange(100, 10000)
        term = get_rand_term()

        # invalid amount (underflow)
        # noinspection PyTypeChecker
        with self.assertRaises(InvalidRequestException) as e:
            inv_amount = randrange(0, FeeEngine._MIN_DEPOSIT_AMOUNT - 1)
            self._engine.add_deposit(context, tx_hash, self._sender, self._score_address,
                                     inv_amount, block_height, term)
        # noinspection PyUnresolvedReferences
        self.assertEqual('Invalid deposit amount', e.exception.message)

        # invalid amount (overflow)
        # noinspection PyTypeChecker
        with self.assertRaises(InvalidRequestException) as e:
            inv_amount = \
                randrange(FeeEngine._MAX_DEPOSIT_AMOUNT + 1, FeeEngine._MAX_DEPOSIT_AMOUNT * 10)
            self._engine.add_deposit(context, tx_hash, self._sender, self._score_address,
                                     inv_amount, block_height, term)
        # noinspection PyUnresolvedReferences
        self.assertEqual('Invalid deposit amount', e.exception.message)

        # invalid term (underflow)
        # noinspection PyTypeChecker
        with self.assertRaises(InvalidRequestException) as e:
            inv_term = randrange(0, FeeEngine._MIN_DEPOSIT_TERM - 1)
            self._engine.add_deposit(context, tx_hash, self._sender, self._score_address,
                                     amount, block_height, inv_term)
        # noinspection PyUnresolvedReferences
        self.assertEqual('Invalid deposit term', e.exception.message)

        # invalid term (overflow)
        # noinspection PyTypeChecker
        with self.assertRaises(InvalidRequestException) as e:
            inv_term = \
                randrange(FeeEngine._MAX_DEPOSIT_TERM + 1, FeeEngine._MAX_DEPOSIT_TERM * 10)
            self._engine.add_deposit(context, tx_hash, self._sender, self._score_address,
                                     amount, block_height, inv_term)
        # noinspection PyUnresolvedReferences
        self.assertEqual('Invalid deposit term', e.exception.message)

        # invalid owner
        # noinspection PyTypeChecker
        with self.assertRaises(InvalidRequestException) as e:
            inv_sender = Address.from_data(AddressPrefix.EOA, os.urandom(20))
            self._engine.add_deposit(context, tx_hash, inv_sender, self._score_address,
                                     amount, block_height, term)
        # noinspection PyUnresolvedReferences
        self.assertEqual('Invalid SCORE owner', e.exception.message)

    def test_deposit_fee_out_of_balance(self):
        context = self.get_context()

        self.icx_storage._put_genesis_data_account(
            context, CoinPartType.GENERAL, self._sender, 10000 * 10 ** 18)

        tx_hash = os.urandom(32)
        amount = 10001 * 10 ** 18
        block_height = randrange(100, 10000)
        term = get_rand_term()

        # out of balance
        # noinspection PyTypeChecker
        with self.assertRaises(OutOfBalanceException) as e:
            self._engine.add_deposit(context, tx_hash, self._sender, self._score_address,
                                     amount, block_height, term)
        # noinspection PyUnresolvedReferences
        self.assertEqual('Out of balance', e.exception.message)

    def test_deposit_fee_available_head_ids(self):
        context = self.get_context()
        tx_hash = os.urandom(32)
        amount = 10000 * 10 ** 18
        block_height = 1000

        self.icx_storage._put_genesis_data_account(context,
                                                   CoinPartType.GENERAL,
                                                   self._sender,
                                                   amount)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)

        self.assertEqual(deposit_meta.available_head_id_of_virtual_step, None)
        self.assertEqual(deposit_meta.available_head_id_of_deposit, None)

        self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, amount, block_height,
                                 FeeEngine._MIN_DEPOSIT_TERM)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.available_head_id_of_virtual_step, tx_hash)
        self.assertEqual(deposit_meta.available_head_id_of_deposit, tx_hash)

    def test_deposit_fee_expires_updated(self):
        context = self.get_context()
        tx_hash = os.urandom(32)
        amount = 10000 * 10 ** 18
        block_height = 1000
        term = FeeEngine._MIN_DEPOSIT_TERM

        self.icx_storage._put_genesis_data_account(context,
                                                   CoinPartType.GENERAL,
                                                   self._sender,
                                                   amount)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)

        self.assertEqual(deposit_meta.expires_of_virtual_step, -1)
        self.assertEqual(deposit_meta.expires_of_deposit, -1)

        self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, amount, block_height, term)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.expires_of_virtual_step, block_height + term)
        self.assertEqual(deposit_meta.expires_of_deposit, block_height + term)

    def test_withdraw_fee_without_penalty(self):
        context = self.get_context()

        tx_hash = os.urandom(32)
        amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
        block_height = randrange(100, 10000)
        term = get_rand_term()

        self._engine.add_deposit(
            context, tx_hash, self._sender, self._score_address, amount, block_height, term)

        before_sender_balance = self._icx_engine.get_balance(context, self._sender)
        self._engine.withdraw_deposit(context, self._sender, tx_hash, block_height + term + 1)
        after_sender_balance = self._icx_engine.get_balance(context, self._sender)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, block_height)
        self.assertIsNone(deposit_info)
        self.assertEqual(amount, after_sender_balance - before_sender_balance)

    def test_withdraw_fee_with_penalty(self):
        context = self.get_context()

        tx_hash = os.urandom(32)
        amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
        block_height = randrange(100, 10000)
        term = get_rand_term()

        self._engine.add_deposit(
            context, tx_hash, self._sender, self._score_address, amount, block_height, term)

        before_sender_balance = self._icx_engine.get_balance(context, self._sender)
        self._engine.withdraw_deposit(context, self._sender, tx_hash, block_height + term - 1)
        after_sender_balance = self._icx_engine.get_balance(context, self._sender)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, block_height)
        self.assertIsNone(deposit_info)
        self.assertGreater(after_sender_balance - before_sender_balance, 0)
        self.assertLessEqual(after_sender_balance - before_sender_balance, amount)

    def test_withdraw_fee_and_updates_previous_and_next_link_ascending(self):
        """
        Given: There are four deposits.
        When : Withdraws all of them sequentially(ascending).
        Then : Checks if the previous and next link update correctly.
        """
        context = self.get_context()

        cnt_deposit = 4
        block_height = randrange(100, 10000)
        arr_tx_hash = []
        for i in range(cnt_deposit):
            arr_tx_hash.append(os.urandom(32))
            amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
            term = get_rand_term()
            block_height += 1
            self._engine.add_deposit(
                context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term)

        for i in range(cnt_deposit):
            target_deposit = self._engine.get_deposit(context, arr_tx_hash[i])
            self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[i], block_height + term // 2)

            if cnt_deposit - 1 == i:
                self.assertIsNone(target_deposit.next_id)
                break

            next_deposit = self._engine.get_deposit(context, target_deposit.next_id)
            self.assertEqual(next_deposit.prev_id, None)

            deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
            self.assertEqual(next_deposit.id, deposit_meta.head_id)

    def test_withdraw_fee_and_updates_previous_and_next_link_descending(self):
        """
        Given: There are four deposits.
        When : Withdraws all of them sequentially(descending).
        Then : Checks if the previous and next link update correctly.
        """
        context = self.get_context()

        cnt_deposit = 4
        block_height = randrange(100, 10000)
        arr_tx_hash = []
        for i in range(cnt_deposit):
            arr_tx_hash.append(os.urandom(32))
            amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
            term = get_rand_term()
            block_height += 1
            self._engine.add_deposit(
                context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term)

        for i in range(cnt_deposit - 1, -1, -1):
            target_deposit = self._engine.get_deposit(context, arr_tx_hash[i])
            self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[i], block_height + term // 2)

            if i == 0:
                self.assertIsNone(target_deposit.prev_id)
                break

            prev_deposit = self._engine.get_deposit(context, target_deposit.prev_id)
            self.assertEqual(prev_deposit.next_id, None)

            deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
            self.assertEqual(prev_deposit.id, deposit_meta.tail_id)

    @unittest.skipIf(FIXED_TERM is True, "FIXED_TERM is true")
    def test_withdraw_fee_when_available_head_id_of_virtual_step_is_same_as_deposit_id(self):
        """
        Given: There are four deposits. Only the last deposit has enough to long term.
        When : Available head id of the virtual step is same as deposit id.
        Then : Searches for next deposit id which is available to use virtual step
               and where expires of the deposit is more than current block height.
               In the test, only the last deposit is available.
        """
        context = self.get_context()

        cnt_deposit = 4
        block_height = randrange(100, 10000)
        arr_tx_hash = []
        for i in range(cnt_deposit):
            arr_tx_hash.append(os.urandom(32))
            amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
            block_height += 1

            if i != cnt_deposit - 1:
                term = FeeEngine._MIN_DEPOSIT_TERM
            else:
                term = FeeEngine._MAX_DEPOSIT_TERM

            self._engine.add_deposit(
                context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.available_head_id_of_virtual_step, arr_tx_hash[0])

        self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[0],
                                      block_height + FeeEngine._MAX_DEPOSIT_TERM // 2)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.available_head_id_of_virtual_step, arr_tx_hash[len(arr_tx_hash) - 1])

    @unittest.skipIf(FIXED_TERM is True, "FIXED_TERM is true")
    def test_withdraw_fee_when_available_head_id_of_deposit_is_same_as_deposit_id(self):
        """
        Given: There are four deposits. Only the third deposit has enough long term.
        When : Available head id of deposit is same as deposit id.
        Then : Searches for next deposit id which is available to use deposit
               and where expires of the deposit is more than current block height.
               In the test, only the third deposit is available.
        """
        context = self.get_context()

        cnt_deposit = 4
        block_height = randrange(100, 10000)
        arr_tx_hash = []
        for i in range(cnt_deposit):
            arr_tx_hash.append(os.urandom(32))
            amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
            block_height += 1

            if i != cnt_deposit - 2:
                term = FeeEngine._MIN_DEPOSIT_TERM
            else:
                term = FeeEngine._MAX_DEPOSIT_TERM

            self._engine.add_deposit(
                context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.available_head_id_of_deposit, arr_tx_hash[0])

        self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[0],
                                      block_height + FeeEngine._MAX_DEPOSIT_TERM // 2)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.available_head_id_of_deposit, arr_tx_hash[len(arr_tx_hash) - 2])

    @unittest.skipIf(FIXED_TERM is True, "FIXED_TERM is true")
    def test_withdraw_fee_to_check_setting_on_next_max_expires(self):
        """
        Given: There are four deposits.
        When : Expires of the withdrawal deposit is same as expires.
        Then : Searches for max expires which is more than current block height.
        """
        context = self.get_context()

        cnt_deposit = 4
        block_height = randrange(100, 10000)
        arr_tx_hash = []
        last_expires = 0
        org_last_expires = 0
        for i in range(cnt_deposit):
            arr_tx_hash.append(os.urandom(32))
            amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
            block_height += 1

            if i != 0:
                term = FeeEngine._MIN_DEPOSIT_TERM
                if block_height + term > last_expires:
                    last_expires = block_height + term
            else:
                term = FeeEngine._MAX_DEPOSIT_TERM
                org_last_expires = block_height + term
            self._engine.add_deposit(
                context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.available_head_id_of_virtual_step, arr_tx_hash[0])
        self.assertEqual(deposit_meta.expires_of_virtual_step, org_last_expires)
        self.assertEqual(deposit_meta.expires_of_deposit, org_last_expires)

        self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[0],
                                      block_height + FeeEngine._MIN_DEPOSIT_TERM // 2)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.expires_of_virtual_step, last_expires)
        self.assertEqual(deposit_meta.expires_of_deposit, last_expires)

    @unittest.skipIf(FIXED_TERM is True, "FIXED_TERM is true")
    def test_withdraw_fee_of_last_deposit_to_check_setting_on_next_max_expires(self):
        """
        Given: There are four deposits.
        When : Expires of the withdrawal deposit which is the last one is same as expires.
        Then : Searches for max expires which is more than current block height.
        """
        context = self.get_context()

        cnt_deposit = 4
        block_height = randrange(100, 10000)
        arr_tx_hash = []
        last_expires = 0
        org_last_expires = 0
        for i in range(cnt_deposit):
            arr_tx_hash.append(os.urandom(32))
            amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
            block_height += 1

            if i != cnt_deposit-1:
                term = FeeEngine._MIN_DEPOSIT_TERM
                if block_height + term > last_expires:
                    last_expires = block_height + term
            else:
                term = FeeEngine._MAX_DEPOSIT_TERM
                org_last_expires = block_height + term
            self._engine.add_deposit(
                context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.available_head_id_of_virtual_step, arr_tx_hash[0])
        self.assertEqual(deposit_meta.available_head_id_of_deposit, arr_tx_hash[0])
        self.assertEqual(deposit_meta.expires_of_virtual_step, org_last_expires)
        self.assertEqual(deposit_meta.expires_of_deposit, org_last_expires)

        # Withdraws the last one
        self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[cnt_deposit - 1],
                                      block_height + FeeEngine._MIN_DEPOSIT_TERM // 2)

        deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address)
        self.assertEqual(deposit_meta.expires_of_virtual_step, last_expires)
        self.assertEqual(deposit_meta.expires_of_deposit, last_expires)

    def test_get_deposit_info(self):
        context = self.get_context()

        tx_hash = os.urandom(32)
        amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
        block_height = randrange(100, 10000)
        term = get_rand_term()

        before_sender_balance = self._icx_engine.get_balance(context, self._sender)
        self._engine.add_deposit(
            context, tx_hash, self._sender, self._score_address, amount, block_height, term)
        after_sender_balance = self._icx_engine.get_balance(context, self._sender)

        self.assertEqual(amount, before_sender_balance - after_sender_balance)

        deposit = self._engine.get_deposit(context, tx_hash)

        self.assertEqual(tx_hash, deposit.id)
        self.assertEqual(self._score_address, deposit.score_address)
        self.assertEqual(self._sender, deposit.sender)
        self.assertEqual(amount, deposit.deposit_amount)
        self.assertEqual(block_height, deposit.created)
        self.assertEqual(block_height + term, deposit.expires)

    def test_charge_transaction_fee_without_sharing(self):
        context = self.get_context()

        step_price = 10 ** 10
        used_step = 10 ** 10

        tx_hash = os.urandom(32)
        amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
        block_height = randrange(100, 10000)
        term = get_rand_term()

        self._engine.add_deposit(
            context, tx_hash, self._sender, self._score_address, amount, block_height, term)

        before_sender_balance = self._icx_engine.get_balance(context, self._sender)

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, block_height)

        after_sender_balance = self._icx_engine.get_balance(context, self._sender)

        self.assertEqual(step_price * used_step, before_sender_balance - after_sender_balance)

    def test_charge_transaction_fee_sharing_deposit(self):
        context = self.get_context()

        step_price = 10 ** 10
        used_step = 10 ** 10

        tx_hash = os.urandom(32)
        amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT)
        block_height = randrange(100, 10000)
        term = get_rand_term()
        self._engine.add_deposit(
            context, tx_hash, self._sender, self._score_address, amount, block_height, term)

        ratio = 50
        context.fee_sharing_proportion = ratio

        before_sender_balance = self._icx_engine.get_balance(context, self._sender)

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, block_height)

        after_sender_balance = self._icx_engine.get_balance(context, self._sender)

        score_charging_step = used_step * ratio // 100
        sender_charging_step = used_step - score_charging_step
        self.assertEqual(
            step_price * sender_charging_step, before_sender_balance - after_sender_balance)

    def test_charge_fee_from_score_by_virtual_step_single_deposit(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
        When :  Current  block is 120 so  1st deposit is unavailable
        Then :  Pays fee by virtual step of 2nd.
                update indices to 2nd
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 100),
            (os.urandom(32), 50, 180, 100, 100),
            (os.urandom(32), 70, 150, 100, 100),
            (os.urandom(32), 90, 250, 100, 100),
            (os.urandom(32), 110, 200, 100, 100)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 120
        used_step = 80

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        before_virtual_step = deposit_info.available_virtual_step

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(used_step, before_virtual_step - after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(deposits[1][0], deposit_meta.available_head_id_of_virtual_step)

    def test_charge_fee_from_score_by_virtual_step_single_deposit_next_head(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
        When :  Current  block is 120 so  1st deposit is unavailable
        Then :  Pays fee by virtual step of 2nd.
                the virtual steps in 2nd are fully consumed
                update indices to 3rd
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 100),
            (os.urandom(32), 50, 180, 100, 100),
            (os.urandom(32), 70, 150, 100, 100),
            (os.urandom(32), 90, 250, 100, 100),
            (os.urandom(32), 110, 200, 100, 100)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 120
        used_step = 100

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        before_virtual_step = deposit_info.available_virtual_step

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(used_step, before_virtual_step - after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(deposits[2][0], deposit_meta.available_head_id_of_virtual_step)

    def test_charge_fee_from_score_by_virtual_step__single_deposit_next_head_next_expire(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
        When :  Current  block is 190 so  4th, 5th deposits are available
        Then :  Pays fee by virtual step of 4th.
                the virtual steps in 4th are fully consumed
                update indices to 5th
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 100),
            (os.urandom(32), 50, 180, 100, 100),
            (os.urandom(32), 70, 150, 100, 100),
            (os.urandom(32), 90, 250, 100, 100),
            (os.urandom(32), 110, 200, 100, 100)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 190
        used_step = 100

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        before_virtual_step = deposit_info.available_virtual_step

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(used_step, before_virtual_step - after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(deposits[4][0], deposit_meta.available_head_id_of_virtual_step)
        self.assertEqual(deposits[4][2], deposit_meta.expires_of_virtual_step)

    def test_charge_fee_from_score_by_virtual_step__single_deposit_next_head_next_expire_none(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
        When :  Current  block is 210 so only 4th deposit is available
        Then :  Pays fee by virtual step of 4th.
                the virtual steps in 4th are fully consumed
                should update indices but there are no more available deposits
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 100),
            (os.urandom(32), 50, 180, 100, 100),
            (os.urandom(32), 70, 150, 100, 100),
            (os.urandom(32), 90, 250, 100, 100),
            (os.urandom(32), 110, 200, 100, 100)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 210
        used_step = 100

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        before_virtual_step = deposit_info.available_virtual_step

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(used_step, before_virtual_step - after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step)
        self.assertEqual(-1, deposit_meta.expires_of_virtual_step)

    def test_charge_fee_from_score_by_virtual_step_multiple_deposit(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
        When :  Current  block is 120 so 1st deposit is unavailable
        Then :  Pays fee by virtual step through 2nd, 3rd, 4th.
                the virtual steps in 2nd, 3rd are fully consumed
                update indices to 4th
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 100),
            (os.urandom(32), 50, 180, 100, 100),
            (os.urandom(32), 70, 150, 100, 100),
            (os.urandom(32), 90, 250, 100, 100),
            (os.urandom(32), 110, 200, 100, 100)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 120
        used_step = 250

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        before_virtual_step = deposit_info.available_virtual_step

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(used_step, before_virtual_step - after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(deposits[3][0], deposit_meta.available_head_id_of_virtual_step)

    def test_charge_fee_from_score_by_combine_by_single_deposit(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
                Remaining virtual steps are in 5th deposit
        When :  Current  block is 120 so 1st deposit is unavailable
                Remaining virtual steps are not enough to pay fees
        Then :  Pays fee by virtual step first.
                Pays remaining fee by deposit of 2nd
                update indices to 2nd
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 0),
            (os.urandom(32), 50, 180, 100, 0),
            (os.urandom(32), 70, 150, 100, 0),
            (os.urandom(32), 90, 250, 100, 0),
            (os.urandom(32), 110, 200, 100, 50)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 120
        used_step = 70

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(0, after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step)
        self.assertEqual(-1, deposit_meta.expires_of_virtual_step)

        self.assertEqual(deposits[1][0], deposit_meta.available_head_id_of_deposit)

    def test_charge_fee_from_score_by_combine_next_head(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
                Remaining virtual steps are in 5th deposit
        When :  Current  block is 120 so 1st deposit is unavailable
                Remaining virtual steps are not enough to pay fees
        Then :  Pays fee by virtual step first.
                Pays remaining fee by deposit of 2nd
                2nd deposit is fully consumed so update indices to 3rd
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 0),
            (os.urandom(32), 50, 180, 100, 0),
            (os.urandom(32), 70, 150, 100, 0),
            (os.urandom(32), 90, 250, 100, 0),
            (os.urandom(32), 110, 200, 100, 50)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 120
        used_step = 140

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(0, after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step)
        self.assertEqual(-1, deposit_meta.expires_of_virtual_step)

        self.assertEqual(deposits[2][0], deposit_meta.available_head_id_of_deposit)

    def test_charge_fee_from_score_by_combine_next_head_next_expire(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
                Remaining virtual steps are in 5th deposit
        When :  Current  block is 190 so 1st, 2nd, 3rd deposits are unavailable
                Remaining virtual steps are not enough to pay fees
        Then :  Pays fee by virtual step first.
                Pays remaining fee by deposit of 4th
                4th deposit is fully consumed so update indices to 5th
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 0),
            (os.urandom(32), 50, 180, 100, 0),
            (os.urandom(32), 70, 150, 100, 0),
            (os.urandom(32), 90, 250, 100, 0),
            (os.urandom(32), 110, 200, 100, 50)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 190
        used_step = 140

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(0, after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step)
        self.assertEqual(-1, deposit_meta.expires_of_virtual_step)

        self.assertEqual(deposits[4][0], deposit_meta.available_head_id_of_deposit)
        self.assertEqual(deposits[4][2], deposit_meta.expires_of_deposit)

    def test_charge_fee_from_score_by_combine_next_head_next_expire_none(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
                Remaining virtual steps are in 4th and 5th deposit
        When :  Current  block is 220 so 5th deposit is unavailable
                Remaining virtual steps are not enough to pay fees
        Then :  Pays fee by virtual step first.
                Pays remaining fee by deposit of 4th
                All available deposits are consumed so make the SCORE disabled
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 0),
            (os.urandom(32), 50, 180, 100, 0),
            (os.urandom(32), 70, 150, 100, 0),
            (os.urandom(32), 90, 250, 100, 50),
            (os.urandom(32), 110, 200, 100, 100)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 220
        used_step = 140

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(0, after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step)
        self.assertEqual(-1, deposit_meta.expires_of_virtual_step)

        self.assertEqual(None, deposit_meta.available_head_id_of_deposit)
        self.assertEqual(-1, deposit_meta.expires_of_deposit)

    def test_charge_fee_from_score_by_combine_multiple_deposit(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
                Remaining virtual steps are in 5th deposit
        When :  Current  block is 120 so 1st deposit is unavailable
                Remaining virtual steps are not enough to pay fees
        Then :  Pays fee by virtual step first.
                Pays remaining fee by deposit through 2nd and 3rd deposit.
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 0),
            (os.urandom(32), 50, 180, 100, 0),
            (os.urandom(32), 70, 150, 100, 0),
            (os.urandom(32), 90, 250, 100, 0),
            (os.urandom(32), 110, 200, 100, 50)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 120
        used_step = 230

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(0, after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step)
        self.assertEqual(-1, deposit_meta.expires_of_virtual_step)

        # Asserts indices are updated
        self.assertEqual(deposits[3][0], deposit_meta.available_head_id_of_deposit)
        self.assertEqual(deposits[3][2], deposit_meta.expires_of_deposit)

    def test_charge_fee_from_score_by_combine_additional_pay(self):
        """
        Given:  Five deposits. The fourth deposit is the max expire.
                Remaining virtual steps are in 4th and 5th deposit
        When :  Current  block is 220 so 5th deposit is unavailable
                Remaining virtual steps are not enough to pay fees
                Available deposits are also not enough to pay fees
        Then :  Pays fees regardless minimum remaining amount
                and make the SCORE disabled
        """

        context = self.get_context()

        # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount
        deposits = [
            (os.urandom(32), 10, 100, 100, 0),
            (os.urandom(32), 50, 180, 100, 0),
            (os.urandom(32), 70, 150, 100, 0),
            (os.urandom(32), 90, 250, 100, 50),
            (os.urandom(32), 110, 200, 100, 100)
        ]
        self._set_up_deposits(context, deposits)

        step_price = 1
        current_block = 220
        used_step = 150

        self._engine.charge_transaction_fee(
            context, self._sender, self._score_address, step_price, used_step, current_block)

        deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block)
        after_virtual_step = deposit_info.available_virtual_step

        self.assertEqual(0, after_virtual_step)

        deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address)
        # Asserts virtual step disabled
        self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step)
        self.assertEqual(-1, deposit_meta.expires_of_virtual_step)

        # Asserts deposit disabled
        self.assertEqual(None, deposit_meta.available_head_id_of_deposit)
        self.assertEqual(-1, deposit_meta.expires_of_deposit)

    def _set_up_deposits(self, context, deposits):
        context.fee_sharing_proportion = 100

        self._engine._MIN_DEPOSIT_TERM = 50
        self._engine._MIN_DEPOSIT_AMOUNT = 10

        for deposit in deposits:
            tx_hash = deposit[0]
            amount = deposit[3]
            block_height = deposit[1]
            term = deposit[2] - block_height

            # self._engine._calculate_virtual_step_issuance = Mock(return_value=deposit[4])
            VirtualStepCalculator.calculate_virtual_step = Mock(return_value=deposit[4])

            self._engine.add_deposit(
                context, tx_hash, self._sender, self._score_address, amount, block_height, term)
class TestIconZipDeploy(unittest.TestCase):
    _SCORE_ROOT_PATH = 'tests/score'
    _TEST_DB_PATH = 'tests/test_db'

    @classmethod
    def setUpClass(cls):
        db_path = os.path.join(TEST_ROOT_PATH, cls._TEST_DB_PATH)
        ContextDatabaseFactory.open(db_path,
                                    ContextDatabaseFactory.Mode.SINGLE_DB)

    @classmethod
    def tearDownClass(cls):
        ContextDatabaseFactory.close()

    def setUp(self):
        db_path: str = os.path.join(TEST_ROOT_PATH, self._TEST_DB_PATH)
        # score_root_path: str = os.path.join(TEST_ROOT_PATH, self._SCORE_ROOT_PATH)

        self._tx_index = 0
        self.__ensure_dir(db_path)

        self._icx_db = ContextDatabaseFactory.create_by_name('icx_db')
        self._icx_db.address = ICX_ENGINE_ADDRESS
        self._icx_storage = IcxStorage(self._icx_db)
        self._icon_deploy_storage = DeployStorage(self._icx_db)

        self._icon_score_mapper = IconScoreMapper()

        IconScoreContextUtil.validate_score_blacklist = Mock()
        IconScoreContextUtil.get_owner = Mock()
        IconScoreContextUtil.get_icon_score = Mock()
        IconScoreContextUtil.is_service_flag_on = Mock()

        self.from_address = create_address(AddressPrefix.EOA)

        self._engine = DeployEngine()
        IconScoreContext.storage = ContextStorage(
            deploy=self._icon_deploy_storage,
            fee=None,
            icx=None,
            iiss=None,
            prep=None,
            issue=None,
            rc=None,
            meta=None)
        self.make_context()
        self._icon_deploy_storage.open(self._context)
        self._engine.open(self._icon_deploy_storage)

        self._one_icx = 1 * 10**18
        self._one_icx_to_token = 1

    def make_context(self):
        self._tx_index += 1
        self._context = IconScoreContext(IconScoreContextType.DIRECT)
        self._context.msg = Message(self.from_address, 0)

        tx_hash = create_tx_hash()
        self._context.new_icon_score_mapper = IconScoreMapper()
        self._context.tx = Transaction(tx_hash,
                                       origin=self.from_address,
                                       timestamp=randrange(1, 1000),
                                       nonce=randrange(1, 1000))
        self._context.block = Block(1, create_block_hash(), 0, None, 0)
        self._context.icon_score_mapper = self._icon_score_mapper
        self._context.icx = IcxEngine()
        self._context.icx.open(self._icx_storage)
        ContextContainer._push_context(self._context)
        self._context.validate_score_blacklist = Mock()
        self._context.is_service_flag_on = Mock(return_value=False)

    def tearDown(self):
        self._engine = None
        ContextContainer._pop_context()
        self._icon_score_mapper.close()

        path = os.path.join(TEST_ROOT_PATH, 'tests')
        remove_path(path)
        path = os.path.join(TEST_ROOT_PATH, self._TEST_DB_PATH)
        remove_path(path)
        IconScoreContextUtil.validate_score_blacklist = VALIDATE_SCORE_BLACK_LIST
        IconScoreContextUtil.get_owner = GET_OWNER
        IconScoreContextUtil.get_icon_score = GET_ICON_SCORE
        IconScoreContextUtil.is_service_flag_on = IS_SERVICE_FLAG_ON

    @staticmethod
    def __ensure_dir(dir_path):
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)

    @staticmethod
    def read_zipfile_as_byte(archive_path: str) -> bytes:
        with open(archive_path, 'rb') as f:
            byte_data = f.read()
            return byte_data

    def test_deploy(self):
        content: bytes = self.read_zipfile_as_byte(
            os.path.join(TEST_ROOT_PATH, 'legacy_unittest/sample',
                         'normal_score.zip'))

        data = {
            "contentType": "application/zip",
            "content": f'0x{bytes.hex(content)}'
        }
        self._icon_deploy_storage.get_next_tx_hash = Mock(
            return_value=ZERO_TX_HASH)
        self._context.step_counter = Mock()

        sample_token_address = self._engine.invoke(self._context,
                                                   SYSTEM_SCORE_ADDRESS, data)

        deploy_info = self._context.storage.deploy.get_deploy_info(
            self._context, sample_token_address)
        self.assertEqual(DeployState.INACTIVE, deploy_info.deploy_state)
        self.assertEqual(sample_token_address, deploy_info.score_address)
        self.assertEqual(self._context.tx.origin, deploy_info.owner)
        self.assertEqual(self._context.tx.hash, deploy_info.next_tx_hash)
        self.assertEqual(ZERO_TX_HASH, deploy_info.current_tx_hash)