Version: 0.0.4 Date: 2025‑11‑08 Status: Draft Authors: Fucory
- Executive Summary
- Problem Statement
- Solution Overview
- Core Concepts
- System Architecture
- Detailed Requirements
- Protocol Specifications
- Worked Examples
- Implementation Roadmap
- Technical Decisions & Rationale
- Security Considerations
- Performance Targets
- Out of Scope
- Open Questions
- References & Prior Art
Vision. A rule‑based, provable communication protocol for local‑first apps with near‑infinite aggregate throughput and ~0 marginal cost per update.
We enable ephemeral, high‑throughput sessions (games, collaboration, micro‑markets) that:
- run locally with instant UX,
- synchronize via event‑sourced channels,
- derive state deterministically in an embedded SQL engine, and
- resolve disputes on Ethereum/L2 with interactive one‑step proofs (no expensive per‑step ZK).
Reducers are WASM modules (e.g., AssemblyScript, Rust, Zig → WASM). Proof backend: a single WASM one‑step proof (WASM‑OSP).
Success metrics.
- Dev adoption: 10+ independent apps using the framework launched in 6 months; 50+ in 12 months.
- UX: <100ms perceived latency; zero gas per in‑session action.
- Traction: 10k+ MAU on a showcase title.
- Protocol quality: <1% disputes; zero loss of funds.
- Perf: 1000+ updates/sec/channel locally; one‑step proof fits L2 gas budgets.
Practical note: “Near‑infinite” refers to off‑chain processing across many peers; bounded by device/network and rare on‑chain disputes.
- State channels have excellent UX for live, high‑frequency interactions (e.g., matches, collaborative editing).
- They’re a poor fit for DeFi’s global liquidity and composability needs (which benefit from on‑chain shared state).
Historically, state‑channel stacks have lacked:
- A simple application model for rich, non‑balance logic.
- Queryable, verifiable state beyond balances.
- A credible proof path without expensive per‑step ZK.
Thesis. The application layer should run at application latency with ~0 marginal cost per action, while remaining provable and settleable on‑chain when needed.
Approach. Combine an append‑only event log, a deterministic WASM reducer, an embedded SQL materialization, and a WASM one‑step proof for disputes.
Three‑layer design
App (WASM reducer)
↓ messages
State Channel Engine — Event Store • Derivation (WASM) • Embedded SQL (PGlite)
↓ disputes/settlement
Ethereum/L2 — Adjudicator + WASM One‑Step Proof contracts
- Local‑first: instant local application, durable sync from logs.
- Provability: bisection to a single VM step; on‑chain one‑step verifier checks that step.
- Determinism: no clocks, no non‑seeded RNG, no host I/O; bounded resources.
- Event‑sourced channels: source of truth is the message log.
- Deterministic reducer (WASM):
reduce(db_state_bytes, message_bytes) -> db_state_bytes. - Embedded SQL (PGlite): reducer drives a Postgres‑compatible subset compiled to WASM; storage is Merkleized to produce
db_root. - Commitment: each signed state binds log +
db_root. - Disputes: bisection over reducer steps → one‑step verification (WASM‑OSP).
Components
- Reducer (WASM): app logic compiled to WASM.
- Derivation Engine (Zig): loads reducer, executes steps, updates PGlite, emits snapshots.
- Event Store: append‑only log (+ snapshots
(db_state, db_root, turn)). - P2P Transport: libp2p or TCP/WebRTC fallback (pluggable).
- Chain Service: handles adjudication & one‑step proofs.
- On‑chain: Adjudicator + WASM one‑step verifier.
Proof‑friendly execution
State tuple S = { pc, regs/globals, mem_root, table_root, db_root }
(S, op) -> S' // With Merkle witnesses for memory + DB leaves
R6.1 Reducers
- Load WASM reducers with caps (fuel/time/mem).
- No host calls; deterministic imports only.
- Stable reducer ABI and versioning.
R6.2 PGlite
- Deterministic WASM build; host‑call isolation.
- Merkleized storage; update
db_rootper step. - Stable snapshot format (
serialize/deserialize) with versioning.
R6.3 Proof
- WASM profile: no FP, bounded linear memory, no host I/O.
- Bisection: canonical expansion from turn→steps; logarithmic depth.
- One‑step API (Solidity):
oneStepVerify(preState, op, witness, postState).
R6.4 Contracts
- Adjudicator: deposits, challenges, withdrawals.
- Proof Manager: bisection + WASM one‑step.
- App Contract: validates append‑only
appData, turn‑taking, reducer versioning.
R6.5 Messages
- JSON initially, MessagePack optional.
- Per‑message step budget.
Versioned reducers
- Channel
fixedPartincludesreducerId = H(code) || profile(WASM);appDatacommits to it. Upgrades → new channel.
Commitments
turn: i
appData: [m_1..m_i]
db_root: H(DB_i)
state_hash = H(channelId, appData_hash, outcome, turn, isFinal, db_root)
Both parties sign state_hash.
Disputes
- Challenger submits
(pre, post, range); defender splits; recurse to a single step. - Final step checked by
oneStepVerify(WASM‑OSP).
Minimal example showing a deterministic reducer. Here the “DB” is a fixed‑size binary state for clarity (9 cells + next + winner). In production, reducers drive PGlite; the idea is the same: apply message → update state deterministically.
// assembly/index.ts (AssemblyScript)
// ABI: reduce(db_ptr, db_len, msg_ptr, msg_len, out_ptr_ptr, out_len_ptr) -> i32
// Return 0 on success, non-zero error code on invalid moves, etc.
const STATE_LEN: i32 = 11; // 9 cells + next + winner
// Cell values: 0 empty, 1 = X, 2 = O
// next: whose turn next (1 or 2)
// winner: 0 none, 1 X, 2 O, 3 draw
@inline
function loadU8(p: usize, off: i32): u8 { return load<u8>(p + <usize>off); }
@inline
function storeU8(p: usize, off: i32, v: u8): void { store<u8>(p + <usize>off, v); }
function checkWin(boardPtr: usize): u8 {
// Winning triplets (indexes 0..8)
const lines: StaticArray<i32> = [
0,1,2, 3,4,5, 6,7,8, // rows
0,3,6, 1,4,7, 2,5,8, // cols
0,4,8, 2,4,6 // diags
];
for (let i = 0; i < 8; i++) {
let a = loadU8(boardPtr, unchecked(lines[i*3]));
let b = loadU8(boardPtr, unchecked(lines[i*3+1]));
let c = loadU8(boardPtr, unchecked(lines[i*3+2]));
if (a != 0 && a == b && b == c) return a; // winner 1 or 2
}
// check draw
for (let j = 0; j < 9; j++) if (loadU8(boardPtr, j) == 0) return 0;
return 3; // draw
}
export function reduce(
db_ptr: usize, db_len: i32,
msg_ptr: usize, msg_len: i32,
out_ptr_ptr: usize, out_len_ptr: usize
): i32 {
// State layout (11 bytes): [0..8]=board cells, [9]=next, [10]=winner
if (db_len != STATE_LEN || msg_len < 2) return 1;
// Read state
const boardPtr = db_ptr;
let next = loadU8(db_ptr, 9);
let winner = loadU8(db_ptr, 10);
// Message layout: [0]=op, [1]=pos (0..8)
// op: 1=move
const op = loadU8(msg_ptr, 0);
const pos = loadU8(msg_ptr, 1);
if (op != 1 || pos > 8) return 2;
// If game finished, reject
if (winner != 0) return 3;
// Apply move by 'next'
let cur = loadU8(boardPtr, pos);
if (cur != 0) return 4; // occupied
if (next != 1 && next != 2) return 5; // invalid next
storeU8(boardPtr, pos, next);
// Check winner/draw
winner = checkWin(boardPtr);
storeU8(db_ptr, 10, winner);
// Toggle turn if no winner/draw
if (winner == 0) {
next = <u8>(3 - next); // 1<->2
storeU8(db_ptr, 9, next);
}
// Allocate output and copy updated state
const out = memory.data(STATE_LEN);
for (let i = 0; i < STATE_LEN; i++) {
storeU8(out, i, loadU8(db_ptr, i));
}
// Write out pointer/len
store<usize>(out_ptr_ptr, out);
store<i32>(out_len_ptr, STATE_LEN);
return 0;
}Message encoding (example):
op=1, pos∈[0..8]. State encoding (11 bytes):- bytes
[0..8]board cells,[9]next,[10]winner.
This reducer is pure, deterministic, bounded, and performs no host I/O.
Goal: compute an initial token distribution off‑chain (gasless UX), then commit the result on‑chain so users can claim their balances.
Flow
- Channel setup: issuer + auditor (or a set of signers) open a channel with a MintReducer.
- Off‑chain events: messages like
Mint(to, amount)accumulate; the reducer builds a balances map and a running Merkleized DB state (db_root). - Finalize: parties co‑sign the final
db_root. - On‑chain commit: submit
db_rootto an InitialMint contract gated by the adjudicator (optionally behind the dispute window). - User claims: anyone can later
claim(address, amount, proof)verified against the committed root.
Mint reducer invariants (examples)
- Total supply ≤ cap.
- No negative balances;
Mintonly allowed beforeFinalize. - Deterministic ordering; exact integer arithmetic.
Minimal Solidity sketch (read‑only verifier)
// Pseudocode: assumes adjudicator exposes a finalized (channelId, dbRoot).
interface IAdjudicator {
function finalizedDbRoot(bytes32 channelId) external view returns (bytes32);
}
contract InitialMint {
IAdjudicator public adjudicator;
mapping(address => bool) public claimed;
constructor(address _adj) { adjudicator = IAdjudicator(_adj); }
// Merkle leaf = keccak256(abi.encode(addr, amount))
function claim(bytes32 channelId, address to, uint256 amount, bytes32[] calldata proof) external {
require(!claimed[to], "already claimed");
bytes32 root = adjudicator.finalizedDbRoot(channelId);
require(root != bytes32(0), "root not finalized");
bytes32 leaf = keccak256(abi.encode(to, amount));
require(verify(proof, root, leaf), "bad proof");
claimed[to] = true;
_mint(to, amount); // hook to a token or mint here
}
function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
bytes32 h = leaf;
for (uint i = 0; i < proof.length; i++) {
bytes32 p = proof[i];
h = (h < p) ? keccak256(abi.encode(h, p)) : keccak256(abi.encode(p, h));
}
return h == root;
}
function _mint(address to, uint256 amount) internal {
// Implement: mint in this contract or call an external token with MINTER_ROLE.
}
}Why this fits the protocol
- Off‑chain work is free and instant for users.
- If someone cheats, counterparties challenge via WASM one‑step until the correct
db_rootis finalized. - On‑chain state only stores the succinct commitment (
db_root); individual user mints are self‑serve with Merkle proofs.
Phase 1 — Core Engine & PGlite Spike (M1‑M3)
- Event loop & objectives, Nitro‑style adjudicator, TCP P2P.
- Compile pglite‑wasm; prove determinism; snapshots.
- WASM reducer loader + resource caps; Tic‑Tac‑Toe demo.
Exit: 1000 turns/sec locally; cooperative close.
Phase 2 — WASM Proof Path (M4‑M6)
- Restricted WASM profile + state tuple; Solidity one‑step verifier.
- Merkle memory & DB witnesses; end‑to‑end dispute on a counter reducer.
Exit: Bisection + one‑step working on test reducer.
Phase 3 — Examples & SDK (M6‑M8)
- Polished Tic‑Tac‑Toe example & off‑chain mint example.
- SDK helpers (encodings, snapshots, test harness).
Phase 4 — Optional: Solidity Reducers via EVM‑in‑WASM (M8‑M10)
- Guillotine integration (optional milestone), storage→DB adapter.
Phase 5 — Production Hardening (M10‑M12)
- Audits, perf passes, ops playbooks; hosted hub economics (if any).
- Single backend (WASM‑OSP): aligns with browser/WASM dev flow; avoids a second toolchain; gas costs are explicit and tunable.
- SQL materialization (PGlite): rich queries and audits; deterministic storage →
db_root. - Event sourcing: source of truth is the log; re‑derive to audit; snapshots for speed.
- Proof soundness: formal WASM profile; adversarial test corpus; negative tests for forged witnesses and mismatched roots.
- Sandboxing: hard caps (fuel/time/mem); no host calls.
- Code‑hash pinning:
reducerIdin channelfixedPart. - Replay safety: signatures bind log +
db_root. - Mint example: finalize root only after dispute window; claims are one‑way (no double claim).
- Local reducer step: <10ms p95; WAN perceived <100ms per action.
- Cold re‑derive (1k msgs): <5s on laptop‑class hardware.
- Bisection depth: O(log steps); one‑step gas: <1–2M on L2.
- Commit sizes:
db_root32B; witnesses a few KB/step.
- Sync / custom consensus rules: we sync via custom rule‑based consensus, but the exact protocol is out of scope for this doc.
- Adapters for Replicache/ElectricSQL: not supported here; they are prior art/inspiration, not part of the design.
- Exact restricted WASM opcode set (no FP/SIMD initially).
- PGlite Merkle layout: page vs. row → witness size vs. update cost.
- SQL subset guardrails (e.g., CTE recursion off by default).
- Reducer upgrades & migrations between
reducerIds. - Spectator feeds: db deltas vs. raw messages.
- Optional future: recursive proofs to batch steps (Mina‑style receipts).
- State channels & force‑move: Nitro/go‑nitro, Perun, Counterfactual, Raiden.
- Interactive fault proofs: Optimism Cannon (MIPS), Arbitrum Nitro (AVM one‑step), Truebit (historical).
- zk & recursion (contrast / future compression): Mina Protocol (recursive zkSNARKs, o1js), RISC Zero, SP1, zkWASM, zkEVM, StarkNet/Cairo.
- Deterministic WASM / EVM‑in‑WASM: deterministic WASM profiles; evmone‑wasm / Sputnik‑wasm; Guillotine (for later).
- Event sourcing & embedded SQL: CQRS/event‑sourcing; pglite‑wasm.
- Local‑first & sync systems (inspiration only): ElectricSQL, Replicache, CRDT literature.