示例#1
0
    def test_get_transaction_from_block_uint256(self):
        call_flags = Integer(CallFlags.ALL).to_byte_array(signed=True, min_length=1)
        method = String('getTransactionFromBlock').to_bytes()

        expected_output = (
            Opcode.INITSLOT
            + b'\x00\x02'
            + Opcode.LDARG1
            + Opcode.LDARG0
            + Opcode.PUSH2
            + Opcode.PACK
            + Opcode.PUSHDATA1
            + Integer(len(call_flags)).to_byte_array()
            + call_flags
            + Opcode.PUSHDATA1
            + Integer(len(method)).to_byte_array()
            + method
            + Opcode.PUSHDATA1
            + Integer(len(constants.LEDGER_SCRIPT)).to_byte_array()
            + constants.LEDGER_SCRIPT
            + Opcode.SYSCALL
            + Interop.CallContract.interop_method_hash
            + Opcode.RET
        )
        path = self.get_contract_path('GetTransactionFromBlockUInt256.py')
        output = Boa3.compile(path)
        self.assertEqual(expected_output, output)

        path_burn_gas = self.get_contract_path('../runtime', 'BurnGas.py')
        engine = TestEngine()

        engine.increase_block(10)
        sender = bytes(range(20))
        self.run_smart_contract(engine, path_burn_gas, 'main', 100, signer_accounts=[sender])

        block_10 = engine.current_block
        block_hash = block_10.hash
        self.assertIsNotNone(block_hash)
        txs = block_10.get_transactions()
        tx_hash = txs[0].hash
        tx_script = txs[0]._script

        engine.increase_block()

        result = self.run_smart_contract(engine, path, 'main', block_hash, 0)
        self.assertEqual(8, len(result))
        if isinstance(result[0], str):
            result[0] = String(result[0]).to_bytes()
        self.assertEqual(UInt256(tx_hash), UInt256(result[0]))  # hash
        self.assertIsInstance(result[1], int)  # version
        self.assertIsInstance(result[2], int)  # nonce
        if isinstance(result[3], str):
            result[3] = String(result[3]).to_bytes()
        self.assertEqual(UInt160(sender), UInt160(result[3]))  # sender
        self.assertIsInstance(result[4], int)  # system_fee
        self.assertIsInstance(result[5], int)  # network_fee
        self.assertIsInstance(result[6], int)  # valid_until_block
        if isinstance(result[7], str):
            result[7] = String(result[7]).to_bytes()
        self.assertEqual(tx_script, result[7])  # script
示例#2
0
    def _verify_is_valid_contract_hash(contract_hash: str) -> bool:
        """
        Verifies if a given contract hash is valid.

        :return: whether the given contract hash is valid or not
        """
        if contract_hash.startswith('0x'):
            try:
                from boa3.neo3.core.types import UInt160
                # if contract_hash is not a valid UInt160, it will raise a ValueError
                UInt160.from_string(contract_hash[2:])
                return True
            except ValueError:
                pass
        return False
示例#3
0
    def test_contract_interface_code_optimization(self):
        from boa3.model.builtin.interop.interop import Interop
        from boa3.neo.vm.opcode.Opcode import Opcode
        from boa3.neo.vm.type.Integer import Integer
        from boa3.neo.vm.type.String import String
        from boa3.neo3.core.types import UInt160

        external_contract_name = 'symbol'
        function_name_bytes = String(external_contract_name).to_bytes()
        contract_script_bytes = UInt160.from_string(
            '21f19f84e144f91abe755efb21a6798ac95c2e70').to_array()

        expected_output = (
            Opcode.NEWARRAY0  # arguments list
            + Opcode.PUSH15  # CallFlag
            + Opcode.PUSHDATA1  # function name
            + Integer(len(function_name_bytes)).to_byte_array() +
            function_name_bytes + Opcode.PUSHDATA1  # contract script
            + Integer(len(contract_script_bytes)).to_byte_array() +
            contract_script_bytes + Opcode.SYSCALL +
            Interop.CallContract.interop_method_hash + Opcode.RET)

        path = self.get_contract_path('ContractInterfaceCodeOptimization.py')
        output, manifest = self.compile_and_save(path)
        self.assertEqual(expected_output, output)

        nep17_path = self.get_contract_path('examples', 'nep17.py')
        engine = TestEngine()

        nep17_result = self.run_smart_contract(engine, nep17_path, 'symbol')
        result = self.run_smart_contract(engine, path, 'nep17_symbol')
        self.assertEqual(nep17_result, result)
