Help us shape the future of Go! Take the 2026 Go Developer Survey

rayls-sovereign-gnark-api

module
v0.0.0-...-be20e7f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 15, 2026 License: Apache-2.0

README ΒΆ

Rayls Gnark API

Groth16 proof generation and verification service for Rayls Enygma β€” the gnark-based proofs API used by the Privacy Ledgers and the Private Network Hub.

Go

Discord X LinkedIn YouTube

Quick start | Development | Ceremony | Git LFS | License

What is this?

A Go HTTP service, built on gnark, that generates and verifies the Groth16 zero-knowledge proofs behind Rayls Enygma (confidential transfers and DVP). The compiled circuits and their proving/verifying keys are stored under last_build/ and tracked with Git LFS. See the note on Proving & Verifying Keys below for their trust assumptions.

πŸš€ Quick Start

Standard Workflow With Docker (No Circuit Changes)

NOTHING, just up the container.

Standard Workflow Without Docker (No Circuit Changes)
# 1. Compile and generate executables
./compile_circuits_gen_executables.sh

# 2. Start the server
./run_gnark_server.sh

πŸ“ Development Workflows

When Circuits Are Modified

If you've changed any circuit logic, you MUST regenerate keys and verifiers. The steps below use a single-party setup and are for development only; production keys come from the trusted-setup ceremony. Once the ceremony has a release, generate_keys_verifiers.sh refuses to overwrite its keys; --force does it anyway for local experiments, which must not be committed (restore with git checkout -- last_build). A circuit change for production needs a new ceremony: start it with ./ceremony.sh init, which retires the old one.

# A. Generate new development keys and verifiers (local only; do not commit them)
./generate_keys_verifiers.sh

# B. Compile executables
./compile_circuits_gen_executables.sh

# C. Start the server
./run_gnark_server.sh

When development and testing are done, run a full trusted-setup ceremony for the new circuits. Only the ceremony's keys and verifiers are committed and pushed, here and in the contracts repository; development keys and verifiers never are.

When Only Server Code Changes

For API or server logic updates (no circuit modifications):

# 1. Recompile executables
./compile_circuits_gen_executables.sh

# 2. Restart the server
./run_gnark_server.sh
Just Running the Server

If no changes were made:

./run_gnark_server.sh

πŸ§ͺ Testing

Run the unit and circuit tests (honest and forged witnesses for every circuit):

go test ./...

Run the load test against a running server:

./tests/stress_test.sh

πŸ“¦ Git LFS Management

Essential Commands

Check what's tracked by LFS:

git lfs ls-files        # List all LFS files
git lfs status          # Show pending LFS changes

Save space by removing old versions:

git lfs prune --verify-remote
Troubleshooting

Missing files after clone?

git lfs pull

Verify LFS configuration:

cat .gitattributes | grep last_build
# Expected: last_build/** filter=lfs diff=lfs merge=lfs -text

Check if files are properly stored as LFS pointers:

