Пример #1
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()
Пример #2
0
    def setUp(self):
        empty_address = MalformedAddress.from_string('')
        short_address_without_hx = MalformedAddress.from_string('12341234')
        short_address = MalformedAddress.from_string('hx1234512345')
        long_address_without_hx = MalformedAddress.from_string(
            'cf85fac2d0b507a2db9ce9526e6d01476f16a2d269f51636f9c4b2d512017faf')
        long_address = MalformedAddress.from_string(
            'hxcf85fac2d0b507a2db9ce9526e6d01476f16a2d269f51636f9c4b2d512017faf'
        )
        self.addresses = [
            empty_address, short_address_without_hx, short_address,
            long_address_without_hx, long_address
        ]

        self.db_name = 'icx.db'
        db = ContextDatabase.from_path(self.db_name)
        self.assertIsNotNone(db)

        self.storage = IcxStorage(db)

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

        self.context = context
Пример #3
0
class TestIcxStorageForMalformedAddress(unittest.TestCase):
    def setUp(self):
        empty_address = MalformedAddress.from_string('')
        short_address_without_hx = MalformedAddress.from_string('12341234')
        short_address = MalformedAddress.from_string('hx1234512345')
        long_address_without_hx = MalformedAddress.from_string(
            'cf85fac2d0b507a2db9ce9526e6d01476f16a2d269f51636f9c4b2d512017faf')
        long_address = MalformedAddress.from_string(
            'hxcf85fac2d0b507a2db9ce9526e6d01476f16a2d269f51636f9c4b2d512017faf'
        )
        self.addresses = [
            empty_address, short_address_without_hx, short_address,
            long_address_without_hx, long_address
        ]

        self.db_name = 'icx.db'
        db = ContextDatabase.from_path(self.db_name)
        self.assertIsNotNone(db)

        self.storage = IcxStorage(db)

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

        self.context = context

    def tearDown(self):
        context = self.context
        self.storage.close(context)

        shutil.rmtree(self.db_name)

    def test_get_put_account(self):
        accounts: list = []
        for address in self.addresses:
            coin_part: 'CoinPart' = CoinPart()
            account: 'Account' = Account(address,
                                         self.context.block.height,
                                         Revision.IISS.value,
                                         coin_part=coin_part)
            account.deposit(10**19)
            accounts.append(account)

        for account in accounts:
            self.storage.put_account(self.context, account)
            account2 = self.storage.get_account(self.context, account.address)
            self.assertEqual(account, account2)
Пример #4
0
    def setUp(self):
        self.db_name = 'icx.db'

        db = ContextDatabase.from_path(self.db_name)
        self.assertIsNotNone(db)

        self.storage = IcxStorage(db)

        context = IconScoreContext(IconScoreContextType.DIRECT)
        context.tx_batch = TransactionBatch()
        mock_block: 'Mock' = Mock(spec=Block)
        mock_block.attach_mock(Mock(return_value=0), 'height')
        context.block = mock_block
        context.block_batch = BlockBatch()
        self.context = context
Пример #5
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
Пример #6
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
Пример #7
0
    def setUp(self):
        empty_address = MalformedAddress.from_string('')
        short_address_without_hx = MalformedAddress.from_string('12341234')
        short_address = MalformedAddress.from_string('hx1234512345')
        long_address_without_hx = MalformedAddress.from_string(
            'cf85fac2d0b507a2db9ce9526e6d01476f16a2d269f51636f9c4b2d512017faf')
        long_address = MalformedAddress.from_string(
            'hxdf85fac2d0b507a2db9ce9526e6d01476f16a2d269f51636f9c4b2d512017faf'
        )
        self.malformed_addresses = [
            empty_address, short_address_without_hx, short_address,
            long_address_without_hx, long_address
        ]

        self.db_name = 'engine.db'
        db = ContextDatabase.from_path(self.db_name)
        self.engine = IcxEngine()
        self._from = Address.from_string('hx' + 'a' * 40)
        self.to = Address.from_string('hx' + 'b' * 40)
        self.genesis_address = Address.from_string('hx' + '0' * 40)
        self.fee_treasury_address = Address.from_string('hx' + '1' * 40)
        self.total_supply = 10**20  # 100 icx

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

        self.storage = IcxStorage(db)
        self.engine.open()

        accounts: list = [{
            'address': self.genesis_address,
            'balance': self.total_supply
        }, {
            'address': self.fee_treasury_address,
            'balance': 0
        }]
        self.context.storage = ContextStorage(deploy=None,
                                              fee=None,
                                              icx=self.storage,
                                              iiss=None,
                                              prep=None,
                                              issue=None,
                                              rc=None,
                                              meta=None)
        self.storage.put_genesis_accounts(self.context, accounts)