示例#4
0
 def _get_script_hash(self) -> Optional[bytes]:
     try:
         return UInt160.from_string(
             self._contract_hash[2:] if self._contract_hash.
             startswith('0x') else self._contract_hash).to_array()
     except BaseException:
         return None
示例#5
0
    def run_smart_contract(self,
                           test_engine: TestEngine,
                           smart_contract_path: Union[str, bytes],
                           method: str,
                           *arguments: Any,
                           reset_engine: bool = False,
                           fake_storage: Dict[Tuple[str, str], Any] = None,
                           signer_accounts: Iterable[bytes] = (),
                           expected_result_type: Type = None,
                           rollback_on_fault: bool = True) -> Any:

        if isinstance(smart_contract_path,
                      str) and smart_contract_path.endswith('.py'):
            if not (os.path.isfile(smart_contract_path.replace('.py', '.nef'))
                    and os.path.isfile(
                        smart_contract_path.replace('.py', '.manifest.json'))):
                # both .nef and .manifest.json are required to execute the smart contract
                self.compile_and_save(smart_contract_path, log=False)
            smart_contract_path = smart_contract_path.replace('.py', '.nef')
        elif isinstance(smart_contract_path, bytes):
            from boa3.neo3.core.types import UInt160
            smart_contract_path = UInt160(smart_contract_path)

        self._set_fake_data(test_engine, fake_storage, signer_accounts)
        result = test_engine.run(smart_contract_path,
                                 method,
                                 *arguments,
                                 reset_engine=reset_engine,
                                 rollback_on_fault=rollback_on_fault)

        return self._filter_result(test_engine, expected_result_type, result)
示例#6
0
    def evaluate_literal(self, *args: Any) -> Any:
        from boa3.neo3.core.types import UInt160

        if len(args) == 0:
            return UInt160.zero().to_array()

        if len(args) == 1:
            arg = args[0]
            if isinstance(arg, int):
                from boa3.neo.vm.type.Integer import Integer
                arg = Integer(arg).to_byte_array(min_length=UInt160._BYTE_LEN)
            if isinstance(arg, bytes):
                value = UInt160(arg).to_array()
                return value

        return super().evaluate_literal(*args)
示例#7
0
    def validate_values(self, *params: Any) -> List[Any]:
        values = []
        if len(params) != 2:
            return values

        origin, visitor = params
        values.append(self.contract_hash)
        from boa3.analyser.astanalyser import IAstAnalyser
        if not isinstance(visitor, IAstAnalyser):
            return values

        from boa3.exception import CompilerError
        if not isinstance(origin, ast.Call) or len(origin.args) < 1:
            visitor._log_error(
                CompilerError.UnfilledArgument(origin.lineno,
                                               origin.col_offset,
                                               list(self.args.keys())[0]))
            return values
        argument_hash = visitor.visit(origin.args[0])

        try:
            if isinstance(argument_hash, str):
                from boa3.neo import from_hex_str
                argument_hash = from_hex_str(argument_hash)

            if isinstance(argument_hash, bytes):
                values[0] = UInt160(argument_hash)
        except BaseException:
            visitor._log_error(
                CompilerError.InvalidUsage(
                    origin.lineno, origin.col_offset,
                    "Only literal values are accepted for 'script_hash' argument"
                ))

        return values
示例#8
0
    def _internal_contract_delete(self, script_hash: types.UInt160, batch=None):
        if batch:
            db = batch
        else:
            db = self._real_db

        db.delete(DBPrefixes.CONTRACTS + script_hash.to_array())
示例#9
0
    def _get_script_from_str(cls, script: str) -> bytes:
        if isinstance(script, str):
            if script.startswith('0x'):
                str_script = script[2:]
            else:
                str_script = script
            script = UInt160.from_string(str_script).to_array()

        return script
示例#10
0
    def __init__(self):
        from boa3.model.type.primitive.bytestringtype import ByteStringType

        identifier = 'contract'
        args: Dict[str, Variable] = {
            'script_hash': Variable(ByteStringType.build())
        }
        super().__init__(identifier, args)
        self.contract_hash = UInt160()
示例#11
0
    def is_valid(self, contract_hash: types.UInt160) -> bool:
        """
        Validate if the group has agreed on allowing the specific contract_hash.

        Args:
            contract_hash:
        """
        return cryptography.verify_signature(contract_hash.to_array(),
                                             self.signature,
                                             self.public_key.encode_point(False))
