Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

SEAL contracts interface 2.0 #3503

@Robbepop

Description

@Robbepop

SRML contracts interface 2.0

For a collaborative HackMD go here.

The current SRML contracts host interface that can be found here with which smart contracts such as being developed by ink! are communicating with their host side has several problems.

This issue is trying to summarize the problems and provide a solution that comes in the form of a new SRML contracts host interface that is presented as documented code below.

Current State

Currently the SRML contract implementation internally makes heavy use of a so-called scratch buffer that acts as a bridge to cache data between associated calls from the client (aka smart contract) side to the host side.

An example of this usage is when a smart contract reads data from its contract storage.
For that it first needs to call ext_get_storage(key). Upon success this writes the storage data found for key into the scratch buffer. Then the smart contract has to load this data by first calling ext_scratch_size in order to know how many bytes it needs to allocate on the client side and followed by a final call to ext_scratch_copy/read to finally copy the actual data to the smart contract side.
In total just for retrieving data of a single storage entry 3 client-to-host calls were necessary and also the interface that works through the scratch buffer mandates implementers (aka the SRML host side) to actually have such a mechanic built into them.
Besides having to use 3 calls there is also an unnecessary copying of the scratch buffer where the host side first copies all the data to the host scratch buffer just to immediately after copy it from the scratch buffer to the client

There are many other calls that directly mandate a scratch buffer and suffer from similar problems.

Summary Problem

So all in all we have the following problems that we want to fix:

  • We do not want to require about 3 calls between the client and host side for most operations.
    Best would be a single call.
  • We do not want to mandate the existence of a scratch buffer by any given interface.
    Note that it shall still be possible for implementers of the SRML host side to use such as scratch buffer (or many of them) in their respective implementations but the interface side no longer mentions them or even mandates their existence. This allows for future proofing the host side and allows for future improvements as soon as we come up with better ideas on how to manage client-host interactions.
  • The mandatory scratch buffer is especially bad when thinking about asynchronous operations sharing of state since having a single scratch buffer creates an interconnection and shared memory between all calls that use the single scratch buffer. An interface that mandates to have a single scratch buffer does not allow for host side implementations that want to provide a more robust or stable solution to their clients.

Solution Overview

The proposed SRML host interface changes several underlying core concepts.

All ext_* functions that were simple getters for some information, e.g. ext_caller, ext_gas_left etc. are being unified with the concept of properties. A contract execution property can be readable, writable or read-and-writable.

The table of properties follows:

Name CODE_NAME Modifiers Description
caller PROPERTY_CALLER read-only The account ID of the caller of the smart contract execution.
address PROPERTY_ADDRESS read-only The account ID of the executed smart contract.
gas price PROPERTY_GAS_PRICE read-only The current price for gas.
gas left PROPERTY_GAS_LEFT read-only The amount of gas left for further execution.
balance PROPERTY_BALANCE read-only The amount of balance the contract currently owns.
value transferred PROPERTY_VALUE_TRANSFERRED read-only The amount of value that has been transferred upon contract execution.
now PROPERTY_NOW read-only The time stamp of the latest block.
rent allowance PROPERTY_RENT_ALLOWANCE read & write The amount of balance a contract has to have before it is being submerged by storage rent.
block number PROPERTY_BLOCK_NUMBER read-only The number of the current block.
input PROPERTY_INPUT read-only The input buffer provided to a contract upon execution.
output PROPERTY_OUTPUT write-only The output buffer provided by a contract upon finishing execution. This is how a contract returns values to the caller.
existential deposit PROPERTY_EXISTENTIAL_DEPOSIT read-only The amount of balance a contract needs in order to retain.

With these predefined properties we now have a unified way of getting and setting them from within a smart contract with:

/// Getting the value of properties.
fn srml_get_properties(
    properties: Properties,
    result: MutPtr,
    result_len: PtrSize,
    req_result_len: MutPtr
) -> RetCode;

/// Setting the value of properties.
fn srml_set_property(
    propery: Property,
    input: Ptr,
    input_len: PtrSize,
) -> RetCode;

The normal procedure to retrieve a value with this setup is the following:
Let us assume that the smart contract wants to retrieve the current caller of the execution.
For this the smart contract initially has to do the follwing.

