Skip to content

Latest commit

 

History

History
533 lines (367 loc) · 35.4 KB

cap-0046-03.md

File metadata and controls

533 lines (367 loc) · 35.4 KB

Preamble

CAP: 0046-03 (formerly 0051)
Title: Smart Contract Host Functions
Working Group:
    Owner: Jay Geng <@jayz22>, Graydon Hoare <@graydon>
    Authors: Jay Geng <@jayz22>
    Consulted: Leigh McCulloch <@leighmcculloch>, Nicolas Barry <@MonsieurNicolas>, Siddharth Suresh <@sisuresh>
Status: Final
Created: 2022-05-20
Discussion:
Protocol version: 20

Simple Summary

This CAP proposes a set of host functions — interface between the host environment running on the Stellar Core and the WebAssembly-based (Wasm) virtual machine running smart contracts.

Motivation amd Goals Alignment

See the Soroban overview CAP.

Abstract

This CAP specifies the signatures of host functions that serve as the host-VM interface, divided into logical modules. The selection criteria of the host functions and the framework of resource accounting are detailed in the Design Rationale.

Specification

The entire suite of host functions are broken down into logical modules, each evolving around a specific area of functionality (e.g. map, vector, integer).

The host functions, which define the interface between the host environment and the virtual machine (VM), are specified in WebAssembly text format to preserve generality, since implementation of the host functions are supposed to be language agnostic.

There are a few properties and conventions that apply generally to all host functions, they are outlined below to avoid repeating on every function.

Error and trap

Execution of the host function should never cause an exception in the host environment. If the execution fails for any reason, the host will emit a trap to the VM to stop the execution. There can be an variety of reasons causing a host function execution to fail, see error handing.

In general error propagation is not specified as part of the host interface specification. The only exception is the try_call function (inside module d) function, which may the error code on failure if the error is recoverable.

The error conditions on a host function should be self-explainatory and/or clearly documented.

Parameter types and nomenclature

All parameters (input arguments and return value) are 64-bit integers, and they either represent a primitive integer value or a host value type specified in CAP-0046-01.

For clarity, the input parameters are named as "name underscore type" in "snake" case. For example v: VecObject in Rust definition is translated to param $v_vec_object i64.

Immutability

All host functions respect the immutability constraint on the host objects (see CAP-0046-01). Any function that mutates a host object (e.g. vec_push) will create a new host object and return its handle.

With that, we now present the host functions.

"Context" host functions (mod x)

;; Emit a diagnostic event containing a message and sequence of `Val`s.
(func $log_from_linear_memory (param $msg_pos_u32_val i64) (param $msg_len_u32_val i64) (param $vals_pos_u32_val i64) (param $vals_len_u32_val i64) (result i64))

;; Compare two objects, or at least one object to a non-object, structurally. Returns -1 if a<b, 1 if a>b, or 0 if a==b.
(func $obj_cmp (param $a_val i64) (param $b_val i64) (result i64))

;; Records a contract event. `topics` is expected to be a `SCVec` with length <= 4 that cannot contain `Vec`, `Map`, or `Bytes` with length > 32.
(func $contract_event (param $topics_vec_object i64) (param $data_val i64) (result i64))

;; Return the protocol version of the current ledger as a u32.
(func $get_ledger_version (result i64))

;; Return the sequence number of the current ledger as a u32.
(func $get_ledger_sequence (result i64))

;; Return the timestamp number of the current ledger as a u64.
(func $get_ledger_timestamp (result u64))

;; Causes the currently executing contract to fail immediately with a provided error code, which must be of error-type `ScErrorType::Contract`. Does not actually return.
(func $fail_with_error (param $error_error i64) (result i64))

;; Return the network id (sha256 hash of network passphrase) of the current ledger as `Bytes`. The value is always 32 bytes in length.
(func $get_ledger_network_id (result i64))

;; Get the Address object for the current contract.
(func $get_current_contract_address (result i64))

;; Returns the max ledger sequence that an entry can live to (inclusive).
(func $get_max_live_until_ledger (result i64))

"Integer" host functions (mod i)

;; Convert a `u64` to an object containing a `u64`.
(func $obj_from_u64 (param $v u64) (result i64))

;; Convert an object containing a `u64` to a `u64`.
(func $obj_to_u64 (param $obj_u64_object i64) (result u64))

