Skip to content

Commit ae2dbf7

Browse files
authored
Merge pull request #331 from worldfnd/px/expand-claude-md
Expand CLAUDE.md with architecture, invariants, and review rules
2 parents 6ae9bc7 + 8312b02 commit ae2dbf7

1 file changed

Lines changed: 161 additions & 31 deletions

File tree

CLAUDE.md

Lines changed: 161 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,74 +7,204 @@ ProveKit is a zero-knowledge proof system toolkit by the World Foundation. It co
77
## Architecture
88

99
```
10-
Noir Program → [r1cs-compiler] → R1CS → [prover: WHIR] → Proof → [verifier] → Accept/Reject
10+
Noir Circuit (.acir)
11+
↓ [r1cs-compiler]
12+
R1CS (A, B, C matrices) + Witness Builders
13+
↓ [prover]
14+
1. Witness solving (layered: w1 → challenges → w2)
15+
2. R1CS compression (postcard blob, reduces peak memory)
16+
3. W1 commitment (Skyscraper/SHA256/Keccak/Blake3 Merkle tree)
17+
4. W2 commitment (if multi-challenge circuit)
18+
5. WHIR sumcheck
19+
20+
NoirProof { public_inputs, whir_r1cs_proof }
21+
↓ [verifier]
22+
1. Fiat-Shamir transcript replay
23+
2. Commitment verification
24+
3. Sumcheck verification
25+
4. Public input binding check
26+
27+
Accept / Reject
1128
```
1229

