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
justfilewith dozens of recipes and a CI workflow with ~40 steps.
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 workspacetestkit/ that owns artifact-behavior and
cross-chain equivalence testing behind one declarative scenario format and
one runner:
| Chain | In-process runtime | Existing asset to reuse |
|---|---|---|
| EVM | revm | bytecode + .evm-methods selectors already emitted; Foundry stays as an independent second opinion |
| Solana | mollusk-svm | Tests/solana/*_mollusk.rs.tpl already prove the approach; templates become library code |
| NEAR | wasmtime + NEAR host shim | runtime/offline-host is already exactly this; it moves/grows into harness-near |
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:- invokes
proof-forge(vialake env) to emit the artifact per target — the same emit modes CI uses today; - loads the artifact into the matching in-process runtime;
- executes the steps, mapping
call/expectthrough a per-chain adapter (EVM selector + calldata; Solana instruction tag + accounts; NEAR export + Borsh/JSON args); - 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.
Division of labor (what testkit does NOT replace)
| Layer | Owner | Rationale |
|---|---|---|
Compiler-internal checks: IR coverage manifests, diagnostics suites, golden sources, formal anchors (Tests/NearWasmFormal.lean, Tests/IROwnership.lean) | Lean tests | They test the compiler, not artifacts; they belong next to the code and to the FV roadmap (Workstream 25) |
| Artifact behavior + cross-target equivalence | testkit (this RFC) | One harness, one scenario language, deterministic in-process runtimes |
Chain-authentic second opinion: Foundry/Anvil, Surfpool/Web3.js live gates, near-sandbox, dargo, leo | Existing scripts, gradually thinned | Real-toolchain validation stays, but stops being the only functional test path; run in CI on a schedule or per-target label rather than per-PR |
.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
- M1 — skeleton + NEAR: workspace, scenario model, discovery/reporting;
port
runtime/offline-hostintoharness-near; Counter scenario green onwasm-near. Onejust testkitrecipe and one CI step. - 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). - M3 — Solana via Mollusk: absorb the
.rs.tpllogic as library code (program id/keypair handling, account setup); Counter green on all three; ValueVault scenario added. - 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_stateinspects 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).