The execution of smart contracts is performed within a Wasm VM, specifically
Wasmer. This environment
is limited in terms of the data types that can be used. For the most part,
it is only possible to operate on values defined by wasmer::Value.
This creates the need for code that allows us to translate
simple wasmer::Values into higher-order data types that are more useful
(e.g. enums, Results, custom Errors, and so on).
Relevant code for this section can be found in src/runtime and src/sdk/src/.
Smart contracts can only interact with Wasm via functions that map to
one of four 'contract sections' (see also the ContractSection enum). They are:
initializeentrypointupdatemetadataBusiness logic for a contract is contained in functions that are accessible
via the contract's entrypoint. Data is sent via the payload parameter.
All of the functions associated with the contract sections are processed by the call()
function in the runtime. It is this function that acts as the interface between
lower-level Wasm functionality and the APIs that are provided to contract
developers.
Wasm operations are also possible via APIs defined in the DarkFi SDK.
These work as foreign functions, making use of Rust's unsafe and extern
features to interface with Wasm from Rust.
Smart contracts in DarkFi use these lower-level Wasm functions work with state. Therfore, smart contracts must also verify these return values to ensure that errors are properly handled.
Relevant code:
src/sdk/src/util.rssrc/contracts/As of now (Nov. 2023), there is not a single mechanism in the codebase to translate integer values to custom Errors. As such, it is done in an ad-hoc manner in different locations. This is an area of future work -- for now, there are manual conventions that should be followed to reduce potential bugs.
The Wasm functions are configured
in src/runtime/vm_runtime.rs and stored in the Wasmer Instance as function imports.
Exit codes should follow this convention:
0 --> successful execution with no return dataNegative values can be mapped to custom Errors, such as ContractErrors, in order to
provide more information as well as to be handled using e.g. match arms.
Relevant code:
src/sdk/src/error.rssrc/runtime/import/In most cases, i64 is the return type for functions that interface with the wasm runtime.
It should be understood that the values stored in these datatypes are u32.
Any conversion between types should be done with extreme care
to avoid issues such as integer overflow, underflow, or truncation. Return values
should be checked and errors should be handled properly. Further work is needed
to ensure that the code returns an error or panics if a value is found to be outside
of the range of u32.
Note also that wasmer::Values do not actually have a concept of being
"signed" (i.e. negative).