示例#12
0
    def test_transaction_init(self):
        path = self.get_contract_path('Transaction.py')
        engine = TestEngine()

        result = self.run_smart_contract(engine, path, 'main')
        self.assertEqual(8, len(result))
        if isinstance(result[0], str):
            result[0] = String(result[0]).to_bytes()
        self.assertEqual(UInt256(), UInt256(result[0]))  # hash
        self.assertEqual(0, result[1])  # version
        self.assertEqual(0, result[2])  # nonce
        if isinstance(result[3], str):
            result[3] = String(result[3]).to_bytes()
        self.assertEqual(UInt160(), UInt160(result[3]))  # sender
        self.assertEqual(0, result[4])  # system_fee
        self.assertEqual(0, result[5])  # network_fee
        self.assertEqual(0, result[6])  # valid_until_block
        if isinstance(result[7], str):
            result[7] = String(result[7]).to_bytes()
        self.assertEqual(b'', result[7])  # script
示例#13
0
    def test_block_constructor(self):
        path = self.get_contract_path('Block.py')
        engine = TestEngine()

        result = self.run_smart_contract(engine, path, 'main')
        self.assertIsInstance(result, list)
        self.assertEqual(10, len(result))
        for k in range(len(result)):
            if isinstance(result[k], str):
                result[k] = String(result[k]).to_bytes()
        self.assertEqual(UInt256(), UInt256(result[0]))  # hash
        self.assertEqual(0, result[1])  # version
        self.assertEqual(UInt256(), UInt256(result[2]))  # previous_hash
        self.assertEqual(UInt256(), UInt256(result[3]))  # merkle_root
        self.assertEqual(0, result[4])  # timestamp
        self.assertEqual(0, result[5])  # nonce
        self.assertEqual(0, result[6])  # index
        self.assertEqual(0, result[7])  # primary_index
        self.assertEqual(UInt160(), UInt160(result[8]))  # next_consensus
        self.assertEqual(0, result[9])  # transaction_count
示例#14
0
 def get_events(self,
                event_name: str,
                origin: UInt160 = None) -> List[Notification]:
     if origin is None:
         return [n for n in self._notifications if n.name == event_name]
     else:
         origin_bytes = origin.to_array() if isinstance(
             origin, UInt160) else bytes(origin)
         return [
             n for n in self._notifications
             if n.name == event_name and n.origin == origin_bytes
         ]
示例#15
0
    def run_smart_contract(self,
                           test_engine: TestEngine,
                           smart_contract_path: Union[str, bytes],
                           method: str,
                           *arguments: Any,
                           reset_engine: bool = False,
                           fake_storage: Dict[str, Any] = None,
                           signer_accounts: Iterable[bytes] = (),
                           expected_result_type: Type = None,
                           rollback_on_fault: bool = True) -> Any:

        if isinstance(smart_contract_path,
                      str) and smart_contract_path.endswith('.py'):
            if not (os.path.isfile(smart_contract_path.replace('.py', '.nef'))
                    and os.path.isfile(
                        smart_contract_path.replace('.py', '.manifest.json'))):
                # both .nef and .manifest.json are required to execute the smart contract
                self.compile_and_save(smart_contract_path, log=False)
            smart_contract_path = smart_contract_path.replace('.py', '.nef')
        elif isinstance(smart_contract_path, bytes):
            from boa3.neo3.core.types import UInt160
            smart_contract_path = UInt160(smart_contract_path)

        if isinstance(fake_storage, dict):
            test_engine.set_storage(fake_storage)

        for account in signer_accounts:
            test_engine.add_signer_account(account)

        result = test_engine.run(smart_contract_path,
                                 method,
                                 *arguments,
                                 reset_engine=reset_engine,
                                 rollback_on_fault=rollback_on_fault)

        if test_engine.vm_state is not VMState.HALT and test_engine.error is not None:
            raise TestExecutionException(test_engine.error)

        if expected_result_type is not None:
            if expected_result_type is not str and isinstance(result, str):
                result = String(result).to_bytes()

            if expected_result_type is bool:
                if isinstance(result, bytes):
                    result = Integer.from_bytes(result, signed=True)
                if isinstance(result, int) and result in (False, True):
                    result = bool(result)

            if expected_result_type is bytearray and isinstance(result, bytes):
                result = bytearray(result)

        return result
示例#16
0
    def run_oracle_response(self, request_id: int, oracle_response: oracleresponse.OracleResponseCode,
                            result: bytes, reset_engine: bool = False,
                            rollback_on_fault: bool = True) -> Any:
        request_ids = [x.arguments[0] if isinstance(x.arguments, (tuple, list)) and len(x.arguments) > 0
                       else x.arguments
                       for x in self.get_events('OracleRequest', constants.ORACLE_SCRIPT)]

        assert request_id in request_ids, 'Request ID not found'
        self._current_tx = Transaction(b'')
        self._current_tx.add_attribute(oracleresponse.OracleResponse(request_id, oracle_response, result))

        return self.run(UInt160(constants.ORACLE_SCRIPT), 'finish',
                        reset_engine=reset_engine,
                        rollback_on_fault=rollback_on_fault)
