SkillAgentSearch skills...

Achronyme

A programming language for zero-knowledge circuits with dual execution: full VM + optimized circuit compilation.

Install / Use

/learn @achronyme/Achronyme
About this skill

Quality Score

0/100

Supported Platforms

Zed

README

Achronyme

CI

A programming language for zero-knowledge circuits.

Write readable code. Decide what gets proven. Same language for general execution and ZK circuit compilation.

Install

curl -fsSL https://achrony.me/install.sh | sh

This installs the ach binary to ~/.local/bin. Requires Linux or macOS (x86_64 or aarch64).

Build from source

git clone https://github.com/achronyme/achronyme.git
cd achronyme
cargo build --release
cargo test --workspace     # 2,700+ unit tests
bash test/run_tests.sh     # 162 integration tests

Quick Look

General-purpose execution

let make_counter = fn(init) {
    mut n = init
    return fn() { n = n + 1; return n }
}
let c = make_counter(0)
print(c())  // 1
print(c())  // 2

ZK circuit

circuit merkle(root: Public, leaf: Witness, path: Witness Field[3], indices: Witness Field[3]) {
    merkle_verify(root, leaf, path, indices)
}
ach circuit merkle.ach --inputs "root=...,leaf=42,path_0=...,path_1=...,path_2=...,indices_0=0,indices_1=1,indices_2=0"
# → circuit.r1cs + witness.wtns (snarkjs-compatible)

Inline proof generation

let secret = 0p42
let hash = 0p17159...  // poseidon(42, 0)

let p = prove(hash: Public) {
    assert_eq(poseidon(secret, 0), hash)
}

print(proof_json(p))  // Groth16 proof, verifiable on-chain

How It Works

Achronyme has two execution modes from the same source:

VM mode (ach run) — Full language: closures, recursion, GC, arrays, maps, strings, I/O. Code runs like any scripting language.

Circuit mode (ach circuit) — Compiles to arithmetic constraints over a configurable prime field (BN254, BLS12-381, or Goldilocks). No loops at runtime, no I/O — everything is unrolled and flattened into a constraint system for zero-knowledge proofs. Select the field with --prime bn254|bls12-381|goldilocks.

The prove {} block bridges both: it runs inside the VM, compiles its body as a circuit, generates a witness from captured variables, and produces a cryptographic proof — all in one expression.

Source (.ach)
    │
    ├─► Parser (Pratt) → AST
    │       │
    │       ├─► Bytecode → VM          (run mode)
    │       │
    │       └─► SSA IR → Optimize
    │               │
    │           ┌───┴───┐
    │           ▼       ▼
    │        R1CS    Plonkish
    │      (Groth16) (KZG-PlonK)
    │           │       │
    │           ▼       ▼
    │       .r1cs    Gates/Lookups
    │       .wtns    Copy constraints
    │           │       │
    │           └───┬───┘
    │               ▼
    │         Native proof
    │
    └─► prove { } → compile + witness + verify + proof (inline)

Language

Types

| Type | Examples | |------|---------| | Int | 42, -7 | | Bool | true, false | | String | "hello" | | List | [1, 2, 3] | | Map | {"a": 1, "b": 2} | | Field | 0p42, 0pxFF, 0pb1010 | | BigInt256 | 0i256xFF, 0i256d42 | | BigInt512 | 0i512xFF, 0i512d100 | | Function | fn(x) { x + 1 } | | Proof | result of prove { } | | Nil | nil |

Control Flow

if x > 0 { print("positive") } else { print("non-positive") }

while n > 0 { n = n - 1 }

for item in list { print(item) }

for i in 0..10 { print(i) }

Functions and Closures

let add = fn(a, b) { a + b }

let fib = fn fib(n) {
    if n < 2 { return n }
    return fib(n - 1) + fib(n - 2)
}

// Closures capture environment
let make_adder = fn(x) { fn(y) { x + y } }
let add5 = make_adder(5)
print(add5(3))  // 8

Field Elements

Prime field elements (BN254 by default, configurable via --prime). Created with the 0p prefix:

let a = 0p42
let b = 0pxFF
let c = 0pb1010

let sum = a + b
let prod = a * b
let inv = 0p1 / a

BigInt (VM only)

Fixed-width unsigned integers (256-bit and 512-bit) for cryptographic operations:

let a = 0i256xFF
let b = bigint256(42)
let bits = a.to_bits()
let masked = a.bit_and(b)

Circuit Features

Declarations

Circuit parameters declare visibility and type in the function signature:

circuit example(output: Public, secret: Witness, arr: Witness Field[4]) {
    // output → public input (instance)
    // secret → private input (witness)
    // arr    → witness array (arr_0, arr_1, arr_2, arr_3)
}

Builtins

| Builtin | Description | R1CS cost | Plonkish cost | |---------|-------------|-----------|---------------| | assert_eq(a, b) | Enforce equality | 1 | 1 | | assert(expr) | Enforce boolean true | 2 | 2 | | poseidon(a, b) | Poseidon 2-to-1 hash | 361 | 361 | | poseidon_many(a, b, c, ...) | Left-fold Poseidon | 361*(n-1) | 361*(n-1) | | mux(cond, a, b) | Conditional select | 2 | 1 | | range_check(x, bits) | Value fits in N bits | bits+1 | 1 (lookup) | | merkle_verify(root, leaf, path, indices) | Merkle membership proof | ~1090/level | ~1090/level | | len(arr) | Compile-time array length | 0 | 0 |