;; Convert an `i64` to an object containing an `i64`.
(func $obj_from_i64 (param $v i64) (result i64))

;; Convert an object containing an `i64` to an `i64`.
(func $obj_to_i64 (param $obj_i64_object i64) (result i64))

;; Convert the high and low 64-bit words of a u128 to an object containing a u128.
(func $obj_from_u128_pieces (param $hi u64) (param $lo u64) (result i64))

;; Extract the low 64 bits from an object containing a u128.
(func $obj_to_u128_lo64 (param $obj_u128_object i64) (result u64))

;; Extract the high 64 bits from an object containing a u128.
(func $obj_to_u128_hi64 (param $obj_u128_object i64) (result u64))

;; Convert the high and low 64-bit words of an i128 to an object containing an i128.
(func $obj_from_i128_pieces (param $hi i64) (param $lo u64) (result i64))

;; Extract the low 64 bits from an object containing an i128.
(func $obj_to_i128_lo64 (param $obj_i128_object i64) (result u64))

;; Extract the high 64 bits from an object containing an i128.
(func $obj_to_i128_hi64 (param $obj_i128_object i64) (result i64))

;; Convert the four 64-bit words of a u256 (big-endian) to an object containing a u256.
(func $obj_from_u256_pieces (param $hi_hi u64) (param $hi_lo u64) (param $lo_hi u64) (param $lo_lo u64) (result i64))

;; Create a U256 `Val` from its representation as a byte array in big endian.
(func $u256_val_from_be_bytes (param $bytes_bytes_object i64) (result i64))

;; Return the memory representation of this U256 `Val` as a byte array in big endian byte order.
(func $u256_val_to_be_bytes (param $val_u256_val i64) (result i64))

;; Extract the highest 64-bits (bits 192-255) from an object containing a u256.
(func $obj_to_u256_hi_hi (param $obj_u256_object i64) (result u64))

;; Extract bits 128-191 from an object containing a u256.
(func $obj_to_u256_hi_lo (param $obj_u256_object i64) (result u64))

;; Extract bits 64-127 from an object containing a u256.
(func $obj_to_u256_lo_hi (param $obj_u256_object i64) (result u64))

;; Extract the lowest 64-bits (bits 0-63) from an object containing a u256.
(func $obj_to_u256_lo_lo (param $obj_u256_object i64) (result u64))

;; Convert the four 64-bit words of an i256 (big-endian) to an object containing an i256.
(func $obj_from_i256_pieces (param $hi_hi i64) (param $hi_lo u64) (param $lo_hi u64) (param $lo_lo u64) (result i64))

;; Create a I256 `Val` from its representation as a byte array in big endian.
(func $i256_val_from_be_bytes (param $bytes_bytes_object i64) (result i64))

;; Return the memory representation of this I256 `Val` as a byte array in big endian byte order.
(func $i256_val_to_be_bytes (param $val_i256_val i64) (result i64))

;; Extract the highest 64-bits (bits 192-255) from an object containing an i256.
(func $obj_to_i256_hi_hi (param $obj_i256_object i64) (result i64))

;; Extract bits 128-191 from an object containing an i256.
(func $obj_to_i256_hi_lo (param $obj_i256_object i64) (result u64))

;; Extract bits 64-127 from an object containing an i256.
(func $obj_to_i256_lo_hi (param $obj_i256_object i64) (result u64))

;; Extract the lowest 64-bits (bits 0-63) from an object containing an i256.
(func $obj_to_i256_lo_lo (param $obj_i256_object i64) (result u64))

;; Performs checked integer addition. Computes `lhs + rhs`, returning `ScError` if overflow occurred.
(func $u256_add (param $lhs_u256_val i64) (param $rhs_u256_val i64) (result i64))

;; Performs checked integer subtraction. Computes `lhs - rhs`, returning `ScError` if overflow occurred.
(func $u256_sub (param $lhs_u256_val i64) (param $rhs_u256_val i64) (result i64))

;; Performs checked integer multiplication. Computes `lhs * rhs`, returning `ScError` if overflow occurred.
(func $u256_mul (param $lhs_u256_val i64) (param $rhs_u256_val i64) (result i64))

;; Performs checked integer division. Computes `lhs / rhs`, returning `ScError` if `rhs == 0` or overflow occurred.
(func $u256_div (param $lhs_u256_val i64) (param $rhs_u256_val i64) (result i64))

;; Performs checked Euclidean modulo. Computes `lhs % rhs`, returning `ScError` if `rhs == 0` or overflow occurred.
(func $u256_rem_euclid (param $lhs_u256_val i64) (param $rhs_u256_val i64) (result i64))

;; Performs checked exponentiation. Computes `lhs.exp(rhs)`, returning `ScError` if overflow occurred.
(func $u256_pow (param $lhs_u256_val i64) (param $rhs_u32_val i32) (result i64))

;; Performs checked shift left. Computes `lhs << rhs`, returning `ScError` if `rhs` is larger than or equal to the number of bits in `lhs`.
(func $u256_shl (param $lhs_u256_val i64) (param $rhs_u32_val i32) (result i64))

;; Performs checked shift right. Computes `lhs >> rhs`, returning `ScError` if `rhs` is larger than or equal to the number of bits in `lhs`.
(func $u256_shr (param $lhs_u256_val i64) (param $rhs_u32_val i32) (result i64))

;; Performs checked integer addition. Computes `lhs + rhs`, returning `ScError` if overflow occurred.
(func $i256_add (param $lhs_i256_val i64) (param $rhs_i256_val i64) (result i64))

;; Performs checked integer subtraction. Computes `lhs - rhs`, returning `ScError` if overflow occurred.
(func $i256_sub (param $lhs_i256_val i64) (param $rhs_i256_val i64) (result i64))

;; Performs checked integer multiplication. Computes `lhs * rhs`, returning `ScError` if overflow occurred.
(func $i256_mul (param $lhs_i256_val i64) (param $rhs_i256_val i64) (result i64))

;; Performs checked integer division. Computes `lhs / rhs`, returning `ScError` if `rhs == 0` or overflow occurred.
(func $i256_div (param $lhs_i256_val i64) (param $rhs_i256_val i64) (result i64))

;; Performs checked Euclidean modulo. Computes `lhs % rhs`, returning `ScError` if `rhs == 0` or overflow occurred.
(func $i256_rem_euclid (param $lhs_i256_val i64) (param $rhs_i256_val i64) (result i64))

;; Performs checked exponentiation. Computes `lhs.exp(rhs)`, returning `ScError` if overflow occurred.
(func $i256_pow (param $lhs_i256_val i64) (param $rhs_u32_val i32) (result i64))

;; Performs checked shift left. Computes `lhs << rhs`, returning `ScError` if `rhs` is larger than or equal to the number of bits in `lhs`.
(func $i256_shl (param $lhs_i256_val i64) (param $rhs_u32_val i32) (result i64))

;; Performs checked shift right. Computes `lhs >> rhs`, returning `ScError` if `rhs` is larger than or equal to the number of bits in `lhs`.
(func $i256_shr (param $lhs_i256_val i64) (param $rhs_u32_val i32) (result i64))

;; Convert a `u64` to a `Timepoint` object.
(func $timepoint_obj_from_u64 (param $v u64) (result i64))

;; Convert a `Timepoint` object to a `u64`.
(func $timepoint_obj_to_u64 (param $obj_timepoint_object i64) (result u64))

;; Convert a `u64` to a `Duration` object.
(func $duration_obj_from_u64 (param $v u64) (result i64))

;; Convert a `Duration` object a `u64`.
(func $duration_obj_to_u64 (param $obj_duration_object i64) (result u64))

"Map" host functions (mod m)

;; Create an empty new map.
(func $map_new (result i64))

;; Insert a key/value mapping into an existing map, and return the map object handle. If the map already has a mapping for the given key, the previous value is overwritten.
(func $map_put (param $m_map_object i64) (param $k_val i64) (param $v_val i64) (result i64))

;; Get the value for a key from a map. Traps if key is not found.
(func $map_get (param $m_map_object i64) (param $k_val i64) (result i64))

;; Remove a key/value mapping from a map if it exists, traps if doesn't.
(func $map_del (param $m_map_object i64) (param $k_val i64) (result i64))

;; Get the size of a map.
(func $map_len (param $m_map_object i64) (result i64))

;; Test for the presence of a key in a map. Returns Bool.
(func $map_has (param $m_map_object i64) (param $k_val i64) (result i64))

;; Get the key from a map at position `i`. If `i` is an invalid position, return ScError.
(func $map_key_by_pos (param $m_map_object i64) (param $i_u32_val i64) (result i64))

;; Get the value from a map at position `i`. If `i` is an invalid position, return ScError.
(func $map_val_by_pos (param $m_map_object i64) (param $i_u32_val i64) (result i64))

;; Return a new vector containing all the keys in a map. The new vector is ordered in the original map's key-sorted order.
(func $map_keys (param $m_map_object i64) (result i64))

;; Return a new vector containing all the values in a map. The new vector is ordered in the original map's key-sorted order.
(func $map_values (param $m_map_object i64) (result i64))

;; Return a new map initialized from a set of input slices given by linear-memory addresses and lengths.
(func $map_new_from_linear_memory (param $keys_pos_u32_val i64) (param $vals_pos_u32_val i64) (param $len_u32_val i64) (result i64))

;; Copy the Val values of a map, as described by set of input keys, into an array at a given linear-memory address.
(func $map_unpack_to_linear_memory (param $map_map_object i64) (param $keys_pos_u32_val i64) (param $vals_pos_u32_val i64) (param $len_u32_val i64) (result i64))

"Vec" host functions (mod v)

;; Creates an empty new vector.
(func $vec_new () (result i64))

;; Update the value at index `i` in the vector. Return the new vector. Trap if the index is out of bounds.
(func $vec_put (param $v_vec_object i64) (param $i_u32_val i64) (param $x_val i64) (result i64))

;; Returns the element at index `i` of the vector. Traps if the index is out of bound.
(func $vec_get (param $v_vec_object i64) (param $i_u32_val i64) (result i64))

;; Delete an element in a vector at index `i`, shifting all elements after it to the left. Return the new vector. Traps if the index is out of bound.
(func $vec_del (param $v_vec_object i64) (param $i_u32_val i64) (result i64))

;; Returns length of the vector.
(func $vec_len (param $v_vec_object i64) (result i64))

;; Push a value to the front of a vector.
(func $vec_push_front (param $v_vec_object i64) (param $x_val i64) (result i64))

;; Removes the first element from the vector and returns the new vector. Traps if original vector is empty.
(func $vec_pop_front (param $v_vec_object i64) (result i64))

;; Appends an element to the back of the vector.
(func $vec_push_back (param $v_vec_object i64) (param $x_val i64) (result i64))

;; Removes the last element from the vector and returns the new vector. Traps if original vector is empty.
(func $vec_pop_back (param $v_vec_object i64) (result i64))

;; Return the first element in the vector. Traps if the vector is empty
(func $vec_front (param $v_vec_object i64) (result i64))

;; Return the last element in the vector. Traps if the vector is empty
(func $vec_back (param $v_vec_object i64) (result i64))

;; Inserts an element at index `i` within the vector, shifting all elements after it to the right. Traps if the index is out of bound
(func $vec_insert (param $v_vec_object i64) (param $i_u32_val i64) (param $x_val i64) (result i64))

;; Clone the vector `v1`, then moves all the elements of vector `v2` into it. Return the new vector. Traps if number of elements in the vector overflows a u32.
(func $vec_append (param $v1_vec_object i64) (param $v2_vec_object i64) (result i64))

;; Copy the elements from `start` index until `end` index, exclusive, in the vector and create a new vector from it. Return the new vector. Traps if the index is out of bound.
(func $vec_slice (param $v_vec_object i64) (param $start_u32_val i64) (param $end_u32_val i64) (result i64))

;; Get the index of the first occurrence of a given element in the vector. Returns the u32 index of the value if it's there. Otherwise, it returns `Void`.
(func $vec_first_index_of (param $v_vec_object i64) (param $x_val i64) (result i64))

;; Get the index of the last occurrence of a given element in the vector. Returns the u32 index of the value if it's there. Otherwise, it returns `Void`.
(func $vec_last_index_of (param $v_vec_object i64) (param $x_val i64) (result i64))

;; Binary search a sorted vector for a given element. If it exists, the high-32 bits of the return value is 0x0001 and the low-32 bits contain the u32 index of the element. If it does not exist, the high-32 bits of the return value is 0x0000_0001 and the low-32 bits contain the u32 index at which the element would need to be inserted into the vector to maintain sorted order.
(func $vec_binary_search (param $v_vec_object i64) (param $x_val i64) (result u64))