示例#17
0
    def _internal_storage_find(self, contract_script_hash: types.UInt160, key_prefix: bytes):
        prefix = DBPrefixes.STORAGES + contract_script_hash.to_array() + key_prefix

        res = {}
        with self._real_db.iterator(prefix=prefix, include_key=True, include_value=True) as it:
            for key, value in it:
                # strip off prefix
                k = storage.StorageKey.deserialize_from_bytes(key[1:])
                v = storage.StorageItem.deserialize_from_bytes(value)
                res[k] = v

        # yielding outside of iterator to make sure the LevelDB iterator is closed and not leaking resources
        for k, v in res.items():
            yield k, v
示例#18
0
    def _get_contract_hash_or_name(
            self, contract_id: Union[str, bytes]) -> Union[str, UInt160]:
        if isinstance(contract_id, str):
            if contract_id in self._contracts:
                contract = self._contracts[contract_id]
                return contract.name if hasattr(
                    contract, 'name') else contract.script_hash

            try:
                return UInt160.from_string(contract_id)
            except BaseException as e:
                if not contract_id.endswith('.nef'):
                    raise e
                manifest_file = contract_id.replace('.nef', '.manifest.json')
                if os.path.exists(manifest_file):
                    with open(manifest_file) as manifest_output:
                        import json
                        manifest = json.loads(manifest_output.read())
                        return manifest['name']

                raise e
        else:
            return UInt160(contract_id)
示例#19
0
    def test_contract_manual_interface_code_optimization(self):
        from boa3.model.builtin.interop.interop import Interop
        from boa3.neo.vm.opcode.Opcode import Opcode
        from boa3.neo.vm.type.Integer import Integer
        from boa3.neo.vm.type.String import String
        from boa3.neo3.core.types import UInt160

        external_contract_name = 'symbol'
        function_name_bytes = String(external_contract_name).to_bytes()
        contract_script_bytes = UInt160.from_string(
            '21f19f84e144f91abe755efb21a6798ac95c2e70').to_array()

        expected_output = (
            # start public method
            Opcode.LDSFLD0  # generated cls arg
            + Opcode.CALL + Integer(35).to_byte_array() + Opcode.RET
            # end public method
            # start initialize method
            + Opcode.INITSSLOT + b'\x01' + Opcode.PUSH1 + Opcode.NEWARRAY +
            Opcode.STSFLD0 + Opcode.PUSHDATA1 +
            Integer(len(contract_script_bytes)).to_byte_array() +
            contract_script_bytes + Opcode.PUSH0 + Opcode.LDSFLD0 +
            Opcode.REVERSE3 + Opcode.SETITEM + Opcode.RET
            # end initialize method
            # start 'symbol' class method
            + Opcode.INITSLOT + b'\x00\x01' +
            Opcode.NEWARRAY0  # arguments list
            + Opcode.PUSH15  # CallFlag
            + Opcode.PUSHDATA1  # function name
            + Integer(len(function_name_bytes)).to_byte_array() +
            function_name_bytes + Opcode.LDARG0  # contract script
            + Opcode.PUSH0 + Opcode.PICKITEM + Opcode.SYSCALL +
            Interop.CallContract.interop_method_hash + Opcode.RET
            # start class method
        )

        path = self.get_contract_path(
            'ContractManualInterfaceCodeOptimization.py')
        output, manifest = self.compile_and_save(path)
        self.assertEqual(expected_output, output)

        nep17_path = self.get_contract_path('examples', 'nep17.py')
        engine = TestEngine()

        nep17_result = self.run_smart_contract(engine, nep17_path, 'symbol')
        result = self.run_smart_contract(engine, path, 'nep17_symbol')
        self.assertEqual(nep17_result, result)
示例#20
0
    def from_json(cls, json: Dict[str, Any]) -> TestRunnerLog:
        """
        Creates a Log object from a json.

        :param json: json that contains the log data
        :return: a Log object
        :rtype: TestRunnerLog
        """
        keys = set(json.keys())
        if not keys.issubset([cls._contract_key, cls._message_key]):
            return None

        script: bytes = json[
            cls._contract_key] if cls._contract_key in json else b''
        message: str = json[
            cls._message_key] if cls._message_key in json else ""

        if isinstance(script, str):
            script = UInt160.from_string(
                script[2:] if script.startswith('0x') else script).to_array()

        return cls(script, message)
