C++ Native Functions

Native functions give services the ability to print debugging messages, abort transactions on errors, access databases and event logs, and synchronously call other services. There aren't many native functions since services implement most psibase functionality.

Types For Native Functions

psibase::Action

struct psibase::Action {
    AccountNumber     sender;  // Account sending the action
    AccountNumber     service; // Service to execute the action
    MethodNumber      method;  // Service method to execute
    std::vector<char> rawData; // Data for the method
};

A synchronous call.

An Action represents a synchronous call between services. It is the argument to call and can be fetched using getCurrentAction.

Transactions also contains actions requested by the transaction authorizers.

psibase::DbId

enum psibase::DbId: uint32_t {
    service             = 0, // Services should store their tables here
    writeOnly           = 1, // Data for RPC
    subjective          = 2, // The first 64 bits of the key match the service.
    nativeConstrained   = 3, // Tables used by native code
    nativeUnconstrained = 4, // Tables used by native code
    blockLog            = 5, // Block log
    historyEvent        = 6, // Long-term history event storage
    uiEvent             = 7, // Short-term history event storage
    merkleEvent         = 8, // Events which go into the merkle tree
    blockProof          = 9, // block signatures
    numDatabases        = 10, // Number of defined databases
};

Identify database to operate on.

Native functions expose a set of databases which serve various purposes. This enum identifies which database to use when invoking those functions.

psibase::DbId::service

Services should store their tables here.

The first 64 bits of the key match the service.

psibase::DbId::writeOnly

Data for RPC.

Write-only during transactions, and read-only during RPC. Individual nodes may modify this database, expire data from this database, or wipe it entirely at will.

The first 64 bits of the key match the service.

psibase::DbId::subjective

The first 64 bits of the key match the service..

psibase::DbId::nativeConstrained

Tables used by native code.

This database enforces constraints during write. Only writable by privileged services, but readable by all services.

Some writes to this database indicate chain upgrades. If a privileged service writes to a table that an older node version doesn't know about, or writes new fields to an existing table that an older node doesn't know about, then that node will reject the write. If the producers accepted the write into a block, then the node will stop following the chain until it's upgraded to a newer version.

psibase::DbId::nativeUnconstrained

Tables used by native code.

This database doesn't enforce constraints during write. Only writable by privileged services, but readable by all services.

psibase::DbId::blockLog

Block log.

Transactions don't have access to this, but RPC does.

psibase::DbId::historyEvent

Long-term history event storage.

Write-only during transactions, and read-only during RPC. Individual nodes may modify this database, expire data from this database, or wipe it entirely at will.

TODO: this policy may eventually change to allow time-limited read access during transactions.

Key is an auto-incremented, 64-bit unsigned number.

Value must begin with:

  • 32 bit: block number
  • 64 bit: service

Only usable with these native functions:

TODO: right now the value must begin with the service. Revisit whether beginning with the block number is useful.

psibase::DbId::uiEvent

Short-term history event storage.

These events are erased once the block that produced them becomes final. They notify user interfaces which subscribe to activity.

Write-only during transactions, and read-only during RPC. Individual nodes may modify this database, expire data from this database, or wipe it entirely at will.

Key is an auto-incremented, 64-bit unsigned number.

Value must begin with:

  • 32 bit: block number
  • 64 bit: service

Only usable with these native functions:

TODO: right now the value must begin with the service. Revisit whether beginning with the block number is useful. TODO: consider removing UI events

psibase::DbId::merkleEvent

Events which go into the merkle tree.

TODO: read support; right now only RPC mode can read

Services may produce these events during transactions and may read them up to 1 hour (configurable) after they were produced, or they reach finality, which ever is longer.

Key is an auto-incremented, 64-bit unsigned number.

Value must begin with:

  • 32 bit: block number
  • 64 bit: service

Only usable with these native functions:

TODO: right now the value must begin with the service. Revisit whether beginning with the block number is useful.

psibase::DbId::blockProof

block signatures.

psibase::DbId::numDatabases

Number of defined databases.

This number may grow in the future

Wrapped Native Functions

These functions wrap the Raw Native Functions.

psibase::abortMessage

void psibase::abortMessage(
    std::string_view message
);

Abort with message.

Message should be UTF8.

psibase::call

std::vector<char> psibase::call(
    const Action & action
);

Call a service and return its result.

psibase::call

std::vector<char> psibase::call(
    const char * action,
    uint32_t     len
);

Call a service and return its result.

action must contain a fracpacked Action.

psibase::call

std::vector<char> psibase::call(
    psio::input_stream action
);

Call a service and return its result.

action must contain a fracpacked Action.

psibase::check

void psibase::check(
    bool             cond,
    std::string_view message
);

Abort with message if !cond.

Message should be UTF8.

psibase::getCurrentAction

Action psibase::getCurrentAction();

Get the currently-executing action.

This function unpacks the data into the Action struct. For large data, getCurrentActionView can be more efficient.

If the service, while handling action A, calls itself with action B:

  • Before the call to B, getCurrentAction() returns A.
  • After the call to B, getCurrentAction() returns B.
  • After B returns, getCurrentAction() returns A.

Note: The above only applies if the service uses call. Actor uses call.

psibase::getCurrentActionView

psio::shared_view_ptr<Action> psibase::getCurrentActionView();

Get the currently-executing action.

This function creates a view, which can save time for large data. For small data, getCurrentAction can be more efficient.

If the service, while handling action A, calls itself with action B:

  • Before the call to B, getCurrentAction() returns A.
  • After the call to B, getCurrentAction() returns B.
  • After B returns, getCurrentAction() returns A.

Note: The above only applies if the service uses call. Actor uses call.

psibase::getKey

std::vector<char> psibase::getKey();

Get key.

Other functions set the key.

psibase::getResult

std::vector<char> psibase::getResult();

Get result.

Other functions set result.

psibase::getResult

std::vector<char> psibase::getResult(
    uint32_t size
);

Get result when size is known.

Other functions set result.

Caution: this does not verify size.

psibase::getSequential

template<typename V, typename Type>
std::optional<V> psibase::getSequential(
    DbId                  db,
    uint64_t              id,
    const AccountNumber * matchService = nullptr,
    const Type *          matchType = nullptr,
    AccountNumber *       service = nullptr,
    Type *                type = nullptr
);

Get a sequentially-numbered record, if available.

  • If matchService is non-null, and the record wasn't written by matchService, then return nullopt. This prevents a spurious abort from mismatched serialization.
  • If matchType is non-null, and the record type doesn't match, then return nullopt. This prevents a spurious abort from mismatched serialization.
  • If service is non-null, then it receives the service that wrote the record. It is left untouched if the record is not available.
  • If type is non-null, then it receives the record type. It is left untouched if either the record is not available or if matchService is not null but doesn't match.

psibase::getSequentialRaw

std::optional<std::vector<char>> psibase::getSequentialRaw(
    DbId     db,
    uint64_t id
);

Get a sequentially-numbered record, if available.

psibase::kvGet

template<typename V, typename K>
std::optional<V> psibase::kvGet(
    DbId      db,
    const K & key
);

Get a key-value pair, if any.

psibase::kvGet

template<typename V, typename K>
std::optional<V> psibase::kvGet(
    const K & key
);

Get a key-value pair, if any.

psibase::kvGetOrDefault

template<typename V, typename K>
V psibase::kvGetOrDefault(
    DbId      db,
    const K & key
);

Get a value, or the default if not found.

psibase::kvGetOrDefault

template<typename V, typename K>
V psibase::kvGetOrDefault(
    const K & key
);

Get a value, or the default if not found.

psibase::kvGetRaw

std::optional<std::vector<char>> psibase::kvGetRaw(
    DbId               db,
    psio::input_stream key
);

Get a key-value pair, if any.

psibase::kvGetSize

template<typename K>
std::optional<uint32_t> psibase::kvGetSize(
    DbId      db,
    const K & key
);

Get size of stored value, if any.

psibase::kvGetSize

template<typename K>
std::optional<uint32_t> psibase::kvGetSize(
    const K & key
);

Get size of stored value, if any.

psibase::kvGetSizeRaw

std::optional<uint32_t> psibase::kvGetSizeRaw(
    DbId               db,
    psio::input_stream key
);

Get size of stored value, if any.

psibase::kvGreaterEqual

template<typename V, typename K>
std::optional<V> psibase::kvGreaterEqual(
    DbId      db,
    const K & key,
    uint32_t  matchKeySize
);

Get the first key-value pair which is greater than or equal to key.

If one is found, and the first matchKeySize bytes of the found key matches the provided key, then returns the value. Use getKey to get the found key.

psibase::kvGreaterEqual

template<typename V, typename K>
std::optional<V> psibase::kvGreaterEqual(
    const K & key,
    uint32_t  matchKeySize
);

Get the first key-value pair which is greater than or equal to key.

If one is found, and the first matchKeySize bytes of the found key matches the provided key, then returns the value. Use getKey to get the found key.

psibase::kvGreaterEqualRaw