Operators in Circuits

| Operation | R1CS | Plonkish | |-----------|------|----------| | +, - | 0 | 0 | | * | 1 | 1 | | / | 2 | 2 | | ^ (constant exp) | O(log n) | O(log n) | | ==, != | 2 | 2 | | <, <=, >, >= | ~760 | ~760 | | &&, \|\| | 3 | 3 | | ! | 1 | 1 |

Functions in Circuits

Functions are inlined at each call site. No dynamic dispatch, no recursion.

circuit main(out: Public, a: Witness, b: Witness) {
    fn hash_pair(x, y) { poseidon(x, y) }
    assert_eq(hash_pair(a, b), out)
}

Control Flow in Circuits

if/else compiles to mux (both branches are evaluated). for loops are statically unrolled. while, break, continue are rejected at compile time.

circuit sum_check(total: Public, vals: Witness Field[4]) {
    let sum = vals[0]
    let sum = sum + vals[1]
    let sum = sum + vals[2]
    let sum = sum + vals[3]
    assert_eq(sum, total)
}

CLI

# Run a program
ach run script.ach

# Run with PlonK prove backend
ach run script.ach --prove-backend plonkish

# Compile circuit (in-source declarations)
ach circuit circuit.ach --inputs "x=42,y=7"

# Compile circuit (CLI declarations)
ach circuit circuit.ach --public "out" --witness "a,b" --inputs "out=42,a=6,b=7"

# Plonkish backend
ach circuit circuit.ach --backend plonkish --inputs "x=42,y=7"

# Generate Plonkish proof
ach circuit circuit.ach --backend plonkish --inputs "x=42" --prove

# Generate Solidity verifier contract
ach circuit circuit.ach --inputs "x=42,y=7" --solidity

# Compile to bytecode
ach compile script.ach --output script.achb

# Disassemble
ach disassemble script.ach

Output .r1cs and .wtns files are compatible with snarkjs:

snarkjs groth16 setup circuit.r1cs pot12_final.ptau circuit.zkey
snarkjs groth16 prove circuit.zkey witness.wtns proof.json public.json
snarkjs groth16 verify verification_key.json public.json proof.json

Prove Blocks

prove {} compiles a circuit, captures variables from the enclosing scope, generates a witness, and returns a proof — all inline.

let a = 0p6
let b = 0p7
let product = 0p42

let p = prove(product: Public) {
    assert_eq(a * b, product)
}

Variables listed in the parameter list (e.g. product: Public) become public inputs visible to the verifier. All other captured variables (a, b) are automatically inferred as witnesses. Integer values are automatically promoted to field elements.

The result is a Proof object (Groth16 or PlonK depending on --prove-backend). Extract components with proof_json(p), proof_public(p), proof_vkey(p). Verify with verify_proof(p).

If no proving backend is available, the block still compiles the circuit, generates the witness, and verifies constraints locally (returns nil).


Optimization Passes

The SSA IR runs four optimization passes before constraint generation:

  • Constant folding — Evaluates arithmetic on known constants at compile time
  • Dead code elimination — Removes unused instructions
  • Boolean propagation — Tracks proven-boolean variables, skips redundant enforcement
  • Taint analysis — Warns about under-constrained or unused inputs

Disable with --no-optimize.


Project Structure

achronyme/
├── achronyme-parser/   Hand-written Pratt lexer + recursive descent parser
├── ir/                 SSA intermediate representation, optimization passes
├── compiler/           Bytecode compiler, R1CS backend, Plonkish backend
├── vm/                 Register-based VM (37 opcodes, prototype method dispatch)
├── memory/             Heap, GC, FieldElement<F> (BN254, BLS12-381, Goldilocks), BigInt
├── constraints/        R1CS/Plonkish systems, Poseidon hash, binary export
├── cli/                CLI, native Groth16 (ark-groth16) & PlonK (halo2-KZG)
├── std/                Standard library (NativeModule: parse_int, join, I/O)
├── ach-macros/         Proc-macros: #[ach_native], #[ach_module]
├── docs/               Documentation site (Astro + Starlight, 83 pages, EN/ES)
└── test/
    ├── vm/             VM/interpreter integration tests
    ├── circuit/        Circuit compilation tests
    ├── prove/          Prove block tests
    └── run_tests.sh    Integration test runner (162 tests)

Global Functions

16 global functions are available without imports. Most operations now use method syntax.

| Function | Arity | Description | |----------|-------|-------------| | print(...) | variadic | Print values to stdout | | typeof(x) | 1 | Type name as String | | assert(x) | 1 | Runtime assertion | | time() | 0 | Unix timestamp (ms) | | gc_stats() | 0 | GC statistics as map | | poseidon(a, b) | 2 | Poseidon 2-to-1 hash (BN254) | | poseidon_many(a, b, ...) | variadic | Left-fold Poseidon hash | | `veri

View on GitHub
GitHub Stars6
CategoryDevelopment
Updated6h ago
Forks0

Languages

Rust

Security Score

90/100

Audited on Apr 6, 2026

No findings