Пример #8
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()
Пример #9
0
    def setUp(self):

        self.db_name = 'engine.db'
        db = ContextDatabase.from_path(self.db_name)
        self.engine = IcxEngine()
        self.storage = IcxStorage(db)
        self.from_ = Address.from_string('hx' + 'a' * 40)
        self.to = Address.from_string('hx' + 'b' * 40)
        self.genesis_address = Address.from_string('hx' + '0' * 40)
        self.fee_treasury_address = Address.from_string('hx' + '1' * 40)
        self.total_supply = 10**20  # 100 icx
        self.fee_treasury_address_icx_amount = 0

        self.context = IconScoreContext(IconScoreContextType.DIRECT)

        block = Mock(spec=Block)
        block.attach_mock(Mock(return_value=0), 'height')
        self.context.block = block

        self.engine.open()

        accounts: list = [{
            'address': self.genesis_address,
            'balance': self.total_supply
        }, {
            'address': self.fee_treasury_address,
            'balance': 0
        }]
        self.context.storage = ContextStorage(deploy=None,
                                              fee=None,
                                              icx=self.storage,
                                              iiss=None,
                                              prep=None,
                                              issue=None,
                                              rc=None,
                                              meta=None)
        self.storage.put_genesis_accounts(self.context, accounts)
Пример #10
0
    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.sample_token_address = create_address(AddressPrefix.CONTRACT)

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

        self._one_icx = 1 * 10 ** 18
        self._one_icx_to_token = 1
Пример #11
0
def context_with_icx_storage(context_db, genesis_address,
                             fee_treasury_address):
    accounts: list = [{
        'address': genesis_address,
        'balance': TOTAL_SUPPLY
    }, {
        'address': fee_treasury_address,
        'balance': 0
    }]
    storage = IcxStorage(context_db)
    context = IconScoreContext(IconScoreContextType.DIRECT)
    block = Mock(spec=Block)
    block.attach_mock(Mock(return_value=0), 'height')
    context.block = block
    context.storage = ContextStorage(icx=storage)
    storage.put_genesis_accounts(context, accounts)
    yield context
    storage.close(context)
