Crate piecrust

Source
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 gas. The limit for gas 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 runtime 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.

§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, 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.

§32 vs 64-bit

Contracts can be compiled to either 32 or 64-bit WASM - i.e. the memory64 proposal. 32-bit contracts have a maximum memory size of 4GiB, while 64-bit contracts have a maximum memory size of 4TiB.

§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(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§

contract_bytecode

Structs§

ArchivedEvent
An archived Event
CallReceipt
The receipt given for a call execution using one of either call or call_raw.
CallTree
The tree of contract calls.
CallTreeElem
An element of the call tree.
ContractData
ContractDataBuilder
ContractId
ID to identify the wasm contracts after they have been deployed
ContractIdResolver
The resolver for an archived ContractId
Event
And event emitted by a contract.
EventResolver
The resolver for an archived Event
PageOpening
A Merkle opening for page in the state.
Session
A running mutation to a state.
SessionData
VM
A handle to the piecrust virtual machine.

Enums§

ArchivedContractError
An archived ContractError
ContractError
The error possibly returned on an inter-contract-call.
ContractErrorResolver
The resolver for an archived ContractError
Error
The error type returned by the piecrust VM.

Constants§

ARGBUF_LEN
The size of the argument buffer in bytes
CONTRACT_ID_BYTES
The length of ContractId in bytes
SCRATCH_BUF_BYTES
How many bytes to use for scratch space when serializing

Traits§

HostQuery
A query executable on the host.

Type Aliases§

StandardBufSerializer
Type with rkyv serialization capabilities for specific types.