示例#21
0
    def get_contract(cls, value: Any) -> Optional[UInt160]:
        if cls._is_wildcard(value):
            return None

        # TODO: Permissions are not implemented yet
        return UInt160()
示例#22
0
    def run(self, contract_id: Union[str, UInt160], method: str, *arguments: Any, reset_engine: bool = False,
            rollback_on_fault: bool = True) -> Any:
        import json
        import subprocess

        if isinstance(contract_id, str) and contract_id not in self.contracts:
            self.add_contract(contract_id)

        # build an UInt160 value if the contract_id is not a path
        if isinstance(contract_id, bytes) and not isinstance(contract_id, UInt160):
            contract_id = UInt160(contract_id)

        test_engine_args = self.to_json(contract_id, method, *arguments)
        param_json = json.dumps(test_engine_args, separators=(',', ':'))

        try:
            process = subprocess.Popen(['dotnet', self._test_engine_path, param_json],
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT,
                                       text=True)
        except BaseException:
            json_path = '{0}/test-engine-test.json'.format(path.curdir)
            with open(json_path, 'wb+') as json_file:
                json_file.write(String(param_json).to_bytes())
                json_file.close()

            process = subprocess.Popen(['dotnet', self._test_engine_path, json_path],
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT,
                                       text=True)

        stdout, stderr = process.communicate()

        if reset_engine:
            self.reset_engine()
        else:
            self.reset_state()

        stdout = stdout.splitlines()[-1]

        try:
            result = json.loads(stdout)

            self._error_message = result['error'] if 'error' in result else None

            if 'vmstate' in result:
                self._vm_state = VMState.get_vm_state(result['vmstate'])

            if 'executedscripthash' in result:
                self._executed_script_hash = UInt160.from_string(result['executedscripthash'])

            if 'gasconsumed' in result:
                self._gas_consumed = int(result['gasconsumed'])

            if 'resultstack' in result:
                if isinstance(result['resultstack'], list):
                    self._result_stack = [stack_item_from_json(value) for value in result['resultstack']]
                else:
                    self._result_stack = [stack_item_from_json(result['resultstack'])]

            if self._vm_state is VMState.HALT or not rollback_on_fault:
                if 'notifications' in result:
                    json_storage = result['notifications']
                    if not isinstance(json_storage, list):
                        json_storage = [json_storage]

                    notifications = []
                    for n in json_storage:
                        new = Notification.from_json(n)
                        if new is not None:
                            notifications.append(new)
                    self._notifications.extend(notifications)

                if 'storage' in result:
                    json_storage = result['storage']
                    self._storage = Storage.from_json(json_storage)

                    for contract in self._contract_paths.copy():
                        if (not isinstance(contract, TestContract)
                                or contract.script_hash is None
                                or not self._storage.has_contract(contract.script_hash)):
                            self.remove_contract(contract.path)

                if 'currentblock' in result:
                    current_block = Block.from_json(result['currentblock'])

                    existing_block = next((block for block in self._blocks if block.index == current_block.index), None)
                    if existing_block is not None:
                        self._blocks.remove(existing_block)
                    self._blocks.append(current_block)

                if 'transaction' in result and self._vm_state is VMState.HALT:
                    block = self.current_block
                    if block is None:
                        block = self.increase_block(self.height)

                    tx = Transaction.from_json(result['transaction'])
                    block.add_transaction(tx)

        except BaseException as e:
            self._error_message = str(e)

        # TODO: convert the result to the return type of the function in the manifest
        return self._result_stack[-1] if len(self._result_stack) > 0 else VoidType
示例#23
0
 def from_json(cls, json: Dict[str, Any]) -> Signer:
     account_hex = json['account']
     account = UInt160(from_hex_str(account_hex))
     scopes = WitnessScope.get_from_neo_name(
         json['scopes']) if 'scopes' in json else WitnessScope.CalledByEntry
     return cls(account, scopes)
示例#24
0
 def add_signer_account(self, account_address: bytes, account_scope: WitnessScope = WitnessScope.CalledByEntry):
     account = Signer(UInt160(account_address), account_scope)
     if account not in self._accounts:
         self._accounts.append(account)
示例#25
0
 def from_json(cls, json: Dict[str, Any]) -> Signer:
     account_hex = json['account']
     account = UInt160(from_hex_str(account_hex))
     return cls(account)
示例#26
0
    def _internal_contract_get(self, script_hash: types.UInt160):
        contract_bytes = self._real_db.get(DBPrefixes.CONTRACTS + script_hash.to_array())
        if contract_bytes is None:
            raise KeyError

        return storage.ContractState.deserialize_from_bytes(contract_bytes)