Example #1
0
    def _post_transfer(self, engine: contracts.ApplicationEngine,
                       account_from: types.UInt160, account_to: types.UInt160,
                       amount: vm.BigInteger, data: vm.StackItem,
                       call_on_payment: bool) -> None:
        state = vm.ArrayStackItem(vm.ReferenceCounter())
        if account_from == types.UInt160.zero():
            state.append(vm.NullStackItem())
        else:
            state.append(vm.ByteStringStackItem(account_from.to_array()))
        if account_to == types.UInt160.zero():
            state.append(vm.NullStackItem())
        else:
            state.append(vm.ByteStringStackItem(account_to.to_array()))
        state.append(vm.IntegerStackItem(amount))

        msgrouter.interop_notify(self.hash, "Transfer", state)

        # wallet or smart contract
        if not call_on_payment \
                or account_to == types.UInt160.zero() \
                or contracts.ManagementContract().get_contract(engine.snapshot, account_to) is None:
            return

        if account_from == types.UInt160.zero():
            from_: vm.StackItem = vm.NullStackItem()
        else:
            from_ = vm.ByteStringStackItem(account_from.to_array())
        engine.call_from_native(
            self.hash, account_to, "onNEP17Payment",
            [from_, vm.IntegerStackItem(amount), data])
Example #2
0
def get_read_only_context(
        engine: contracts.ApplicationEngine) -> storage.StorageContext:
    contract = contracts.ManagementContract().get_contract(
        engine.snapshot, engine.current_scripthash)
    if contract is None:
        raise ValueError("Contract not deployed")
    return storage.StorageContext(contract.id, True)
Example #3
0
def contract_call(engine: contracts.ApplicationEngine,
                  contract_hash: types.UInt160, method: str,
                  call_flags: contracts.CallFlags,
                  args: vm.ArrayStackItem) -> None:
    if method.startswith("_"):
        raise ValueError("Invalid method name")

    target_contract = contracts.ManagementContract().get_contract(
        engine.snapshot, contract_hash)
    if target_contract is None:
        raise ValueError("[System.Contract.Call] Can't find target contract")

    method_descriptor = target_contract.manifest.abi.get_method(
        method, len(args))
    if method_descriptor is None:
        raise ValueError(
            f"[System.Contract.Call] Method '{method}' does not exist on target contract"
        )

    has_return_value = method_descriptor.return_type != contracts.ContractParameterType.VOID
    if not has_return_value:
        engine.current_context.evaluation_stack.push(vm.NullStackItem())

    engine._contract_call_internal2(target_contract, method_descriptor,
                                    call_flags, has_return_value, list(args))
    def checkwitness(self, hash_: types.UInt160) -> bool:
        """
        Check if the hash is a valid witness for the engines script_container
        """
        with suppress(ValueError):
            if hash_ == self.calling_scripthash:
                return True

        if isinstance(self.script_container, payloads.Transaction):
            tx = self.script_container

            response = tx.try_get_attribute(payloads.OracleResponse)
            if response is None:
                signers = tx.signers
            else:
                signers = []
                request = contracts.OracleContract().get_request(self.snapshot, response.id)
                if request:
                    tmp_tx = contracts.LedgerContract().get_tx_for_contract(self.snapshot, request.original_tx_id)
                    if tmp_tx:
                        signers = tmp_tx.signers

            for s in signers:
                if s.account == hash_:
                    signer = s
                    break
            else:
                return False

            if signer.scope == payloads.WitnessScope.GLOBAL:
                return True

            if payloads.WitnessScope.CALLED_BY_ENTRY in signer.scope:
                if self.calling_scripthash == self.entry_scripthash:
                    return True

            if payloads.WitnessScope.CUSTOM_CONTRACTS in signer.scope:
                if self.current_scripthash in signer.allowed_contracts:
                    return True

            if payloads.WitnessScope.CUSTOM_GROUPS in signer.scope:
                self._validate_callflags(contracts.CallFlags.READ_STATES)

                contract = contracts.ManagementContract().get_contract(self.snapshot, self.calling_scripthash)
                if contract is None:
                    return False
                group_keys = set(map(lambda g: g.public_key, contract.manifest.groups))
                if any(group_keys.intersection(signer.allowed_groups)):
                    return True
            return False

        self._validate_callflags(contracts.CallFlags.READ_STATES)

        # for other IVerifiable types like Block
        hashes_for_verifying = self.script_container.get_script_hashes_for_verifying(self.snapshot)
        return hash_ in hashes_for_verifying
