diff --git a/README.md b/README.md index e49e28e..f5ed2aa 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Groth16 prover written in Nim ----------------------------- -This is Groth16 prover implementation in Nim, using the +This is a Groth16 prover implementation in Nim, using the [`constantine`](https://github.com/mratsim/constantine) library as an arithmetic / curve backend. @@ -10,6 +10,36 @@ The implementation is compatible with the `circom` + `snarkjs` ecosystem. At the moment only the `BN254` (aka. `alt-bn128`) curve is supported. +### Partial proofs + +Groth16 has an interesting internal structure in that most of the proof +computation is _linear_ in the witness. + +This implies that if you have to generate a lot of proofs where a large part +of the witness is unchanged, then you can precalculate quite a lot, making +subsequent proofs faster. A situation where this happens is when you have +a long-term identity, which however you don't want to reveal (eg. anonymous +credentials). + +As splitting the proof into two has negligible overhead in practice, this +is an essentially free win in such situations. However the speedup potential +is limited, because the single nonlinear term depends on the full witness. +In practice we've seen 2x-3x speedups when 10% of the witness changes. + +This is implemented under `groth16/partial/`. + +### Semi-dynamic proofs + +In a recent paper ["Dynark: Making Groth16 Dynamic"](https://eprint.iacr.org/2025/1897) +(2025), a solution was proposed for also speeding up the computation of the nonlinear +term. This results in an asymptotic speedup, however, unlike the previous +(essentially trivial) solution, this one is not _for free_: There is a somewhat +heavy precalculation when the full witness changes. + +Whether that's worth it depends on the particular use-case. + +A version of this idea is implemented under `groth16/dynamic/`. + ### License Licensed and distributed under either of the @@ -19,16 +49,15 @@ at your choice. ### TODO -- [x] find and fix the _second_ totally surreal bug - [ ] clean up the code -- [x] make it compatible with the latest constantine and also Nim 2.0.x -- [x] make it a nimble package +- [ ] more comprehensive testing - [ ] compare `.r1cs` to the "coeffs" section of `.zkey` - [x] generate fake circuit-specific setup ourselves - [x] make a CLI interface - [x] multithreading support (MSM, and possibly also FFT) -- [ ] add Groth16 notes +- [ ] add Groth16 explanatory notes - [ ] document the `snarkjs` circuit-specific setup `H` points convention - [x] precalculate stuff for "partial" proofs - [ ] implement Dynark style "dynamic proofs" too +- [ ] benchmarks - [ ] make it work for different curves diff --git a/groth16/bn128/arrays.nim b/groth16/bn128/arrays.nim index 184ed57..eeb96d2 100644 --- a/groth16/bn128/arrays.nim +++ b/groth16/bn128/arrays.nim @@ -6,6 +6,29 @@ import groth16/bn128/fields import groth16/bn128/curves import groth16/bn128/rnd +#------------------------------------------------------------------------------- + +# proper mathematical modulo operation. +# (all mainstream programming languages are fucked up... Except Haskell, Haskell gets it right) +# +# TODO: maybe move this somewhere else? +func safeMod*(x: int, N: int): int = + if x >= 0: + return (x mod N) + else: + let d = ((-x) div N) + let y = x + (d+1)*N + return (y mod N) + +#------------------------------------------------------------------------------- +# generic arrays + +func constSeq*[T]( N: int , y: T ): seq[T] = + var xs : seq[T] = newSeq[T]( N ) + for i in 0..