Ejemplo n.º 1
0
    def invoke(self, engine: contracts.ApplicationEngine, version: int) -> None:
        """
        Calls a contract function

        Reads the required arguments from the engine's stack and converts them to the appropiate contract function types

        Args:
            engine: the engine executing the smart contract
            version: which version of the smart contract to load

        Raises:
             ValueError: if the request contract version is not
             ValueError: if the function to be called does not exist on the contract
             ValueError: if trying to call a function without having the correct CallFlags
        """
        if version != 0:
            raise ValueError(f"Native contract version {version} is not active")  # type: ignore

        context = engine.current_context
        flags = contracts.CallFlags(context.call_flags)
        method = self._methods.get(context.ip, None)
        if method is None:
            raise ValueError(f"Method at IP \"{context.ip}\" does not exist on contract {self.service_name()}")
        if method.required_flags not in flags:
            raise ValueError(f"Method requires call flag: {method.required_flags} received: {flags}")

        engine.add_gas(method.cpu_price
                       * contracts.PolicyContract().get_exec_fee_factor(engine.snapshot)
                       + method.storage_price
                       * contracts.PolicyContract().get_storage_price(engine.snapshot))

        params: List[Any] = []
        if method.add_engine:
            params.append(engine)

        if method.add_snapshot:
            params.append(engine.snapshot)

        for t in method.parameter_types:
            params.append(engine._stackitem_to_native(context.evaluation_stack.pop(), t))

        if len(params) > 0:
            return_value = method.handler(*params)
        else:
            return_value = method.handler()
        if method.return_type is not None:
            context.evaluation_stack.push(engine._native_to_stackitem(return_value, type(return_value)))
Ejemplo n.º 2
0
    def transfer(self,
                 engine: contracts.ApplicationEngine,
                 account_from: types.UInt160,
                 account_to: types.UInt160,
                 amount: vm.BigInteger,
                 data: vm.StackItem
                 ) -> bool:
        """
        Transfer tokens from one account to another.

        Raises:
            ValueError: if the requested amount is negative.

        Returns:
            True on success. False otherwise.
        """
        if amount.sign < 0:
            raise ValueError("Can't transfer a negative amount")

        # transfer from an account not owned by the smart contract that is requesting the transfer
        # and there is no signature that approves we are allowed todo so
        if account_from != engine.calling_scripthash and not engine.checkwitness(account_from):
            return False

        storage_key_from = self.key_account + account_from
        storage_item_from = engine.snapshot.storages.try_get(storage_key_from, read_only=False)

        if storage_item_from is None:
            return False

        state_from = storage_item_from.get(self._state)
        if amount == vm.BigInteger.zero():
            self.on_balance_changing(engine, account_from, state_from, amount)
        else:
            if state_from.balance < amount:
                return False

            if account_from == account_to:
                self.on_balance_changing(engine, account_from, state_from, vm.BigInteger.zero())
            else:
                self.on_balance_changing(engine, account_from, state_from, -amount)
                if state_from.balance == amount:
                    engine.snapshot.storages.delete(storage_key_from)
                else:
                    state_from.balance -= amount

                storage_key_to = self.key_account + account_to
                storage_item_to = engine.snapshot.storages.try_get(storage_key_to, read_only=False)
                if storage_item_to is None:
                    storage_item_to = storage.StorageItem(self._state().to_array())
                    engine.snapshot.storages.put(storage_key_to, storage_item_to)

                state_to = storage_item_to.get(self._state)

                self.on_balance_changing(engine, account_to, state_to, amount)
                state_to.balance += amount

        self._post_transfer(engine, account_from, account_to, amount, data, True)
        return True