// This variable will tell us how large the buffer is required to be (in bytes) to hold the actual result.
let mut req_result_len = 0;
// This call is going to write into our `req_result_len` and provides us with the amount of bytes
// needed for the `result` buffer to hold the caller value.
let ret_code = srml_get_properties(PROPERTY_CALLER, 0, 0, &mut req_result_len);
if ret_code == 0 { // 0 is SUCCESS
    let mut result = vec![0x0; req_result_len];
    let ret_code = srml_get_properties(PROPERTY_CALLER, &mut result, result.len(), 0);
    if ret_code == 0 {
        // our value has been loaded successfully
    }
}

This only required two calls between client and host and also avoids an additional mandatory copying of bytes within the host side (to the scratch buffer that is no longer even mentioned here).
Note that some SRML host implementations might still use a scratch buffer internally and thus might still suffer from the problems of double-copying. However, this interface allows implementers to be creative and find better ways.

Also this interface allows to go even further.
If the client (smart contract) side already knows ahead of time what size of a buffer is required in order to hold the result (or if it just provides a big enough buffer conservatively) the first call to query the size is no longer needed and we can instead do:

let mut result = vec![0x0; req_result_len];
let ret_code = srml_get_properties(PROPERTY_CALLER, &mut result, result.len(), 0);
if ret_code == 0 {
    // our value has been loaded successfully
}

This way we only have a single call between client and host side.
Generally it is to be expected that a smart contract knows the encoded sizes of many of the values that have a fixed encoding such as AccountId, Hash or Balance of the default SRML setup.

The same kind of informational retrieval can be applied upon setting the value via srml_set_property.

Property Checking upon putCode

Upon putting the code of a smart contract on the chain it would be ideal if there was a guard that checks if calls to srml_get_properties are always using a compile-time known value for the properties field.
This allows us to treat srml_get_properties
as if it was a normal function for all possible combinations of valid sets of properties and thus allows for simple analysis of the smart contract code of future static code analysis tools.

Note that this is not a requirement for the proposed interface.

Multi Return

Another feature this interface allows is to retrieve multiple properties at the same time.
So if a smart contract requires the information about the caller, its value and its address it can simply invoke the following code to do that:

srml_get_properties(
    PROPERTY_CALLER | PROPERTY_BALANCE | PROPERTY_ADDRESS,
    ...
);

Note that these values are going to be returned by srml_get_properties as a tuple in the order that is specified by their bit representation. Since PROPERTY_CALLER is defined as 1 << 0, PROPERTY_BALANCE as 1 << 4 and PROPERTY_ADDRESS as 1 << 1 the order in which they are being returned as a tuple is: (PROPERTY_CALLER, PROPERTY_ADDRESS, PROPERTY_BALANCE).

This way we have just 2 or even just 1 call instead of 3*N calls between the client and the host where N is the number of properties the client needs to load.

Multi Write

Similar to multi read we could also provide a multi write approach to write multiple properties at once. However, I felt that the cost-complexity scale is not as good as the in the multi-read approach since users of the library would have to take extra care upon encoding the values in the correct order while at multi-read this is done by the host side once and for all.

No more Scratch Buffer

We no longer have the notion of ext_scratch_read/write/copy and also any other function provided by this interface no longer requires the existence of the scratch buffer.

Note again that implementers of the SRML host side are free to implement the proposed interface with a scratch buffer internally. This has the effect that it should generally be not too much work to apply the proposed interface to the current SRML contracts host implementation and future proofing it by that by allowing to go further as soon as ideas for progress come up.

Contract Input

When a contract is called by calling create or call the scratch buffer was formerly used to provide it with the input data for its execution.
This is now replaced by the PROPERTY_INPUT property.
Instead of copying from the scratch buffer the smart contract can simply call srml_get_properties(PROPERTY_INPUT, ...) in order to retrieve its input data.

A smart contract may only make a single call to srml_get_properties where PROPERTY_INPUT is contained in the properties set. Upon further calls the SRML contracts host implementer is free to return an appropriate host-defined error is back to the client.

Contract Output

When a contract finished execution in the current interface it stores the encoded return value into the scratch buffer.
With the proposed interface the smart contract would instead make use of the PROPERTY_OUTPUT property in order to store the encoded result value upon finishing its execution.
This is done by calling srml_set_property(PROPERTY_OUTPUT, ...).