Пример #12
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)
Пример #13
0
class TestScoreDeployEngine(unittest.TestCase):
    _ROOT_SCORE_PATH = 'tests/score'
    _TEST_DB_PATH = 'tests/test_db'

    @classmethod
    def setUpClass(cls):
        db_path = os.path.join(PROJECT_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 = 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()

    def tearDown(self):
        try:
            self._context = IconScoreContext(IconScoreContextType.DIRECT)
            self._icx_storage.close(self._context)
        finally:
            remove_path = os.path.join(PROJECT_ROOT_PATH,
                                       self._ROOT_SCORE_PATH)
            rmtree(remove_path)
            remove_path = os.path.join(PROJECT_ROOT_PATH, self._TEST_DB_PATH)
            rmtree(remove_path)

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

        self._context.tx = Transaction(create_tx_hash(), origin=self.addr1)
        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.__step_counter_factory = IconScoreStepCounterFactory()
        self._context.revision = 0
        self._context.new_icon_score_mapper = {}
        self._step_counter: IconScoreStepCounter = \
            self.__step_counter_factory.create(IconScoreContextType.INVOKE)
        self._context.step_counter = self._step_counter
        self._context.icx.open(self._icx_storage)
        self._context.event_logs = Mock(spec=list)
        self._context.traces = Mock(spec=list)

    def _invoke_setUp(self, _is_audit_needed: bool):
        self._score_deploy_engine._is_audit_needed = Mock(
            return_value=_is_audit_needed)
        self._score_deploy_engine.deploy = Mock()
        self._deploy_storage.put_deploy_info_and_tx_params = Mock()

    def _is_audit_needed_setUp(self, revision: int, get_owner,
                               is_service_flag_on: bool):
        self._context.revision = revision
        IconScoreContextUtil.is_service_flag_on.return_value = is_service_flag_on
        IconScoreContextUtil.get_owner.return_value = get_owner

    def _deploy_setUp(self, get_deploy_tx_params=None):
        self._score_deploy_engine._score_deploy = Mock()
        self._deploy_storage.update_score_info = Mock()
        self._deploy_storage.get_deploy_tx_params = Mock(
            return_value=get_deploy_tx_params)

    def _score_deploy_setUp(self, legacy_tbears_mode: bool = False):
        self._score_deploy_engine._on_deploy = Mock()
        self._context.legacy_tbears_mode = legacy_tbears_mode

    # case when icon_score_address is in (None, ZERO_ADDRESS)
    @patch_several(VALIDATE_SCORE_BLACKLIST_PATCHER,
                   IS_SERVICE_FLAG_ON_PATCHER, VALIDATE_DEPLOYER)
    def test_invoke_case1(self):
        self._invoke_setUp(True)

        with self.assertRaises(AssertionError):
            self._score_deploy_engine.invoke(self._context,
                                             GOVERNANCE_SCORE_ADDRESS,
                                             ZERO_SCORE_ADDRESS, {})
        IconScoreContextUtil.validate_score_blacklist.assert_not_called()
        IconScoreContextUtil.validate_deployer.assert_not_called()
        self._deploy_storage.put_deploy_info_and_tx_params.assert_not_called()
        self._score_deploy_engine._is_audit_needed.assert_not_called()
        self._score_deploy_engine.deploy.assert_not_called()

        with self.assertRaises(AssertionError):
            self._score_deploy_engine.invoke(self._context,
                                             GOVERNANCE_SCORE_ADDRESS, None,
                                             {})
        IconScoreContextUtil.validate_score_blacklist.assert_not_called()
        IconScoreContextUtil.validate_deployer.assert_not_called()
        self._deploy_storage.put_deploy_info_and_tx_params.assert_not_called()
        self._score_deploy_engine._is_audit_needed.assert_not_called()
        self._score_deploy_engine.deploy.assert_not_called()

    # case when deployer_white_list flag on, ignore audit
    @patch_several(IS_SERVICE_FLAG_ON_PATCHER, VALIDATE_DEPLOYER)
    def test_invoke_case2(self):
        self._invoke_setUp(False)

        self._score_deploy_engine.invoke(self._context, ZERO_SCORE_ADDRESS,
                                         GOVERNANCE_SCORE_ADDRESS, {})

        IconScoreContextUtil.validate_deployer.assert_called_with(
            self._context, self._context.tx.origin)
        self._deploy_storage.put_deploy_info_and_tx_params.\
            assert_called_with(self._context, GOVERNANCE_SCORE_ADDRESS, DeployType.INSTALL, self._context.tx.origin,
                               self._context.tx.hash, {})
        self._score_deploy_engine.deploy.assert_called_with(
            self._context, self._context.tx.hash)

    # case when deployer_white_list flag on, audit
    @patch_several(IS_SERVICE_FLAG_ON_PATCHER, VALIDATE_DEPLOYER)
    def test_invoke_case3(self):
        self._invoke_setUp(True)

        self._score_deploy_engine.invoke(self._context, ZERO_SCORE_ADDRESS,
                                         GOVERNANCE_SCORE_ADDRESS, {})

        IconScoreContextUtil.validate_deployer.assert_called_with(
            self._context, self._context.tx.origin)
        self._deploy_storage.put_deploy_info_and_tx_params.\
            assert_called_with(self._context, GOVERNANCE_SCORE_ADDRESS, DeployType.INSTALL, self._context.tx.origin,
                               self._context.tx.hash, {})
        self._score_deploy_engine.deploy.assert_not_called()

    # case when deployer_white_list flag off, audit
    @patch_several(IS_SERVICE_FLAG_ON_PATCHER, VALIDATE_DEPLOYER)
    def test_invoke_case4(self):
        self._invoke_setUp(False)

        self._score_deploy_engine.invoke(self._context, ZERO_SCORE_ADDRESS,
                                         GOVERNANCE_SCORE_ADDRESS, {})

        IconScoreContextUtil.validate_deployer.assert_called_with(
            self._context, self._context.tx.origin)
        self._deploy_storage.put_deploy_info_and_tx_params. \
            assert_called_with(self._context, GOVERNANCE_SCORE_ADDRESS, DeployType.INSTALL, self._context.tx.origin,
                               self._context.tx.hash, {})
        self._score_deploy_engine.deploy.assert_called_with(
            self._context, self._context.tx.hash)

    # case when deployer_white_list flag off, audit on
    @patch_several(IS_SERVICE_FLAG_ON_PATCHER, VALIDATE_DEPLOYER)
    def test_invoke_case5(self):
        self._invoke_setUp(True)

        self._score_deploy_engine.invoke(self._context, ZERO_SCORE_ADDRESS,
                                         GOVERNANCE_SCORE_ADDRESS, {})

        IconScoreContextUtil.validate_deployer.assert_called_with(
            self._context, self._context.tx.origin)
        self._score_deploy_engine.deploy.assert_not_called()

    # case when revision0, owner, audit false
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case1(self):
        self._is_audit_needed_setUp(0, self.addr1, False)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertFalse(result)

    # case when revision0, owner x, audit false
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case2(self):
        self._is_audit_needed_setUp(0, self.addr2, False)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertFalse(result)

    # case when revision0, owner, audit true
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case3(self):
        self._is_audit_needed_setUp(0, self.addr1, True)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertTrue(result)

    # case when revision0, owner x, audit true
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case4(self):
        self._is_audit_needed_setUp(0, self.addr2, True)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertTrue(result)

    # case when revision2, transaction requested by owner, audit true, system_score
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case5(self):
        self._is_audit_needed_setUp(2, self.addr1, True)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertFalse(result)

    # case when revision2, transaction requested by stranger, audit true, system_score
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case6(self):
        self._is_audit_needed_setUp(2, self.addr2, True)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertTrue(result)

    # case when revision2, transaction requested by owner, audit false, system_score
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case7(self):
        self._is_audit_needed_setUp(2, self.addr1, False)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertFalse(result)

    # case when revision2, transaction requested by stranger, audit false, system_score
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case8(self):
        self._is_audit_needed_setUp(2, self.addr1, False)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertFalse(result)

    # case when revision2, transaction requested by owner, audit true, normal_score
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case9(self):
        self._is_audit_needed_setUp(2, self.addr1, True)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, self.score_address)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, self.score_address)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertTrue(result)

    # case when revision2, transaction requested by stranger, audit true, normal_score
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case10(self):
        self._is_audit_needed_setUp(2, self.addr2, True)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, self.score_address)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, self.score_address)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertTrue(result)

    # case when revision2, transaction requested by owner, audit false, normal_score
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case11(self):
        self._is_audit_needed_setUp(2, self.addr1, False)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, self.score_address)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, self.score_address)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertFalse(result)

    # case when revision2, transaction requested by stranger, audit false, normal_score
    @patch_several(GET_OWNER_PATCHER, IS_SERVICE_FLAG_ON_PATCHER)
    def test_is_audit_needed_case12(self):
        self._is_audit_needed_setUp(2, self.addr1, False)

        result = self._score_deploy_engine._is_audit_needed(
            self._context, self.score_address)
        IconScoreContextUtil.get_owner.assert_called_with(
            self._context, self.score_address)
        IconScoreContextUtil.is_service_flag_on.assert_called_with(
            self._context, IconServiceFlag.AUDIT)
        self.assertFalse(result)

    # case when tx_param is None
    @patch_several(GET_DEPLOY_TX_PARAMS_PATCHER)
    def test_deploy_case1(self):
        self._deploy_setUp()

        with self.assertRaises(InvalidParamsException) as e:
            self._score_deploy_engine.deploy(self._context,
                                             self._context.tx.hash)

        self._deploy_storage.get_deploy_tx_params.assert_called_with(
            self._context, self._context.tx.hash)
        self.assertEqual(e.exception.code, ExceptionCode.INVALID_PARAMETER)
        self._score_deploy_engine._score_deploy.assert_not_called()
        IconScoreContext.storage.deploy.update_score_info.assert_not_called()

    # case when tx_param is not None
    @patch_several(GET_DEPLOY_TX_PARAMS_PATCHER)
    def test_deploy_case2(self):
        tx_params = Mock(spec=IconScoreDeployTXParams)
        tx_params.configure_mock(score_address=GOVERNANCE_SCORE_ADDRESS)
        self._deploy_setUp(tx_params)

        self._score_deploy_engine.deploy(self._context, self._context.tx.hash)

        self._score_deploy_engine._score_deploy.assert_called_with(
            self._context, tx_params)
        IconScoreContext.storage.deploy.\
            update_score_info.assert_called_with(self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash)

    # test for tbears mode, legacy_tbears_mode is True
    def test_score_deploy_case1(self):
        self._score_deploy_setUp(True)
        tx_params = Mock(spec=IconScoreDeployTXParams)
        tx_params.configure_mock(deploy_data={
            'contentType': 'application/tbears',
            'content': '0x1234'
        })

        self._score_deploy_engine._score_deploy(self._context, tx_params)

        self._score_deploy_engine._on_deploy.assert_called_with(
            self._context, tx_params)

    # test for tbears mode, and legacy_tbears_mode is False
    def test_score_deploy_case2(self):
        tx_params = Mock(spec=IconScoreDeployTXParams)
        tx_params.configure_mock(deploy_data={
            'contentType': 'application/tbears',
            'content': '0x1234'
        })
        self._score_deploy_setUp()

        with self.assertRaises(InvalidParamsException) as e:
            self._score_deploy_engine._score_deploy(self._context, tx_params)
        self.assertEqual(e.exception.code, ExceptionCode.INVALID_PARAMETER)
        self.assertEqual(e.exception.message,
                         f"Invalid contentType: application/tbears")
        self._score_deploy_engine._on_deploy.assert_not_called()

    # test for zip mode
    def test_score_deploy_case3(self):
        tx_params = Mock(spec=IconScoreDeployTXParams)
        tx_params.configure_mock(deploy_data={
            'contentType': 'application/zip',
            'content': '0x1234'
        })
        self._score_deploy_setUp()

        self._score_deploy_engine._score_deploy(self._context, tx_params)

        self._score_deploy_engine._on_deploy.assert_called_with(
            self._context, tx_params)

    # test for wrong contentType
    def test_score_deploy_case4(self):
        self._score_deploy_setUp()
        tx_params = Mock(spec=IconScoreDeployTXParams)
        tx_params.configure_mock(deploy_data={
            'contentType': 'wrong/content',
            'content': '0x1234'
        })

        with self.assertRaises(InvalidParamsException) as e:
            self._score_deploy_engine._score_deploy(self._context, tx_params)
        self.assertEqual(e.exception.code, ExceptionCode.INVALID_PARAMETER)
        self.assertEqual(e.exception.message,
                         f'Invalid contentType: wrong/content')
        self._score_deploy_engine._on_deploy.assert_not_called()

    # Case when deploy_info is not None, zip, revision0, score validator flag False, SCORE is not None
    @patch_several(VALIDATE_PACKAGE_PATCHER)
    def test_on_deploy(self):
        mock_score = MockScore()
        mock_score.owner = self.addr1
        deploy_params = {"a": 1}
        deploy_data = {"params": deploy_params}
        deploy_info = Mock(spec=IconScoreDeployInfo)
        deploy_type = 'deploy_type'
        next_tx_hash = b'\00\01' * 16
        deploy_info.configure_mock(next_tx_hash=next_tx_hash)
        tx_params = Mock(spec=IconScoreDeployTXParams)
        tx_params.configure_mock(score_address=self.score_address,
                                 deploy_data=deploy_data,
                                 deploy_type=deploy_type,
                                 params=deploy_params)

        backup_msg, backup_tx = self._context.msg, self._context.tx

        self._score_deploy_engine._write_score_to_filesystem = Mock()
        score_info = Mock()
        score_info.configure_mock(get_score=Mock(return_value=mock_score))
        self._score_deploy_engine._create_score_info = Mock(
            return_value=score_info)
        self._deploy_storage.get_deploy_info = Mock(return_value=deploy_info)
        self._score_deploy_engine._initialize_score = Mock()

        self._score_deploy_engine._on_deploy(self._context, tx_params)

        self._deploy_storage.get_deploy_info.assert_called_with(
            self._context, self.score_address)
        self._score_deploy_engine._write_score_to_filesystem.assert_called_with(
            self._context, self.score_address, next_tx_hash, deploy_data)
        IconScoreContextUtil.validate_score_package.assert_called_with(
            self._context, self.score_address, next_tx_hash)
        self._score_deploy_engine._create_score_info.assert_called_with(
            self._context, self.score_address, next_tx_hash)
        score_info.get_score.assert_called_with(self._context.revision)
        self._score_deploy_engine._initialize_score.assert_called_with(
            deploy_type, mock_score, deploy_params)

        self.assertEqual(self._context.msg, backup_msg)
        self.assertEqual(self._context.tx, backup_tx)

    # tbears mode
    def test_write_score_to_filesystem_case1(self):
        content = '0x1234'
        deploy_data = {'contentType': 'application/tbears', 'content': content}
        self._score_deploy_engine._write_score_to_score_deploy_path_on_tbears_mode = Mock(
        )

        self._score_deploy_engine._write_score_to_filesystem(
            self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash,
            deploy_data)
        self._score_deploy_engine._write_score_to_score_deploy_path_on_tbears_mode.\
            assert_called_with(self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash, content)

    # normal mode
    def test_write_score_to_filesystem_case2(self):
        content = '0x1234'
        deploy_data = {'contentType': 'application/zip', 'content': content}
        self._score_deploy_engine._write_score_to_score_deploy_path = Mock()

        self._score_deploy_engine._write_score_to_filesystem(
            self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash,
            deploy_data)
        self._score_deploy_engine._write_score_to_score_deploy_path. \
            assert_called_with(self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash, content)

    # case when current_score_info is None
    @patch_several(GET_SCORE_INFO_PATCHER, CREATE_SCORE_INFO_PATCHER)
    def test_create_score_info_case1(self):
        IconScoreContextUtil.get_score_info.return_value = None

        self._score_deploy_engine._create_score_info(self._context,
                                                     GOVERNANCE_SCORE_ADDRESS,
                                                     self._context.tx.hash)
        IconScoreContextUtil.create_score_info.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash,
            None)

    # case when current_score_info is not None
    @patch_several(GET_SCORE_INFO_PATCHER, CREATE_SCORE_INFO_PATCHER)
    def test_create_score_info_case2(self):
        db = 'db'
        score_info_mock = Mock()
        score_info_mock.configure_mock(score_db=db)
        IconScoreContextUtil.get_score_info.return_value = score_info_mock

        self._score_deploy_engine._create_score_info(self._context,
                                                     GOVERNANCE_SCORE_ADDRESS,
                                                     self._context.tx.hash)
        IconScoreContextUtil.create_score_info.assert_called_with(
            self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash, db)

    @patch_several(GET_SCORE_PATH_PATCHER, GET_SCORE_DEPLOY_PATH_PATCHER,
                   SYMLINK_PATCHER, MAKE_DIR_PATCHER)
    def test_write_score_to_score_deploy_path_on_tbears_mode(self):
        score_deploy_path = 'score_deploy_path'
        isde.get_score_deploy_path.return_value = score_deploy_path
        score_path = 'score_path'
        isde.get_score_path.return_value = score_path

        self._score_deploy_engine._write_score_to_score_deploy_path_on_tbears_mode\
            (self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash, None)

        isde.get_score_path.assert_called_with(self._context.score_root_path,
                                               GOVERNANCE_SCORE_ADDRESS)
        os.makedirs.assert_called_with(score_path, exist_ok=True)
        os.symlink.assert_called_with(None,
                                      score_deploy_path,
                                      target_is_directory=True)

    # case when revision3
    @patch_several(DEPLOY_PATCHER, REMOVE_PATH_PATCHER,
                   GET_SCORE_DEPLOY_PATH_PATCHER, OS_PATH_JOIN_PATCHER)
    def test_write_score_to_score_deploy_path_case1(self):
        self._context.revision = 3
        score_path = 'score_path'
        score_deploy_path = 'score_deploy_path'
        isde.get_score_deploy_path.return_value = 'score_deploy_path'
        os.path.join.return_value = score_path
        self._score_deploy_engine._write_score_to_score_deploy_path(
            self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash,
            None)

        isde.get_score_deploy_path.assert_called_with(
            self._context.score_root_path, GOVERNANCE_SCORE_ADDRESS,
            self._context.tx.hash)
        os.path.join.assert_called_with(
            self._context.score_root_path,
            GOVERNANCE_SCORE_ADDRESS.to_bytes().hex(),
            f"0x{self._context.tx.hash.hex()}")
        isde.remove_path.assert_called_with(score_path)
        IconScoreDeployer.deploy.assert_called_with(score_deploy_path, None, 3)

    # case when revision2
    @patch_several(DEPLOY_PATCHER, REMOVE_PATH_PATCHER,
                   GET_SCORE_DEPLOY_PATH_PATCHER, OS_PATH_JOIN_PATCHER)
    def test_write_score_to_score_deploy_path_case1(self):
        self._context.revision = 2
        score_path = 'score_path'
        score_deploy_path = 'score_deploy_path'
        isde.get_score_deploy_path.return_value = 'score_deploy_path'
        os.path.join.return_value = score_path
        self._score_deploy_engine._write_score_to_score_deploy_path(
            self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash,
            None)

        isde.get_score_deploy_path.assert_called_with(
            self._context.score_root_path, GOVERNANCE_SCORE_ADDRESS,
            self._context.tx.hash)
        os.path.join.assert_not_called()
        isde.remove_path.assert_not_called()
        IconScoreDeployer.deploy.assert_called_with(score_deploy_path, None, 2)

    # case when revision0
    @patch_several(DEPLOY_PATCHER, DEPLOY_LEGACY_PATCHER, REMOVE_PATH_PATCHER,
                   GET_SCORE_DEPLOY_PATH_PATCHER, OS_PATH_JOIN_PATCHER)
    def test_write_score_to_score_deploy_path_case1(self):
        score_path = 'score_path'
        score_deploy_path = 'score_deploy_path'
        isde.get_score_deploy_path.return_value = 'score_deploy_path'
        os.path.join.return_value = score_path
        self._score_deploy_engine._write_score_to_score_deploy_path(
            self._context, GOVERNANCE_SCORE_ADDRESS, self._context.tx.hash,
            None)

        isde.get_score_deploy_path.assert_called_with(
            self._context.score_root_path, GOVERNANCE_SCORE_ADDRESS,
            self._context.tx.hash)
        os.path.join.assert_not_called()
        isde.remove_path.assert_not_called()
        IconScoreDeployer.deploy.assert_not_called()
        IconScoreDeployer.deploy_legacy.assert_called_with(
            score_deploy_path, None)

    # case on_install
    @patch_several(MAKE_ANNOTATIONS_FROM_METHOD_PATCHER,
                   CONVERT_DATA_PARAMS_PATCHER)
    def test_initialize_score_case1(self):
        mock_score = MockScore()
        on_install = Mock()
        mock_score.on_install = on_install
        deploy_type = DeployType.INSTALL
        params = {"param1": '0x1', "param2": "string"}

        self._score_deploy_engine._initialize_score(deploy_type, mock_score,
                                                    params)
        TypeConverter.make_annotations_from_method.assert_called_with(
            on_install)
        TypeConverter.convert_data_params.assert_called_with(
            'annotations', params)
        on_install.assert_called_with(**params)

    # case on_update
    @patch_several(MAKE_ANNOTATIONS_FROM_METHOD_PATCHER,
                   CONVERT_DATA_PARAMS_PATCHER)
    def test_initialize_score_case2(self):
        mock_score = MockScore()
        on_update = Mock()
        mock_score.on_update = on_update
        deploy_type = DeployType.UPDATE
        params = {"param1": '0x1', "param2": "string"}

        self._score_deploy_engine._initialize_score(deploy_type, mock_score,
                                                    params)
        TypeConverter.make_annotations_from_method.assert_called_with(
            on_update)
        TypeConverter.convert_data_params.assert_called_with(
            'annotations', params)
        on_update.assert_called_with(**params)

    # case strange method name
    @patch_several(MAKE_ANNOTATIONS_FROM_METHOD_PATCHER,
                   CONVERT_DATA_PARAMS_PATCHER)
    def test_initialize_score_case3(self):
        mock_score = MockScore()
        on_strange = Mock()
        mock_score.on_install = on_strange
        deploy_type = "strange"
        params = {"param1": '0x1', "param2": "string"}

        with self.assertRaises(InvalidParamsException) as e:
            self._score_deploy_engine._initialize_score(
                deploy_type, mock_score, params)

        self.assertEqual(e.exception.code, ExceptionCode.INVALID_PARAMETER)
        self.assertEqual(e.exception.message,
                         f"Invalid deployType: {deploy_type}")
        TypeConverter.make_annotations_from_method.assert_not_called()
        TypeConverter.convert_data_params.assert_not_called()
        on_strange.assert_not_called()

    @staticmethod
    def __ensure_dir(dir_path):
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)
Пример #14
0
class TestIcxEngine(unittest.TestCase, ContextContainer):
    def setUp(self):

        self.db_name = 'engine.db'
        db = ContextDatabase.from_path(self.db_name)
        self.engine = IcxEngine()
        self.storage = IcxStorage(db)
        self.from_ = Address.from_string('hx' + 'a' * 40)
        self.to = Address.from_string('hx' + 'b' * 40)
        self.genesis_address = Address.from_string('hx' + '0' * 40)
        self.fee_treasury_address = Address.from_string('hx' + '1' * 40)
        self.total_supply = 10**20  # 100 icx
        self.fee_treasury_address_icx_amount = 0

        self.context = IconScoreContext(IconScoreContextType.DIRECT)

        block = Mock(spec=Block)
        block.attach_mock(Mock(return_value=0), 'height')
        self.context.block = block

        self.engine.open()

        accounts: list = [{
            'address': self.genesis_address,
            'balance': self.total_supply
        }, {
            'address': self.fee_treasury_address,
            'balance': 0
        }]
        self.context.storage = ContextStorage(deploy=None,
                                              fee=None,
                                              icx=self.storage,
                                              iiss=None,
                                              prep=None,
                                              issue=None,
                                              rc=None,
                                              meta=None)
        self.storage.put_genesis_accounts(self.context, accounts)

    def tearDown(self):
        self._clear_context()
        self.storage.close(self.context)

        # Remove a state db for test
        shutil.rmtree(self.db_name)

    def test_get_balance(self):
        address = Address.from_string(
            'hx0123456789012345678901234567890123456789')
        balance = self.engine.get_balance(self.context, address)

        self.assertEqual(0, balance)

    def test_get_charge_fee(self):
        pass

    def test_get_account(self):
        pass

    def test_transfer(self):
        context = self.context
        amount = 10**18  # 1 icx
        _from = self.genesis_address

        self.engine.transfer(context=context,
                             from_=_from,
                             to=self.to,
                             amount=amount)

        from_balance = self.engine.get_balance(context, self.genesis_address)
        fee_treasury_balance = self.engine.get_balance(
            context, self.fee_treasury_address)
        to_balance = self.engine.get_balance(context, self.to)

        self.assertEqual(amount, to_balance)
        self.assertEqual(0, fee_treasury_balance)
        self.assertEqual(self.total_supply,
                         from_balance + to_balance + fee_treasury_balance)
Пример #15
0
class TestIcxEngineForMalformedAddress(unittest.TestCase, ContextContainer):
    def setUp(self):
        empty_address = MalformedAddress.from_string('')
        short_address_without_hx = MalformedAddress.from_string('12341234')
        short_address = MalformedAddress.from_string('hx1234512345')
        long_address_without_hx = MalformedAddress.from_string(
            'cf85fac2d0b507a2db9ce9526e6d01476f16a2d269f51636f9c4b2d512017faf')
        long_address = MalformedAddress.from_string(
            'hxdf85fac2d0b507a2db9ce9526e6d01476f16a2d269f51636f9c4b2d512017faf'
        )
        self.malformed_addresses = [
            empty_address, short_address_without_hx, short_address,
            long_address_without_hx, long_address
        ]

        self.db_name = 'engine.db'
        db = ContextDatabase.from_path(self.db_name)
        self.engine = IcxEngine()
        self._from = Address.from_string('hx' + 'a' * 40)
        self.to = Address.from_string('hx' + 'b' * 40)
        self.genesis_address = Address.from_string('hx' + '0' * 40)
        self.fee_treasury_address = Address.from_string('hx' + '1' * 40)
        self.total_supply = 10**20  # 100 icx

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

        self.storage = IcxStorage(db)
        self.engine.open()

        accounts: list = [{
            'address': self.genesis_address,
            'balance': self.total_supply
        }, {
            'address': self.fee_treasury_address,
            'balance': 0
        }]
        self.context.storage = ContextStorage(deploy=None,
                                              fee=None,
                                              icx=self.storage,
                                              iiss=None,
                                              prep=None,
                                              issue=None,
                                              rc=None,
                                              meta=None)
        self.storage.put_genesis_accounts(self.context, accounts)

    def tearDown(self):
        self.storage.close(self.context)
        self.engine = None

        # Remove a state db for test
        shutil.rmtree(self.db_name)

    def test_get_balance(self):
        for address in self.malformed_addresses:
            balance = self.engine.get_balance(self.context, address)
            self.assertEqual(0, balance)

    def test_transfer(self):
        context = self.context
        amount = 10**18  # 1 icx
        from_ = self.genesis_address

        for i, to in enumerate(self.malformed_addresses):
            self.engine.transfer(context=context,
                                 from_=from_,
                                 to=to,
                                 amount=amount)

            from_balance = self.engine.get_balance(context, from_)
            fee_treasury_balance = self.engine.get_balance(
                context, self.fee_treasury_address)
            to_balance = self.engine.get_balance(context, to)

            self.assertEqual(amount, to_balance)
            self.assertEqual(0, fee_treasury_balance)
            self.assertEqual(
                from_balance + fee_treasury_balance + amount * (i + 1),
                self.total_supply)
Пример #16
0
class TestIcxStorage(unittest.TestCase):
    def setUp(self):
        self.db_name = 'icx.db'

        db = ContextDatabase.from_path(self.db_name)
        self.assertIsNotNone(db)

        self.storage = IcxStorage(db)

        context = IconScoreContext(IconScoreContextType.DIRECT)
        context.tx_batch = TransactionBatch()
        mock_block: 'Mock' = Mock(spec=Block)
        mock_block.attach_mock(Mock(return_value=0), 'height')
        context.block = mock_block
        context.block_batch = BlockBatch()
        self.context = context

    def tearDown(self):
        context = self.context
        self.storage.close(context)

        shutil.rmtree(self.db_name)

    def test_get_put_account(self):
        context = self.context

        address = create_address(AddressPrefix.EOA)
        coin_part: 'CoinPart' = CoinPart()
        account: 'Account' = Account(address,
                                     0,
                                     Revision.IISS.value,
                                     coin_part=coin_part)
        account.deposit(10**19)

        self.storage.put_account(context, account)

        address: 'Address' = create_address(AddressPrefix.EOA)
        coin_part: 'CoinPart' = CoinPart()
        account: 'Account' = Account(address,
                                     self.context.block.height,
                                     Revision.IISS.value,
                                     coin_part=coin_part)

        account.deposit(10**19)

        self.storage.put_account(self.context, account)

        account2 = self.storage.get_account(self.context, account.address)
        self.assertEqual(account, account2)

    def test_get_put_text(self):
        context = self.context
        key_name = 'test_genesis'
        expected_text = json.dumps({
            'version':
            0,
            'address':
            str(create_address(AddressPrefix.EOA))
        })

        self.storage.put_text(context, key_name, expected_text)

        actual_stored_text = self.storage.get_text(context, key_name)
        self.assertEqual(expected_text, actual_stored_text)

    def test_get_put_total_supply(self):
        context = self.context
        current_total_supply = self.storage.get_total_supply(context)
        self.assertEqual(0, current_total_supply)

        putting_total_supply_amount = 1000
        self.storage.put_total_supply(context, putting_total_supply_amount)
        actual_stored_total_supply = self.storage.get_total_supply(context)
        self.assertEqual(putting_total_supply_amount,
                         actual_stored_total_supply)