Example #5
0
    def init(self):
        self._methods: Dict[int, _NativeMethodMeta] = {}  # offset, meta

        self._management = contracts.ManagementContract()
        self._neo = contracts.NeoToken()
        self._gas = contracts.GasToken()
        self._policy = contracts.PolicyContract()
        self._oracle = contracts.OracleContract()
        self._ledger = contracts.LedgerContract()
        self._role = contracts.DesignationContract()
        self._crypto = contracts.CryptoContract()
        self._stdlib = contracts.StdLibContract()

        # Find all methods that have been augmented by the @register decorator
        # and turn them into methods that can be called by VM scripts
        methods_meta = []
        for pair in inspect.getmembers(self,
                                       lambda m: hasattr(m, "native_call")):
            methods_meta.append(_NativeMethodMeta(pair[1]))

        methods_meta.sort(
            key=lambda x: (x.descriptor.name, len(x.descriptor.parameters)))

        sb = vm.ScriptBuilder()
        for meta in methods_meta:
            meta.descriptor.offset = len(sb)
            sb.emit_push(0)
            self._methods.update({len(sb): meta})
            sb.emit_syscall(1736177434)  # "System.Contract.CallNative"
            sb.emit(vm.OpCode.RET)

        self._script: bytes = sb.to_array()
        self.nef = contracts.NEF("neo-core-v3.0", self._script)

        sender = types.UInt160.zero()  # OpCode.PUSH1
        sb = vm.ScriptBuilder()
        sb.emit(vm.OpCode.ABORT)
        sb.emit_push(sender.to_array())
        sb.emit_push(0)
        sb.emit_push(self.service_name())
        self._hash: types.UInt160 = to_script_hash(sb.to_array())
        self._manifest: contracts.ContractManifest = contracts.ContractManifest(
        )
        self._manifest.name = self.service_name()
        self._manifest.abi.methods = list(
            map(lambda m: m.descriptor, methods_meta))

        if self._id != NativeContract._id:
            self._contracts.update({self.service_name(): self})
            self._contract_hashes.update({self._hash: self})

        self.active_block_index = settings.native_contract_activation.get(
            self.service_name, 0)
    def _contract_call_internal(self,
                                contract_hash: types.UInt160,
                                method: str,
                                flags: contracts.CallFlags,
                                has_return_value: bool,
                                args: List[vm.StackItem]) -> vm.ExecutionContext:
        target_contract = contracts.ManagementContract().get_contract(self.snapshot, contract_hash)
        if target_contract is None:
            raise ValueError("[System.Contract.Call] Can't find target contract")

        method_descriptor = target_contract.manifest.abi.get_method(method, len(args))
        if method_descriptor is None:
            raise ValueError(f"[System.Contract.Call] Method '{method}' with {len(args)} arguments does not exist on "
                             f"target contract")
        return self._contract_call_internal2(target_contract, method_descriptor, flags, has_return_value, args)
Example #7
0
    def _contract_call_internal2(
            self, target_contract: contracts.ContractState,
            method_descriptor: contracts.ContractMethodDescriptor,
            flags: contracts.CallFlags, has_return_value: bool,
            args: List[vm.StackItem]):
        if method_descriptor.safe:
            flags &= ~(contracts.CallFlags.WRITE_STATES
                       | contracts.CallFlags.ALLOW_NOTIFY)
        else:
            current_contract = contracts.ManagementContract().get_contract(
                self.snapshot, self.current_scripthash)
            if current_contract and not current_contract.can_call(
                    target_contract, method_descriptor.name):
                raise ValueError(
                    f"[System.Contract.Call] Not allowed to call target method '{method_descriptor.name}' according "
                    f"to manifest")

        counter = self._invocation_counter.get(target_contract.hash, 0)
        self._invocation_counter.update({target_contract.hash: counter + 1})

        state = self.current_context
        calling_script_hash_bytes = state.scripthash_bytes
        calling_flags = state.call_flags

        arg_len = len(args)
        expected_len = len(method_descriptor.parameters)
        if arg_len != expected_len:
            raise ValueError(
                f"[System.Contract.Call] Invalid number of contract arguments. Expected {expected_len} actual {arg_len}"
            )  # noqa

        if has_return_value ^ (method_descriptor.return_type !=
                               contracts.ContractParameterType.VOID):
            raise ValueError("Return value type does not match")

        context_new = self.load_contract(target_contract, method_descriptor,
                                         flags & calling_flags)
        if context_new is None:
            raise ValueError
        context_new.calling_scripthash_bytes = calling_script_hash_bytes

        for item in reversed(args):
            context_new.evaluation_stack.push(item)

        return context_new