A smart contract may only make a single call to srml_set_property(PROPERTY_OUTPUT, ...) per execution. An SRML contracts hosts implementer is free to ignore further calls to it, to always take the last set output or to return an appropriate host-defined error back to the client.

Naming Convention

Since ext_* is too general as a namespace in my opinion I preferred the slightly longer srml_* prefix for the 2.0 interface. Open to discussions about changing this. (Opinionated)

Error Handling

Error handling in this interface is unified in that every interface function always returns a RetCode which indicates if a call was successful or errorsome.
In case of an error a detailed list of errors allows the client (smart contract) to easily identify the type of error of the respective call.

All interface functions return RETCODE_INVALID_INPUTS whenever the combination of inputs given to it was not allowed by the function. An example for this was if srml_get_properties was called with a non-null result buffer and a non-null req_result_len value.

Other interface functions might have specialized error codes such as
srml_deposit_event that returns RETCODE_TOO_MANY_EVENT_TOPICS in case there were given too many event topics.

Note to Implementers

Peak Memory Limit

Any valid SRML host implementer has to make sure that the total
memory consumption required by a concrete implementation is always below PREAK_MEMORY_LIMIT. Where PEAK_MEMORY_LIMIT is generally defined as

/// The maximum amount of bytes that may be used by any SRML host 
/// implementation at the same time.
///
/// # Note
///
/// This value exists as special requirement to implementers and is
/// generally not in technical use.
/// This value must always been greater or equal to `MAX_BUFFER_LEN`.
const PEAK_MEMORY_LIMIT: u32 = 1 << 24;

Guaranteed Double-Fetch protection

A getter function in the SRML contracts host interface is any interface function that uses the result, result_len and req_result_len combination of in- and out-parameters in order to provide the user with the flexible 2-way or 1-way approach of accessing information from the host side.

For all getter functions in the SRML contracts host interface any implementer has to make sure that the concrete implementation guarantees protection from double-fetches.

This means that if a user for example calls

let mut req_result_len = 0;
let _err = srml_get_properties(PROPERTY_CALLER, &mut 0, 0, &mut req_result_len);
let result = vec![0x0; req_result_len];
let _err = srml_get_properties(PROPERTY_CALLER, &mut result, result.len(), &mut 0);

The above code queries the required number of bytes to hold the actual result with the first call to srml_get_properties and is actually loading the information with the second call to it.

Protection from double-fetch means that the implementer has to make sure that the property in such a scenario is only loaded once. Where we regard the loading of the property to be the actually costly operation while everything else is cheap in comparison.

Calling & Instantiating

The same counts for srml_call_contract and srml_create_contract where the implementer in the same vein has to make sure that the call to the contract or the deployment of one happens exactly once per call chain.

Combo Breaker

If there was a call to another property or another host function of the SRML host interface then this double-fetch protection is not required to be uphold.

An example for this:

let mut req_result_len_1 = 0;
let mut req_result_len_2 = 0;
let _err = srml_get_properties(PROPERTY_CALLER, &mut 0, 0, &mut req_result_len_1);
let _err = srml_get_properties(PROPERTY_ADDRESS, &mut 0, 0, &mut req_result_len_2);
let result_1 = vec![0x0; req_result_len_1];
let result_2 = vec![0x0; req_result_len_2];
let _err = srml_get_properties(PROPERTY_CALLER, &mut result_1, result_1.len(), &mut 0);
let _err = srml_get_properties(PROPERTY_ADDRESS, &mut result_2, result_2.len(), &mut 0);

Here the above code first queries the required number of bytes to hold the value of PROPERTY_CALLER and then does the same for PROPERTY_ADDRESS without actually loading the PROPERTY_CALLER information into the respective buffer on the client side using the second call with srml_get_properties(PROPERTY_CALLER, &mut result_1, result_1.len(), &mut 0);.

In this case the SRML hosts interface is free to disrespect the protection against double-fetch. Implementers might still choose to prevent double fetching but clients can no longer rely on it.

In the above use case the client should have simply made use of the multi-load functionality provided by srml_get_properties.

