Quickstart
# An SP1 shard, proven and verified. sp1-zorch is SP1's prover rebuilt on zorch
# blocks: `ShardProver` runs SP1's four stages over one Fiat-Shamir transcript —
# trace commitment, LogUp-GKR over the lookup buses, ZeroCheck of the per-row AIR
# constraints, jagged-PCS opening — and `ShardVerifier` is its stage-for-stage
# dual. The statement is a `ShardClaim` (vk, public values, chip row counts); the
# trace is the `ShardWitness` it never names.
#
# A prover's input is a TRACE, not an ELF, so the guest is a tiny SP1-style chip:
# two columns, `a` pinned to 1 on real rows (the AIR constraint (a-1)·(b-1) = 0),
# on the lookup bus via one `rw_constraints` Interaction — SP1's stand-in for
# cross-row constraints. Change HEIGHT or the seed and re-run.
import frx.numpy as fnp
import numpy as np
from rw_constraints import Interaction, VirtualPairCol
from zk_dtypes import koalabear_mont as F
from zorch.commit.smcs import SingleMatrixCommitmentScheme
from zorch.hash.compression import Compression, CompressionParams
from zorch.hash.poseidon2.poseidon2 import Poseidon2
from zorch.hash.sponge import Sponge, SpongeParams
from zorch.pcs.jagged.region import JaggedRegion
from zorch.testkit.transcript import cheap_transcript
from sp1_zorch.logup_gkr.circuit import GkrChip
from sp1_zorch.poseidon2.koalabear16 import koalabear16_params
from sp1_zorch.shard_prover.prove_shard import ShardProver
from sp1_zorch.shard_prover.verify_shard import ShardVerifier
from sp1_zorch.types import (
ChipMetadata,
ChipWidths,
MachineVerifyingKey,
ShardClaim,
ShardWitness,
)
HEIGHT, WIDTH = 4, 2 # a 4-row, 2-column chip
MAX_LOG_ROWS = 5 # the machine's max chip height, 2^5
LOG_STACK = 3 # jagged stacking height (chip area is a multiple of it)
class WitnessChip:
"""One per-row AIR constraint: column a is 1 on every real row, so
(a - 1)·(b - 1) vanishes there — the SP1 zerocheck's per-row form."""
def eval_constraints(self, trace, public_values):
a, b = trace[:, 0], trace[:, 1]
one = fnp.ones((), trace.dtype)
return fnp.stack([(a - one) * (b - one)], axis=-1)
def rand(seed, shape):
return fnp.array(np.random.default_rng(seed).integers(1, 1 << 30, shape, np.int64), dtype=F)
# Trace: column 0 all-ones (satisfies the constraint), column 1 the witness.
trace = fnp.concatenate([fnp.ones((HEIGHT, 1), F), rand(1, (HEIGHT, 1))], axis=1)
main_region = JaggedRegion.from_chips(
[trace], log_stacking_height=LOG_STACK, max_log_row_count=MAX_LOG_ROWS,
chip_names=("alpha",))
public_values = rand(30, (8,))
vk = MachineVerifyingKey(
preprocessed_commit=rand(31, (8,)), pc_start=rand(32, (3,)),
cum_sum_x=rand(33, (7,)), cum_sum_y=rand(34, (7,)), enable_untrusted=0)
# One lookup interaction: send column 1 on the bus with multiplicity from column 0.
gkr_chips = (
GkrChip("alpha", (Interaction(
values=(VirtualPairCol.single_main(1),),
multiplicity=VirtualPairCol.single_main(0),
kind=3, is_send=True),)),
)
perm = Poseidon2(koalabear16_params())
smcs = SingleMatrixCommitmentScheme(
Sponge(perm, SpongeParams(rate=8, out=8)),
Compression(perm, CompressionParams(arity=2, chunk=8)))
chips = {"alpha": WitnessChip()}
# Row counts change shard to shard, so they ride the claim; column counts are
# fixed by the AIR, so they configure the role.
shared = dict(smcs=smcs, log_blowup=1, gkr_chips=gkr_chips, chips=chips, num_betas=3,
num_row_variables=MAX_LOG_ROWS - 1, max_log_row_count=MAX_LOG_ROWS)
claim = ShardClaim(vk, public_values, ChipMetadata(("alpha",), (HEIGHT,)))
witness = ShardWitness(main_region, None)
# Each stage's reduced claim is the next stage's source claim; the four
# reductions land on the trivial claim and their messages are the proof.
proof = ShardProver(open_num_queries=2, **shared).prove(
claim, witness, cheap_transcript(F)).reduction_proof
# One dual per prover stage, in the prover's order, ANDing each ok.
# `verify_public_values=False`: that leg balances the GKR cumulative sum against
# the digest of a real 187-element SP1 public-values vector, which the 8 random
# values above are not. Every real shard runs it.
verifier = ShardVerifier(
chip_names=("alpha",), chip_widths={"alpha": ChipWidths(WIDTH)},
log_stacking_height=LOG_STACK, open_num_queries=2, verify_public_values=False,
**shared)
def accepts(claimed_public_values):
try:
restated = ShardClaim(vk, claimed_public_values, claim.chip_metadata)
return bool(verifier.verify(restated, proof, cheap_transcript(F)).ok)
except Exception:
return False
print(f"proved a {HEIGHT}-row SP1 shard (1 chip, 1 lookup interaction) over KoalaBear")
print("verifier accepts the shard proof: ", accepts(public_values))
print("... and rejects a wrong statement:", not accepts(rand(99, (8,))))
sp1-zorch
A lean SP1 prover built on zorch's
scheme-agnostic SNARK building blocks. zorch provides the reusable pieces
(hashing, Merkle commitment, sumcheck, fold, …); sp1-zorch adds only the
SP1-specific glue on top — domain separator, verify codes, heap proof layout,
and the FFI byte-match against the SP1 reference prover.
frx ──▶ zorch (scheme-/zkVM-agnostic blocks) ──▶ sp1-zorch (SP1 glue)
Status
The full SP1 shard proving scheme runs on zorch blocks: a sequence of
trace commit → LogUp-GKR → zerocheck → jagged PCS, byte-matching SP1's reference
prover end to end (its sp1_verify_shard accepts the assembled proof). See
docs/architecture.md.
Installation
Python 3.11 on Linux x86_64, or macOS on Apple Silicon. (frxlib ships a
cp311 wheel for those two platforms only — not 3.12/3.13, not Intel Macs.)
CPU
pip install sp1-zorch
GPU (CUDA 12)
pip install sp1-zorch 'frx[cuda12]' \
--extra-index-url https://fractalyze.github.io/pypi/simple/
The extra index carries the CUDA plugin wheels, which are too large for PyPI's per-file limit. It is not needed for the CPU tier.
Verify
python -c "import frx, sp1_zorch; print(frx.devices()); print(sp1_zorch.__version__)"
[CpuDevice(id=0)] means the CPU tier; a CUDA install prints the GPU devices.
Development
Pure Python on frx (Field, Ring Accelerated), built with Bazel (bzlmod), with
zorch consumed as a Bazel module pinned in MODULE.bazel.
python3.11 -m venv .venv && . .venv/bin/activate
pip install -r requirements.in \
--extra-index-url https://fractalyze.github.io/pypi/simple/
pre-commit install --install-hooks --hook-type pre-commit --hook-type commit-msg
bazel test //sp1_zorch/... --test_tag_filters=-gpu_only
Both --hook-type flags matter: plain pre-commit install wires only the
pre-commit stage, leaving the commit-message linter inactive so a malformed
message sails through to CI.
docs/development.md has the rest — devving against a
local zorch checkout, the coupled zorch/frx pin, the GPU-plugin gotcha, test
conventions, the staged local shard-prove harness, and the per-phase SP1
baseline.
Documentation
docs/ indexes the architecture, development, and
conventions guides.
License
Licensed under the Apache License, Version 2.0 (see LICENSE).