Example #8
0
    def _post_transfer(self,
                       engine: contracts.ApplicationEngine,
                       account_from: types.UInt160,
                       account_to: types.UInt160,
                       token_id: bytes) -> None:
        state = vm.ArrayStackItem(engine.reference_counter)
        if account_from == types.UInt160.zero():
            state.append(vm.NullStackItem())
        else:
            state.append(vm.ByteStringStackItem(account_from.to_array()))
        if account_to == types.UInt160.zero():
            state.append(vm.NullStackItem())
        else:
            state.append(vm.ByteStringStackItem(account_to.to_array()))
        state.append(vm.IntegerStackItem(1))
        state.append(vm.ByteStringStackItem(token_id))

        msgrouter.interop_notify(self.hash, "Transfer", state)

        if account_to != types.UInt160.zero() and \
                contracts.ManagementContract().get_contract(engine.snapshot, account_to) is not None:
            engine.call_from_native(self.hash, account_to, "onNEP17Payment", list(state))
Example #9
0
def test_native_contract(contract_hash: types.UInt160,
                         operation: str,
                         args=None):
    engine = test_engine(has_snapshot=True)
    block = test_block(0)
    # or we won't pass the native deploy call
    engine.snapshot.persisting_block = block

    sb = vm.ScriptBuilder()
    sb.emit_dynamic_call(contract_hash, operation)

    script = vm.Script(sb.to_array())
    engine.load_script(script)

    # storing the current script in a contract otherwise "System.Contract.Call" will fail its checks
    nef = contracts.NEF(script=sb.to_array())
    manifest = contracts.ContractManifest("test_contract")
    next_id = contracts.ManagementContract().get_next_available_id(
        engine.snapshot)
    contract = contracts.ContractState(next_id + 1, nef, manifest, 0,
                                       to_script_hash(nef.script))
    engine.snapshot.contracts.put(contract)

    return engine
Example #10
0
    def _request(self,
                 engine: contracts.ApplicationEngine,
                 url: str,
                 filter: str,
                 callback: str,
                 user_data: vm.StackItem,
                 gas_for_response: int) -> None:
        if len(url.encode('utf-8')) > self._MAX_URL_LENGTH or \
                len(filter.encode('utf-8')) > self._MAX_FILTER_LEN or \
                len(callback.encode('utf-8')) > self._MAX_CALLBACK_LEN or \
                callback.startswith("_") or \
                gas_for_response < 10000000:
            raise ValueError

        engine.add_gas(self.get_price(engine.snapshot))
        engine.add_gas(gas_for_response)
        self._gas.mint(engine, self.hash, vm.BigInteger(gas_for_response), False)

        si_item_id = engine.snapshot.storages.get(self.key_request_id, read_only=False)
        item_id = vm.BigInteger(si_item_id.value)
        si_item_id.value = (item_id + 1).to_array()

        if contracts.ManagementContract().get_contract(engine.snapshot, engine.calling_scripthash) is None:
            raise ValueError

        oracle_request = OracleRequest(self._get_original_txid(engine),
                                       gas_for_response,
                                       url,
                                       filter,
                                       engine.calling_scripthash,
                                       callback,
                                       contracts.BinarySerializer.serialize(user_data, self._MAX_USER_DATA_LEN))
        engine.snapshot.storages.put(self.key_request + int(item_id).to_bytes(8, 'little', signed=False),
                                     storage.StorageItem(oracle_request.to_array())
                                     )

        sk_id_list = self.key_id_list + self._get_url_hash(url)
        si_id_list = engine.snapshot.storages.try_get(sk_id_list, read_only=False)
        if si_id_list is None:
            si_id_list = storage.StorageItem(b'\x00')

        with serialization.BinaryReader(si_id_list.value) as reader:
            count = reader.read_var_int()
            id_list = []
            for _ in range(count):
                id_list.append(reader.read_uint64())

        id_list.append(item_id)
        if len(id_list) >= 256:
            raise ValueError("Oracle has too many pending responses for this url")

        with serialization.BinaryWriter() as writer:
            writer.write_var_int(len(id_list))
            for id in id_list:
                writer.write_uint64(id)
            si_id_list.value = writer.to_array()
        engine.snapshot.storages.update(sk_id_list, si_id_list)

        state = vm.ArrayStackItem(
            engine.reference_counter,
            [vm.IntegerStackItem(item_id),
             vm.ByteStringStackItem(engine.calling_scripthash.to_array()),
             vm.ByteStringStackItem(url.encode()),
             vm.ByteStringStackItem(filter.encode()),
             ]
        )

        msgrouter.interop_notify(self.hash, "OracleRequest", state)