def test_revert(self, mocker):
        mocker.patch.object(IconServiceEngine, "_charge_transaction_fee")
        mocker.patch.object(IconScoreEngine, "invoke")

        context = ContextContainer._get_context()

        icon_service_engine = IconServiceEngine()
        icon_service_engine._icx_engine = Mock(spec=IcxEngine)
        icon_service_engine._icon_score_deploy_engine = \
            Mock(spec=DeployEngine)

        icon_service_engine._icon_pre_validator = Mock(spec=IconPreValidator)
        context.tx_batch = TransactionBatch()
        context.clear_batch = Mock()
        context.update_batch = Mock()

        from_ = Address.from_data(AddressPrefix.EOA, os.urandom(20))
        to_ = Address.from_data(AddressPrefix.CONTRACT, os.urandom(20))
        tx_index = randrange(0, 100)
        context.tx = Transaction(os.urandom(32), tx_index, from_, 0)
        context.msg = Message(from_)

        def intercept_charge_transaction_fee(*args, **kwargs):
            return {}, Mock(spec=int)

        IconServiceEngine._charge_transaction_fee.side_effect = \
            intercept_charge_transaction_fee

        icon_service_engine._icon_score_deploy_engine.attach_mock(
            Mock(return_value=False), 'is_data_type_supported')

        reason = Mock(spec=str)
        code = ExceptionCode.SCORE_ERROR
        mock_revert = Mock(side_effect=IconScoreException(reason))
        IconScoreEngine.invoke.side_effect = mock_revert

        raise_exception_start_tag("test_revert")
        tx_result = icon_service_engine._handle_icx_send_transaction(
            context, {
                'version': 3,
                'from': from_,
                'to': to_
            })
        raise_exception_end_tag("test_revert")
        assert tx_result.status == 0

        IconServiceEngine._charge_transaction_fee.assert_called()
        context.traces.append.assert_called()
        trace = context.traces.append.call_args[0][0]
        assert trace.trace == TraceType.REVERT
        assert trace.data[0] == code
        assert trace.data[1] == reason
    def test_throw(self, IconServiceEngine_charge_transaction_fee):
        context = ContextContainer._get_context()

        self._icon_service_engine = IconServiceEngine()
        self._icon_service_engine._flag = 0
        self._icon_service_engine._icx_engine = Mock(spec=IcxEngine)
        self._icon_service_engine._icon_score_deploy_engine = \
            Mock(spec=IconScoreDeployEngine)

        self._icon_service_engine._icon_score_engine = Mock(
            spec=IconScoreEngine)
        self._icon_service_engine._icon_pre_validator = Mock(
            spec=IconPreValidator)
        context.tx_batch = TransactionBatch()

        from_ = Mock(spec=Address)
        to_ = Mock(spec=Address)

        def intercept_charge_transaction_fee(*args, **kwargs):
            return Mock(spec=int), Mock(spec=int)

        IconServiceEngine_charge_transaction_fee.side_effect = \
            intercept_charge_transaction_fee

        self._icon_service_engine._icon_score_deploy_engine.attach_mock(
            Mock(return_value=False), 'is_data_type_supported')

        error = Mock(spec=str)
        code = ExceptionCode.SCORE_ERROR
        mock_exception = Mock(side_effect=IconScoreException(error, code))
        self._icon_service_engine._icon_score_engine.attach_mock(
            mock_exception, "invoke")

        raise_exception_start_tag("test_throw")
        tx_result = self._icon_service_engine._handle_icx_send_transaction(
            context, {
                'version': 3,
                'from': from_,
                'to': to_
            })
        raise_exception_end_tag("test_throw")
        self.assertEqual(0, tx_result.status)

        IconServiceEngine_charge_transaction_fee.assert_called()
        context.traces.append.assert_called()
        trace = context.traces.append.call_args[0][0]
        self.assertEqual(TraceType.THROW, trace.trace)
        self.assertEqual(code, trace.data[0])
        self.assertEqual(error, trace.data[1])
    def test_score_invoke_with_revert(self):

        table = {ConfigKey.SERVICE_FEE: True,
                 ConfigKey.SERVICE_AUDIT: False,
                 ConfigKey.SERVICE_DEPLOYER_WHITE_LIST: False,
                 ConfigKey.SERVICE_SCORE_PACKAGE_VALIDATOR: False}
        # TODO: must apply the service flags to the engine
        # self._engine._flag = self._engine._make_service_flag(table)

        block_height = 1
        block_hash = create_block_hash(b'block')
        block_timestamp = 0
        tx_hash = create_tx_hash()
        value = 1 * 10 ** 18

        self._to = create_address(AddressPrefix.CONTRACT)

        tx_v3 = {
            'method': 'icx_sendTransaction',
            'params': {
                'version': 3,
                'from': self._genesis_address,
                'to': self._to,
                'value': value,
                'stepLimit': 20000,
                'timestamp': 1234567890,
                'txHash': tx_hash
            }
        }

        block = Block(block_height,
                      block_hash,
                      block_timestamp,
                      self.genesis_block.hash,
                      0)

        context = _create_context(IconScoreContextType.QUERY)
        before_from_balance: int = \
            IconScoreContext.engine.icx.get_balance(context, self.from_)

        self._engine._handle_score_invoke = \
            Mock(return_value=None, side_effect=IconScoreException("force revert"))

        raise_exception_start_tag("test_score_invoke_with_revert")
        tx_results, state_root_hash, _, _ = self._engine.invoke(block, [tx_v3])
        raise_exception_end_tag("test_score_invoke_with_revert")
        self.assertIsInstance(state_root_hash, bytes)
        self.assertEqual(len(state_root_hash), 32)

        self.assertEqual(len(tx_results), 1)

        tx_result: 'TransactionResult' = tx_results[0]
        self.assertIsNotNone(tx_result.failure)
        self.assertIsNone(tx_result.score_address)
        self.assertEqual(tx_result.status, 0)
        self.assertEqual(tx_result.block_height, block_height)
        self.assertEqual(tx_result.block_hash, block_hash)
        self.assertEqual(tx_result.tx_index, 0)
        self.assertEqual(tx_result.tx_hash, tx_hash)

        # step_used MUST BE 10**6 on protocol v2
        step_unit = self._engine._step_counter_factory.get_step_cost(
            StepType.DEFAULT)

        self.assertEqual(tx_result.step_used, step_unit)

        step_price = self._engine._step_counter_factory.get_step_price()
        if IconScoreContextUtil._is_flag_on(IconScoreContext.icon_service_flag, IconServiceFlag.FEE):
            # step_price MUST BE 10**10 on protocol v2
            self.assertEqual(
                step_price, self._engine._step_counter_factory.get_step_price())
        else:
            self.assertEqual(step_price, 0)
        self.assertEqual(tx_result.step_price, step_price)

        self._engine.commit(block.height, block.hash, None)

        # Check whether fee charging works well
        context = _create_context(IconScoreContextType.QUERY)
        after_from_balance: int = \
            IconScoreContext.engine.icx.get_balance(context, self.from_)

        fee = tx_result.step_price * tx_result.step_used
        self.assertEqual(after_from_balance, before_from_balance - fee)