Skip to content

mwaddip/santa

Repository files navigation

SANTA

Sigma-Anchored Node Test Apparatus — a cross-implementation conformance test suite for Ergo consensus.

Built in the open, slice by slice. The design takes shape from working deliveries rather than a big up-front spec — see SPEC.md for the current architecture + roadmap, and BOOTSTRAP.md for the rationale behind each call.

What this is

Language-agnostic conformance test vectors + thin per-implementation runners + CI, so multiple independent Ergo implementations can be checked against the same canonical inputs and expected outputs — the way Ethereum's execution-specs (the executable spec + test framework, formerly execution-spec-tests / EEST) lets geth / besu / nethermind / reth prove consensus-equivalence.

The guiding principle is "the wire is the spec": a vector is raw serialized bytes in → expected output out, since every implementation already parses the wire format. Expected outputs are anchored to canonical oracles, never to any single implementation:

  • block validity → the chain — a block on mainnet is valid by definition;
  • fine-grained eval outputs (value, cost, reduced sigma-tree) → the JVM reference node (ergo-core / sigma-state), the de-facto spec.

Four tiers:

  • Wire tier — serialization: bytes ⇄ structure (constants, boxes, trees, txs, headers). The broadest — every wallet/SDK serializes (ergots, sigma-rust, scorex, Fleet, …); and a boxId is the hash of serialized bytes, so it's squarely consensus.
  • Eval / transition tier — operation-level (ErgoTree + context → typed value, with cost). Run by the consensus libraries, no full node required.
  • Transaction tier — library-decidable tx validity given full inputs (script-verify
    • conservation / tokens / min-value / cost). No full node required; blessed by ergo-core validateStateful.
  • Block tierblock H → valid? / state-root. Run by full nodes.

Status