std::optional<std::vector<char>> psibase::kvGreaterEqualRaw(
    DbId               db,
    psio::input_stream key,
    uint32_t           matchKeySize
);

Get the first key-value pair which is greater than or equal to key.

If one is found, and the first matchKeySize bytes of the found key matches the provided key, then returns the value. Use getKey to get the found key.

psibase::kvLessThan

template<typename V, typename K>
std::optional<V> psibase::kvLessThan(
    DbId      db,
    const K & key,
    uint32_t  matchKeySize
);

Get the key-value pair immediately-before provided key.

If one is found, and the first matchKeySize bytes of the found key matches the provided key, then returns the value. Use getKey to get the found key.

psibase::kvLessThan

template<typename V, typename K>
std::optional<V> psibase::kvLessThan(
    const K & key,
    uint32_t  matchKeySize
);

Get the key-value pair immediately-before provided key.

If one is found, and the first matchKeySize bytes of the found key matches the provided key, then returns the value. Use getKey to get the found key.

psibase::kvLessThanRaw

std::optional<std::vector<char>> psibase::kvLessThanRaw(
    DbId               db,
    psio::input_stream key,
    uint32_t           matchKeySize
);

Get the key-value pair immediately-before provided key.

If one is found, and the first matchKeySize bytes of the found key matches the provided key, then returns the value. Use getKey to get the found key.

psibase::kvMax

template<typename V, typename K>
std::optional<V> psibase::kvMax(
    DbId      db,
    const K & key
);

Get the maximum key-value pair which has key as a prefix.

If one is found, then returns the value. Use getKey to get the found key.

psibase::kvMax

template<typename V, typename K>
std::optional<V> psibase::kvMax(
    const K & key
);

Get the maximum key-value pair which has key as a prefix.

If one is found, then returns the value. Use getKey to get the found key.

psibase::kvMaxRaw

std::optional<std::vector<char>> psibase::kvMaxRaw(
    DbId               db,
    psio::input_stream key
);

Get the maximum key-value pair which has key as a prefix.

If one is found, then returns the value. Use getKey to get the found key.

psibase::kvPut

template<typename K, NotOptional V>
void psibase::kvPut(
    DbId      db,
    const K & key,
    const V & value
);

Set a key-value pair.

If key already exists, then replace the existing value.

psibase::kvPut

template<typename K, NotOptional V>
void psibase::kvPut(
    const K & key,
    const V & value
);

Set a key-value pair.

If key already exists, then replace the existing value.

psibase::kvPutRaw

void psibase::kvPutRaw(
    DbId               db,
    psio::input_stream key,
    psio::input_stream value
);

Set a key-value pair.

If key already exists, then replace the existing value.

psibase::kvRemove

template<typename K>
void psibase::kvRemove(
    DbId      db,
    const K & key
);

Remove a key-value pair if it exists.

psibase::kvRemove

template<typename K>
void psibase::kvRemove(
    const K & key
);

Remove a key-value pair if it exists.

psibase::kvRemoveRaw

void psibase::kvRemoveRaw(
    DbId               db,
    psio::input_stream key
);

Remove a key-value pair if it exists.

psibase::putSequential

template<typename Type, NotOptional V>
uint64_t psibase::putSequential(
    DbId          db,
    AccountNumber service,
    Type          type,
    const V &     value
);

Add a sequentially-numbered record.

Returns the id.

psibase::putSequentialRaw

uint64_t psibase::putSequentialRaw(
    DbId               db,
    psio::input_stream value
);

Add a sequentially-numbered record.

Returns the id.

psibase::setRetval

template<typename T>
void psibase::setRetval(
    const T & retval
);

Set the return value of the currently-executing action.

psibase::setRetvalBytes

void psibase::setRetvalBytes(
    psio::input_stream s
);

Set the return value of the currently-executing action.

psibase::writeConsole

void psibase::writeConsole(
    const std::string_view & sv
);

Write message to console.

Message should be UTF8.

Raw Native Functions

This is the set of raw native functions (wasm imports). They are available for services to use directly, but we recommend using the Wrapped Native Functions instead.

psibase::raw::abortMessage

void psibase::raw::abortMessage(
    const char *  message,
    std::uint32_t len
);

Abort with message.

Message should be UTF8.

psibase::raw::call

uint32_t psibase::raw::call(
    const char * action,
    uint32_t     len
);

Call a service, store the return value into result, and return the result size.

action must contain a fracpacked Action.

Use getResult to get result.

psibase::raw::clockTimeGet

