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
psio_get_reflect_impl(...);
};
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::Action::psio_get_reflect_impl
psio_reflect_impl_Action<Action> psibase::Action::psio_get_reflect_impl(
Action * ,
::psio::ReflectDummyParam *
);
psibase::DbId
enum psibase::DbId: uint32_t {
service = 0, // Services should store their tables here
writeOnly = 1, // Data for RPC
native = 2, // Tables used by native code
blockLog = 3, // Block log
historyEvent = 4, // Long-term history event storage
uiEvent = 5, // Short-term history event storage
merkleEvent = 6, // Events which go into the merkle tree
blockProof = 7, // block signatures
prevAuthServices = 8, // Not accessible to WASM. During joint consensus, this holds a subset of native as of the last irreversible block. Outside joint consensus, it is empty.
numChainDatabases = 9, // Not accessible to WASM. During joint consensus, this holds a subset of native as of the last irreversible block. Outside joint consensus, it is empty.
beginIndependent = 64, // Not accessible to WASM. During joint consensus, this holds a subset of native as of the last irreversible block. Outside joint consensus, it is empty.
subjective = 64, // Data that is not part of consensus
nativeSubjective = 65, // Subjective tables used by native code
endIndependent = 66, // Subjective tables used by native code
temporary = 67, // Subjective tables that are local to the transaction/query/callback context.
};
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::native
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::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::prevAuthServices
Not accessible to WASM. During joint consensus, this holds a subset of native as of the last irreversible block. Outside joint consensus, it is empty..
psibase::DbId::numChainDatabases
Not accessible to WASM. During joint consensus, this holds a subset of native as of the last irreversible block. Outside joint consensus, it is empty..
psibase::DbId::beginIndependent
Not accessible to WASM. During joint consensus, this holds a subset of native as of the last irreversible block. Outside joint consensus, it is empty..
psibase::DbId::subjective
Data that is not part of consensus.
Only accessible to subjective services and during RPC. Doesn't
undo from aborting transactions, aborting blocks, or forking
blocks. Individual nodes may modify this database or wipe
it entirely at will. Can only be accessed within
PSIBASE_SUBJECTIVE_TX
.
The first 64 bits of the key match the service. Services are not allowed to read or write keys belonging to other services.
psibase::DbId::nativeSubjective
Subjective tables used by native code.
Not fully implemented yet and not available to services. Doesn't undo from aborting transactions, aborting blocks, or forking blocks.
psibase::DbId::endIndependent
Subjective tables used by native code.
Not fully implemented yet and not available to services. Doesn't undo from aborting transactions, aborting blocks, or forking blocks.
psibase::DbId::temporary
Subjective tables that are local to the transaction/query/callback context..
Wrapped Native Functions
These functions wrap the Raw Native Functions.
- psibase::abortMessage
- psibase::call
- psibase::check
- psibase::getCurrentAction
- psibase::getCurrentActionView
- psibase::getKey
- psibase::getResult
- psibase::getSequential
- psibase::getSequentialRaw
- psibase::kvGet
- psibase::kvGetOrDefault
- psibase::kvGetRaw
- psibase::kvGetSize
- psibase::kvGetSizeRaw
- psibase::kvGreaterEqual
- psibase::kvGreaterEqualRaw
- psibase::kvLessThan
- psibase::kvLessThanRaw
- psibase::kvMax
- psibase::kvMaxRaw
- psibase::kvPut
- psibase::kvPutRaw
- psibase::kvRemove
- psibase::kvRemoveRaw
- psibase::putSequential
- psibase::putSequentialRaw
- psibase::setRetval
- psibase::setRetvalBytes
- psibase::writeConsole
- PSIBASE_SUBJECTIVE_TX
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 bymatchService
, 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 ifmatchService
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.
PSIBASE_SUBJECTIVE_TX
#define PSIBASE_SUBJECTIVE_TX
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
- psibase::raw::call
- psibase::raw::clockTimeGet
- psibase::raw::getCurrentAction
- psibase::raw::getKey
- psibase::raw::getResult
- psibase::raw::getSequential
- psibase::raw::kvGet
- psibase::raw::kvGreaterEqual
- psibase::raw::kvLessThan
- psibase::raw::kvMax
- psibase::raw::kvPut
- psibase::raw::kvRemove
- psibase::raw::putSequential
- psibase::raw::setRetval
- psibase::raw::writeConsole
- psibase::raw::checkoutSubjective
- psibase::raw::commitSubjective
- psibase::raw::abortSubjective
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.
psibase::raw::checkoutSubjective
void psibase::raw::checkoutSubjective();
Loads the subjective database.
- The subjective database may not be accessed without calling this first
- If the subjective database is already checked out, this will create a nested checkout
- If an action checks out the subjective database and does not commit it before returning, any changes will be discarded.
There exists a total ordering of all top-level checkouts. Every read of the subjective database returns the most recent write in the current checkout or in previous checkouts that were successfully committed.
The following state is controlled by checkoutSubjective
- DbId::subjective
- Socket auto-close
State that is not affected includes
- Databases that follow forks
- WASM linear memory
- Socket messages sent
psibase::raw::commitSubjective
bool psibase::raw::commitSubjective();
Attempts to commit changes to the subjective database.
- If changes were committed successfully, returns true and closes the subjective database
- If changes were not successfully committed, returns false and reloads the subjective database, starting a new checkout
- It is an error if the current action did not check out the subjective database
If there were no writes to the subjective database or if this is a nested checkout, commit always succeeds.
psibase::raw::abortSubjective
void psibase::raw::abortSubjective();
Closes the current checkout and discards any changes made to the subjective database.