Caching Behaviour

The following table shows the required caching behaviour that controls when double-fetch protection must be used.

For this an implementer has to make sure that the listed parameters
are always the same for two consecutive calls to the same function in order to apply double-fetch protection.

Function Guard Input
srml_get_properties(properties: Properties, ...) properties
srml_get_contract_storage(key: Ptr, ...) *key
srml_call_contract(callee: Ptr, callee_len: PtrSize, gas_limit: u64, endowment: Ptr, endowment_len: PtrSize, input_data: Ptr, input_data_len: PtrSize, ...) *callee, callee_len, gas_limit, *endowment, endowment_len, *input_data, input_data_len
srml_create_contract(code_hash: Ptr, code_hash_len: PtrSize, gas_limit: u64, endowment: Ptr, endowment_len: PtrSize, input_data: Ptr, input_data_len: PtrSize, ...) *code_hash, code_hash_len, gas_limit, *endowment, endowment_len, *input_data, input_data_len
srml_random(subject: Ptr, subject_len: PtrSize, ...) *subject, subject_len

It might be worth for implementers to compute a simple u64 hash value over some calls in order to quickly identify double-fetch protection breakers.

Extending Properties

In order to make retrieving of properties more future proof we could add another input field to it:

fn srml_get_properties(properties: Properties, input: Ptr, input_len: PtrSize, ...)

With this input buffer we could generically encode certain optionally required inputs to some properties. By this for example we could allow returning the PROPERTY_BALANCE property of some other contract if the input is non-zero but instead contains the encoded account ID of the contract of which we want to query the balance.

Properties that do not expect any further input require these two additional inputs to be zero always and will otherwise return RETCODE_INVALID_INPUTS.

This is just an idea and not yet part of the interface proposal.
Also as drawback this would make multi-return even more complex for the user side.

Code

All you get is text but all you want is code. So here you go:

/// The type of a single property.
///
/// The 32 bits currently allow us to handle up to 32 different properties.
/// If we need more later we can always extend this to `u64` or `u128` (or something even bigger).
/// Keep in mind though that this would be a breaking change for contracts.
type Property = u32;

/// Can hold a set of properties.
///
/// Must be at least the same size of `Property`.
type Properties = Property;

/// The caller's `AccountId` of a contract. (ReadOnly)
const PROPERTY_CALLER: Property            = 1 << 0;
/// The address of a contract. (ReadOnly)
const PROPERTY_ADDRESS: Property           = 1 << 1;
/// The current gas price. (ReadOnly)
const PROPERTY_GAS_PRICE: Property         = 1 << 2;
/// The gas left for the current contract execution. (ReadOnly)
const PROPERTY_GAS_LEFT: Property          = 1 << 3;
/// The balance of the contract. (ReadOnly)
const PROPERTY_BALANCE: Property           = 1 << 4;
/// The value that has been transferred upon the current execution. (ReadOnly)
const PROPERTY_VALUE_TRANSFERRED: Property = 1 << 5;
/// The latest recorded time. (ReadOnly)
const PROPERTY_NOW: Property               = 1 << 6;
/// The rent allowance of the contract. (Read & Write)
const PROPERTY_RENT_ALLOWANCE: Property    = 1 << 7;
/// The current block number. (ReadOnly)
const PROPERTY_BLOCK_NUMBER: Property      = 1 << 8;
/// The input buffer that was given to the contract upon `call` or `create`. (ReadOnly)
const PROPERTY_INPUT: Property             = 1 << 9;
/// The result or what the contract returns upon end of execution. (WriteOnly)
const PROPERTY_OUTPUT: Property            = 1 << 10;

/// Type of return codes.
type RetCode = u32;

/// User provided errors from `call` and `create` functions are only
/// valid if within the bounds of `1..=255`.
/// Errors outside of this bound are SRML defined errors.
const SRML_ERROR_OFFSET: u32 = 255;

/// Returns if an operation was successful.
const RETCODE_SUCCESS: RetCode                  = 0;

/// Returned whenever some combination of inputs was invalid.
const RETCODE_INVALID_INPUTS: RetCode           = SRML_ERROR_OFFSET + 1;

