The host interface
On this page
A program talks to the chain only through twelve functions it imports from module
ting. With them it reads its input and context, uses its own storage, learns which
code another contract runs, calls or creates contracts, and produces output.
Anything more, such as checking signatures, moving money or charging fees, is the
business of programs, not of the interface. This page is the program author's guide
to the twelve functions. The exact rules, including the order of every check and
charge, are in
section 22 of the protocol rules.
| Purpose | Imports |
|---|---|
| Read invocation data | input, context |
| Use its own storage | storage_get, storage_set, storage_delete |
| Learn about other contracts | code_hash |
| Call or create contracts | call, deploy, call_result |
| Produce output | emit, return, revert |
Conventions#
- Spans are checked. Every argument is a 32-bit integer, read as unsigned. A byte string is passed as a pointer and a length into the program's exported memory, and every such span is checked against the memory before anything is copied. A span outside it traps the invocation.
- Bytes are opaque. Input, results, events and storage values have no protocol format. Each program defines its own encodings.
- Bytes cost compute. Every copy across the interface, in either direction, costs
bytes(n, s): the word price of the program's memory size for each started 32 bytes, and the span price for each span that holds a byte (memory prices). Up to 2 MiB of memory the span price is zero; above that it is the price of a store. - Records cost compute and S. Every record read costs 1,900 C, for hashing the record's key, and one S, and every value written also costs the price of hashing it into its leaf (hashing).
- A charge fits whole or not at all. A charge that does not fit ends the invocation with the status of the exhausted counter, and a refused charge moves no bytes.
Signatures#
Every parameter and every ordinary result is i32, and an import's type must match
its row exactly (module shape).
| Import | Parameters | Result |
|---|---|---|
input |
out_ptr, capacity | required length |
context |
selector, out_ptr, capacity | required length |
storage_get |
key_ptr, out_ptr, capacity | length, or 2^32 − 1 when absent |
storage_set |
key_ptr, value_ptr, value_len | none |
storage_delete |
key_ptr | none |
code_hash |
target_ptr, out_ptr, capacity | 32, or 2^32 − 1 when absent |
call |
target_ptr, input_ptr, input_len, budget_ptr, flags | child status, or 128 |
deploy |
code_ptr, code_len, input_ptr, input_len, budget_ptr | child status, or 128 |
call_result |
out_ptr, capacity | length of the last child result |
emit |
payload_ptr, payload_len | none |
return |
result_ptr, result_len | none |
revert |
result_ptr, result_len | none |
return and revert never come back to the program, although their type has
no result. Keys and targets are 32-byte spans. A budget is a 32-byte span holding
four little-endian u64 fields in C, S, D, G order. Call flags are 0 for an
ordinary call and 1 for a read-only one.
Getters#
input, context, storage_get, code_hash and call_result copy a value into an
output buffer, given as a pointer and a capacity, and return the value's length as an
unsigned 32-bit number:
- The full length is returned. If the value fits the capacity, it is copied and the copy is charged. If it does not, nothing is copied and no copy is charged, so a program can ask with a small buffer, learn the length and call again with a larger one.
- Absent is not empty.
storage_getreturns2^32 − 1, every bit set (−1 read as signed), for a record that does not exist, and 0 for a record that exists and is empty.code_hashreturns2^32 − 1for a contract that does not exist. No value is ever that long: a record value is at most2^32 − 2bytes (storage writes). - Every call pays again. A getter's charges repeat each time it is called; nothing is discounted for having been read before.
The output span is checked in full even when the value turns out to be absent or
shorter. storage_get reads a record in the executing contract's own storage
(namespaces). It pays for copying its 32-byte key,
1,900 C and one S for the read, and the copy of the value when it fits. The read
needs only the record's length until the value is copied, so a length query does no
work in proportion to the value.
Storage writes#
storage_set sets a record of the executing contract to a value of any length;
storage_delete deletes one. Both fail with status 11 in a
read-only invocation. Each pays:
- the copy of its arguments: the 32-byte key and, for
storage_set, the value; - 1,900 C and one S for the key and for reading the old record, of which only the length is needed;
- for
storage_set, the price of hashing the value into its leaf,100 + 900 × ceil((n + 62) / 64)C fornbytes, and for either, a second S and the record's growth in G, which is zero for a deletion.
Both S units are charged even for a write that changes nothing and for deleting a
record that does not exist. The key is 32 bytes and a value at most 2^32 − 2
bytes, one fewer than memory allows, so that no length can be mistaken for absence.
There is no other size limit. The runtime's own writes of a manifest and a code
record during deployment are priced the same way.
Output#
return and revert end the invocation, with status Success or Revert, and their
bytes become its result either way. Each result byte costs one D, plus the compute
of copying it out. revert rolls back the invocation's effects
(checkpoints), so a program can fail with an explanation its
caller can read.
emit appends an event holding the executing ContractId and the payload bytes
(events). An event of n payload bytes costs 36 + n D,
plus the copy. emit fails with status 11 in a read-only invocation.
Calls and deployments#
call runs another contract as a child with a sub-budget the caller chooses, and
deploy runs a deployment the same way: it installs
new code and runs its initializer. Both go through the same stages:
- A
callwith flags other than 0 or 1 traps, and adeployin a read-only invocation fails with status 11. Otherwise the caller pays for copying the 32-byte budget, and for acallthe 32-byte target too, and its last-result buffer is emptied. - If the requested budget is larger, in any counter, than what the caller has left, the import returns 128 and no child is created. The caller simply continues.
- Otherwise the child opens with that budget and pays, out of it, for receiving its
input and, for a
deploy, its code. If it cannot, it ends with status 3 having consumed nothing, and the import returns 3. - Otherwise the child runs, read-only when the call's flags are 1 or the caller is itself read-only, and the import returns the child's status.
The child's status is a receipt status code; 128 is
only ever a host return value, never a receipt status. A call to a contract that does
not exist returns 8. After a call, the last-result buffer holds the child's result
bytes. After a deploy it holds the new 32-byte ContractId on success, or the failed
initializer's result bytes, if any. call_result is a getter over that buffer:
reading it costs the copy but no D, and the parent pays the ordinary D only if it
returns those bytes itself.
Because a child pays for receiving its own input and code, a caller that hands on everything it has left, less a fixed reserve for its own work afterwards, can pass an input of any size: the copy comes out of the child's share, never out of the reserve. What the child consumes stays charged to every enclosing budget, and what it leaves unused is available to the caller again (calls and rollback). The exact stages and their charges are in section 22.7 of the protocol rules.
Context#
context copies one field, chosen by a selector:
| Selector | Field | Encoding |
|---|---|---|
| 0 | ChainId | 32 bytes |
| 1 | TransactionId | 32 bytes |
| 2 | Executing ContractId | 32 bytes |
| 3 | Calling ContractId | 33 bytes: tag 0 and 32 zero bytes when there is none, or tag 1 and the identifier |
| 4 | Height | u64, little-endian |
| 5 | Block timestamp | u64, little-endian |
| 6 | Parent BlockId | 32 bytes |
| 7 | Producer data | its original bytes, exactly as the block body carries them (the body) |
| 8 | Remaining budget | four u64, little-endian, C/S/D/G, of the executing frame |
Any other selector traps. A top-level invocation has no calling contract; an initializer's calling contract is its deployer, or none for a top-level deployment.
Selector 8 reports the executing frame's budget minus what it has consumed so far, taken before the getter's own copy is charged. It lets a program give a child exactly what it can afford instead of guessing, and it is deterministic, since it follows from the declared budget and the consumption so far. A program that forwards it to a child must first subtract what it will spend before the call.
The current block's own ID, its nonce, the current state root, the wall clock, local resource timings and other contracts' storage are deliberately unavailable. A candidate block's execution therefore does not depend on the nonce search or on the block's eventual hash.
Code hashes#
code_hash takes a ContractId and returns that contract's CodeHash. It costs
bytes(32, 1) for copying the target, 1,900 C and one S for reading its manifest,
and bytes(32, 1) to copy the hash out when the buffer holds it. A program uses it
to verify which code another contract runs, for example to accept only known
implementations before calling or trusting one.
Failures#
Besides the instruction traps, the host traps, with status
2, for an invalid span, a storage_set value of 2^32 − 1 bytes, an unknown context
selector and invalid call flags. A write, deployment or event attempted in read-only
mode fails with status 11 instead, and a charge that does not fit fails with the
status of its exhausted counter. Each of these ends the invocation that made the
host call, without a result; its caller receives the status and continues. A
sub-budget that does not fit (128) and a child that cannot pay for its input or code
(3) are not failures of the caller at all: the import returns, and the caller
decides. When several faults apply at once, the one checked first decides the status
and the counters; the order is in
section 22.10 of the protocol rules.