;; Return a new vec initialized from an input slice of Vals given by a linear-memory address and length.
(func $vec_new_from_linear_memory (param $vals_pos_u32_val i64) (param $len_u32_val i64) (result i64))

;; Copy the Vals of a vec into an array at a given linear-memory address.
(func $vec_unpack_to_linear_memory (param $vec_vec_object i64) (param $vals_pos_u32_val i64) (param $len_u32_val i64) (result i64))

"Ledger" host functions (mod l)

(func $put_contract_data (param $k_val i64) (param $v_val i64) (param $t_storage_type i64) (result i64))

(func $has_contract_data (param $k_val i64) (param $t_storage_type i64) (result i64))

(func $get_contract_data (param $k_val i64) (param $t_storage_type i64) (result i64))

(func $del_contract_data (param $k_val i64) (param $t_storage_type i64) (result i64))

;; Creates the contract instance on behalf of `deployer`. `deployer` must authorize this call via Soroban auth framework, i.e. this calls `deployer.require_auth` with respective arguments. `wasm_hash` must be a hash of the contract code that has already been uploaded on this network. `salt` is used to create a unique contract id. Returns the address of the created contract.
(func $create_contract (param $deployer_address_object i64) (param $wasm_hash_bytes_object i64) (param $salt_bytes_object i64) (result i64))

;; Creates the instance of Stellar Asset contract corresponding to the provided asset. `serialized_asset` is `stellar::Asset` XDR serialized to bytes format. Returns the address of the created contract.
(func $create_asset_contract (param $serialized_asset_bytes_object i64) (result i64))

;; Uploads provided `wasm` bytecode to the network and returns its identifier (SHA-256 hash). No-op in case if the same Wasm object already exists.
(func $upload_wasm (param $wasm_bytes_object i64) (result i64))

;; Replaces the executable of the current contract with the provided Wasm code identified by a hash. Wasm entry corresponding to the hash has to already be present in the ledger. The update happens only after the current contract invocation has successfully finished, so this can be safely called in the middle of a function.
(func $update_current_contract_wasm (param $hash_bytes_object i64) (result i64))

;; If the entry's TTL is below `threshold` ledgers, extend `live_until_ledger_seq` such that TTL == `extend_to`, where TTL is defined as live_until_ledger_seq - current ledger.
(func $extend_contract_data_ttl (param $k_val i64) (param $t_storage_type i64) (param $threshold_u32_val i64) (param $extend_to_u32_val i64) (result i64))

;; If the TTL for the current contract instance and code (if applicable) is below `threshold` ledgers, extend `live_until_ledger_seq` such that TTL == `extend_to`, where TTL is defined as live_until_ledger_seq - current ledger.
(func $extend_current_contract_instance_and_code_ttl (param $threshold_u32_val i64) (param $extend_to_u32_val i64) (result i64))

;; If the TTL for the provided contract instance and code (if applicable) is below `threshold` ledgers, extend `live_until_ledger_seq` such that TTL == `extend_to`, where TTL is defined as live_until_ledger_seq - current ledger.
(func $extend_contract_instance_and_code_ttl (param $contract_address_object i64) (param $threshold_u32_val i64) (param $extend_to_u32_val i64) (result i64))

;; Get the id of a contract without creating it. `deployer` is address of the contract deployer. `salt` is used to create a unique contract id. Returns the address of the would-be contract.
(func $get_contract_id (param $deployer_address_object i64) (param $salt_bytes_object i64) (result i64))

;; Get the id of the Stellar Asset contract corresponding to the provided asset without creating the instance. `serialized_asset` is `stellar::Asset` XDR serialized to bytes format. Returns the address of the would-be asset contract.
(func $get_asset_contract_id (param $serialized_asset_bytes_object i64) (result i64))

"Call" host functions (mod d)

;; Calls a function in another contract with arguments contained in vector `args`. If the call is successful, returns the result of the called function. Traps otherwise.
(func $call (param $contract_address_object i64) (param $func_symbol i64) (param $args_vec_object i64) (result i64))

