Expand description
Piecrust VM for WASM smart contract execution.
A VM is instantiated by calling VM::new using a directory for
storage of commits.
Once instantiation has been successful, Sessions can be started using
VM::session. A session represents the execution of a sequence of
call and deploy calls, and stores mutations to the underlying state
as a result. This sequence of mutations may be committed - meaning written
to the VM’s directory - using commit. After a commit, the resulting
state may be used by starting a new session with it as a base.
Contract execution is metered in terms of points. The limit for the number
of points used in a call or deploy is passed in their respective
function signatures. If the limit is exceeded during the call an error will
be returned. To learn more about the compiler middleware used to achieve
this, please refer to the relevant wasmer docs.
State Representation and Session/Commit Mechanism
Smart Contracts are represented on disk by two separate files: their WASM bytecode and their linear memory at a given commit. The collection of all the memories of smart contracts at a given commit is referred to as the state of said commit.
During a session, each contract called in the sequence of queries/transactions is loaded by:
- Reading the contract’s bytecode file
- Memory mapping the linear memory file copy-on-write (CoW)
Using copy-on-write mappings of linear memories ensures that each commit is
never mutated in place by a session, with the important exception of
deletions and squashes.
Session Concurrency
Multiple sessions may be started concurrently on the same VM, and then
passed on to different threads. These sessions are then non-overlapping
sequences of mutations of a state and may all be committed/dropped
simultaneously.
use piecrust::{Session, VM};
fn assert_send<T: Send>() {}
// Both VM and Session are `Send`
assert_send::<VM>();
assert_send::<Session>();This is achieved by synchronizing commit deletions, squashes, and session spawns/commits using a synchronization loop started on VM instantiation.
Call Atomicity
Contract calls are executed atomically, that is, they are either executed completely or they are not executed at all.
In other words, if the call succeeds, all the state mutations they produce are kept, while if they produce an error (e.g. they panic), all such mutations are reverted.
If the call was made within another call (i.e., the caller was a contract), we ensure all mutations are reverted by undoing the whole call stack of the current transact/query execution, and re-executing it with the exception of the error-producing call, which returns an error without being actually executed.
Usage
use piecrust::{contract_bytecode, ContractData, SessionData, VM};
let mut vm = VM::ephemeral().unwrap();
const OWNER: [u8; 32] = [0u8; 32];
const LIMIT: u64 = 1_000_000;
let mut session = vm.session(SessionData::builder()).unwrap();
let counter_id = session.deploy(contract_bytecode!("counter"), ContractData::builder(OWNER), LIMIT).unwrap();
assert_eq!(session.call::<_, i64>(counter_id, "read_value", &(), LIMIT).unwrap().data, 0xfc);
session.call::<_, ()>(counter_id, "increment", &(), LIMIT).unwrap();
assert_eq!(session.call::<_, i64>(counter_id, "read_value", &(), LIMIT).unwrap().data, 0xfd);
let commit_root = session.commit().unwrap();
assert_eq!(commit_root, vm.commits()[0]);Macros
Structs
- An archived
Event - An archived
RawCall - An archived
RawResult - ID to identify the wasm contracts after they have been deployed
- The resolver for an archived
ContractId - And event emitted by a contract.
- The resolver for an archived
Event - A
RawCallis a contract call that doesn’t care about types and only operates on raw data. - The resolver for an archived
RawCall - The resolver for an archived
RawResult - A running mutation to a state.
- A handle to the piecrust virtual machine.
Enums
- An archived
ContractError - The error possibly returned on an inter-contract-call.
- The resolver for an archived
ContractError - The error type returned by the piecrust VM.
Constants
- The size of the argument buffer in bytes
- The length of
ContractIdin bytes - How many bytes to use for scratch space when serializing
Traits
- A query executable on the host.
Type Definitions
- Type with
rkyvserialization capabilities for specific types.