/// Returned when the provided buffer is too small to hold the returned value.
const RETCODE_BUFFER_TOO_SMALL: RetCode         = SRML_ERROR_OFFSET + 2;
/// Returned when the provided buffer is bigger than the maximumly allowed length.
const RETCODE_BUFFER_TOO_BIG: RetCode           = SRML_ERROR_OFFSET + 3;

/// Returned when using an unknown or otherwise invalid property.
const RETCODE_INVALID_PROPERTY: RetCode         = SRML_ERROR_OFFSET + 4;
/// Returned when trying to write to a read-only property.
const RETCODE_READ_ONLY_PROPERTY: RetCode       = SRML_ERROR_OFFSET + 5;
/// Returned when trying to read from a write-only property.
const RETCODE_WRITE_ONLY_PROPERTY: RetCode      = SRML_ERROR_OFFSET + 6;

/// Returned when trying to call a non-existing smart contract.
const RETCODE_INVALID_CALLEE: RetCode           = SRML_ERROR_OFFSET + 7;
/// Returned when trying to instantiate from a non-existing or invalid code hash.
const RETCODE_INVALID_CODE_HASH: RetCode        = SRML_ERROR_OFFSET + 8;
/// Returned when the execution of a called smart contract ran out of gas.
const RETCODE_CALLEE_OUT_OF_GAS: RetCode        = SRML_ERROR_OFFSET + 9;
/// Returned when a called smart contract has trapped during execution.
const RETCODE_CALLEE_TRAPPED: RetCode           = SRML_ERROR_OFFSET + 10;

/// Returned when trying to deposit an event with too many topics.
const RETCODE_TOO_MANY_EVENT_TOPICS: RetCode    = SRML_ERROR_OFFSET + 11;

/// Returned when trying to dispatch a runtime call with invalid dispatch data.
const RETCODE_INVALID_DISPATCH_CALL: RetCode    = SRML_ERROR_OFFSET + 12;

/// Returned when trying to enqueue a restoration request where
/// the computed checksum hash does not meet the required restoration hash.
const RETCODE_INVALID_RESTORATION_HASH: RetCode = SRML_ERROR_OFFSET + 13;

/// Returned when trying to retrieve storage from a vacant entry.
const RETCODE_VACANT_ENTRY: RetCode             = SRML_ERROR_OFFSET + 14;

/// Represents a pointer to an immutable buffer.
///
/// The SRML host side is not allowed to mutate the contents of the buffer.
type Ptr = u32;

/// Represents a pointer to a mutable buffer.
///
/// The SRML host side is allowed to mutate the contents of the buffer.
type MutPtr = u32;

/// Used for buffer length information.
/// Required to be the same size of a pointer in the Wasm environment.
type PtrSize = u32;

/// The maximum buffer length in bytes that the SRML contracts side allows to operate on.
///
/// This exists mainly to prevent certain types of attacks.
/// The value of `1 << 16` is unstable and might change in the future.
const MAX_BUFFER_LEN: u32 = 1 << 16;

trait Interface {
    /// Returns the values denoted by the set of properties given via `properties` in their
    /// respective order (denoted by their bit representation) and writes the result in the form
    /// of a tuple into the `result` buffer.
    ///
    /// Obviously if given only one property only loads and writes the single property into
    /// the provided result buffer.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Errors
    ///
    /// - If `result != 0` and `req_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result == 0` and `req_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `result_len == 0` returns `RETCODE_BUFFER_TOO_SMALL`
    /// - If `result != 0` and `result_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If `properties` contains an invalid property returns `RETCODE_INVALID_PROPERTY`
    /// - If `properties` contains a property that is `write-only` returns `RETCODE_WRITE_ONLY_PROPERTY`
    ///
    /// # Batch Return
    ///
    /// Allows to batch return a set of propeties by calling `ext_get_properties` like this:
    /// ```
    /// ext_get_properties(PROPERTY_CALLER | PROPERTY_BALANCE | PROPERTY_ADDRESS, result, result_len, &mut 0);
    /// ```
    /// Returns the values `PROPERTY_CALLER`, `PROPERTY_ADDRESS` and `PROPERTY_BALANCE` in this order
    /// encoded as tuple into the `ret_buf` if `result_len` is big enough to hold all values.
    fn srml_get_properties(properties: Properties, result: MutPtr, result_len: PtrSize, req_result_len: MutPtr) -> RetCode;