;; Calls a function in another contract with arguments contained in vector `args`, returning either the result of the called function or an `Error` if the called function failed. The returned error is either a custom `ContractError` that the called contract returns explicitly, or an error with type `Context` and code `InvalidAction` in case of any other error in the called contract (such as a host function failure that caused a trap). `try_call` might trap in a few scenarios where the error can't be meaningfully recovered from, such as running out of budget.
(func $try_call (param $contract_address_object i64) (param $func_symbol i64) (param $args_vec_object i64) (result i64))

"Buf" host functions (mod b)

;; Serializes an (SC)Val into XDR opaque `Bytes` object.
(func $serialize_to_bytes (param $v_val i64) (result i64))

;; Deserialize a `Bytes` object to get back the (SC)Val.
(func $deserialize_from_bytes (param $b_bytes_object i64) (result i64))

;; Copies a slice of bytes from a `Bytes` object specified at offset `b_pos` with length `len` into the linear memory at position `lm_pos`. Traps if either the `Bytes` object or the linear memory doesn't have enough bytes.
(func $bytes_copy_to_linear_memory (param $b_bytes_object i64) (param $b_pos_u32_val i32) (param $lm_pos_u32_val i32) (param $len_u32_val i32) (result i64))

;; Copies a segment of the linear memory specified at position `lm_pos` with length `len`, into a `Bytes` object at offset `b_pos`. The `Bytes` object may grow in size to accommodate the new bytes. Traps if the linear memory doesn't have enough bytes.
(func $bytes_copy_from_linear_memory (param $b_bytes_object i64) (param $b_pos_u32_val i32) (param $lm_pos_u32_val i32) (param $len_u32_val i32) (result i64))

;; Constructs a new `Bytes` object initialized with bytes copied from a linear memory slice specified at position `lm_pos` with length `len`.
(func $bytes_new_from_linear_memory (param $lm_pos_u32_val i32) (param $len_u32_val i32) (result i64))

;; Create an empty new `Bytes` object.
(func $bytes_new (result i64))

;; Update the value at index `i` in the `Bytes` object. Return the new `Bytes`. Trap if the index is out of bounds.
(func $bytes_put (param $b_bytes_object i64) (param $i_u32_val i32) (param $u_u32_val i32) (result i64))

;; Returns the element at index `i` of the `Bytes` object. Traps if the index is out of bound.
(func $bytes_get (param $b_bytes_object i64) (param $i_u32_val i32) (result i32))

;; Delete an element in a `Bytes` object at index `i`, shifting all elements after it to the left. Return the new `Bytes`. Traps if the index is out of bound.
(func $bytes_del (param $b_bytes_object i64) (param $i_u32_val i32) (result i64))

;; Returns length of the `Bytes` object.
(func $bytes_len (param $b_bytes_object i64) (result i32))

;; Appends an element to the back of the `Bytes` object.
(func $bytes_push (param $b_bytes_object i64) (param $u_u32_val i32) (result i64))

;; Removes the last element from the `Bytes` object and returns the new `Bytes`. Traps if original `Bytes` is empty.
(func $bytes_pop (param $b_bytes_object i64) (result i64))

;; Return the first element in the `Bytes` object. Traps if the `Bytes` is empty
(func $bytes_front (param $b_bytes_object i64) (result i32))

;; Return the last element in the `Bytes` object. Traps if the `Bytes` is empty
(func $bytes_back (param $b_bytes_object i64) (result i32))

;; Inserts an element at index `i` within the `Bytes` object, shifting all elements after it to the right. Traps if the index is out of bound
(func $bytes_insert (param $b_bytes_object i64) (param $i_u32_val i32) (param $u_u32_val i32) (result i64))

;; Clone the `Bytes` object `b1`, then moves all the elements of `Bytes` object `b2` into it. Return the new `Bytes`. Traps if its length overflows a u32.
(func $bytes_append (param $b1_bytes_object i64) (param $b2_bytes_object i64) (result i64))

;; Copies the elements from `start` index until `end` index, exclusive, in the `Bytes` object and creates a new `Bytes` from it. Returns the new `Bytes`. Traps if the index is out of bound.
(func $bytes_slice (param $b_bytes_object i64) (param $start_u32_val i32) (param $end_u32_val i32) (result i64))