13-
**Core crates:** `provekit/common`, `provekit/r1cs-compiler`, `provekit/prover`, `provekit/verifier`
14-
**Crypto primitives:** `skyscraper/*` (BN254 field ops), `ntt` (number theoretic transform), `poseidon2` (hash)
15-
**Tooling:** `tooling/cli`, `tooling/provekit-ffi` (C/Swift/Kotlin/Python bindings), `tooling/verifier-server`
16-
**Go verifier:** `recursive-verifier/` (gnark-based WHIR verification)
30+
### Crate Structure
31+
32+
**Core proof system (Rust):**
33+
- `provekit/common` — Shared types: R1CS, SparseMatrix, Interner, PrefixCovector, WitnessBuilders, HashConfig, serialization
34+
- `provekit/r1cs-compiler` — Noir ACIR → R1CS compilation with optimizations (binop batching, range check batching, spread table caching)
35+
- `provekit/prover` — WHIR proving: witness solving, memory compression, commitment, sumcheck
36+
- `provekit/verifier` — WHIR verification: transcript replay, sumcheck check, public input binding
37+
38+
**Cryptographic primitives:**
39+
- `skyscraper/core` — Custom BN254 hash engine with SIMD-accelerated field arithmetic (aarch64). Registered globally at startup.
40+
- `ntt` — Number Theoretic Transform for polynomial evaluation/interpolation. Supports interleaved polynomials.
41+
- `poseidon2` — Poseidon2 hash function (BN254-specific). Used in R1CS compilation for Poseidon2 black box calls.
42+
43+
**Tooling:**
44+
- `tooling/cli` — Main CLI for prove/verify commands
45+
- `tooling/provekit-ffi` — C-compatible FFI bindings (iOS, Android, Python, Swift, Kotlin)
46+
- `tooling/provekit-gnark` — gnark integration for Go interop
47+
- `tooling/provekit-bench` — Benchmarking utilities
48+
- `tooling/verifier-server` — HTTP server combining Rust API + Go verifier
49+
50+
**Go recursive verifier** (`recursive-verifier/`):
51+
- Takes WHIR proof and produces Groth16 proof for on-chain verification via gnark
52+
- CLI (`cmd/cli/`) and HTTP server (`cmd/server/`) modes
53+
- R1CS must match the WHIR proof being verified; PK/VK must be generated together
54+
55+
## Critical Invariants
56+
57+
These invariants are critical for soundness. Violations can produce unsound proofs or verification failures.
58+
59+
### R1CS Constraint Satisfaction
60+
```
61+
For all constraints i: (A[i] · w) * (B[i] · w) = C[i] · w (mod BN254 prime)
62+
```
63+
Changes to `noir_to_r1cs()`, WitnessBuilder variants, or R1CS optimization passes must preserve this.
64+
65+
### Fiat-Shamir Transcript Determinism
66+
Prover and verifier must construct identical Fiat-Shamir transcripts. The domain separator is derived from the serialized `WhirR1CSScheme`. Any change to proof structure, commitment ordering, or message sequencing breaks transcript consistency and causes verification failure.
67+
68+
### Public Input Binding
69+
```
70+
public_inputs[i] == witness[1 + i] for all i < num_public_inputs
71+
```
72+
The witness at position 0 is the constant `1`. `make_public_weight` and `compute_public_eval` use `n = num_public_inputs + 1` to account for this. Off-by-one here is a soundness vulnerability (see PR #321).
73+
74+
### Witness Layer Scheduling
75+
Witness builders execute in layers. All builders depending on a `Challenge` must be in a later layer than the challenge source. Within a layer, execution order is irrelevant. Layers of type `Inverse` use Montgomery's batch inversion trick (single field inverse + multiplications). Violating layer ordering causes panics in `solve_witness_vec()`.
76+
77+
### Prover Message vs Prover Hint
78+
- `prover_message`: Absorbed into Fiat-Shamir transcript — verifier derives challenges from it. Use for values that must be transcript-bound.
79+
- `prover_hint_ark`: NOT absorbed into transcript — prover sends it but it doesn't affect challenges. Use only for values independently verified by WHIR (e.g., committed polynomial evaluations).
80+
81+
Misusing `prover_hint_ark` for a value that should be transcript-bound is a soundness vulnerability — a malicious prover can substitute arbitrary values without detection.
82+
83+
### NTT/Hash Engine Registration
84+
```rust
85+
provekit_common::register_ntt(); // Must be called once at startup
86+
```
87+
Registers the WHIR ArkNtt and Skyscraper hash engine globally. Forgetting this causes runtime panics.
88+
89+
## Key Types
90+
91+
### R1CS
92+
```rust
93+
pub struct R1CS {
94+
pub num_public_inputs: usize,
95+
pub interner: Interner, // Deduplicates field elements in matrices
96+
pub a: SparseMatrix, // Left constraint matrix
97+
pub b: SparseMatrix, // Middle constraint matrix
98+
pub c: SparseMatrix, // Right constraint matrix
99+
}
100+
```
101+
102+
### SparseMatrix
103+
Uses delta-encoded column indices within rows (reduces serialized size ~30-50%). Key operations: `set()`, `iter_row()`, `transpose()`, `multiply()` (parallel via rayon).
104+
105+
### PrefixCovector / OffsetCovector
106+
Memory-optimized covectors for zero-padded vectors. PrefixCovector stores only the non-zero prefix; OffsetCovector stores weights at an offset. Both implement `LinearForm` for MLE evaluation.
107+
108+
### NoirProof
109+
```rust
110+
pub struct NoirProof {
111+
pub public_inputs: PublicInputs,
112+
pub whir_r1cs_proof: WhirR1CSProof, // narg_string (raw bytes) + hints
113+
}
114+
```
115+
Serialized as postcard binary (`.np`) or JSON (`.json`). Hash config stored as 1-byte header.
116+
117+
### WitnessBuilder
118+
~15+ variants: Constant, Sum, Product, Inverse, Challenge, DigitalDecomposition, SpiceWitnesses (RAM/ROM), LogUpInverse, BinOpLookupDenominator, etc. Each variant knows how to compute its witness value from prior witnesses.
119+
120+
### HashConfig
121+
Runtime-selectable: Skyscraper (default, custom optimized), SHA256, Keccak, Blake3. Determines Merkle tree hash and Fiat-Shamir sponge.
17122

18123
## Build & Test
19124

20125
```bash
21126
# Rust (requires nightly)
22127
cargo fmt --all --check
23-
cargo clippy --all-targets --all-features
24-
cargo build --all-targets --all-features
25-
cargo test --no-fail-fast --all-features --lib --tests --bins
26-
cargo test --doc --all-features
128+
cargo clippy --all-targets --all-features --verbose
129+
cargo build --all-targets --all-features --verbose
130+
cargo test --no-fail-fast --all-features --verbose --lib --tests --bins
131+
cargo test --doc --all-features --verbose
132+
cargo doc --workspace --all-features --no-deps --document-private-items # with RUSTDOCFLAGS="--cfg doc_cfg -D warnings"
27133

28134
# Go (recursive-verifier/)
29135
cd recursive-verifier && go build ./... && go test -v -cover ./...
30136
```
31137

32-
Toolchain: Rust nightly (nightly-2026-03-04), Go 1.24.
138+
Toolchain: Rust nightly (nightly-2026-03-04), edition 2021, rust-version 1.85. Go 1.24.
139+
140+
CI runs on `ubuntu-24.04-arm`. End-to-end tests run on self-hosted ARM64 (`provekit-build`).
33141

34142
## Code Conventions
35143

36144
### Rust
37145

38-
- Use `cargo fmt` and `cargo clippy`. CI enforces both — clippy warnings are errors.
39-
- Error handling: use `anyhow::Result<T>` with `.context("description")` for adding context. Do not use `.unwrap()` or `.expect()` in library code — only in tests and CLI entry points.
40-
- All `unsafe` blocks must have a `// SAFETY:` comment explaining the invariant. CI denies missing safety docs.
41-
- All public items must have `///` doc comments. CI runs `cargo doc` with `-D warnings`.
42-
- Use `#[instrument(skip_all)]` from `tracing` for performance-critical function tracing. Do not log large objects.
43-
- Prefer `rayon` for parallelism. Do not spawn raw threads.
44-
- Use workspace dependencies from root `Cargo.toml` — do not add crate-specific dependency versions.
45-
- Tests use `#[test_case]` for parameterized tests, `proptest`/`quickcheck` for property-based tests.
146+
- **Formatting**: `cargo fmt`. CI enforces.
147+
- **Linting**: `cargo clippy` with workspace-level lints: `warn` on `perf`, `complexity`, `style`, `correctness`, `suspicious`. `deny` on `missing_safety_doc`.
148+
- **Error handling**: `anyhow::Result<T>` with `.context("description")`. Use `ensure!()` for invariant checks, `bail!()` for early returns. No `.unwrap()` or `.expect()` in library code — only in tests and CLI entry points.
149+
- **Unsafe code**: All `unsafe` blocks must have `// SAFETY:` comment explaining the invariant. Avoid `unsafe` in proof system core (`provekit/*`) without team review.
150+
- **Documentation**: All `pub` items need `///` doc comments. CI runs `cargo doc -D warnings`.
151+
- **Tracing**: Use `#[instrument(skip_all)]` from `tracing` for function tracing. Do not log large objects.
152+
- **Parallelism**: Use `rayon` for data parallelism. Do not spawn raw threads.
153+
- **Dependencies**: Declare in root `Cargo.toml` `[workspace.dependencies]`, reference with `{ workspace = true }` in crate `Cargo.toml`.
154+
- **Testing**: `#[test_case]` for parameterized tests, `proptest`/`quickcheck` for property-based tests. Integration tests compile Noir programs end-to-end.
46155

47156
### Go (recursive-verifier/)
48157

49-
- Run `gofmt`, `go vet`, and `golangci-lint` before submitting.
50-
- Keep `go.mod` tidy (`go mod tidy` must produce no diff).
158+
- `gofmt`, `go vet`, `golangci-lint` must pass.
159+
- `go mod tidy` must produce no diff.
160+
161+
## FFI Safety Rules (`tooling/provekit-ffi`)
162+
163+
- All `*const c_char` parameters must be valid null-terminated UTF-8.
164+
- `pk_init()` must be called exactly once before any prove/verify.
165+
- `pk_configure_memory()` must be called before `pk_init()` if using custom allocator.
166+
- All returned `PKBuf` must be freed exactly once via `pk_free_buf()`.
167+
- All FFI functions wrap Rust code with `catch_panic()` to prevent unwinding across the FFI boundary.
168+
- Dual allocator modes: callback-based (host delegates alloc/dealloc) or mmap-based (swap-to-disk).
169+
170+
## Serialization Compatibility
171+
172+
- **Proof format**: `NoirProof` serialized via postcard (`.np`) or serde_json (`.json`). Hash config stored as 1-byte header.
173+
- **Proof scheme** (`.pkp`): Serialized Prover/Verifier containing R1CS, witness builders, WHIR config.
174+
- **SparseMatrix**: Uses delta-encoded column indices on disk. `encode_col_deltas` / `decode_col_deltas` must roundtrip correctly.
175+
- **Breaking changes**: Do not change serialization formats without migration plan — deployed verifiers and mobile clients depend on compatibility.
51176

52177
## Security Rules
53178

54179
This is a cryptographic proof system. Correctness and soundness are critical.
55180

56181
- Never skip or weaken constraint checks in prover/verifier code without explicit justification.
57182
- Sensitive field elements must use `zeroize` for cleanup.
58-
- Do not introduce `unsafe` code in the proof system core (`provekit/*`) without team review.
59-
- FFI boundaries (`tooling/provekit-ffi`) must validate all input pointers and lengths.
60-
- Do not change serialization formats (`postcard`, proof encoding) without migration plan — deployed verifiers depend on compatibility.
183+
- Do not introduce `unsafe` code in `provekit/*` without team review.
184+
- FFI boundaries must validate all input pointers and lengths.
185+
- Do not change serialization formats without migration plan.
186+
- All field arithmetic is mod BN254 prime. Montgomery representation used internally by arkworks — transparent to users.
61187

62188
## Code Review Checklist
63189

64190
When reviewing PRs, check for:
65191

66-
- **Soundness**: Changes to constraint generation, prover, or verifier must not weaken soundness guarantees. Any modification to R1CS compilation, witness generation, or proof verification logic must be scrutinized for missing constraints or bypassed checks.
67-
- **Unsafe code**: New `unsafe` blocks must have `// SAFETY:` comments. Prefer safe alternatives.
68-
- **Error handling**: No `.unwrap()` in library code. Use `anyhow::Result` with context.
192+
- **Soundness**: Changes to constraint generation (`noir_to_r1cs`), witness builders, prover transcript messages (`prover_message` vs `prover_hint_ark`), or verification logic must not weaken soundness. Scrutinize for missing constraints, wrong witness layer ordering, or transcript inconsistencies.
193+
- **Public input binding**: Any change to `make_public_weight`, `compute_public_eval`, or `verify_public_input_binding` must account for the constant-1 witness at position 0 (`n = num_public_inputs + 1`).
194+
- **Transcript consistency**: Prover and verifier must produce identical Fiat-Shamir transcripts. Check that new prover messages are mirrored in verifier, and vice versa.
195+
- **Unsafe code**: New `unsafe` blocks must have `// SAFETY:` comments. Prefer safe alternatives. Extra scrutiny for `skyscraper/` SIMD code.
196+
- **Error handling**: No `.unwrap()` in library code. Use `anyhow::Result` with `context()`/`ensure!()`/`bail!()`.
69197
- **Public API docs**: All new `pub` items need `///` doc comments.
70-
- **Test coverage**: New functionality must include tests. Bug fixes must include regression tests.
71-
- **Workspace deps**: Dependencies must be declared in root `Cargo.toml` `[workspace.dependencies]`, not inline in crate `Cargo.toml` files.
72-
- **Performance**: Changes to hot paths (NTT, polynomial operations, matrix multiplication) must not regress performance. Use `#[instrument]` for tracing.
73-
- **Clippy compliance**: All clippy warnings must be resolved. The workspace enforces `warn` on `perf`, `complexity`, `style`, `correctness`, and `suspicious` lints.
198+
- **Test coverage**: New functionality must include tests. Bug fixes must include regression tests. Property-based tests (`proptest`/`quickcheck`) preferred for mathematical operations.
199+
- **Workspace deps**: Dependencies must be in root `Cargo.toml` `[workspace.dependencies]`.
200+
- **Serialization**: Changes to serialized types (`NoirProof`, `R1CS`, `SparseMatrix`, `WhirR1CSProof`) must maintain backward compatibility or include migration.
201+
- **Performance**: Changes to hot paths (NTT, polynomial ops, sparse matrix multiply, sumcheck inner loop) must not regress performance. Use `#[instrument]` for tracing.
202+
- **FFI safety**: Changes to `provekit-ffi` must maintain panic safety (`catch_panic`), pointer validation, and `PKBuf` lifetime correctness.
203+
- **Clippy compliance**: All clippy warnings resolved. Workspace enforces `warn` on `perf`, `complexity`, `style`, `correctness`, `suspicious`.
74204
- **Formatting**: `cargo fmt` for Rust, `gofmt` for Go. No exceptions.
75205

76206
## PR Guidelines
77207

78-
- Branch names: use prefix format like `username/description` (e.g., `px/fix-verifier`).
208+
- Branch names: `username/description` (e.g., `px/fix-verifier`).
79209
- Keep PRs focused — one logical change per PR.
80210
- CI must pass before merge. Do not bypass branch protection without justification.

0 commit comments

Comments
 (0)