    /// Sets the given property to the value represented by the value stored in the input buffer.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Errors
    ///
    /// - If `input == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `input != 0` and `input_len == 0` returns `RETCODE_BUFFER_TOO_SMALL`
    /// - If `input != 0` and `input_len < MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If `properties` is an invalid property returns `RETCODE_INVALID_PROPERTY`
    /// - If the `property` is immutable returns `RETCODE_READ_ONLY_PROPERTY`
    fn srml_set_property(propery: Property, input: Ptr, input_len: PtrSize) -> RetCode;

    /// Change the value at the given key position in the contract's storage or remove the entry.
    ///
    /// If `value_buf != 0` sets the value denoted by `key` to the encoded value
    /// stored at `value_buf` with length `value_buf_len`. Note that if `value_buf_len == 0`
    /// the storage entry exists but is empty.
    /// If `value_buf == 0` and `value_buf_len == 0` the entry denoted by `key` is removed.
    ///
    /// The `key` param is expected to point to a 32 bytes sized buffer.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Errors
    ///
    /// - If `value_buf == 0` and `value_buf_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `key == 0` returns `RETCODE_INVALID_INPUTS`
    fn srml_set_contract_storage(key: Ptr, value_buf: Ptr, value_buf_len: PtrSize) -> RetCode;

    /// Used to retrieve the value at the given location from the contract's storage.
    ///
    /// - If `result != 0` and `req_result_len == 0` loads the value stored at the contract's storage
    ///   denoted by `key` into the `result` if `result_len` is big enough to store the value.
    /// - If `result == 0` and `req_result_len != 0` returns the minimum required size of the `result` into
    ///   `req_result_len`.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Errors
    ///
    /// - If `key == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If the storage at `key` is vacant returns `RETCODE_VACANT_ENTRY`
    /// - If `result == 0` and `req_result_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `req_result_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `result_len == 0` returns `RETCODE_BUFFER_TOO_SMALL`
    /// - If `result != 0` and `result_len` is too small for the encoded result value
    ///   returns `RETCODE_BUFFER_TOO_SMALL`.
    /// - If `result != 0` and `result_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    fn srml_get_contract_storage(key: Ptr, result: MutPtr, result_len: PtrSize, req_result_len: MutPtr) -> RetCode;

    /// Calls a smart contract and returns its return value.
    ///
    /// The smart contract denoted by the `callee` `AccountId` is called with the given
    /// upper limit for gas and is provided with a value given by `endowment` to it upon execution.
    /// The input to the called smart contract is given via `input_data`.
    ///
    /// - If `result != 0` and `req_result_len == 0` and `result_len` fits the required bytes
    ///   the returned data of the execution is written into the `result` buffer.
    /// - If `result == 0` and `req_result_len != 0` the minimum required length of a result
    ///   buffer to store the result of the execution is stored in `req_result_len`.
    /// - If `result != 0` and `result_len == 0` writing of result data is skipped.
    ///   This is useful if the callee is not interested in the actual returned data.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Note
    ///
    /// - A `gas_limit` of 0 denotes that there is no limit for the amount of gas that the
    ///   callee is allowed to spend.
    /// - If `endowment == 0` and `endowment_len == 0` no endowment is provided.
    /// - If `input_data == 0` and `input_data_len == 0` empty input data is provided.
    ///
    /// # Errors
    ///
    /// - If `callee == 0` or `callee_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result == 0` and `req_result_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `req_result_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `input_data == 0` and `input_data_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `input_data != 0` and `input_data_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `endowment == 0` and `endowment_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `endowment != 0` and `endowment_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `result_len` does not fit the required bytes returns `RETCODE_BUFFER_TOO_SMALL`
    /// - If `result != 0` and `result_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If the account ID referred to by `callee` is not a callable contract returns
    ///   `RETCODE_INVALID_CALLEE`
    /// - If the callee ran out of gas upon execution returns `RETCODE_CALLEE_OUT_OF_GAS`
    /// - If the callee trapped unexpectedly upon execution returns `RETCODE_CALLEE_TRAPPED`
    fn srml_call_contract(
        callee: Ptr,
        callee_len: PtrSize,
        gas_limit: u64,
        endowment: Ptr,
        endowment_len: PtrSize,
        input_data: Ptr,
        input_data_len: PtrSize,
        result: MutPtr,
        result_len: PtrSize,
        req_result_len: MutPtr,
    ) -> RetCode;

