Inline Proofs
Using prove blocks for inline proof generation.
The prove {} block lets you generate zero-knowledge proofs inline, without leaving the VM. It compiles its body as a circuit at compile time via ProveIR, generates a witness from captured variables, and returns a cryptographic proof.
Basic Example
let x: Field = 0p6
let y: Field = 0p7
let product: Field = 0p42
let p = prove(product: Public) {
assert_eq(x * y, product)
}
print(proof_json(p))
The prove block:
- Compiles
assert_eq(x * y, product)as a circuit at compile time - Captures
xandyfrom the outer scope as witness inputs (auto-inferred) - Marks
productas a public input (declared asproduct: Public) - Generates a Groth16 proof at runtime
- Returns a
ProofObject
Witness Auto-Inference
With the prove(name: Public) syntax, you only declare which variables are public — everything else is automatically inferred as a witness:
let secret: Field = 0p42
let hash: Field = 0p17159...
let p = prove(hash: Public) {
assert_eq(poseidon(secret, 0), hash)
}
hashis declared ashash: Public→ public input (visible to the verifier)secretis referenced in the body but not in the public list → witness (private, auto-inferred)
The verifier learns only that some value hashes to hash, without learning secret.
Explicit Declarations (Classic Syntax)
The explicit witness/public declaration syntax is also supported:
let secret: Field = 0p42
let hash: Field = 0p17159...
let p = prove {
witness secret // private — prover knows this
public hash // public — verifier sees this
assert_eq(poseidon(secret, 0), hash)
}
Both syntaxes produce identical circuits. The prove(name: Public) form is more concise; the explicit form gives full control. You cannot mix both in the same block.
Integer Auto-Conversion
Integers captured by prove blocks are automatically converted to field elements. This is the only place where Int→Field conversion happens implicitly:
let a = 10
let b = 20
let sum = 30
prove(sum: Public) {
assert_eq(a + b, sum)
}
Outside prove blocks, mixing Int and Field raises a TypeMismatch error. Use 0p field literals for explicit conversion.
Accessing Proof Components
A ProofObject contains three JSON components:
let p = prove(y: Public) {
assert_eq(x, y)
}
// Groth16 proof data (pi_a, pi_b, pi_c)
let proof = proof_json(p)
// Public inputs as decimal string array
let inputs = proof_public(p)
// Verification key (for on-chain verification)
let vkey = proof_vkey(p)
print(proof)
print(inputs)
print(vkey)
Verifying Proofs
Use verify_proof() to verify a proof within the VM:
let p = prove(hash: Public) {
assert_eq(poseidon(secret, 0), hash)
}
let ok = verify_proof(p)
print(ok) // true
Arrays and Functions
Prove blocks support arrays, functions, and all circuit-mode features:
let v0: Field = 0p10
let v1: Field = 0p20
let v2: Field = 0p30
let total: Field = 0p60
prove(total: Public) {
let vals: Field[3] = [v0, v1, v2]
let acc: Field = vals[0] + vals[1] + vals[2]
assert_eq(acc, total)
assert_eq(len(vals), 3)
}
Poseidon in Prove Blocks
Cryptographic builtins work inside prove blocks:
let a: Field = 0p1
let b: Field = 0p2
let h: Field = 0p7853200120776062878684798364095072458815029376092732009249414926327459813530
prove(h: Public) {
assert_eq(poseidon(a, b), h)
}
Range Checks
Enforce that a value fits within a certain number of bits:
let val: Field = 0p200
prove() {
range_check(val, 8) // 0 ≤ val < 256
}
Multiple Prove Blocks
You can use multiple prove blocks in sequence:
let a: Field = 0p3
let b: Field = 0p4
let sum: Field = 0p7
let product: Field = 0p12
prove(sum: Public) {
assert_eq(a + b, sum)
}
prove(product: Public) {
assert_eq(a * b, product)
}
Each prove block compiles and proves independently.
Running with Proof Generation
To generate actual Groth16 proofs, provide a Powers of Tau file:
ach run my_program.ach --ptau pot12_final.ptau
Without --ptau, prove blocks still verify constraints but return VerifiedOnly (no cryptographic proof is generated).
The zkey is cached in ~/.achronyme/cache/ for faster subsequent runs.
Backend Selection
By default, prove blocks use the R1CS/Groth16 backend. To use Plonkish/KZG-PlonK:
ach run my_program.ach --prove-backend plonkish