예제 #1
0
def check_multisig_with_ECDSA_Secp256r1(engine: contracts.ApplicationEngine,
                                        public_keys: List[bytes],
                                        signatures: List[bytes]) -> bool:
    len_pub_keys = len(public_keys)
    len_sigs = len(signatures)
    if len_sigs == 0:
        raise ValueError("No signatures supplied")
    if len_pub_keys == 0:
        raise ValueError("No public keys supplied")
    if len_sigs > len_pub_keys:
        raise ValueError(f"Verification requires {len_sigs} public keys, got only {len_pub_keys}")

    message = engine.script_container.get_hash_data(settings.network.magic)

    engine.add_gas(len_pub_keys * CHECKSIG_PRICE * engine.exec_fee_factor)

    i = 0
    j = 0
    try:
        while i < len_sigs and j < len_pub_keys:
            if cryptography.verify_signature(message, signatures[i], public_keys[j], cryptography.ECCCurve.SECP256R1):
                i += 1
            j += 1

            if len_sigs - i > len_pub_keys - j:
                return False
    except cryptography.ECCException as e:
        return False
    return True
예제 #2
0
    def register_candidate(self, engine: contracts.ApplicationEngine,
                           public_key: cryptography.ECPoint) -> bool:
        """
        Register a candidate for consensus node election.

        Args:
            engine: Application engine instance
            public_key: the candidate's public key

        Returns:
            True is succesfully registered. False otherwise.
        """
        script_hash = to_script_hash(
            contracts.Contract.create_signature_redeemscript(public_key))
        if not engine.checkwitness(script_hash):
            return False

        engine.add_gas(self.get_register_price(engine.snapshot))
        storage_key = self.key_candidate + public_key
        storage_item = engine.snapshot.storages.try_get(storage_key,
                                                        read_only=False)
        if storage_item is None:
            state = _CandidateState()
            state.registered = True
            storage_item = storage.StorageItem(state.to_array())
            engine.snapshot.storages.put(storage_key, storage_item)
        else:
            state = storage_item.get(_CandidateState)
            state.registered = True
        self._candidates_dirty = True
        return True
예제 #3
0
def _storage_put_internal(engine: contracts.ApplicationEngine,
                          context: storage.StorageContext, key: bytes,
                          value: bytes, flags: storage.StorageFlags) -> None:
    if len(key) > MAX_STORAGE_KEY_SIZE:
        raise ValueError(
            f"Storage key length exceeds maximum of {MAX_STORAGE_KEY_SIZE}")
    if len(value) > MAX_STORAGE_VALUE_SIZE:
        raise ValueError(
            f"Storage value length exceeds maximum of {MAX_STORAGE_VALUE_SIZE}"
        )
    if context.is_read_only:
        raise ValueError("Cannot persist to read-only storage context")

    storage_key = storage.StorageKey(context.script_hash, key)
    item = engine.snapshot.storages.try_get(storage_key, read_only=False)

    is_constant = storage.StorageFlags.CONSTANT in flags
    if item is None:
        new_data_len = len(key) + len(value)
        item = storage.StorageItem(b'', is_constant)
        engine.snapshot.storages.put(storage_key, item)
    else:
        if item.is_constant:
            raise ValueError("StorageItem is marked as constant")
        if len(value) <= len(item.value):
            new_data_len = 1
        else:
            new_data_len = len(value) - len(item.value)

    engine.add_gas(new_data_len * STORAGE_PRICE)
    item.value = value
    item.is_constant = is_constant