    /// Instantiates a smart contract and returns its address.
    ///
    /// The smart contract code denoted by the `code_hash` is used to instantiate the new contract
    /// with the given upper limit for gas and is provided with a value given by `endowment` to it upon execution.
    /// The input to the instantiated contract is given via `input_data`.
    ///
    /// - If `result != 0` and `req_result_len == 0` the `AccountId` of the successfully instantiated
    ///   contract is written into the `result` buffer.
    /// - If `result == 0` and `req_result_len != 0` the minimum number of bytes that is required for
    ///   the `result` buffer to hold the returned `AccountId` is written into `req_result_len`.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Note
    ///
    /// - A `gas_limit` of 0 denotes that there is no limit for the amount of gas that the
    ///   callee is allowed to spend.
    /// - If `endowment == 0` and `endowment_len == 0` no endowment is provided.
    /// - If `input_data == 0` and `input_data_len == 0` empty input data is provided.
    ///
    /// # Errors
    ///
    /// - If `callee == 0` or `callee_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result == 0` and `req_result_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `req_result_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `input_data == 0` and `input_data_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `input_data != 0` and `input_data_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `endowment == 0` and `endowment_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `endowment != 0` and `endowment_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `result_len` does not fit the required bytes returns `RETCODE_BUFFER_TOO_SMALL`
    /// - If `result != 0` and `result_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If the code hash referred to by `code_hash` is not a valid or existing code hash returns
    ///   `RETCODE_INVALID_CODE_HASH`
    /// - If the callee ran out of gas upon instantiation returns `RETCODE_CALLEE_OUT_OF_GAS`
    /// - If the callee trapped unexpectedly upon instantiation returns `RETCODE_CALLEE_TRAPPED`
    fn srml_create_contract(
        code_hash: Ptr,
        code_hash_len: PtrSize,
        gas_limit: u64,
        endowment: Ptr,
        endowment_len: PtrSize,
        input_data: Ptr,
        input_data_len: PtrSize,
        result: MutPtr,
        result_len: PtrSize,
        req_result_len: MutPtr,
    ) -> RetCode;

    /// Dispatches a call into the runtime.
    ///
    /// The value stored in `call_data` is used for dispatching to the correct runtime
    /// function and for providing input arguments to it.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Note
    ///
    /// This operation acts as fire-and-forget.
    /// After successfully dispatching the call a smart contract will have no way
    /// to receive a callback, returned data or otherwise any signals comming from
    /// the callee.
    /// Also the execution is asynchronous and immediate execution cannot be relied upon.
    ///
    /// # Usage
    ///
    /// The dispatcher expects `call_data` to be storing a single entity of
    /// the entity that is going to be called. This is chain dependent and won't
    /// work cross-chain.
    ///
    /// # Errors
    ///
    /// - If `call_data == 0` and `call_data_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `call_data != 0` and `call_data_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `call_data == 0` and `call_data_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If the value encoded by `call_data` is not a valid dispatchable call returns
    ///   `RETCODE_INVALID_DISPATCH_CALL`
    fn srml_dispatch_call(call_data: Ptr, call_data_len: PtrSize) -> RetCode;

