Trie API
Here we provide a specification of trie API. After this NEP is merged, the cases where our current implementation does not follow the specification are considered to be bugs that need to be fixed.
In an external call (see Context API), the storage functions below can also panic with
RecordedStorageExceeded: the trie nodes and values the call reads go into the chunk's storage proof, and an external
call may add at most max_external_call_recorded_size (20 KiB) to it. A read that would record a value past the limit
fails without reading it. A read that records the trie node that crosses the limit fails after recording it, and so does
a storage_remove that crosses it. The call is then rejected, and nobody pays for it (see
Gas in External Contract Calls).
Removals are costly in an external call. Each storage_remove counts toward the limit with a fixed estimate of 2000
bytes, even for a key that isn't present, so a call can remove no more than ten keys, and fewer, as the trie nodes it
reads count as well. Larger cleanups belong in the receipts the call creates, once external calls can create them.
storage_write
#![allow(unused)] fn main() { storage_write(key_len: u64, key_ptr: u64, value_len: u64, value_ptr: u64, register_id: u64) -> u64 }
Writes key-value into storage.
Normal operation
- If key is not in use it inserts the key-value pair and does not modify the register;
- If key is in use it inserts the key-value and copies the old value into the
register_id.
Returns
- If key was not used returns
0; - If key was used returns
1.
Panics
- If
key_len + key_ptrorvalue_len + value_ptrexceeds the memory container it panics withMemoryAccessViolation. (When we say that something panics with the given error, it means that the host function returns this error and the execution of the contract stops. For mocks of the host that would only cause a non-name panic.) - If
key_lenisu64::MAXandkey_ptris an unused register it panics withInvalidRegisterId. The same applies tovalue_lenandvalue_ptr; - If returning the preempted value into the registers exceed the memory container it panics with
MemoryAccessViolation; - If the key is longer than
max_length_storage_key(2,048 bytes) it panics withKeyLengthExceeded; - If the value is longer than
max_length_storage_value(4,194,304 bytes) it panics withValueLengthExceeded; - If called in a view call it panics with
ProhibitedInView; - If the storage proof recorded for this receipt exceeds
per_receipt_storage_proof_size_limitit panics withRecordedStorageExceeded;
storage_read
#![allow(unused)] fn main() { storage_read(key_len: u64, key_ptr: u64, register_id: u64) -> u64 }
Reads the value stored under the given key.
Normal operation
- If key is used copies the content of the value into the
register_id, even if the content is zero bytes; - If key is not present then does not modify the register.
Returns
- If key was not present returns
0; - If key was present returns
1.
Panics
- If
key_len + key_ptrexceeds the memory container it panics withMemoryAccessViolation; - If
key_lenisu64::MAXandkey_ptris an unused register it panics withInvalidRegisterId; - If returning the preempted value into the registers exceed the memory container it panics with
MemoryAccessViolation; - If the key is longer than
max_length_storage_key(2,048 bytes) it panics withKeyLengthExceeded; - If the storage proof recorded for this receipt exceeds
per_receipt_storage_proof_size_limitit panics withRecordedStorageExceeded;
storage_remove
#![allow(unused)] fn main() { storage_remove(key_len: u64, key_ptr: u64, register_id: u64) -> u64 }
Removes the value stored under the given key.
Normal operation
Very similar to storage_read:
- If key is used, removes the key-value from the trie and copies the content of the value into the
register_id, even if the content is zero bytes. - If key is not present then does not modify the register.
Returns
- If key was not present returns
0; - If key was present returns
1.
Panics
- If
key_len + key_ptrexceeds the memory container it panics withMemoryAccessViolation; - If
key_lenisu64::MAXandkey_ptris an unused register it panics withInvalidRegisterId; - If the registers exceed the memory limit panics with
MemoryAccessViolation; - If returning the preempted value into the registers exceed the memory container it panics with
MemoryAccessViolation; - If the key is longer than
max_length_storage_key(2,048 bytes) it panics withKeyLengthExceeded; - If called in a view call it panics with
ProhibitedInView; - If the storage proof recorded for this receipt exceeds
per_receipt_storage_proof_size_limitit panics withRecordedStorageExceeded; - If called in an external call and the removal, counted as 2000 bytes even if the key is not present, crosses
max_external_call_recorded_sizeit panics withRecordedStorageExceeded(see above);
storage_has_key
#![allow(unused)] fn main() { storage_has_key(key_len: u64, key_ptr: u64) -> u64 }
Checks if there is a key-value pair.
Normal operation
- If key is used returns
1, even if the value is zero bytes; - Otherwise returns
0.
Panics
- If
key_len + key_ptrexceeds the memory container it panics withMemoryAccessViolation; - If
key_lenisu64::MAXandkey_ptris an unused register it panics withInvalidRegisterId; - If the key is longer than
max_length_storage_key(2,048 bytes) it panics withKeyLengthExceeded; - If the storage proof recorded for this receipt exceeds
per_receipt_storage_proof_size_limitit panics withRecordedStorageExceeded;
storage_iter_prefix
#![allow(unused)] fn main() { storage_iter_prefix(prefix_len: u64, prefix_ptr: u64) -> u64 }
DEPRECATED, calling it will result in HostError::Deprecated error. It charges no gas.
storage_iter_range
#![allow(unused)] fn main() { storage_iter_range(start_len: u64, start_ptr: u64, end_len: u64, end_ptr: u64) -> u64 }
DEPRECATED, calling it will result in HostError::Deprecated error. It charges no gas.
storage_iter_next
#![allow(unused)] fn main() { storage_iter_next(iterator_id: u64, key_register_id: u64, value_register_id: u64) -> u64 }
DEPRECATED, calling it will result in HostError::Deprecated error. It charges no gas.