Beispiel #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
    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
Beispiel #3
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)
    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()
Beispiel #5
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
Beispiel #6
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
Beispiel #7
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
Beispiel #8
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)
Beispiel #9
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)
Beispiel #10
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)
Beispiel #11
0
 def from_json(cls, json: Dict[str, Any]) -> Signer:
     account_hex = json['account']
     account = UInt160(from_hex_str(account_hex))
     return cls(account)
Beispiel #12
0
    def get_contract(cls, value: Any) -> Optional[UInt160]:
        if cls._is_wildcard(value):
            return None

        # TODO: Permissions are not implemented yet
        return UInt160()
Beispiel #13
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)
Beispiel #14
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
Beispiel #15
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)