Ejemplo n.º 3
0
    def contract_update_with_data(self, engine: contracts.ApplicationEngine,
                                  nef_file: bytes, manifest: bytes,
                                  data: vm.StackItem) -> None:
        nef_len = len(nef_file)
        manifest_len = len(manifest)

        engine.add_gas(engine.STORAGE_PRICE * (nef_len + manifest_len))

        contract = engine.snapshot.contracts.try_get(engine.calling_scripthash,
                                                     read_only=False)
        if contract is None:
            raise ValueError("Can't find contract to update")

        if nef_len == 0:
            raise ValueError(f"Invalid NEF length: {nef_len}")

        # update contract
        contract.nef = contracts.NEF.deserialize_from_bytes(nef_file)

        if manifest_len == 0 or manifest_len > contracts.ContractManifest.MAX_LENGTH:
            raise ValueError(f"Invalid manifest length: {manifest_len}")

        manifest_new = contracts.ContractManifest.from_json(
            json.loads(manifest.decode()))
        if manifest_new.name != contract.manifest.name:
            raise ValueError("Error: cannot change contract name")
        if not contract.manifest.is_valid(contract.hash):
            raise ValueError("Error: manifest does not match with script")
        contract.manifest = manifest_new

        self.validate(contract.nef.script, contract.manifest.abi)

        contract.update_counter += 1

        if len(nef_file) != 0:
            method_descriptor = contract.manifest.abi.get_method("_deploy", 2)
            if method_descriptor is not None:
                engine.call_from_native(self.hash, contract.hash,
                                        method_descriptor.name,
                                        [data, vm.BooleanStackItem(True)])

        msgrouter.interop_notify(
            self.hash, "Update",
            vm.ArrayStackItem(engine.reference_counter,
                              vm.ByteStringStackItem(
                                  contract.hash.to_array())))
Ejemplo n.º 4
0
    def vote(self, engine: contracts.ApplicationEngine, account: types.UInt160,
             vote_to: cryptography.ECPoint) -> bool:
        """
        Vote on a consensus candidate
        Args:
            engine: Application engine instance.
            account: source account to take voting balance from
            vote_to: candidate public key

        Returns:
            True is vote registered succesfully. False otherwise.
        """
        if not engine.checkwitness(account):
            return False

        storage_key_account = self.key_account + account
        storage_item = engine.snapshot.storages.try_get(storage_key_account,
                                                        read_only=False)
        if storage_item is None:
            return False
        account_state = storage_item.get(self._state)

        storage_key_candidate = self.key_candidate + vote_to
        storage_item_candidate = engine.snapshot.storages.try_get(
            storage_key_candidate, read_only=False)
        if storage_item_candidate is None:
            return False

        candidate_state = storage_item_candidate.get(_CandidateState)
        if not candidate_state.registered:
            return False

        if account_state.vote_to.is_zero():
            si_voters_count = engine.snapshot.storages.get(
                self.key_voters_count, read_only=False)

            old_value = vm.BigInteger(si_voters_count.value)
            new_value = old_value + account_state.balance
            si_voters_count.value = new_value.to_array()

        self._distribute_gas(engine, account, account_state)

        if not account_state.vote_to.is_zero():
            sk_validator = self.key_candidate + account_state.vote_to
            si_validator = engine.snapshot.storages.get(sk_validator,
                                                        read_only=False)
            validator_state = si_validator.get(_CandidateState)
            validator_state.votes -= account_state.balance

            if not validator_state.registered and validator_state.votes == 0:
                engine.snapshot.storages.delete(sk_validator)

        account_state.vote_to = vote_to
        candidate_state.votes += account_state.balance
        self._candidates_dirty = True

        return True