head -n 3 last_build/*.sol
# Should show: version, oid, size (not actual file content)

Force re-download all LFS files:

git lfs fetch --all
git lfs checkout

⚠️ Important Notes

  • Circuit changes = New keys required: run generate_keys_verifiers.sh for development keys; production keys come from the ceremony
  • Large files: The last_build/ directory contains large binary files managed by Git LFS
  • Team collaboration: All team members must have Git LFS installed
  • Development keys stay local: never commit keys or verifiers from generate_keys_verifiers.sh; restore the release with git checkout -- last_build. After development and testing, run a full ceremony and push only its keys and verifiers. update_last_build_lfs.sh (commits only last_build/, pushes only with --push) is for artifacts that belong in the repository
  • Storage optimization: Periodically run git lfs prune to remove old artifact versions

πŸ“Š Workflow Decision Tree

Did you modify circuits?
β”œβ”€ YES β†’ Run: A β†’ B β†’ C
β”‚        (generate_keys β†’ compile β†’ server)
β”œβ”€ NO β†’ Did you modify server code?
β”‚       β”œβ”€ YES β†’ Run: 1 β†’ 2
β”‚       β”‚        (compile β†’ server)
β”‚       └─ NO β†’ Run: 2
β”‚                (server only)

πŸ”€ Merging Between Release Branches

When merging changes from one release branch to another (e.g., release/2.6 β†’ release/2.6.1), you must exclude the last_build/ folder. Each release version has its own generated keys and verifiers that should not be overwritten.

Why?
  • Keys and verifiers in last_build/ are always generated from scripts
  • Each release version must maintain its own artifacts
  • Merging these files would break the target release
Merge Procedure
# 1. Create a temporary branch from the source release
git checkout release/2.6
git checkout -b merge-2.6-to-2.6.1

# 2. Reset the last_build folder to match the target release
git checkout release/2.6.1 -- last_build/

# 3. Commit this change
git commit -m "Exclude last_build changes for merge to 2.6.1"

# 4. Push the branch
git push origin merge-2.6-to-2.6.1

git merge origin/version/2.6.1 

fix conflicts

# 5. Open a PR from branch merge-2.6-to-2.6.1 into your target release branch

should have no conflicts

Any error, just regenerate keys and verifiers (check contracts repo too).

This ensures only code changes are merged while preserving the target branch's generated artifacts.

πŸ”‘ Proving & Verifying Keys

last_build/ holds the keys of the latest ceremony release, and ./ceremony.sh verify proves they were derived from the transcript in ceremony/.

./generate_keys_verifiers.sh produces keys from a single-party groth16.Setup(...) instead. They are development/testing artifacts: whoever ran that setup could forge proofs, so it refuses to overwrite a ceremony release unless given --force. ./convert_verifiers.sh only converts the Solidity verifiers in last_build/ and never touches keys.

πŸ” Trusted-Setup Ceremony

Groth16 needs a one-time setup whose secret randomness ("toxic waste") could forge proofs if anyone kept it. This repository makes that setup perpetual and verifiable:

  • Phase 1 (the generic "powers of tau") is imported from the public Perpetual Powers of Tau, a ceremony with 80 independent contributors. Nobody here has to run it.
  • Phase 2 (specific to our circuits) is where you add randomness. It never closes: anyone can contribute at any time, on top of everyone before them, and each contribution produces a new release of keys once a public random beacon (drand) it announced is published.

The keys of a release are safe as long as at least one of its phase 2 contributors discarded their randomness. So if you contribute, you can trust every release from then on without trusting anyone else. Everything is a signed commit under ceremony/, and anyone can replay it and re-derive the keys in last_build/.

[!IMPORTANT] For full trust and security, phase 2 needs a contribution from someone other than whoever made the previous releases, and ideally from you. Until then, a release only rests on the contributors it already includes: the first release, with a single contributor, rests entirely on that one participant. Ideally, every institution or user running this repository contributes once, with ./ceremony.sh join. Your own contribution is the only one you don't have to trust.

Background and design: docs/trusted-setup-ceremony.md.

Prerequisites
  • Go (the version in go.mod), git 2.34 or newer, Git LFS, and curl

  • An SSH key for signing, e.g. ~/.ssh/id_ed25519.pub, loaded in ssh-agent or with its private key next to it. On a fresh machine there is none yet, and ceremony.sh stops with "SSH public key not found". Create one, then rerun:

    ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -C "<your name or institution>"
    

    Each contributor needs its own key: it is registered under your --name and signs your ceremony commits.

  • About 150 MB of disk for the Perpetual Powers of Tau file (downloaded once to ~/.cache/rayls-ceremony) and about 2.5 GB of memory per parallel job (--jobs; by default half the CPU cores, at most 9, and no more than available memory allows)

  • For contributing: ideally a fresh machine or VM that you destroy afterwards, kept awake and plugged in for the whole contribution (see How long it takes)

Join the ceremony (one command)

Phase 1 is already imported, so a new institution or user joins with a single command. It runs every step below in order and stops at the first error:

git switch main
./ceremony.sh join --name bank-b --note "Fresh VM, destroyed afterwards" \
    --contracts ../rayls-sovereign-contracts --push

Before anything else, join runs git pull and git lfs pull here and git pull in the contracts repository, so it always starts from the latest ceremony state.

Step What join does Time
1. verify Checks everything done so far, so you don't have to trust it (--skip-verify skips it; contribute still checks every earlier contribution). Rebuilds last_build/circuits/, which is not in git, from the code first ~20 min
2. register Registers your SSH key under --name; skipped if it already is seconds
3. contribute Adds your randomness and announces a drand round --beacon-delay minutes ahead (default 45) ~20 min
4. finalize Waits for that round, then releases keys that include your contribution into last_build/ the rest of the delay, then ~20 min
5. copy-verifiers With --contracts DIR, copies the new verifiers there and commits them seconds

Everything is committed on a ceremony/join-NAME branch (when started from main; from any other branch, on that branch), and the verifiers on a branch of the same name in the contracts repository, so the two pull requests match. --push pushes both. Keep the machine awake and plugged in until it finishes.

Before opening the pull requests

A complete run leaves four commits in two repositories. Check they are all there before opening the pull requests; only then open them, one in each repository:

Repository Commits Check with
rayls-sovereign-gnark-api 3: ceremony: register contributor NAME, ceremony: contribution N by NAME, ceremony: release vN git log --oneline origin/main..HEAD
rayls-sovereign-contracts 1: chore(enygma): verifiers from trusted-setup ceremony release vN git -C ../rayls-sovereign-contracts log --oneline origin/main..HEAD

If the contracts commit is missing (e.g. --contracts was left out), run ./ceremony.sh copy-verifiers --push before destroying the machine: a release whose verifiers never reach the contracts repository can't be deployed. If you were already registered, the gnark-api repository has 2 commits (no register commit). With --no-finalize there is no release and no contracts commit yet; whoever runs finalize later makes them and opens both pull requests.

Write down the contribution hash it prints and publish it: it is how you (and anyone else) confirm later that your contribution is in the keys.

Options:

  • --beacon-delay MIN: how far ahead the beacon is. It must still be in the future when your contribution finishes (~20 minutes). The default of 45 is enough on a fast machine; use more on a slow or memory-limited one.
  • --no-finalize: stop after contributing, e.g. to let other institutions contribute before the next release. Anyone can release later with ./ceremony.sh finalize --wait.
  • --key PUBKEY, --jobs N, --note TEXT: as for contribute.

Each contributor needs its own SSH key: register refuses a key already registered under another name.

Steps, one by one

join runs steps 2 to 6 below. Run them separately to control each one, e.g. to batch several contributions into one release.

Run everything from the repository root, on an up-to-date checkout. Nothing is pushed unless you pass --push.

git switch main && git pull

1. init: first time only, for the whole repository. Skip this if ceremony/manifest.json already exists, unless the circuits changed (see below). It downloads the Perpetual Powers of Tau, imports it as phase 1 and commits:

./ceremony.sh init

2. register: once per institution or user. Adds your SSH public key, so every later commit in your name must be signed by it:

./ceremony.sh register --name bank-a

3. contribute: once per institution or user. Keep the machine awake until it finishes:

./ceremony.sh contribute --name bank-a --note "Fresh VM, destroyed afterwards"

It:

  1. checks you are up to date and verifies every earlier contribution;
  2. announces a future drand round (--beacon-delay, default 45 minutes ahead) as the beacon for the next release, and prints when that is;
  3. adds your randomness to every circuit on top of the latest contribution (in memory only, never written to disk);
  4. writes your attestation and makes a commit signed with your SSH key, on a ceremony/... branch.

It prints your contribution hash. Write it down and publish it: it is how you (and anyone else) confirm later that your contribution is in the keys.

4. finalize: release the new keys. Once the announced drand round is published, anyone (usually you, right after contributing) releases keys that include your contribution:

./ceremony.sh finalize --wait

This writes the new release's keys, R1CS and Solidity verifiers to last_build/ and commits, on a ceremony/release-vN branch when run from main. --wait waits for the round if it isn't published yet.

5. verify: everyone, at every release.

./ceremony.sh verify
./ceremony.sh status   # find your name and the hash you wrote down

verify checks phase 1 against the Perpetual Powers of Tau file, replays every phase 2 contribution cryptographically, re-derives every release, checks each release's beacon against drand, checks every contribution is signed by the registered key of the institution it names, and checks last_build/ matches the latest release (keys and R1CS byte for byte, Solidity verifiers by their verifying-key constants).

6. copy-verifiers, push, and deploy.

./ceremony.sh copy-verifiers --push # copies and commits into ../rayls-sovereign-contracts (or --contracts DIR)
git push -u origin HEAD

copy-verifiers commits in the contracts repository on a branch with the same name as the one this repository is on (ceremony/release-vN if this one is on main). Then check the commits in both repositories and open the two pull requests, as in Before opening the pull requests.

Every release changes the keys, so the new verifiers, the gnark-api build with the new last_build/ and the relayer must be deployed together, and every institution must upgrade at the same time: proofs made with one release's keys don't verify against another's. Batching several contributions into one release keeps upgrades rare.

How long it takes

contribute, finalize and verify work on every circuit; their running time is dominated by the 2^16 and 2^17 circuits.

Step Measured Notes
init a few minutes plus the 150 MB download the first time
verify about 20 minutes on a 20-core, 32 GB laptop (6 circuits at a time) was over 3 hours before the parallel preparation
contribute, finalize similar to verify they do the same preparation, plus a few seconds per circuit

The announced beacon round must still be in the future when contribute finishes. If it isn't (for example because the machine slept), the contribution is discarded automatically and you run contribute again with a larger --beacon-delay. The default of 45 minutes leaves about a 25-minute margin on a machine like the one above; on a much slower or memory-limited machine (fewer --jobs), use more.

If something goes wrong
Situation What to do
You pressed Ctrl+C during contribute or finalize The script removes the partial files itself. Run the command again.
The machine slept or crashed during a run Run ./ceremony.sh clean, then the command again.
"uncommitted changes in ceremony/ or last_build/" Leftovers of an interrupted run: ./ceremony.sh clean.
"… was discarded; rerun with a larger --beacon-delay" The beacon was published before your contribution finished. Run contribute again with a larger --beacon-delay, and keep the machine awake.
"you are N commit(s) behind origin/main" Someone contributed meanwhile: git pull, then contribute on top of them.
drand temporarily unreachable finalize --wait keeps retrying; other commands ask you to try again later.
Rehearse first (about 10 minutes)

Demo mode runs the same steps with two tiny circuits and an insecure local phase 1 in ceremony-demo/; it never touches ceremony/ or last_build/. Use a scratch clone:

git clone <this repo> ceremony-rehearsal && cd ceremony-rehearsal
export CEREMONY_DEMO=1
./ceremony.sh init
./ceremony.sh join --name my-bank           # announces a beacon 3 minutes ahead in demo mode
./ceremony.sh verify

To rehearse a second participant, create another key (ssh-keygen -t ed25519 -f /tmp/other) and run ./ceremony.sh join --name other-bank --key /tmp/other.pub.

Starting over after a circuit change

Changing any circuit voids every contribution, so the ceremony starts a new epoch, and everyone contributes again. Run init on the branch that has the final circuits: it retires the old ceremony on its own. On main it opens a ceremony/restart-epoch-N branch; on any other branch (e.g. the one with the circuit change) it commits there, and the whole new ceremony happens on that branch.

git pull && git lfs pull       # on the branch with the circuit change
./ceremony.sh init --push      # checks the circuits changed, tags ceremony-epoch-N,
                               # removes ceremony/, starts epoch N+1

init stops if the circuits still match the ceremony (pass --restart to start over anyway). The old transcript stays in git history under the ceremony-epoch-N tag. Then contributors run join as usual, on that same branch (git switch BRANCH && git pull first); the verifiers go on a branch of the same name in the contracts repository. The new epoch's first release replaces last_build/ and the verifiers; don't deploy before it. Details: docs/trusted-setup-ceremony.md.

Command reference
# Command What it does What it commits
join --name NAME [--contracts DIR] [--beacon-delay MIN] [--no-finalize] [--skip-verify] Pulls, then steps 5, 2, 3, 4 and 6, in that order, for a new participant everything steps 2–4 and 6 commit
1 init [--ptau FILE] [--restart] Imports phase 1 (once per repository); after a circuit change, retires the old ceremony and starts the next epoch ceremony/manifest.json, phase 1 parameters, the Git LFS rule; the removal of the old ceremony/ and the tag ceremony-epoch-N
2 register --name NAME [--key PUBKEY] Registers your signing key ceremony/contributors/NAME.pub, allowed_signers
3 contribute --name NAME [--note TEXT] [--beacon-delay MIN] [--jobs N] Adds your phase 2 contribution and announces the next beacon the contribution, its attestation, the manifest
4 finalize [--wait] [--jobs N] Releases keys once the announced beacon is published last_build/, the release record
5 verify [--no-ptau] [--jobs N] Checks everything (first rebuilds last_build/circuits/, which is not in git) nothing
6 copy-verifiers [--contracts DIR] Copies the release's verifiers into the contracts repository the verifiers, in the contracts repository
status Shows phase 1, contributions and releases nothing
clean Removes leftovers of an interrupted run nothing
round-at "YYYY-MM-DD HH:MM UTC" Prints the drand round for a time nothing

init, join, register, contribute, finalize and copy-verifiers also accept --push. Run ./ceremony.sh help for all options; CEREMONY_JOBS sets the default for --jobs (0, the default, picks it automatically).

Tips for contributors
  • Keep the machine awake and plugged in for the whole contribution (e.g. set Windows or macOS sleep to "never", or use a tool such as PowerToys Awake or caffeinate).
  • Use a clean machine if you can: a fresh VM or live USB, destroyed afterwards. Say what you did in --note; it goes into your signed attestation.
  • Publish your contribution hash somewhere outside git (e.g. your institution's announcement channel).

Contributing

We are not accepting external contributions at this time β€” see CONTRIBUTING.md. Please also read our Code of Conduct.

Security

To report a security vulnerability, see SECURITY.md β€” please do not open a public issue.

πŸ“„ License

Licensed under the Apache License, Version 2.0 β€” see LICENSE.

Third-party code incorporated in this repository (gnark/gnark-crypto, the generated Groth16 verifier template, and the iden3 Poseidon constants) remains under its own license β€” see NOTICE.

Copyright 2026 Rayls Core Ltd.

Directories ΒΆ

Path Synopsis
cmd
ceremony command
Command ceremony runs the perpetual Groth16 trusted setup for the production circuits.
Command ceremony runs the perpetual Groth16 trusted setup for the production circuits.
server command
setup command
setup/setup_keys_verifiers command
Command setup_keys_verifiers generates DEVELOPMENT keys and Solidity verifiers for the production circuits with a single-party groth16.Setup.
Command setup_keys_verifiers generates DEVELOPMENT keys and Solidity verifiers for the production circuits with a single-party groth16.Setup.
internal
ceremony
Package ceremony runs a Groth16 trusted setup (gnark's mpcsetup) whose transcript is a directory of files plus a manifest, so it can be committed to git and replayed by anyone.
Package ceremony runs a Groth16 trusted setup (gnark's mpcsetup) whose transcript is a directory of files plus a manifest, so it can be committed to git and replayed by anyone.
pkg
api
circuits/enygma/enygma-dvp/internal/dvptest
Package dvptest builds honest witness data for the DvP circuit tests, using the same off-circuit helpers the relayer uses.
Package dvptest builds honest witness data for the DvP circuit tests, using the same off-circuit helpers the relayer uses.
circuits/enygma/enygma-payments/internal/paytest
Package paytest builds honest witness data for the Enygma payment circuits (transfer, deposit, withdraw), computing every value off-circuit the same way the circuits constrain it.
Package paytest builds honest witness data for the Enygma payment circuits (transfer, deposit, withdraw), computing every value off-circuit the same way the circuits constrain it.
circuits/internal/circuittest
Package circuittest holds the scaffolding shared by the circuit tests: compiling, solving, running tables of forged witnesses, checking that a proof binds its public inputs, and setting witness fields by name on circuit structs that share field names but not a type.
Package circuittest holds the scaffolding shared by the circuit tests: compiling, solving, running tables of forged witnesses, checking that a proof binds its public inputs, and setting witness fields by name on circuit structs that share field names but not a type.
logsafe
Package logsafe neutralises untrusted text before it is written to logs.
Package logsafe neutralises untrusted text before it is written to logs.
The Poseidon round constants and MDS values in this file are copied from github.com/iden3/go-iden3-crypto (Copyright 2023 0kims Association), licensed under the MIT License.
The Poseidon round constants and MDS values in this file are copied from github.com/iden3/go-iden3-crypto (Copyright 2023 0kims Association), licensed under the MIT License.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL