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 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
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)
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 _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
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
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 setUp(self): context = IconScoreContext(IconScoreContextType.DIRECT) block = Mock(spec=Block) block.attach_mock(Mock(return_value=0), 'height') context.block = block self._sender = Address.from_data(AddressPrefix.EOA, os.urandom(20)) self._score_address = Address.from_data(AddressPrefix.CONTRACT, os.urandom(20)) context_db = create_context_db() self.deploy_storage = DeployStorage(context_db) deploy_info = IconScoreDeployInfo(self._score_address, DeployState.ACTIVE, self._sender, os.urandom(32), os.urandom(32)) self.icx_storage = IcxStorage(context_db) self._icx_engine = IcxEngine() self.fee_storage = FeeStorage(context_db) patch_fee_storage(self.fee_storage) self.deploy_storage.put_deploy_info(context, deploy_info) context.storage = ContextStorage(deploy=self.deploy_storage, fee=self.fee_storage, icx=self.icx_storage, iiss=None, prep=None, issue=None, rc=None, meta=None) context.engine = ContextEngine(deploy=None, fee=None, icx=self._icx_engine, iiss=None, prep=None, issue=None) self._icx_engine.open(self.icx_storage) self.icx_storage._put_genesis_data_account(context, CoinPartType.GENERAL, self._sender, 100000000 * 10 ** 18) self.icx_storage._put_genesis_data_account(context, CoinPartType.TREASURY, Address.from_data(AddressPrefix.EOA, os.urandom(20)), 0) self._engine = FeeEngine()
def setUp(self): 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 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
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)
class TestFeeEngine(unittest.TestCase): def setUp(self): context = IconScoreContext(IconScoreContextType.DIRECT) block = Mock(spec=Block) block.attach_mock(Mock(return_value=0), 'height') context.block = block self._sender = Address.from_data(AddressPrefix.EOA, os.urandom(20)) self._score_address = Address.from_data(AddressPrefix.CONTRACT, os.urandom(20)) context_db = create_context_db() self.deploy_storage = DeployStorage(context_db) deploy_info = IconScoreDeployInfo(self._score_address, DeployState.ACTIVE, self._sender, os.urandom(32), os.urandom(32)) self.icx_storage = IcxStorage(context_db) self._icx_engine = IcxEngine() self.fee_storage = FeeStorage(context_db) patch_fee_storage(self.fee_storage) self.deploy_storage.put_deploy_info(context, deploy_info) context.storage = ContextStorage(deploy=self.deploy_storage, fee=self.fee_storage, icx=self.icx_storage, iiss=None, prep=None, issue=None, rc=None, meta=None) context.engine = ContextEngine(deploy=None, fee=None, icx=self._icx_engine, iiss=None, prep=None, issue=None) self._icx_engine.open(self.icx_storage) self.icx_storage._put_genesis_data_account(context, CoinPartType.GENERAL, self._sender, 100000000 * 10 ** 18) self.icx_storage._put_genesis_data_account(context, CoinPartType.TREASURY, Address.from_data(AddressPrefix.EOA, os.urandom(20)), 0) self._engine = FeeEngine() def tearDown(self): ContextContainer._clear_context() clear_inner_task() VirtualStepCalculator.calculate_virtual_step = calculate_virtual_step def get_context(self): context = IconScoreContext(IconScoreContextType.INVOKE) context.step_counter = Mock(spec=IconScoreStepCounter) context.step_counter.step_price = 10 ** 10 context.tx = Mock(spec=Transaction) context.tx.to = self._score_address block = Mock(spec=Block) block.attach_mock(Mock(return_value=0), 'height') context.block = block context.storage = ContextStorage(deploy=self.deploy_storage,fee=self.fee_storage, icx=self.icx_storage, iiss=None, prep=None, issue=None, rc=None, meta=None) context.engine = ContextEngine(deploy=None, fee=None, icx=self._icx_engine, iiss=None, prep=None, issue=None) return context def _deposit_bulk(self, count): self.context = self.get_context() self.block_height = 0 input_params = [] for i in range(count): tx_hash = os.urandom(32) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height = randrange(100, 10000) term = get_rand_term() before_sender_balance = self._icx_engine.get_balance(self.context, self._sender) self._engine.add_deposit( self.context, tx_hash, self._sender, self._score_address, amount, block_height, term) after_sender_balance = self._icx_engine.get_balance(self.context, self._sender) self.assertEqual(amount, before_sender_balance - after_sender_balance) input_params.append((tx_hash, amount, block_height, term)) return input_params def test_deposit_fee(self): context = self.get_context() block_height = 0 size = randrange(10, 100) input_param = self._deposit_bulk(size) deposit_info = self._engine.get_deposit_info(context, self._score_address, block_height) self.assertEqual(size, len(deposit_info.deposits)) for i in range(size): tx_hash, amount, block_height, term = input_param[i] deposit = deposit_info.deposits[i] self.assertEqual(tx_hash, deposit.id) self.assertEqual(self._sender, deposit.sender) self.assertEqual(self._score_address, deposit.score_address) self.assertEqual(amount, deposit.deposit_amount) self.assertEqual(block_height, deposit.created) self.assertEqual(block_height + term, deposit.expires) def test_deposit_append_and_delete(self): size = randrange(10, 100) deposit_list = self._deposit_bulk(size) for i in range(size): index = randrange(0, size) size -= 1 withdrawal_deposit_id = deposit_list.pop(index)[0] self._engine.withdraw_deposit(self.context, self._sender, withdrawal_deposit_id, 1) deposit_info = self._engine.get_deposit_info(self.context, self._score_address, 1) for j in range(size): deposit = deposit_info.deposits[j] self.assertEqual(deposit.id, deposit_list[j][0]) self.assertEqual(self._sender, deposit.sender) self.assertEqual(self._score_address, deposit.score_address) self.assertEqual(deposit.deposit_amount, deposit_list[j][1]) self.assertEqual(deposit.created, deposit_list[j][2]) self.assertEqual(deposit.expires, deposit_list[j][2] + deposit_list[j][3]) input_param = self._deposit_bulk(100) deposit_info = self._engine.get_deposit_info(self.context, self._score_address, self.block_height) self.assertEqual(100, len(deposit_info.deposits)) for i in range(size): tx_hash, amount, block_height, term = input_param[i] deposit = deposit_info.deposits[i] self.assertEqual(tx_hash, deposit.id) self.assertEqual(self._sender, deposit.sender) self.assertEqual(self._score_address, deposit.score_address) self.assertEqual(amount, deposit.deposit_amount) self.assertEqual(block_height, deposit.created) self.assertEqual(block_height + term, deposit.expires) def test_deposit_fee_invalid_param(self): context = self.get_context() tx_hash = os.urandom(32) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height = randrange(100, 10000) term = get_rand_term() # invalid amount (underflow) # noinspection PyTypeChecker with self.assertRaises(InvalidRequestException) as e: inv_amount = randrange(0, FeeEngine._MIN_DEPOSIT_AMOUNT - 1) self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, inv_amount, block_height, term) # noinspection PyUnresolvedReferences self.assertEqual('Invalid deposit amount', e.exception.message) # invalid amount (overflow) # noinspection PyTypeChecker with self.assertRaises(InvalidRequestException) as e: inv_amount = \ randrange(FeeEngine._MAX_DEPOSIT_AMOUNT + 1, FeeEngine._MAX_DEPOSIT_AMOUNT * 10) self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, inv_amount, block_height, term) # noinspection PyUnresolvedReferences self.assertEqual('Invalid deposit amount', e.exception.message) # invalid term (underflow) # noinspection PyTypeChecker with self.assertRaises(InvalidRequestException) as e: inv_term = randrange(0, FeeEngine._MIN_DEPOSIT_TERM - 1) self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, amount, block_height, inv_term) # noinspection PyUnresolvedReferences self.assertEqual('Invalid deposit term', e.exception.message) # invalid term (overflow) # noinspection PyTypeChecker with self.assertRaises(InvalidRequestException) as e: inv_term = \ randrange(FeeEngine._MAX_DEPOSIT_TERM + 1, FeeEngine._MAX_DEPOSIT_TERM * 10) self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, amount, block_height, inv_term) # noinspection PyUnresolvedReferences self.assertEqual('Invalid deposit term', e.exception.message) # invalid owner # noinspection PyTypeChecker with self.assertRaises(InvalidRequestException) as e: inv_sender = Address.from_data(AddressPrefix.EOA, os.urandom(20)) self._engine.add_deposit(context, tx_hash, inv_sender, self._score_address, amount, block_height, term) # noinspection PyUnresolvedReferences self.assertEqual('Invalid SCORE owner', e.exception.message) def test_deposit_fee_out_of_balance(self): context = self.get_context() self.icx_storage._put_genesis_data_account( context, CoinPartType.GENERAL, self._sender, 10000 * 10 ** 18) tx_hash = os.urandom(32) amount = 10001 * 10 ** 18 block_height = randrange(100, 10000) term = get_rand_term() # out of balance # noinspection PyTypeChecker with self.assertRaises(OutOfBalanceException) as e: self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, amount, block_height, term) # noinspection PyUnresolvedReferences self.assertEqual('Out of balance', e.exception.message) def test_deposit_fee_available_head_ids(self): context = self.get_context() tx_hash = os.urandom(32) amount = 10000 * 10 ** 18 block_height = 1000 self.icx_storage._put_genesis_data_account(context, CoinPartType.GENERAL, self._sender, amount) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.available_head_id_of_virtual_step, None) self.assertEqual(deposit_meta.available_head_id_of_deposit, None) self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, amount, block_height, FeeEngine._MIN_DEPOSIT_TERM) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.available_head_id_of_virtual_step, tx_hash) self.assertEqual(deposit_meta.available_head_id_of_deposit, tx_hash) def test_deposit_fee_expires_updated(self): context = self.get_context() tx_hash = os.urandom(32) amount = 10000 * 10 ** 18 block_height = 1000 term = FeeEngine._MIN_DEPOSIT_TERM self.icx_storage._put_genesis_data_account(context, CoinPartType.GENERAL, self._sender, amount) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.expires_of_virtual_step, -1) self.assertEqual(deposit_meta.expires_of_deposit, -1) self._engine.add_deposit(context, tx_hash, self._sender, self._score_address, amount, block_height, term) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.expires_of_virtual_step, block_height + term) self.assertEqual(deposit_meta.expires_of_deposit, block_height + term) def test_withdraw_fee_without_penalty(self): context = self.get_context() tx_hash = os.urandom(32) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height = randrange(100, 10000) term = get_rand_term() self._engine.add_deposit( context, tx_hash, self._sender, self._score_address, amount, block_height, term) before_sender_balance = self._icx_engine.get_balance(context, self._sender) self._engine.withdraw_deposit(context, self._sender, tx_hash, block_height + term + 1) after_sender_balance = self._icx_engine.get_balance(context, self._sender) deposit_info = self._engine.get_deposit_info(context, self._score_address, block_height) self.assertIsNone(deposit_info) self.assertEqual(amount, after_sender_balance - before_sender_balance) def test_withdraw_fee_with_penalty(self): context = self.get_context() tx_hash = os.urandom(32) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height = randrange(100, 10000) term = get_rand_term() self._engine.add_deposit( context, tx_hash, self._sender, self._score_address, amount, block_height, term) before_sender_balance = self._icx_engine.get_balance(context, self._sender) self._engine.withdraw_deposit(context, self._sender, tx_hash, block_height + term - 1) after_sender_balance = self._icx_engine.get_balance(context, self._sender) deposit_info = self._engine.get_deposit_info(context, self._score_address, block_height) self.assertIsNone(deposit_info) self.assertGreater(after_sender_balance - before_sender_balance, 0) self.assertLessEqual(after_sender_balance - before_sender_balance, amount) def test_withdraw_fee_and_updates_previous_and_next_link_ascending(self): """ Given: There are four deposits. When : Withdraws all of them sequentially(ascending). Then : Checks if the previous and next link update correctly. """ context = self.get_context() cnt_deposit = 4 block_height = randrange(100, 10000) arr_tx_hash = [] for i in range(cnt_deposit): arr_tx_hash.append(os.urandom(32)) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) term = get_rand_term() block_height += 1 self._engine.add_deposit( context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term) for i in range(cnt_deposit): target_deposit = self._engine.get_deposit(context, arr_tx_hash[i]) self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[i], block_height + term // 2) if cnt_deposit - 1 == i: self.assertIsNone(target_deposit.next_id) break next_deposit = self._engine.get_deposit(context, target_deposit.next_id) self.assertEqual(next_deposit.prev_id, None) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(next_deposit.id, deposit_meta.head_id) def test_withdraw_fee_and_updates_previous_and_next_link_descending(self): """ Given: There are four deposits. When : Withdraws all of them sequentially(descending). Then : Checks if the previous and next link update correctly. """ context = self.get_context() cnt_deposit = 4 block_height = randrange(100, 10000) arr_tx_hash = [] for i in range(cnt_deposit): arr_tx_hash.append(os.urandom(32)) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) term = get_rand_term() block_height += 1 self._engine.add_deposit( context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term) for i in range(cnt_deposit - 1, -1, -1): target_deposit = self._engine.get_deposit(context, arr_tx_hash[i]) self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[i], block_height + term // 2) if i == 0: self.assertIsNone(target_deposit.prev_id) break prev_deposit = self._engine.get_deposit(context, target_deposit.prev_id) self.assertEqual(prev_deposit.next_id, None) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(prev_deposit.id, deposit_meta.tail_id) @unittest.skipIf(FIXED_TERM is True, "FIXED_TERM is true") def test_withdraw_fee_when_available_head_id_of_virtual_step_is_same_as_deposit_id(self): """ Given: There are four deposits. Only the last deposit has enough to long term. When : Available head id of the virtual step is same as deposit id. Then : Searches for next deposit id which is available to use virtual step and where expires of the deposit is more than current block height. In the test, only the last deposit is available. """ context = self.get_context() cnt_deposit = 4 block_height = randrange(100, 10000) arr_tx_hash = [] for i in range(cnt_deposit): arr_tx_hash.append(os.urandom(32)) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height += 1 if i != cnt_deposit - 1: term = FeeEngine._MIN_DEPOSIT_TERM else: term = FeeEngine._MAX_DEPOSIT_TERM self._engine.add_deposit( context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.available_head_id_of_virtual_step, arr_tx_hash[0]) self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[0], block_height + FeeEngine._MAX_DEPOSIT_TERM // 2) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.available_head_id_of_virtual_step, arr_tx_hash[len(arr_tx_hash) - 1]) @unittest.skipIf(FIXED_TERM is True, "FIXED_TERM is true") def test_withdraw_fee_when_available_head_id_of_deposit_is_same_as_deposit_id(self): """ Given: There are four deposits. Only the third deposit has enough long term. When : Available head id of deposit is same as deposit id. Then : Searches for next deposit id which is available to use deposit and where expires of the deposit is more than current block height. In the test, only the third deposit is available. """ context = self.get_context() cnt_deposit = 4 block_height = randrange(100, 10000) arr_tx_hash = [] for i in range(cnt_deposit): arr_tx_hash.append(os.urandom(32)) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height += 1 if i != cnt_deposit - 2: term = FeeEngine._MIN_DEPOSIT_TERM else: term = FeeEngine._MAX_DEPOSIT_TERM self._engine.add_deposit( context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.available_head_id_of_deposit, arr_tx_hash[0]) self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[0], block_height + FeeEngine._MAX_DEPOSIT_TERM // 2) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.available_head_id_of_deposit, arr_tx_hash[len(arr_tx_hash) - 2]) @unittest.skipIf(FIXED_TERM is True, "FIXED_TERM is true") def test_withdraw_fee_to_check_setting_on_next_max_expires(self): """ Given: There are four deposits. When : Expires of the withdrawal deposit is same as expires. Then : Searches for max expires which is more than current block height. """ context = self.get_context() cnt_deposit = 4 block_height = randrange(100, 10000) arr_tx_hash = [] last_expires = 0 org_last_expires = 0 for i in range(cnt_deposit): arr_tx_hash.append(os.urandom(32)) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height += 1 if i != 0: term = FeeEngine._MIN_DEPOSIT_TERM if block_height + term > last_expires: last_expires = block_height + term else: term = FeeEngine._MAX_DEPOSIT_TERM org_last_expires = block_height + term self._engine.add_deposit( context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.available_head_id_of_virtual_step, arr_tx_hash[0]) self.assertEqual(deposit_meta.expires_of_virtual_step, org_last_expires) self.assertEqual(deposit_meta.expires_of_deposit, org_last_expires) self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[0], block_height + FeeEngine._MIN_DEPOSIT_TERM // 2) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.expires_of_virtual_step, last_expires) self.assertEqual(deposit_meta.expires_of_deposit, last_expires) @unittest.skipIf(FIXED_TERM is True, "FIXED_TERM is true") def test_withdraw_fee_of_last_deposit_to_check_setting_on_next_max_expires(self): """ Given: There are four deposits. When : Expires of the withdrawal deposit which is the last one is same as expires. Then : Searches for max expires which is more than current block height. """ context = self.get_context() cnt_deposit = 4 block_height = randrange(100, 10000) arr_tx_hash = [] last_expires = 0 org_last_expires = 0 for i in range(cnt_deposit): arr_tx_hash.append(os.urandom(32)) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height += 1 if i != cnt_deposit-1: term = FeeEngine._MIN_DEPOSIT_TERM if block_height + term > last_expires: last_expires = block_height + term else: term = FeeEngine._MAX_DEPOSIT_TERM org_last_expires = block_height + term self._engine.add_deposit( context, arr_tx_hash[i], self._sender, self._score_address, amount, block_height, term) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.available_head_id_of_virtual_step, arr_tx_hash[0]) self.assertEqual(deposit_meta.available_head_id_of_deposit, arr_tx_hash[0]) self.assertEqual(deposit_meta.expires_of_virtual_step, org_last_expires) self.assertEqual(deposit_meta.expires_of_deposit, org_last_expires) # Withdraws the last one self._engine.withdraw_deposit(context, self._sender, arr_tx_hash[cnt_deposit - 1], block_height + FeeEngine._MIN_DEPOSIT_TERM // 2) deposit_meta = self._engine._get_or_create_deposit_meta(context, self._score_address) self.assertEqual(deposit_meta.expires_of_virtual_step, last_expires) self.assertEqual(deposit_meta.expires_of_deposit, last_expires) def test_get_deposit_info(self): context = self.get_context() tx_hash = os.urandom(32) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height = randrange(100, 10000) term = get_rand_term() before_sender_balance = self._icx_engine.get_balance(context, self._sender) self._engine.add_deposit( context, tx_hash, self._sender, self._score_address, amount, block_height, term) after_sender_balance = self._icx_engine.get_balance(context, self._sender) self.assertEqual(amount, before_sender_balance - after_sender_balance) deposit = self._engine.get_deposit(context, tx_hash) self.assertEqual(tx_hash, deposit.id) self.assertEqual(self._score_address, deposit.score_address) self.assertEqual(self._sender, deposit.sender) self.assertEqual(amount, deposit.deposit_amount) self.assertEqual(block_height, deposit.created) self.assertEqual(block_height + term, deposit.expires) def test_charge_transaction_fee_without_sharing(self): context = self.get_context() step_price = 10 ** 10 used_step = 10 ** 10 tx_hash = os.urandom(32) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height = randrange(100, 10000) term = get_rand_term() self._engine.add_deposit( context, tx_hash, self._sender, self._score_address, amount, block_height, term) before_sender_balance = self._icx_engine.get_balance(context, self._sender) self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, block_height) after_sender_balance = self._icx_engine.get_balance(context, self._sender) self.assertEqual(step_price * used_step, before_sender_balance - after_sender_balance) def test_charge_transaction_fee_sharing_deposit(self): context = self.get_context() step_price = 10 ** 10 used_step = 10 ** 10 tx_hash = os.urandom(32) amount = randrange(FeeEngine._MIN_DEPOSIT_AMOUNT, FeeEngine._MAX_DEPOSIT_AMOUNT) block_height = randrange(100, 10000) term = get_rand_term() self._engine.add_deposit( context, tx_hash, self._sender, self._score_address, amount, block_height, term) ratio = 50 context.fee_sharing_proportion = ratio before_sender_balance = self._icx_engine.get_balance(context, self._sender) self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, block_height) after_sender_balance = self._icx_engine.get_balance(context, self._sender) score_charging_step = used_step * ratio // 100 sender_charging_step = used_step - score_charging_step self.assertEqual( step_price * sender_charging_step, before_sender_balance - after_sender_balance) def test_charge_fee_from_score_by_virtual_step_single_deposit(self): """ Given: Five deposits. The fourth deposit is the max expire. When : Current block is 120 so 1st deposit is unavailable Then : Pays fee by virtual step of 2nd. update indices to 2nd """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 100), (os.urandom(32), 50, 180, 100, 100), (os.urandom(32), 70, 150, 100, 100), (os.urandom(32), 90, 250, 100, 100), (os.urandom(32), 110, 200, 100, 100) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 120 used_step = 80 deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) before_virtual_step = deposit_info.available_virtual_step self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(used_step, before_virtual_step - after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(deposits[1][0], deposit_meta.available_head_id_of_virtual_step) def test_charge_fee_from_score_by_virtual_step_single_deposit_next_head(self): """ Given: Five deposits. The fourth deposit is the max expire. When : Current block is 120 so 1st deposit is unavailable Then : Pays fee by virtual step of 2nd. the virtual steps in 2nd are fully consumed update indices to 3rd """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 100), (os.urandom(32), 50, 180, 100, 100), (os.urandom(32), 70, 150, 100, 100), (os.urandom(32), 90, 250, 100, 100), (os.urandom(32), 110, 200, 100, 100) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 120 used_step = 100 deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) before_virtual_step = deposit_info.available_virtual_step self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(used_step, before_virtual_step - after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(deposits[2][0], deposit_meta.available_head_id_of_virtual_step) def test_charge_fee_from_score_by_virtual_step__single_deposit_next_head_next_expire(self): """ Given: Five deposits. The fourth deposit is the max expire. When : Current block is 190 so 4th, 5th deposits are available Then : Pays fee by virtual step of 4th. the virtual steps in 4th are fully consumed update indices to 5th """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 100), (os.urandom(32), 50, 180, 100, 100), (os.urandom(32), 70, 150, 100, 100), (os.urandom(32), 90, 250, 100, 100), (os.urandom(32), 110, 200, 100, 100) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 190 used_step = 100 deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) before_virtual_step = deposit_info.available_virtual_step self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(used_step, before_virtual_step - after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(deposits[4][0], deposit_meta.available_head_id_of_virtual_step) self.assertEqual(deposits[4][2], deposit_meta.expires_of_virtual_step) def test_charge_fee_from_score_by_virtual_step__single_deposit_next_head_next_expire_none(self): """ Given: Five deposits. The fourth deposit is the max expire. When : Current block is 210 so only 4th deposit is available Then : Pays fee by virtual step of 4th. the virtual steps in 4th are fully consumed should update indices but there are no more available deposits """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 100), (os.urandom(32), 50, 180, 100, 100), (os.urandom(32), 70, 150, 100, 100), (os.urandom(32), 90, 250, 100, 100), (os.urandom(32), 110, 200, 100, 100) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 210 used_step = 100 deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) before_virtual_step = deposit_info.available_virtual_step self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(used_step, before_virtual_step - after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step) self.assertEqual(-1, deposit_meta.expires_of_virtual_step) def test_charge_fee_from_score_by_virtual_step_multiple_deposit(self): """ Given: Five deposits. The fourth deposit is the max expire. When : Current block is 120 so 1st deposit is unavailable Then : Pays fee by virtual step through 2nd, 3rd, 4th. the virtual steps in 2nd, 3rd are fully consumed update indices to 4th """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 100), (os.urandom(32), 50, 180, 100, 100), (os.urandom(32), 70, 150, 100, 100), (os.urandom(32), 90, 250, 100, 100), (os.urandom(32), 110, 200, 100, 100) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 120 used_step = 250 deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) before_virtual_step = deposit_info.available_virtual_step self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(used_step, before_virtual_step - after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(deposits[3][0], deposit_meta.available_head_id_of_virtual_step) def test_charge_fee_from_score_by_combine_by_single_deposit(self): """ Given: Five deposits. The fourth deposit is the max expire. Remaining virtual steps are in 5th deposit When : Current block is 120 so 1st deposit is unavailable Remaining virtual steps are not enough to pay fees Then : Pays fee by virtual step first. Pays remaining fee by deposit of 2nd update indices to 2nd """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 0), (os.urandom(32), 50, 180, 100, 0), (os.urandom(32), 70, 150, 100, 0), (os.urandom(32), 90, 250, 100, 0), (os.urandom(32), 110, 200, 100, 50) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 120 used_step = 70 self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(0, after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step) self.assertEqual(-1, deposit_meta.expires_of_virtual_step) self.assertEqual(deposits[1][0], deposit_meta.available_head_id_of_deposit) def test_charge_fee_from_score_by_combine_next_head(self): """ Given: Five deposits. The fourth deposit is the max expire. Remaining virtual steps are in 5th deposit When : Current block is 120 so 1st deposit is unavailable Remaining virtual steps are not enough to pay fees Then : Pays fee by virtual step first. Pays remaining fee by deposit of 2nd 2nd deposit is fully consumed so update indices to 3rd """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 0), (os.urandom(32), 50, 180, 100, 0), (os.urandom(32), 70, 150, 100, 0), (os.urandom(32), 90, 250, 100, 0), (os.urandom(32), 110, 200, 100, 50) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 120 used_step = 140 self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(0, after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step) self.assertEqual(-1, deposit_meta.expires_of_virtual_step) self.assertEqual(deposits[2][0], deposit_meta.available_head_id_of_deposit) def test_charge_fee_from_score_by_combine_next_head_next_expire(self): """ Given: Five deposits. The fourth deposit is the max expire. Remaining virtual steps are in 5th deposit When : Current block is 190 so 1st, 2nd, 3rd deposits are unavailable Remaining virtual steps are not enough to pay fees Then : Pays fee by virtual step first. Pays remaining fee by deposit of 4th 4th deposit is fully consumed so update indices to 5th """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 0), (os.urandom(32), 50, 180, 100, 0), (os.urandom(32), 70, 150, 100, 0), (os.urandom(32), 90, 250, 100, 0), (os.urandom(32), 110, 200, 100, 50) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 190 used_step = 140 self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(0, after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step) self.assertEqual(-1, deposit_meta.expires_of_virtual_step) self.assertEqual(deposits[4][0], deposit_meta.available_head_id_of_deposit) self.assertEqual(deposits[4][2], deposit_meta.expires_of_deposit) def test_charge_fee_from_score_by_combine_next_head_next_expire_none(self): """ Given: Five deposits. The fourth deposit is the max expire. Remaining virtual steps are in 4th and 5th deposit When : Current block is 220 so 5th deposit is unavailable Remaining virtual steps are not enough to pay fees Then : Pays fee by virtual step first. Pays remaining fee by deposit of 4th All available deposits are consumed so make the SCORE disabled """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 0), (os.urandom(32), 50, 180, 100, 0), (os.urandom(32), 70, 150, 100, 0), (os.urandom(32), 90, 250, 100, 50), (os.urandom(32), 110, 200, 100, 100) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 220 used_step = 140 self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(0, after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step) self.assertEqual(-1, deposit_meta.expires_of_virtual_step) self.assertEqual(None, deposit_meta.available_head_id_of_deposit) self.assertEqual(-1, deposit_meta.expires_of_deposit) def test_charge_fee_from_score_by_combine_multiple_deposit(self): """ Given: Five deposits. The fourth deposit is the max expire. Remaining virtual steps are in 5th deposit When : Current block is 120 so 1st deposit is unavailable Remaining virtual steps are not enough to pay fees Then : Pays fee by virtual step first. Pays remaining fee by deposit through 2nd and 3rd deposit. """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 0), (os.urandom(32), 50, 180, 100, 0), (os.urandom(32), 70, 150, 100, 0), (os.urandom(32), 90, 250, 100, 0), (os.urandom(32), 110, 200, 100, 50) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 120 used_step = 230 self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(0, after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step) self.assertEqual(-1, deposit_meta.expires_of_virtual_step) # Asserts indices are updated self.assertEqual(deposits[3][0], deposit_meta.available_head_id_of_deposit) self.assertEqual(deposits[3][2], deposit_meta.expires_of_deposit) def test_charge_fee_from_score_by_combine_additional_pay(self): """ Given: Five deposits. The fourth deposit is the max expire. Remaining virtual steps are in 4th and 5th deposit When : Current block is 220 so 5th deposit is unavailable Remaining virtual steps are not enough to pay fees Available deposits are also not enough to pay fees Then : Pays fees regardless minimum remaining amount and make the SCORE disabled """ context = self.get_context() # tx_hash, from_block, to_block, deposit_amount, virtual_step_amount deposits = [ (os.urandom(32), 10, 100, 100, 0), (os.urandom(32), 50, 180, 100, 0), (os.urandom(32), 70, 150, 100, 0), (os.urandom(32), 90, 250, 100, 50), (os.urandom(32), 110, 200, 100, 100) ] self._set_up_deposits(context, deposits) step_price = 1 current_block = 220 used_step = 150 self._engine.charge_transaction_fee( context, self._sender, self._score_address, step_price, used_step, current_block) deposit_info = self._engine.get_deposit_info(context, self._score_address, current_block) after_virtual_step = deposit_info.available_virtual_step self.assertEqual(0, after_virtual_step) deposit_meta = self.fee_storage.get_deposit_meta(context, self._score_address) # Asserts virtual step disabled self.assertEqual(None, deposit_meta.available_head_id_of_virtual_step) self.assertEqual(-1, deposit_meta.expires_of_virtual_step) # Asserts deposit disabled self.assertEqual(None, deposit_meta.available_head_id_of_deposit) self.assertEqual(-1, deposit_meta.expires_of_deposit) def _set_up_deposits(self, context, deposits): context.fee_sharing_proportion = 100 self._engine._MIN_DEPOSIT_TERM = 50 self._engine._MIN_DEPOSIT_AMOUNT = 10 for deposit in deposits: tx_hash = deposit[0] amount = deposit[3] block_height = deposit[1] term = deposit[2] - block_height # self._engine._calculate_virtual_step_issuance = Mock(return_value=deposit[4]) VirtualStepCalculator.calculate_virtual_step = Mock(return_value=deposit[4]) self._engine.add_deposit( context, tx_hash, self._sender, self._score_address, amount, block_height, term)
class 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)
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)
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)
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)