Ejemplo n.º 5
0
    def do_register(self, engine: contracts.ApplicationEngine, name: str,
                    owner: types.UInt160) -> bool:
        if not self.is_available(engine.snapshot, name):
            raise ValueError(
                f"Registration failure - '{name}' is not available")

        if not engine.checkwitness(owner):
            raise ValueError("CheckWitness failed")
        engine.add_gas(self.get_price(engine.snapshot))

        state = NameState(
            owner, name, (engine.snapshot.persisting_block.timestamp // 1000) +
            self.ONE_YEAR)
        self.mint(engine, state)
        engine.snapshot.storages.put(
            self.key_expiration + state.expiration.to_bytes(4, 'big') +
            name.encode(), storage.StorageItem(b'\x00'))
        return True
Ejemplo n.º 6
0
    def set_admin(self, engine: contracts.ApplicationEngine, name: str,
                  admin: types.UInt160) -> None:
        if not self.REGEX_NAME.match(name):
            raise ValueError("Regex failure - name is not valid")
        names = name.split(".")

        if len(names) != 2:
            raise ValueError("Invalid format")

        if admin != types.UInt160.zero() and not engine.checkwitness(admin):
            raise ValueError("New admin is not valid - check witness failed")

        storage_item = engine.snapshot.storages.get(self.key_token +
                                                    name.encode())
        state = storage_item.get(NameState)
        if not engine.checkwitness(state.owner):
            raise ValueError

        state.admin = admin
Ejemplo n.º 7
0
 def new_engine(
         previous_engine: ApplicationEngine = None) -> ApplicationEngine:
     tx = payloads.Transaction._serializable_init()
     if not previous_engine:
         blockchain.Blockchain.__it__ = None
         snapshot = blockchain.Blockchain(
             store_genesis_block=True
         ).currentSnapshot  # blockchain is singleton
         return ApplicationEngine(contracts.TriggerType.APPLICATION,
                                  tx,
                                  snapshot,
                                  0,
                                  test_mode=True)
     else:
         return ApplicationEngine(contracts.TriggerType.APPLICATION,
                                  tx,
                                  previous_engine.snapshot,
                                  0,
                                  test_mode=True)
Ejemplo n.º 8
0
def do_checkwitness(engine: contracts.ApplicationEngine, data: bytes) -> bool:
    if len(data) == 20:
        hash_ = types.UInt160(data)
    elif len(data) == 33:
        redeemscript = contracts.Contract.create_signature_redeemscript(
            cryptography.ECPoint.deserialize_from_bytes(data))
        hash_ = to_script_hash(redeemscript)
    else:
        raise ValueError("Supplied CheckWitness data is not a valid hash")

    return engine.checkwitness(hash_)
Ejemplo n.º 9
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))
Ejemplo n.º 10
0
def contract_create(engine: contracts.ApplicationEngine, script: bytes, manifest: bytes) -> storage.ContractState:
    script_len = len(script)
    manifest_len = len(manifest)
    if (script_len == 0
            or script_len > engine.MAX_CONTRACT_LENGTH
            or manifest_len == 0
            or manifest_len > contracts.ContractManifest.MAX_LENGTH):
        raise ValueError("Invalid script or manifest length")

    engine.add_gas(engine.STORAGE_PRICE * (script_len + manifest_len))

    hash_ = to_script_hash(script)
    contract = engine.snapshot.contracts.try_get(hash_)
    if contract is not None:
        raise ValueError("Contract already exists")

    contract = storage.ContractState(script, contracts.ContractManifest.from_json(json.loads(manifest.decode())))
    if not contract.manifest.is_valid(hash_):
        raise ValueError("Error: manifest does not match with script")

    engine.snapshot.contracts.put(contract)
    return contract
Ejemplo n.º 11
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))
Ejemplo n.º 12
0
    def invoke_method_of_arbitrary_contract(
            self,
            contract_hash: Union[UInt160, Hash160Str, str],
            method: str,
            params: List = None,
            signers: List[Union[str, UInt160, payloads.Signer]] = None,
            scope: payloads.WitnessScope = payloads.WitnessScope.GLOBAL,
            engine: ApplicationEngine = None):
        if params is None:
            params = []
        params = list(map(lambda param: self.param_auto_checker(param),
                          params))
        if not engine:
            engine = self.new_engine(self.previous_engine)

        contract_hash = self.contract_hash_auto_checker(contract_hash)
        # engine.load_script(vm.Script(contract.script))
        sb = vm.ScriptBuilder()
        if params:
            sb.emit_dynamic_call_with_args(contract_hash, method, params)
        else:
            sb.emit_dynamic_call(contract_hash, method)
        engine.load_script(vm.Script(sb.to_array()))

        if signers and signers != self.NO_SIGNER:
            signers = list(
                map(lambda signer: self.signer_auto_checker(signer, scope),
                    signers))
            engine.script_container.signers = signers
        elif self.signers:  # use signers stored in self when no external signer specified
            engine.script_container.signers = self.signers

        engine.execute()
        engine.snapshot.commit()
        self.previous_engine = engine
        return engine
Ejemplo n.º 13
0
 def _check_committee(self, engine: contracts.ApplicationEngine) -> bool:
     addr = contracts.NeoToken().get_committee_address(engine.snapshot)
     return engine.checkwitness(addr)
Ejemplo n.º 14
0
def get_invocationcounter(engine: contracts.ApplicationEngine) -> int:
    return engine.get_invocation_counter()
Ejemplo n.º 15
0
def do_burn_gas(engine: contracts.ApplicationEngine, gas: int) -> None:
    if gas <= 0:
        raise ValueError("Burn gas cannot be called with negative value")
    engine.add_gas(gas)
Ejemplo n.º 16
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)