diff --git a/groth16/bn128/curves.nim b/groth16/bn128/curves.nim index b4e0d1d..ac44cc9 100644 --- a/groth16/bn128/curves.nim +++ b/groth16/bn128/curves.nim @@ -321,6 +321,15 @@ func addG1*(p,q: G1): G1 = prj.affine(s, r) return s +func subG1*(p,q: G1): G1 = + var r, x, y : ProjG1 + prj.fromAffine(x, p) + prj.fromAffine(y, q) + prj.diff(r, x, y) + var s : G1 + prj.affine(s, r) + return s + #--------------------------------------- func addG2*(p,q: G2): G2 = @@ -332,6 +341,15 @@ func addG2*(p,q: G2): G2 = prj.affine(s, r) return s +func subG2*(p,q: G2): G2 = + var r, x, y : ProjG2 + prj.fromAffine(x, p) + prj.fromAffine(y, q) + prj.diff(r, x, y) + var s : G2 + prj.affine(s, r) + return s + func negG1*(p: G1): G1 = var r : G1 = p neg(r) @@ -347,11 +365,14 @@ func negG2*(p: G2): G2 = func `+`*(p,q: G1): G1 = addG1(p,q) func `+`*(p,q: G2): G2 = addG2(p,q) +func `-`*(p,q: G1): G1 = subG1(p,q) +func `-`*(p,q: G2): G2 = subG2(p,q) + func `+=`*(p: var G1, q: G1) = p = addG1(p,q) func `+=`*(p: var G2, q: G2) = p = addG2(p,q) -func `-=`*(p: var G1, q: G1) = p = addG1(p,negG1(q)) -func `-=`*(p: var G2, q: G2) = p = addG2(p,negG2(q)) +func `-=`*(p: var G1, q: G1) = p = subG1(p,q) +func `-=`*(p: var G2, q: G2) = p = subG2(p,q) #------------------------------------------------------------------------------- # diff --git a/groth16/dynamic/README.md b/groth16/dynamic/README.md new file mode 100644 index 0000000..1fd7181 --- /dev/null +++ b/groth16/dynamic/README.md @@ -0,0 +1,14 @@ + +"Semi-dynamic" Groth16 proofs +----------------------------- + +This is loosely based on the [Dynark paper](https://eprint.iacr.org/2025/1897): + +- _"Dynark: Making Groth16 Dynamic"_ by Tianyu Zhang, Yupeng Ouyang and Yupeng Zhang + +See also [this write-up](https://hackmd.io/@bkomuves/HyBL5V5xMe) (mostly following the paper). + +Here some details are different from the paper though, and our implementation +is noticeably more efficient in practice. + + diff --git a/groth16/dynamic/preprocess.nim b/groth16/dynamic/preprocess.nim new file mode 100644 index 0000000..1591f4e --- /dev/null +++ b/groth16/dynamic/preprocess.nim @@ -0,0 +1,174 @@ + +# +# the goal of Dynark-style preprocessing is to compute the `2d` group elements +# +# U_k := L_k(\tau) * A0(tau) * g1 +# V_k := L_k(\tau) * B0(tau) * g1 +# +# where A0(x), B0(x) are the polynomials corresponding to the partial witness +# +# this is V1, the version closest to the Dynark paper +# + +import std/options + +import constantine/math/arithmetic +import constantine/named/properties_fields + +import groth16/bn128 +import groth16/bn128/arrays + +import groth16/zkey_types + +import groth16/math/domain +import groth16/math/convolution +import groth16/math/poly + +import groth16/dynamic/types +import groth16/dynamic/setup + +#------------------------------------------------------------------------------- + +# computes the vectors A*z, B*z (but skips C*z) +func buildPartialAB*( zkey: ZKey, pwitness: seq[Option[Fr[BN254_Snarks]]] ): PartialAB = + let hdr: GrothHeader = zkey.header + let domSize = hdr.domainSize + + var valuesAz = newSeq[Fr[BN254_Snarks]](domSize) + var valuesBz = newSeq[Fr[BN254_Snarks]](domSize) + + # we also compute the image of the complement of the partial witness under A and B + var complImageA = newSeq[bool](domSize) + var complImageB = newSeq[bool](domSize) + for i in 0..