Beispiel #1
0
    def designate_as_role(self, engine: contracts.ApplicationEngine,
                          role: DesignateRole,
                          nodes: List[cryptography.ECPoint]) -> None:
        if len(nodes) == 0:
            raise ValueError(
                "[DesignateContract] Cannot designate empty nodes list")

        if len(nodes) > 32:
            raise ValueError(
                "[DesignateContract] Cannot designate a nodes list larger than 32"
            )

        if not self._check_committee(engine):
            raise ValueError("[DesignateContract] check committee failed")

        if engine.snapshot.persisting_block is None:
            raise ValueError

        nodes.sort()
        index = engine.snapshot.persisting_block.index + 1
        storage_key = self.create_key(
            role.to_bytes(1, 'little') + self._to_uint32(index))
        with serialization.BinaryWriter() as writer:
            writer.write_serializable_list(nodes)
            storage_item = storage.StorageItem(writer.to_array())
        engine.snapshot.storages.update(storage_key, storage_item)

        state = vm.ArrayStackItem(engine.reference_counter)
        state.append(vm.IntegerStackItem(role.value))
        state.append(
            vm.IntegerStackItem(engine.snapshot.persisting_block.index))
        msgrouter.interop_notify(self.hash, "Designation", state)
Beispiel #2
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])
Beispiel #3
0
    def finish(self, engine: contracts.ApplicationEngine) -> None:
        tx = engine.script_container
        tx = cast(payloads.Transaction, tx)
        response = tx.try_get_attribute(payloads.OracleResponse)
        if response is None:
            raise ValueError("Oracle response not found")

        request = self.get_request(engine.snapshot, response.id)
        if request is None:
            raise ValueError("Oracle request not found")

        state = vm.ArrayStackItem(
            engine.reference_counter,
            [vm.IntegerStackItem(response.id),
             vm.ByteStringStackItem(request.original_tx_id.to_array())
             ]
        )

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

        user_data = contracts.BinarySerializer.deserialize(request.user_data,
                                                           engine.MAX_STACK_SIZE,
                                                           engine.reference_counter)
        args: List[vm.StackItem] = [vm.ByteStringStackItem(request.url.encode()),
                                    user_data,
                                    vm.IntegerStackItem(int(response.code)),
                                    vm.ByteStringStackItem(response.result)]

        engine.call_from_native(self.hash, request.callback_contract, request.callback_method, args)
Beispiel #4
0
    def contract_create_with_data(
            self, engine: contracts.ApplicationEngine, nef_file: bytes,
            manifest: bytes, data: vm.StackItem) -> contracts.ContractState:
        if not isinstance(engine.script_container, payloads.Transaction):
            raise ValueError(
                "Cannot create contract without a Transaction script container"
            )

        nef_len = len(nef_file)
        manifest_len = len(manifest)
        if (nef_len == 0 or nef_len > engine.MAX_CONTRACT_LENGTH
                or manifest_len == 0
                or manifest_len > contracts.ContractManifest.MAX_LENGTH):
            raise ValueError("Invalid NEF or manifest length")

        engine.add_gas(
            max(engine.STORAGE_PRICE * (nef_len + manifest_len),
                self.get_minimum_deployment_fee(engine.snapshot)))

        nef = contracts.NEF.deserialize_from_bytes(nef_file)
        parsed_manifest = contracts.ContractManifest.from_json(
            json.loads(manifest.decode()))

        self.validate(nef.script, parsed_manifest.abi)

        sb = vm.ScriptBuilder()
        sb.emit(vm.OpCode.ABORT)
        sb.emit_push(engine.script_container.sender.to_array())
        sb.emit_push(nef.checksum)
        sb.emit_push(parsed_manifest.name)
        hash_ = to_script_hash(sb.to_array())

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

        contract = contracts.ContractState(
            self.get_next_available_id(engine.snapshot), nef, parsed_manifest,
            0, hash_)
        if not contract.manifest.is_valid(hash_):
            raise ValueError("Error: invalid manifest")
        engine.snapshot.contracts.put(contract)

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

        msgrouter.interop_notify(
            self.hash, "Deploy",
            vm.ArrayStackItem(engine.reference_counter,
                              vm.ByteStringStackItem(
                                  contract.hash.to_array())))
        return contract
Beispiel #5
0
def do_notify(engine: contracts.ApplicationEngine, event_name: bytes, state: vm.ArrayStackItem) -> None:
    """

    Args:
        engine:
        event_name:
        state: values belonging to the notification event.
        e.g. a NEP-17 transfer event might have as state: from script_hash, to script_hash and an ammount
    """
    if len(event_name) > engine.MAX_EVENT_SIZE:
        raise ValueError(
            f"Notify event name length ({len(event_name)}) exceeds maximum allowed ({engine.MAX_EVENT_SIZE})")
    # will validate size + cyclic references
    contracts.BinarySerializer.serialize(state, engine.MAX_NOTIFICATION_SIZE)
    engine.notifications.append((engine.script_container, engine.current_scripthash, event_name, state))
    msgrouter.interop_notify(engine.current_scripthash, event_name.decode('utf-8'), state)
Beispiel #6
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())))
Beispiel #7
0
    def contract_destroy(self, engine: contracts.ApplicationEngine) -> None:
        hash_ = engine.calling_scripthash
        contract = engine.snapshot.contracts.try_get(hash_)

        if contract is None:
            return

        engine.snapshot.contracts.delete(hash_)

        for key, _ in engine.snapshot.storages.find(
                contract.id.to_bytes(4, 'little', signed=True)):
            engine.snapshot.storages.delete(key)

        msgrouter.interop_notify(
            self.hash, "Destroy",
            vm.ArrayStackItem(engine.reference_counter,
                              vm.ByteStringStackItem(
                                  contract.hash.to_array())))
Beispiel #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))
Beispiel #9
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)