int32_t psibase::raw::clockTimeGet(
    uint32_t   id,
    uint64_t * time
);

Gets the current value of a clock in nanoseconds..

This function is non-deterministic and is only available in subjective services.

The following clocks are supported

  • __WASI_CLOCKID_REALTIME returns wall-clock time since the unix epoch.
  • __WASI_CLOCKID_MONOTONIC returns monotonic time since an unspecified epoch. All uses of CLOCK_MONOTONIC within the same block use the same epoch.
  • __WASI_CLOCKID_PROCESS_CPUTIME_ID measures CPU time spent executing the current transaction.

Returns 0 on success or an error code on failure.

Errors:

  • EINVAL: the clock id is not supported

psibase::raw::getCurrentAction

uint32_t psibase::raw::getCurrentAction();

Store the currently-executing action into result and return the result size.

The result contains a fracpacked Action; use getResult to get it.

If the service, while handling action A, calls itself with action B:

  • Before the call to B, getCurrentAction() returns A.
  • After the call to B, getCurrentAction() returns B.
  • After B returns, getCurrentAction() returns A.

Note: The above only applies if the service uses call. Actor uses call.

psibase::raw::getKey

uint32_t psibase::raw::getKey(
    const char * dest,
    uint32_t     destSize
);

Copy min(destSize, key_size) bytes of the most-recent key into dest and return key_size.

Other functions set or clear the key. getResult, getKey, and writeConsole are the only raw functions which leave the current result and key intact.

psibase::raw::getResult

uint32_t psibase::raw::getResult(
    const char * dest,
    uint32_t     destSize,
    uint32_t     offset
);

Copy min(destSize, resultSize - offset) bytes from result + offset into dest and return resultSize.

If offset >= resultSize, then skip the copy.

Other functions set or clear result. getResult, getKey, and writeConsole are the only raw functions which leave the current result and key intact.

psibase::raw::getSequential

uint32_t psibase::raw::getSequential(
    DbId     db,
    uint64_t id
);

Get a sequentially-numbered record.

If id is available, then sets result to value and returns size. If id does not exist, returns -1 and clears result.

psibase::raw::kvGet

uint32_t psibase::raw::kvGet(
    DbId         db,
    const char * key,
    uint32_t     keyLen
);

Get a key-value pair, if any.

If key exists, then sets result to value and returns size. If key does not exist, returns -1 and clears result. Use getResult to get result.

psibase::raw::kvGreaterEqual

uint32_t psibase::raw::kvGreaterEqual(
    DbId         db,
    const char * key,
    uint32_t     keyLen,
    uint32_t     matchKeySize
);

Get the first key-value pair which is greater than or equal to the provided key.

If one is found, and the first matchKeySize bytes of the found key matches the provided key, then sets result to value and returns size. Also sets key. Otherwise returns -1 and clears result. Use getResult to get result and getKey to get found key.

psibase::raw::kvLessThan

uint32_t psibase::raw::kvLessThan(
    DbId         db,
    const char * key,
    uint32_t     keyLen,
    uint32_t     matchKeySize
);

Get the key-value pair immediately-before provided key.

If one is found, and the first matchKeySize bytes of the found key matches the provided key, then sets result to value and returns size. Also sets key. Otherwise returns -1 and clears result. Use getResult to get result and getKey to get found key.

psibase::raw::kvMax

uint32_t psibase::raw::kvMax(
    DbId         db,
    const char * key,
    uint32_t     keyLen
);

Get the maximum key-value pair which has key as a prefix.

If one is found, then sets result to value and returns size. Also sets key. Otherwise returns -1 and clears result. Use getResult to get result and getKey to get found key.

psibase::raw::kvPut

void psibase::raw::kvPut(
    DbId         db,
    const char * key,
    uint32_t     keyLen,
    const char * value,
    uint32_t     valueLen
);

Set a key-value pair.

If key already exists, then replace the existing value.

psibase::raw::kvRemove

void psibase::raw::kvRemove(
    DbId         db,
    const char * key,
    uint32_t     keyLen
);

Remove a key-value pair if it exists.

psibase::raw::putSequential

uint64_t psibase::raw::putSequential(
    DbId         db,
    const char * value,
    uint32_t     valueLen
);

Add a sequentially-numbered record.

Returns the id.

psibase::raw::setRetval

void psibase::raw::setRetval(
    const char * retval,
    uint32_t     len
);

Set the return value of the currently-executing action.

psibase::raw::writeConsole

void psibase::raw::writeConsole(
    const char * message,
    uint32_t     len
);

Write message to console.

Message should be UTF8.