;; Copies a slice of bytes from a `String` object specified at offset `s_pos` with length `len` into the linear memory at position `lm_pos`. Traps if either the `String` object or the linear memory doesn't have enough bytes.
(func $string_copy_to_linear_memory (param $s_string_object i64) (param $s_pos_u32_val i64) (param $lm_pos_u32_val i64) (param $len_u32_val i64) (result i64))

;; Copies a slice of bytes from a `Symbol` object specified at offset `s_pos` with length `len` into the linear memory at position `lm_pos`. Traps if either the `String` object or the linear memory doesn't have enough bytes.
(func $symbol_copy_to_linear_memory (param $s_symbol_object i64) (param $s_pos_u32_val i64) (param $lm_pos_u32_val i64) (param $len_u32_val i64) (result i64))

;; Constructs a new `String` object initialized with bytes copied from a linear memory slice specified at position `lm_pos` with length `len`.
(func $string_new_from_linear_memory (param $lm_pos_u32_val i64) (param $len_u32_val i64) (result i64))

;; Constructs a new `Symbol` object initialized with bytes copied from a linear memory slice specified at position `lm_pos` with length `len`.
(func $symbol_new_from_linear_memory (param $lm_pos_u32_val i64) (param $len_u32_val i64) (result i64))

;; Returns length of the `String` object.
(func $string_len (param $s_string_object i64) (result i64))

;; Returns length of the `Symbol` object.
(func $symbol_len (param $s_symbol_object i64) (result i64))

;; Return the index of a Symbol in an array of linear-memory byte-slices, or trap if not found.
(func $symbol_index_in_linear_memory (param $sym_symbol i64) (param $slices_pos_u32_val i64) (param $len_u32_val i64) (result i64))

"Crypto" host functions (mod c)

;;
(func $compute_hash_sha256 (param $x_bytes_object i64) (result i64))

;;
(func $verify_sig_ed25519 (param $k_bytes_object i64) (param $x_bytes_object i64) (param $s_bytes_object i64) (result i64))

;; Returns the keccak256 hash of given input bytes.
(func $compute_hash_keccak256 (param $x_bytes_object i64) (result i64))

;; Recovers the SEC-1-encoded ECDSA secp256k1 public key that produced a given 64-byte signature over a given 32-byte message digest, for a given recovery_id byte.
(func $recover_key_ecdsa_secp256k1 (param $msg_digest_bytes_object i64) (param $signature_bytes_object i64) (param $recovery_id_u32_val i64) (result i64))

"Address" host functions (mod a)

;; Checks if the address has authorized the invocation of the current contract function with the provided arguments. Traps if the invocation hasn't been authorized.
(func $require_auth_for_args (param $address_address_object i64) (param $args_vec_object i64) (result i64))

;; Checks if the address has authorized the invocation of the current contract function with all the arguments of the invocation. Traps if the invocation hasn't been authorized.
(func $require_auth (param $address_address_object i64) (result i64))

;; Converts a provided Stellar strkey address of an account or a contract ('G...' or 'C...' respectively) to an address object. `strkey` can be either `BytesObject` or `StringObject` (the contents should represent the `G.../C...` string in both cases). Any other valid or invalid strkey (e.g. 'S...') will trigger an error. Prefer directly using the Address objects whenever possible. This is only useful in the context of custom messaging protocols (e.g. cross-chain).
(func $strkey_to_address (param $strkey_val i64) (result i64))

;; Converts a provided address to Stellar strkey format ('G...' for account or 'C...' for contract). Prefer directly using the Address objects whenever possible. This is only useful in the context of custom messaging protocols (e.g. cross-chain).
(func $address_to_strkey (param $address_address_object i64) (result i64))

;; Authorizes sub-contract calls for the next contract call on behalf of the current contract. Every entry in the argument vector corresponds to `InvokerContractAuthEntry` contract type that authorizes a tree of `require_auth` calls on behalf of the current contract. The entries must not contain any authorizations for the direct contract call, i.e. if current contract needs to call contract function F1 that calls function F2 both of which require auth, only F2 should be present in `auth_entries`.
(func $authorize_as_curr_contract (param $auth_entires_vec_object i64) (result i64))

"Test" host functions (mod t)

;; A dummy function taking 0 arguments and performs no-op. This function is for test purpose only, for measuring the roundtrip cost of invoking a host function, i.e. host->Vm->host.
(func $dummy0 (result i64))