The eval tier is closed and scaled, and the conformance loop is already surfacing genuine cross-implementation divergences — which is exactly its job. What runs today:

  • A blessed eval corpus — 2,143 entries across 155 vector files: 2,026 produced by the JVM reference interpreter (sigma-state) from its own language specification, plus 117 authored gap-fillers (oracle-blessed, never spec-copied); version-split into v5 (1,798 entries — the cumulative v5/mainnet method surface) and v6 (345 — the v6 new-feature surface). Each entry is ErgoTree bytes (+ input) → typed value + raw JIT cost, committed with the (activated, ergoTree) version it was blessed under.
  • A runner-agnostic orchestrator — ./conform (presence-as-state over runners/*/, one shared comparator, a per-runner per-slice 🎁/🪨 table). Five runners wired today: Rudolph (the JVM reference — the all-🎁 control that blessed the corpus), Dasher (the pure-TS ergots library, ts-runner/), Blitzen as two submodules pinning sigma-rust at upstream develop (value-only) and the ergo-node-integration fork (--features jit-cost), and Comet (the pure-TS Fleet SDK — wire tier only). Each is graded against the JVM-blessed expected — the runner is SANTA's; the implementation under test is a dependency.
  • Live results — the loop is surfacing real divergences. Dasher is fully green on the v5 spec corpus (1,757 / 1,757) — the 52 healed AvlTree-typed entries initially surfaced a SANTA harness encode gap (the result-encode bridges lacked an AvlTree arm; x.R9[AvlTree].get evaluates correctly in every conformer), fixed same-arc. Blitzen shows the suite working — sigma-rust's ergo-node-integration fork is green suite-wide except two cost entries (the deserialize-bearing pair, blessed at production substituted-constant cost per the contract rule; eni's eval harness charges the lazy form — routed). Everything else — eval + wire + transaction, value and cost — is green (every divergence the suite surfaced is fixed in the fork, latest the Box.getReg method-id and HOF FunDef/currying findings), while plain upstream develop misses 10 v5 values the fork already fixed, plus the v6 surface. A 🪨 is the suite doing its job, never silenced.
  • A frozen runner contract (docs/contract/runner-contract.md)
    • a JVM blesser, the JVM reference runner (Rudolph), and a harness. A runner is total: it emits one faithful outcome for every entry — value + cost on success, else a coarse tag (errored / not-implemented / panicked) — and never drops, hides, or aborts the run on one. A conformer's scope is chosen on the input side (it runs the vector subset it claims; dasher's manifest declares version ≤ v6). A divergence is the deliverable — surfaced and routed, never silenced; a red gate means the suite is working.
  • Machine-checkable gates — a JSON-Schema validator over the whole corpus (155 eval + 4 wire + 4 tx) and an end-to-end conformance gate. The CI seed.
  • Wire tier livesanta-wire/v1 byte-round-trip vectors, 213 entries (Constant 178 · Box 11 · SigmaBoolean 7 · Transaction 17), JVM-canonicalized from ergots' fixture-gen + Fleet's _test-vectors seeds. Rudolph + Blitzen 213/213; Dasher 196 (no tx serializer — growth ledger); Comet 185 (Fleet's honest gaps, recorded as findings). Contract: docs/contract/runner-contract-wire.md.
  • Transaction tier livesanta-transaction/v1 schema; 4 captured vectors (vectors/transaction/v6/captured/), each JVM-blessed via ergo-core 6.0.2.1 validateStateful. Conformer stances: Rudolph out (oracle-tautological + keeps ergo-core out of CI) · Blitzen-eni valid 4/4 · cost 4/4 byte-exact (the initial bless surfaced 3 genuine cost divergences — decomposed, routed, fixed in the fork, re-graded exact: the tier's first divergence→fix→convergence loop) · Blitzen-develop valid 0/4 (upstream bugs; the bigint-downcast seed exposes the tree-version bug eval cannot catch) · Dasher 4 not-implemented (growth ledger) · Comet out-of-scope (wire-only; Fleet has no verifier). Contract: docs/contract/runner-contract-transaction.md.

Still greenfield, and where help is most wanted (see below):

  • the block tier (chain-blessed block vectors) and the tx-tier authored reject arm;
  • more independent runners — the full nodes (sigma-rust is now wired, as Blitzen);
  • the reject arm — authored negative / mutation vectors (rejected for the right reason); and a full CI gate.

Layout

SPEC.md            umbrella spec — architecture, tiers, contracts, roadmap, glossary
BOOTSTRAP.md       design rationale + decision log (the *why*)
docs/contract/     the frozen runner I/O contracts (eval · wire · transaction)
docs/specs/        per-phase subspecs
docs/findings/     recorded cross-implementation divergences
schema/            JSON Schemas for vectors + actuals, and the validator
vectors/eval/      the canonical eval corpus — the "nice list" (v5/ and v6/)
vectors/wire/      wire-tier round-trip vectors
vectors/transaction/  transaction-tier captured vectors (v6/captured/)
jvm-blesser/       Scala: the blesser, the JVM reference runner (Rudolph), the harness
ts-runner/         Dasher — the ergots runner + the conformance gate
runners/           per-conformer dirs (rudolph · dasher · blitzen-develop · blitzen-eni · comet)
conform            the runner-agnostic orchestrator — runs every runner, prints the table
README.md          this file

Re-blessing transaction vectors (maintainer only)

The transaction blesser is env-gated and never runs in CI — committed vectors are the reproducible artifact. To re-bless (e.g. to add new seeds):

  1. Clone ergo-node-build at tag v6.0.2.1.
  2. From that clone: sbt "avldb/publishLocal" "ergoWallet/publishLocal" "ergoCore/publishLocal" — publishes ergo-core 6.0.2.1 to ~/.ivy2/local.
  3. From jvm-blesser/: SANTA_TX_BLESSER=1 sbt -batch "testOnly santa.CapturedTxTest" — stages blessed JSON under jvm-blesser/target/tx-vectors/.
  4. Copy the staged files into vectors/transaction/v6/captured/ and commit.

The bundled jvm-blesser/src/test/resources/chain-testnet.conf is pinned by the transaction runner contract and must not be changed without re-blessing.

Contributing

SANTA is meant to be community-owned conformance ground: the more independent Ergo implementations run the same vectors, the more the vectors are worth. Help is genuinely wanted — especially:

  • Implementations under test — wire a runner for your Ergo implementation (TypeScript, Rust, Scala, or anything that parses the wire format) against the runner I/O contract. It's a thin adapter: vector in → {value, cost, error} per entry.
  • Vectors — more eval-tier operations; wire-tier serialization vectors; captured-block vectors for the block tier; authored mutation / reject vectors.
  • Design — the vector schema, the runner I/O contract, CI topology.

The eval-tier contract is frozen, but the wider design is still taking shape, so a conversation beats a big PR — read SPEC.md + BOOTSTRAP.md, look at jvm-blesser/ and ts-runner/, and open an issue to talk through where you'd like to plug in.

Related

  • Ergo — the protocol under test.
  • sigma-rust — Rust consensus library; a convenience differential target (never the oracle).
  • execution-specs — Ethereum's executable spec + test framework (the former execution-spec-tests / EEST, consolidated in 2025); the project SANTA's structure is modeled on.

Acknowledgements

  • Fleet SDK — its serializer test vectors (packages/serializer/src/_test-vectors) are vendored into SANTA's wire tier: harvested and re-anchored through the JVM oracle, so each harvest doubles as a JVM-vs-Fleet differential pass (178 constants, 17 signed transactions, 7 boxes).
  • ergots' fixture-gen wire fixtures are vendored too (boxes + sigma-booleans). Box round-trips from ergots and Fleet share one slice, each entry tagged by its framework.

License

MIT.

About

Sigma-Anchored Node Test Apparatus

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors