Skip to main content
Status: Draft Date: 2026-07-02

Problem

Chain-behavior validation is fragmented across per-chain harnesses that grew independently on the pre-consolidation branches:
  • ~122 shell scripts under scripts/{evm,solana,near,psy,aleo,ts}/;
  • ~23 Node/Web3.js smokes and Rust Mollusk templates under Tests/solana/, Tests/evm/;
  • ~50 Lean test drivers under Tests/*.lean;
  • a justfile with dozens of recipes and a CI workflow with ~40 steps.
Adding one feature today means writing a new fixture, a new shell script, a new just recipe, and a new CI step — per target. The harness logic (build artifact, load it into a runtime, call entrypoints, assert observable state) is re-implemented in bash/Node/Rust for every chain, and nothing enforces that the same scenario behaves equivalently across chains, even though cross-target equivalence is the platform’s core claim (shared-scenario).

Proposal

Add a Rust workspace testkit/ that owns artifact-behavior and cross-chain equivalence testing behind one declarative scenario format and one runner:
Rust is the right host language because all three priority chains have first-class Rust-native runtimes — no RPC, no local validator, deterministic and CI-friendly:

Scenario manifests, not per-feature test files

The unit of testing is a scenario, declared once in TOML and executed on every target that supports its capabilities:
The runner:
  1. invokes proof-forge (via lake env) to emit the artifact per target — the same emit modes CI uses today;
  2. loads the artifact into the matching in-process runtime;
  3. executes the steps, mapping call/expect through a per-chain adapter (EVM selector + calldata; Solana instruction tag + accounts; NEAR export + Borsh/JSON args);
  4. asserts per-step expectations and cross-target agreement: every target listed must produce the same observable trace (returns, state reads, events) up to the declared encoding.
Adding a feature then means: add or extend a scenario file (and the fixture if new). No new shell script, no new CI step — the runner discovers scenarios and expands the target matrix itself. Capability-gated: if a scenario uses a capability a target does not support, the runner asserts that compilation is rejected with a diagnostic rather than skipping silently (matching the D-028 contract).

Division of labor (what testkit does NOT replace)

Golden .wat/.yul/.psy snapshot comparison also moves into testkit core (a trivial file-diff step type), so golden updates use one --bless-style flow instead of per-target scripts.

Encoding adapters

Each harness implements one trait:
Value is the portable IR value domain (u32/u64/bool/hash/…): the same domain IR/Semantics.lean uses, so testkit expectations and FV-2 semantics traces stay comparable — a scenario expectation can later be derived from the Lean interpreter output (differential testing against the formal semantics, closing the loop with the FV roadmap).

Psy / Aleo / Cloudflare

Out of scope for milestone 1 (the user-priority chains are EVM, Solana, NEAR). The trait is designed so later harnesses wrap external CLIs (dargo execute, leo test, wrangler) as slower, tool-gated executors using the same scenario files.

Milestones

  1. M1 — skeleton + NEAR: workspace, scenario model, discovery/reporting; port runtime/offline-host into harness-near; Counter scenario green on wasm-near. One just testkit recipe and one CI step.
  2. M2 — EVM via revm: load emitted runtime bytecode, dispatch by selector, decode return words; Counter green on evm + first cross-target equivalence assertion (evm ↔ wasm-near).
  3. M3 — Solana via Mollusk: absorb the .rs.tpl logic as library code (program id/keypair handling, account setup); Counter green on all three; ValueVault scenario added.
  4. M4 — migration: golden-file steps and per-fixture behavior scripts move into scenarios; retire duplicated shell scripts; CI collapses the per-fixture steps into just testkit. Live gates (Surfpool, Anvil deploy, near-sandbox) remain as separate scheduled jobs.

Non-goals

  • Not a replacement for chain-authentic tooling (Foundry, Surfpool, real validators); those remain the deployment-confidence layer.
  • Not a fuzzing or property-testing framework in v1 (the scenario model should not preclude adding proptest-style step generators later).
  • No network access in the default path; everything in-process.

Open questions

  • Whether read_state inspects storage directly per chain (fast, but chain-specific) or only through declared query entrypoints (portable, but requires queries in every fixture). Recommendation: queries first, storage inspection as a per-harness extension.
  • Version pinning policy for revm/mollusk-svm/wasmtime (Workstream 24’s CI toolchain-pinning task applies here).