"prng" host functions (mod p)

;; Reseed the frame-local PRNG with a given BytesObject, which should be 32 bytes long.
(func $prng_reseed (param $seed_bytes_object i64) (result i64))

;; Construct a new BytesObject of the given length filled with bytes drawn from the frame-local PRNG.
(func $prng_bytes_new (param $length_u32_val i64) (result i64))

;; Return a u64 uniformly sampled from the inclusive range [lo,hi] by the frame-local PRNG.
(func $prng_u64_in_inclusive_range (param $lo_u64 u64) (param $hi_u64 u64) (result u64))

;; Return a (Fisher-Yates) shuffled clone of a given vector, using the frame-local PRNG.
(func $prng_vec_shuffle (param $vec_vec_object i64) (result i64))

XDR changes

See CAP-0046-01 for detail definition of all the host object types and semantics of their operations.

Design Rationale

The Wasm smart-contract system for the Stellar network is divided into the host context and the guest context, and the host functions define the interface between the host environment (running the host context) and the VM (running the guest code) via which guest code can interact with the compute resources and host objects. For the full definitions of the host and guest context, host environment, virtual machine, please refer to the “Components” section in CAP-0046-01. The guest-host split allows common smart contract operations and computational heavy-lifting to be off-loaded to the host side. This reduces guest code size and results in a variety of benefits outlined in CAP-0046. However on the flip side, this potentially increases the attack surface and maintenance burden for the host function developers. Therefore it is vital to be judicious on host function selection, and it needs to be based on a clear set of criteria. The criteria we choose for host functions in this phase of the project are:

  • Relevance: the functions need to be relevant to a wide spectrum of smart contract applications. In particular, we would like to prioritize the expensive primitives that are common among smart contract operations.
  • Determinism: produces designed outcome deterministically across all relavent platforms.
  • Efficiency: must run within a reasonably limited resource restriction. Any smart contracts that run out of its resource limits will result in a trap.
  • Maintainability: must be reasonably straightforward to implement and easy to maintain. Maintainability requirement also extends to the third-party library we choose for the implementation of a particular host function.

Additional functions/host objects may be included

The list of host functions proposed is an initial set based on the criteria above. It is not meant to be an exhaustive list. The list of host functions will be an evolving set determined based on the requirement and needs of the stellar ecosystem.

Resource metering

All the host functions are subject to resource metering specified in cap-0046-01. Cpu and memory consumptions are tracked by metering during the host function execution, and exceeding the resource limit will result in an SCEC_EXCEEDED_LIMIT error.

Error handling

All host functions (with the exception of try_call) are infallible. An error generated during the host function execution will immediately result in a trap to the guest VM. The alternative approach of making all host functions fallible, i.e. including a success-or-failure signal in the returned 64-bit value. There are a few reasons favoring the infallible interface:

  1. Minimizes the amount of redundant error-handling guest code even on the non-error path (which is most of the time), thus reducing code size and resource cost.
  2. Trapping by default ensures errors are not hidden or forgotten, therefore makes for a safer design.
  3. Including the success-or-failure signal in the return value requires additional implementation complexity on the host, which is paid for by every contract on every host function call.
  4. No easy way to disambiguate "fail with status" vs "ok with status". See explaination below.

The host function reportoire should be clear on the failure conditions, and should contain enough building blocks to help the guest preemptively decide if a failure condition will be triggered before making the call. For example, the vec_get function will trap if the index argument is greater than the length of the vector, but a contract developer can use the vec_len function to check if this would occur before calling vec_get.

try_call

The only fallible host function is try_call, which will return the error code as the result (instead of trapping) on failure.

One downside of allowing error code as return value is the ambiguity of "fail with status" and "ok with status". If a contract function returns an ScError as its ok return value, there is no other mechanism deciding if the error is the ok value or the failure status.

Security Concerns

The security concerns are the same as what have been outlined in CAP-0046. By expanding the host object repertoire and introducing host functions that create and operate on host objects on-the-fly during runtime, we’ve expanded the surface where those concerns manifest. In particular, this CAP aims to address “the risk of mis-metering of guest-controlled resources and denial of service”, by detailing the exact metrics of resource accounting on both the guest and the host side.

Implementation

Host functions have been implemented in rs-soroban-env.