예제 #4
0
def _check_multisig(engine: contracts.ApplicationEngine,
                    stack_item: vm.StackItem, public_keys: List[bytes],
                    signatures: List[bytes],
                    curve: cryptography.ECCCurve) -> bool:
    len_pub_keys = len(public_keys)
    len_sigs = len(signatures)
    if len_sigs == 0:
        raise ValueError("No signatures supplied")
    if len_pub_keys == 0:
        raise ValueError("No public keys supplied")
    if len_sigs > len_pub_keys:
        raise ValueError(
            f"Verification requires {len_sigs} public keys, got only {len_pub_keys}"
        )

    message = stackitem_to_hash_data(engine, stack_item)

    engine.add_gas(len_pub_keys * 1000000)

    i = 0
    j = 0
    try:
        while i < len_sigs and j < len_pub_keys:
            if cryptography.verify_signature(message, signatures[i],
                                             public_keys[j], curve):
                i += 1
            j += 1

            if len_sigs - i > len_pub_keys - j:
                return False
    except cryptography.ECCException as e:
        return False
    return True
예제 #5
0
def storage_put(engine: contracts.ApplicationEngine,
                context: storage.StorageContext, key: bytes,
                value: bytes) -> None:
    if len(key) > MAX_STORAGE_KEY_SIZE:
        raise ValueError(
            f"Storage key length exceeds maximum of {MAX_STORAGE_KEY_SIZE}")
    if len(value) > MAX_STORAGE_VALUE_SIZE:
        raise ValueError(
            f"Storage value length exceeds maximum of {MAX_STORAGE_VALUE_SIZE}"
        )
    if context.is_read_only:
        raise ValueError("Cannot persist to read-only storage context")

    storage_key = storage.StorageKey(context.id, key)
    item = engine.snapshot.storages.try_get(storage_key, read_only=False)

    if item is None:
        new_data_len = len(key) + len(value)
        item = storage.StorageItem(b'')
        engine.snapshot.storages.put(storage_key, item)
    else:
        if len(value) == 0:
            new_data_len = 0
        elif len(value) <= len(item.value):
            new_data_len = (len(value) - 1) // 4 + 1
        elif len(item.value) == 0:
            new_data_len = len(value)
        else:
            new_data_len = (len(item.value) - 1) // 4 + 1 + len(value) - len(
                item.value)

    engine.add_gas(new_data_len * engine.STORAGE_PRICE)
    item.value = value
예제 #6
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)))
예제 #7
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
예제 #8
0
def contract_update(engine: contracts.ApplicationEngine, script: bytes, manifest: bytes) -> None:
    script_len = len(script)
    manifest_len = len(manifest)

    # TODO: In preview 4 revert back to
    # engine.add_gas(engine.STORAGE_PRICE * (script_len + manifest_len))
    # They made a mistake in their storage price calculation logic where manifest size is never taken into account
    engine.add_gas(engine.STORAGE_PRICE * script_len)

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

    if script_len == 0 or script_len > engine.MAX_CONTRACT_LENGTH:
        raise ValueError(f"Invalid script length: {script_len}")

    hash_ = to_script_hash(script)
    if hash_ == engine.current_scripthash or engine.snapshot.contracts.try_get(hash_) is not None:
        raise ValueError("Nothing to update")

    old_contract_has_storage = contract.has_storage
    contract = storage.ContractState(script, contract.manifest)
    contract.manifest.abi.contract_hash = hash_

    engine.snapshot.contracts.put(contract)

    # migrate storage to new contract hash
    with blockchain.Blockchain().backend.get_snapshotview() as snapshot:
        if old_contract_has_storage:
            for key, value in snapshot.storages.find(engine.current_scripthash, b''):
                # delete the old storage
                snapshot.storages.delete(key)
                # update key to new contract hash
                key.contract = contract.script_hash()
                # now persist all data under new contract key
                snapshot.storages.put(key, value)
        snapshot.commit()
    engine.snapshot.contracts.delete(engine.current_scripthash)

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

    contract.manifest = contracts.ContractManifest.from_json(json.loads(manifest.decode()))
    if not contract.manifest.is_valid(contract.script_hash()):
        raise ValueError("Error: manifest does not match with script")
    if (not contract.has_storage
            and len(list(engine.snapshot.storages.find(contract.script_hash(), key_prefix=b''))) != 0):
        raise ValueError("Error: New contract does not support storage while old contract has existing storage")
예제 #9
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())))
예제 #10
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
예제 #11
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
예제 #12
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)
예제 #13
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)