    /// Enqueues a request to restore the caller contract to the given destination.
    ///
    /// The `dest` value is the `AccountId` where the contract should be restored to.
    /// The code hash value must be the same as the one that has been used by the to be restored contract.
    /// The rent allowance is the amount of value the restored contract is going to need
    /// in order to retain.
    ///
    /// TODO: Describe what delta pointer is going to be used for.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Usage
    ///
    /// Normally a contract `A` with code hash `ch(A)` is going to restore a contract `B` with
    /// code hash `ch(B)` where generally `ch(A) != ch(B)`. At first `A` has to make sure to meet
    /// the exact same storage that `B` had when `B` has been killed.
    /// Just then `A` is able to successfully enqueue a restoration request for contract `B`.
    /// `A` must provide the same `code_hash` as the one that has been used by `B` and it must
    /// provide the same `AccountId` that was in use by `B` when it was still alive.
    ///
    /// After a successful enqueue the contract `A` is going to be killed after the transaction.
    /// If there were storage modifications to `A` after enqueuing the restoration procedure or
    /// other state changes that could conflict with the restoration process the restoration
    /// process will not take place and contract `A` will not be killed.
    ///
    /// # Note
    ///
    /// - The value provided by `dest` is expected to be encoded as the chain's `AccountId` type.
    /// - The value provided by `code_hash` is expected to be encoded as the chain's `Hash` type.
    /// - The value provided by `rent_allowance` is expected to be encoded as the chain's
    ///   `Balance` type.
    ///
    /// # Errors
    ///
    /// - If the computed restoration hash does not meet the restoration hash required for successful
    ///   restoration returns `RETCODE_INVALID_RESTORATION_HASH`
    /// - If `dest == 0` and `dest_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `dest != 0` and `dest_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `dest == 0` and `dest_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If `code_hash == 0` and `code_hash_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `code_hash != 0` and `code_hash_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `code_hash == 0` and `code_hash_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If `rent_allowance == 0` and `rent_allowance_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `rent_allowance != 0` and `rent_allowance_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `rent_allowance == 0` and `rent_allowance_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If `delta == 0` and `delta_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `delta != 0` and `delta_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `delta == 0` and `delta_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    fn srml_restore_to(
        dest: Ptr,
        dest_len: PtrSize,
        code_hash: Ptr,
        code_hash_len: PtrSize,
        rent_allowance: Ptr,
        rent_allowance_len: PtrSize,
        delta: Ptr,
        delta_count: PtrSize
    ) -> RetCode;

    /// Used to retrieve pseudo-random values.
    ///
    /// - If `result != 0` and `req_result_len == 0` writes the resulting pseudo-random
    ///   value into the user provided `result` buffer.
    /// - If `result == 0` and `req_result_len != 0` writes the minimum required amount
    ///   of bytes needed for `result` to hold the returned pseudo-random value.
    ///
    /// # Note
    ///
    /// - The returned pseudo-random value is encoded as the chains `Hash` type.
    /// - Calling this function twice within the same block and subject yields
    ///   the same results.
    /// - Providing two different subjects generally yields different outputs.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Errors
    ///
    /// - If `subject == 0` and `subject_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `subject != 0` and `subject_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `subject != 0` and `subject_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    /// - If `result == 0` and `result_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `result_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `result != 0` and `result_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    fn srml_random(
        subject: Ptr,
        subject_len: PtrSize,
        result: MutPtr,
        result_len: PtrSize,
        req_result_len: MutPtr,
    ) -> RetCode;

    /// Deposits a contract event with the given data and optional list of topics.
    ///
    /// The maximum number of topics for a chain is specified by `max_event_topics`.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// If `topics == 0` and `topics_len == 0` it will be assumed that there are no topics.
    ///
    /// # Note
    ///
    /// TODO: Describe what the topics buffer is expected to hold and how its elements are encoded.
    ///
    /// # Errors
    ///
    /// - If `topics == 0` and `topics_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `topics != 0` and `topics_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `topics != 0` and `topics_len` > `max_event_topics` returns `RETCODE_TOO_MANY_EVENT_TOPICS`
    /// - If `data == 0` and `data_len != 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `data != 0` and `data_len == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `data != 0` and `data_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    fn srml_deposit_event(topics: Ptr, topics_len: PtrSize, data: Ptr, data_len: PtrSize) -> RetCode;

    /// Prints the contents denoted by `contents` buffer to the consol log.
    ///
    /// This function is only available in `--dev` runtimes and won't be
    /// accepted upon putting code on-chain if used otherwise.
    ///
    /// Returns `RETCODE_SUCCESS` upon successful execution.
    ///
    /// # Errors
    ///
    /// - If `contents == 0` returns `RETCODE_INVALID_INPUTS`
    /// - If `contents != 0` and `contents_len > MAX_BUFFER_LEN` returns `RETCODE_BUFFER_TOO_BIG`
    fn srml_println(contents: Ptr, contents_len: PtrSize) -> RetCode;
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions