diff --git a/constantine.nimble b/constantine.nimble index dd05e12a..7b0ea59e 100644 --- a/constantine.nimble +++ b/constantine.nimble @@ -556,6 +556,7 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ # Polynomials # ---------------------------------------------------------- ("tests/math_polynomials/t_polynomials.nim", false), + ("tests/math_polynomials/t_finite_field_fft.nim", false), # Protocols # ---------------------------------------------------------- @@ -571,6 +572,9 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[ # Proof systems # ---------------------------------------------------------- ("tests/proof_systems/t_r1cs_parser.nim", false), + ("tests/proof_systems/t_wtns_parser.nim", false), + ("tests/proof_systems/t_zkey_parser.nim", false), + ("tests/proof_systems/t_groth16_prover.nim", false), ("tests/interactive_proofs/t_multilinear_extensions.nim", false), ] diff --git a/constantine/math/arithmetic/finite_fields.nim b/constantine/math/arithmetic/finite_fields.nim index 25dab2f6..6a197dce 100644 --- a/constantine/math/arithmetic/finite_fields.nim +++ b/constantine/math/arithmetic/finite_fields.nim @@ -567,6 +567,19 @@ func pow_vartime*(a: var FF, exponent: openarray[byte]) = FF.getSpareBits() ) +func pow_vartime*(a: var FF, exponent: FF) = + ## Exponentiation modulo p + ## ``a``: a field element to be exponentiated + ## ``exponent``: a field element + ## + ## Warning ⚠️ : + ## This is an optimization for public exponent + ## Otherwise bits of the exponent can be retrieved with: + ## - memory access analysis + ## - power analysis + ## - timing analysis + a.pow_vartime(toBig exponent) + func pow_squareMultiply_vartime(a: var FF, exponent: SomeUnsignedInt) {.tags:[VarTime], meter.} = ## **Variable-time** Exponentiation ## diff --git a/constantine/math/arithmetic/finite_fields_square_root.nim b/constantine/math/arithmetic/finite_fields_square_root.nim index 253d9216..f95b083b 100644 --- a/constantine/math/arithmetic/finite_fields_square_root.nim +++ b/constantine/math/arithmetic/finite_fields_square_root.nim @@ -361,13 +361,13 @@ func invsqrt_if_square_vartime*[Name](r: var Fp[Name], a: Fp[Name]): SecretBool # Legendre symbol / Euler's Criterion / Kronecker's symbol # ------------------------------------------------------------ -func isSquare*(a: Fp): SecretBool = +func isSquare*(a: FF): SecretBool = ## Returns true if ``a`` is a square (quadratic residue) in 𝔽p ## ## Assumes that the prime modulus ``p`` is public. - var aa {.noInit.}: Fp.getBigInt() + var aa {.noInit.}: FF.getBigInt() aa.fromField(a) - let symbol = legendre(aa.limbs, Fp.getModulus().limbs, aa.bits) + let symbol = legendre(aa.limbs, FF.getModulus().limbs, aa.bits) return not(symbol == MaxWord) {.pop.} # inline diff --git a/constantine/math/elliptic/ec_scalar_mul_vartime.nim b/constantine/math/elliptic/ec_scalar_mul_vartime.nim index 89357975..438b403d 100644 --- a/constantine/math/elliptic/ec_scalar_mul_vartime.nim +++ b/constantine/math/elliptic/ec_scalar_mul_vartime.nim @@ -81,7 +81,7 @@ func scalarMul_addchain_4bit_vartime[EC](P: var EC, scalar: BigInt) {.tags:[VarT of 5: var t {.noInit.}: EC t.double(P) - t.double(P) + t.double() P ~+= t of 6: var t {.noInit.}: EC diff --git a/constantine/math/elliptic/ec_shortweierstrass_jacobian.nim b/constantine/math/elliptic/ec_shortweierstrass_jacobian.nim index 19936e0c..be087457 100644 --- a/constantine/math/elliptic/ec_shortweierstrass_jacobian.nim +++ b/constantine/math/elliptic/ec_shortweierstrass_jacobian.nim @@ -332,6 +332,8 @@ template sumImpl[F; G: static Subgroup]( # if P or R were infinity points they would have spread 0 with Z₁Z₂ block: # Infinity points + bind isNeutral + bind ccopy o.ccopy(Q, P.isNeutral()) o.ccopy(P, Q.isNeutral()) @@ -1028,7 +1030,7 @@ func `~-`*(a: EC_ShortW_Jac, b: EC_ShortW_Aff): EC_ShortW_Jac {.noInit, inline.} ## This MUST NOT be used with secret data. ## ## This is highly VULNERABLE to timing attacks and power analysis attacks.] - ## + ## ## Out-of-place functions SHOULD NOT be used in performance-critical subroutines as compilers ## tend to generate useless memory moves or have difficulties to minimize stack allocation ## and our types might be large (Fp12 ...) diff --git a/constantine/math/io/io_ec.nim b/constantine/math/io/io_ec.nim index 2b04ab10..0cff80a5 100644 --- a/constantine/math/io/io_ec.nim +++ b/constantine/math/io/io_ec.nim @@ -56,6 +56,31 @@ func toHex*[EC: EC_ShortW_Prj or EC_ShortW_Jac or EC_ShortW_Aff or EC_ShortW_Jac result.appendHex(aff.y) result &= "\n" & sp & ")" +func toDecimal*[EC: EC_ShortW_Prj or EC_ShortW_Jac or EC_ShortW_Aff or EC_ShortW_JacExt](P: EC, indent: static int = 0): string = + ## Stringify an elliptic curve point to Hex + ## Note. Leading zeros are not removed. + ## Output as decimal. + ## + ## WARNING: NOT constant time! + ## + ## This proc output may change format in the future + + var aff {.noInit.}: EC_ShortW_Aff[EC.F, EC.G] + when EC isnot EC_ShortW_Aff: + aff.affine(P) + else: + aff = P + + const sp = spaces(indent) + + result = sp & $EC & "(\n" & sp & " x: " + result.add toDecimal(aff.x) + result &= ",\n" & sp & " y: " + result.add toDecimal(aff.y) + result &= "\n" & sp & ")" + + + func toHex*[EC: EC_TwEdw_Aff or EC_TwEdw_Prj](P: EC, indent: static int = 0): string = ## Stringify an elliptic curve point to Hex for Twisted Edwards Curve ## Note, leading zeros are not removed. diff --git a/constantine/math/io/io_extfields.nim b/constantine/math/io/io_extfields.nim index 99436f0a..c5b7c0ea 100644 --- a/constantine/math/io/io_extfields.nim +++ b/constantine/math/io/io_extfields.nim @@ -52,6 +52,40 @@ func toHex*(f: ExtensionField, indent = 0, order: static Endianness = bigEndian) ## - no leaks result.appendHex(f, indent, order) +func appendDecimal*(accum: var string, f: Fp, indent = 0, order: static Endianness = bigEndian) = + accum.add toDecimal(f) + +func appendDecimal*(accum: var string, f: ExtensionField, indent = 0, order: static Endianness = bigEndian) = + ## Stringify a tower field element to hex. + ## Note. Leading zeros are not removed. + ## Result is prefixed with 0x + ## + ## Output will be padded with 0s to maintain constant-time. + ## + ## CT: + ## - no leaks + accum.add static($f.typeof.genericHead() & '(') + staticFor i, 0, f.coords.len: + when i != 0: + accum.add ", " + accum.add "\n" & spaces(indent+2) & "c" & $i & ": " + when f is Fp2: + accum.appendDecimal(f.coords[i], order = order) + else: + accum.appendDecimal(f.coords[i], indent+2, order) + accum.add ")" + +func toDecimal*(f: ExtensionField, indent = 0, order: static Endianness = bigEndian): string = + ## Stringify a tower field element to hex. + ## Note. Leading zeros are not removed. + ## Result is prefixed with 0x + ## + ## Output will be padded with 0s to maintain constant-time. + ## + ## CT: + ## - no leaks + result.appendDecimal(f, indent, order) + func fromHex*(dst: var Fp2, c0, c1: string) = ## Convert 2 coordinates to an element of 𝔽p2 ## with dst = c0 + β * c1 diff --git a/constantine/math/io/io_fields.nim b/constantine/math/io/io_fields.nim index c28f2567..e42ed1d5 100644 --- a/constantine/math/io/io_fields.nim +++ b/constantine/math/io/io_fields.nim @@ -116,7 +116,9 @@ func toDecimal*(f: FF): string = ## This function is NOT constant-time at the moment. f.toBig().toDecimal() -func fromDecimal*(dst: var FF, decimalString: string) = +{.pop.} # `fromDecimal` can raise `ValueError` + +func fromDecimal*(dst: var FF, decimalString: string) {.raises: [ValueError].} = ## Convert a decimal string. The input must be packed ## with no spaces or underscores. ## This assumes that bits and decimal length are **public.** @@ -136,7 +138,7 @@ func fromDecimal*(dst: var FF, decimalString: string) = let raw {.noinit.} = fromDecimal(dst.mres.typeof, decimalString) dst.fromBig(raw) -func fromDecimal*(T: type FF, hexString: string): T {.noInit.}= +func fromDecimal*(T: type FF, hexString: string): T {.raises: [ValueError], noInit.}= ## Convert a decimal string. The input must be packed ## with no spaces or underscores. ## This assumes that bits and decimal length are **public.** diff --git a/constantine/math/polynomials/fft.nim b/constantine/math/polynomials/fft.nim index 8af0af95..c3642885 100644 --- a/constantine/math/polynomials/fft.nim +++ b/constantine/math/polynomials/fft.nim @@ -98,7 +98,7 @@ func fft_internal[EC; bits: static int]( for i in 0 ..< half: # FFT Butterfly - y_times_root .scalarMul_vartime(output[i+half], rootsOfUnity[i]) + y_times_root .scalarMul_vartime(rootsOfUnity[i], output[i+half]) output[i+half] .diff_vartime(output[i], y_times_root) output[i] .sum_vartime(output[i], y_times_root) diff --git a/constantine/math/polynomials/fft_fields.nim b/constantine/math/polynomials/fft_fields.nim new file mode 100644 index 00000000..d998d35e --- /dev/null +++ b/constantine/math/polynomials/fft_fields.nim @@ -0,0 +1,225 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + constantine/named/algebras, + constantine/math/arithmetic, + constantine/math/io/io_bigints, + constantine/platforms/[abstractions, allocs, views], + ./fft_lut, + constantine/platforms/bithacks # for nextPowerOf2 + +# ############################################################ +# +# Fast Fourier Transform +# +# ############################################################ + +# Fast Fourier Transform (Number Theoretic Transform - NTT) over finite fields +# ---------------------------------------------------------------- + +type + FFTStatus* = enum + FFTS_Success + FFTS_TooManyValues = "Input length greater than the field 2-adicity (number of roots of unity)" + FFTS_SizeNotPowerOfTwo = "Input must be of a power of 2 length" + + FFT_Descriptor*[F] = object # `F` is either `Fp[Name]` or `Fr[Name]` + ## Metadata for FFT on Elliptic Curve + order*: int + rouGen*: F ## Roots of unity generator based on primitive root of `F`: `ω = g^( (p - 1) // n )` + rootsOfUnity*: ptr UncheckedArray[getBigInt(F)] # `getBigInt` gives us the right type depending on Fr/Fp + ## domain, starting and ending with 1, length is cardinality+1 + ## This allows FFT and inverse FFT to use the same buffer for roots. + +func computeRootsOfUnity[F](ctx: var FFT_Descriptor[F], generatorRootOfUnity: auto) = + static: + doAssert typeof(generatorRootOfUnity) is Fr[F.Name] or typeof(generatorRootOfUnity) is Fp[F.Name] + + ctx.rootsOfUnity[0].setOne() + + var cur = generatorRootOfUnity + for i in 1 .. ctx.order: + ctx.rootsOfUnity[i].fromField(cur) + cur *= generatorRootOfUnity + + doAssert ctx.rootsOfUnity[ctx.order].isOne().bool(), "The given generator does not seem to be a root of unity " & + "of " & $F & " for order: " & $ctx.order & "." + +func init*[Name: static Algebra](T: type FFT_Descriptor, order: int, generatorRootOfUnity: FF[Name]): T = + result.order = order + result.rouGen = generatorRootOfUnity + result.rootsOfUnity = allocHeapArrayAligned(T.F.getBigInt(), order+1, alignment = 64) + + result.computeRootsOfUnity(generatorRootOfUnity) + +proc rootOfUnityGenerator*[F](_: typedesc[F], order: int): F = + ## Computes a root of unity generator for the order `n`, using + ## `ω = g^( (p - 1) // n )` where `g` is the primitive root + ## of the field `F`. + ## + ## `p` = prime of the field (or order of subgroup) + ## `n` = FFT order + ## Highlighted the part we compute in each comment. + # ω = g^( `(p - 1)` // n ) + var exponentI {.noInit.}: BigInt[F.bits()] + exponentI = F.getModulus() + exponentI -= One + var exponent = F.fromBig(exponentI) + + # ω = g^( (p - 1) // `n` ) + var n = F.fromInt(order.uint64) + # ω = g^( (p - 1) `// n` ) + n.inv() + # ω = g^( `(p - 1) // n` ) + exponent *= n + + var g: F = F.fromUint(primitiveRoot(F.Name).uint64) + # ω = `g^( (p - 1) // n )` + g.pow_vartime(toBig(exponent)) + result = g + +proc init*(T: typedesc[FFT_Descriptor], order: int): T = + ## Initialize an `FFT_Descriptor` for the given `order`. The root of unity generator is + ## computed automatically. However, a primitive root is required for the field over which + ## the FFT is to be done. See `fft_lut.nim` for definitions and more information. + let g = rootOfUnityGenerator(T.F, order) + result = T.init(order, g) + +func delete*(ctx: FFT_Descriptor) = + ctx.rootsOfUnity.freeHeapAligned() + +proc toFr[S: static int, Name: static Algebra](x: BigInt[S]): Fr[Name] = + result.fromBig(x) + +proc toFp[S: static int, Name: static Algebra](x: BigInt[S]): Fp[Name] = + result.fromBig(x) + +proc toF[F; S: static int](T: typedesc[F], x: BigInt[S]): auto = + when T is Fr: + toFr[S, T.Name](x) + else: + toFp[S, T.Name](x) + +func simpleFT[F; bits: static int]( + output: var StridedView[F], + vals: StridedView[F], + rootsOfUnity: StridedView[BigInt[bits]]) = + # FFT is a recursive algorithm + # This is the base-case using a O(n²) algorithm + + let L = output.len + var last {.noInit.}, v {.noInit.}: F + + var v0w0 {.noinit}: F + v0w0.prod(vals[0], F.toF(rootsOfUnity[0])) + + for i in 0 ..< L: + last = v0w0 + for j in 1 ..< L: + v.prod(F.toF(rootsOfUnity[(i*j) mod L]), vals[j]) + last.sum(last, v) + output[i] = last + +func fft_internal[F; bits: static int]( + output: var StridedView[F], + vals: StridedView[F], + rootsOfUnity: StridedView[BigInt[bits]]) = + if output.len <= 4: + simpleFT(output, vals, rootsOfUnity) + return + + let (evenVals, oddVals) = vals.splitAlternate() + var (outLeft, outRight) = output.splitHalf() + let halfROI = rootsOfUnity.skipHalf() + + fft_internal(outLeft, evenVals, halfROI) + fft_internal(outRight, oddVals, halfROI) + + let half = outLeft.len + var y_times_root{.noinit.}: F + + for i in 0 ..< half: + # FFT Butterfly + y_times_root.prod(F.toF(rootsOfUnity[i]), output[i+half]) + output[i+half].diff(output[i], y_times_root) + output[i].sum(output[i], y_times_root) + + +func fft_vartime*[F]( + desc: FFT_Descriptor[F], + output: var openarray[F], + vals: openarray[F]): FFT_Status = + if vals.len > desc.order: + return FFTS_TooManyValues + if not vals.len.uint64.isPowerOf2_vartime(): + return FFTS_SizeNotPowerOfTwo + + let rootz = desc.rootsOfUnity + .toStridedView(desc.order) + .slice(0, desc.order-1, desc.order div vals.len) + + var voutput = output.toStridedView() + fft_internal(voutput, vals.toStridedView(), rootz) + return FFTS_Success + +# Similar adjustments would be made for ifft_vartime + +func ifft_vartime*[F]( + desc: FFT_Descriptor[F], + output: var openarray[F], + vals: openarray[F]): FFT_Status = + ## Inverse FFT + if vals.len > desc.order: + return FFTS_TooManyValues + if not vals.len.uint64.isPowerOf2_vartime(): + return FFTS_SizeNotPowerOfTwo + + let rootz = desc.rootsOfUnity + .toStridedView(desc.order+1) # Extra 1 at the end so that when reversed the buffer starts with 1 + .reversed() + .slice(0, desc.order-1, desc.order div vals.len) + + var voutput = output.toStridedView() + fft_internal(voutput, vals.toStridedView(), rootz) + + var invLen {.noInit.}: F.getBigInt() + invLen.fromUint(vals.len.uint64) + invLen.invmod_vartime(invLen, F.getModulus()) + + for i in 0 ..< output.len: + let inp = output[i] + output[i].prod(inp, F.toF(invLen)) + + return FFTS_Success + +func fft_vartime*[F](vals: openarray[F]): seq[F] = + ## Performs an FFT on the given values and returns a seq of the result. + ## + ## For convenience only! + let order = nextPowerOfTwo_vartime(vals.len.uint64) + var fftDesc = FFTDescriptor[F].init(order.int) + defer: fftDesc.delete() + + result = newSeq[F](order) + let status = fftDesc.fft_vartime(result, vals) + + doAssert (status == FFTS_Success).bool, "FFT failed with " & $status + +proc ifft_vartime*[F](vals: openarray[F]): seq[F] = + ## Performs an inverse FFT on the given values and returns a seq of the result. + ## + ## For convenience only! + let order = nextPowerOfTwo_vartime(vals.len.uint64) + var fftDesc = FFTDescriptor[F].init(order.int) + defer: fftDesc.delete() + + result = newSeq[F](order) + let status = fftDesc.ifft_vartime(result, vals) + + doAssert (status == FFTS_Success).bool, "FFT failed with " & $status diff --git a/constantine/math/polynomials/fft_lut.nim b/constantine/math/polynomials/fft_lut.nim new file mode 100644 index 00000000..d1187a7b --- /dev/null +++ b/constantine/math/polynomials/fft_lut.nim @@ -0,0 +1,140 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +import + std/macros, + constantine/platforms/abstractions, + constantine/named/algebras, + constantine/math/arithmetic, + constantine/math/io/[io_fields, io_bigints] + +# TODO automate this +# we can precompute everything in Sage +# and auto-generate the file. + +# SageMath: +# `GF(0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001).primitive_element()` +const BLS12_381_Fr_primitive_root = 7 + +# SageMath: +# `GF(0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001).primitive_element()` +const BN254_Snarks_Fr_primitive_root = 5 + +#[ +def precomp_ts(Fq): + ## From q = p^m with p the prime characteristic of the field Fp^m + ## + ## Returns (s, e) such as + ## q == s * 2^e + 1 + s = Fq.order() - 1 + e = 0 + while s & 1 == 0: + s >>= 1 + e += 1 + return s, e + +def find_any_qnr(Fq): + ## Find a quadratic Non-Residue + ## in GF(p^m) + qnr = Fq(Fq.gen()) + r = Fq.order() + while qnr.is_square(): + qnr += 1 + return qnr + +s, e = precomp_ts(Fr) +qnr = find_any_qnr(Fr) +root_unity = qnr^s +]# + + +proc decomposeFieldOrder(F: type Fr): tuple[s: F, e: uint64] = + ## Decomposes the field order of the given field into the form + ## + ## `q = s * 2^e + 1` + ## + ## where `q = p^m` with `p` the prime characteristic of the field Fp^m. + # `s = p - 1` + var s {.noInit.}: BigInt[F.bits()] + s = F.getModulus() + debugecho "Exp: ", s.tohex() + s -= One + + # `e = 0` + var e: uint64 + + while s.isEven().bool: + s.shiftRight(1) + #e += F.fromUint(1'u64) + inc e + result = (s: F.fromBig(s), e: e) + +proc findAnyQnr(F: type Fr): F = + ## Returns the first quadratic non-residue found, i.e. a + ## number in `F`, which is not a square in the field. + ## + ## That is, a `q` if there is no `x` in the field such that: + ## `x² ≡ q (mod p)` + let one = F.fromUint(1'u64) + var qnr {.noInit.}: F + qnr = one # Start with `1` + while qnr.isSquare().bool: + qnr += one + result = qnr + +func buildRootLUT(F: type Fr, primitive_root: uint64): array[32, F] = + ## [pow(PRIMITIVE_ROOT, (MODULUS - 1) // (2**i), MODULUS) for i in range(32)] + + ## + ## XXX: For some reason this does not work for BN254_Snarks (haven't tried with BLS12-381) + + let (s, e) = decomposeFieldOrder(F) + let qnr = findAnyQnr(F) + + #var exponent {.noInit.}: BigInt[F.bits()] + #exponent = F.getModulus() + #debugecho "Exp: ", exponent.tohex() + #exponent -= One + # + ## Start by the end + #var i = result.len - 1 + #exponent.shiftRight(i) + #debugecho "last Exponent : ", exponent.toHex() + #result[i].fromUint(primitive_root) + #result[i].pow_vartime(exponent) + + + var i = e.int #e.toDecimal() # largest possible power of 2 for this field + var rootUnity = qnr + rootUnity.pow_vartime(s) + result[i] = rootUnity + + + while i > 0: + result[i-1].square(result[i]) + #debugecho "At ", i, ": ", result[i-1].toHex() + + dec i + + # debugEcho "Fr[BLS12_81] - Roots of Unity:" + # for i in 0 ..< result.len: + # debugEcho " ", i, ": ", result[i].toHex() + # debugEcho "Fr[BLS12_81] - Roots of Unity -- FIN\n" + +#let BLS12_381_Fr_ScaleToRootOfUnity* = buildRootLUT(Fr[BLS12_381], BLS12_381_Fr_primitive_root) +let BN254_Snarks_Fr_ScaleToRootOfUnity* = buildRootLUT(Fr[BN254_Snarks], BN254_Snarks_Fr_primitive_root) +let g2 = BN254_Snarks_Fr_ScaleToRootOfUnity # [28 - order] +for i, el in g2: + debugecho "ω^{2^", i, "} = ", el.toHex() + +{.experimental: "dynamicBindSym".} +macro scaleToRootOfUnity*(Name: static Algebra): untyped = + return bindSym($Name & "_Fr_ScaleToRootOfUnity") + +macro primitiveRoot*(Name: static Algebra): untyped = + return bindSym($Name & "_Fr_primitive_root") diff --git a/constantine/named/config_fields_and_curves.nim b/constantine/named/config_fields_and_curves.nim index c3fc397a..0d232cca 100644 --- a/constantine/named/config_fields_and_curves.nim +++ b/constantine/named/config_fields_and_curves.nim @@ -49,6 +49,26 @@ declareCurves: testingCurve: true bitwidth: 3 modulus: "0x5" + curve Fake13: + testingCurve: true + bitWidth: 4 + modulus: "0xd" + curve BLS6_6: + testingCurve: true + bitwidth: 6 + modulus: "0x2b" + family: BarretoLynnScott + order: "0x27" + orderBitwidth: 6 + eq_form: ShortWeierstrass + coef_a: 0 + coef_b: 6 + #nonresidue_fp: 5 # 5 is not a square in 𝔽p + #nonresidue_fp2: (0, 1) # √-5 √-5 is not a square or cube in 𝔽p² + + embedding_degree: 6 + sexticTwist: M_Twist + curve Fake101: testingCurve: true bitwidth: 7 diff --git a/constantine/proof_systems/constraint_systems/r1cs_circom_parser.nim b/constantine/proof_systems/constraint_systems/r1cs_circom_parser.nim index 63d27de6..80f7aa7d 100644 --- a/constantine/proof_systems/constraint_systems/r1cs_circom_parser.nim +++ b/constantine/proof_systems/constraint_systems/r1cs_circom_parser.nim @@ -121,6 +121,29 @@ type R1csCustomGatesList* = object R1csCustomGatesApp* = object + ## XXX: Make this a `R1CS[T]` which takes care of parsing the field elements + ## NOTE: For the time being we don't actually use the parsed data + R1CS* = object + magic*: array[4, char] + version*: uint32 + numberSections*: uint32 + header*: Header + constraints*: seq[Constraint] + w2l*: Wire2Label + + +proc toR1CS*(r1cs: R1csBin): R1CS = + result = R1CS(magic: r1cs.magic, + version: r1cs.version, + numberSections: r1cs.numberSections) + for s in r1cs.sections: + case s.sectionType + of kHeader: result.header = s.header + of kConstraints: result.constraints = s.constraints + of kWire2LabelId: result.w2l = s.w2l + else: + echo "Ignoring: ", s.sectionType + proc initSection(kind: R1csSectionKind, size: uint64): Section = result = Section(sectionType: kind, size: size) diff --git a/constantine/proof_systems/constraint_systems/wtns_binary_parser.nim b/constantine/proof_systems/constraint_systems/wtns_binary_parser.nim new file mode 100644 index 00000000..75bce901 --- /dev/null +++ b/constantine/proof_systems/constraint_systems/wtns_binary_parser.nim @@ -0,0 +1,191 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +# This file implements serialization +# +import + ../../serialization/[io_limbs, parsing], + constantine/platforms/[fileio, abstractions], + ../../named/algebras, # Fr + ../groth16_utils + +#[ +The following is a rough spec of the witness files. Details may vary for different +curves (e.g. field witnesse elements may have different sizes). + +Given that there is no specification for the `.wtns` file format, we assume it +is generally treated like the R1CS binary files. See the note at the top of +`zkey_binary_parser.nim` for more notes. + +1. File Header: + - Magic String: + - Offset: 0 bytes + - Length: 4 bytes + - Content: ASCII string "wtns" + - Version Number: + - Offset: 4 bytes + - Length: 4 bytes + - Content: 32-bit unsigned integer indicating the format version (e.g., 2) + - Section Count: + - Offset: 8 bytes + - Length: 4 bytes + - Content: 32-bit unsigned integer indicating the number of sections (e.g., 1) + +2. Witness Length: + - Witness Count: + - Offset: 12 bytes + - Length: 8 bytes + - Content: 64-bit unsigned integer indicating the number of witness elements + +3. Witness Data: + - Witness Elements: + - Offset: 20 bytes + - Length: 32 bytes per element + - Content: Each witness element is a 256-bit (32 bytes) unsigned integer in Big Endian format +]# + +from std / sequtils import filterIt +from std / strutils import endsWith + +type + WtnsSectionKind* = enum # `kInvalid` used to indicate unset & to make the enum work as field discriminator + kInvalid = 0 + kHeader = 1 + kData = 2 + + WitnessHeader* = object + n8*: uint32 # field size in bytes + r*: seq[byte] # prime order of the field + num*: uint32 # number of witness elements + + Witness* = object + data*: seq[byte] ## Important: The values are *not* Montgomery encoded + + Section* = object + size*: uint64 # NOTE: in the real file the section type is *FIRST* and then the size + # But we cannot map this with a variant type in Nim (without using different + # names for each variant branch) + case sectionType*: WtnsSectionKind + of kInvalid: discard + of kHeader: header*: WitnessHeader + of kData: wtns*: seq[Witness] + + ## `WtnsBin` is binary compatible with an Witness binary file. Meaning it follows the structure + ## of the file (almost) exactly. The only difference is in the section header. The size comes + ## *after* the kind, which we don't reproduce in `Section` above + WtnsBin* = object + magic*: array[4, char] # "wtns" + version*: uint32 + numberSections*: uint32 + sections*: seq[Section] # Note: Because of the unordered nature of the sections + # the `sections` seq won't follow the binary order of + # the data in the file. Instead, we first record (kind, file position) + # of each different section in the file and then parse them in increasing + # order of the section types + + Wtns*[Name: static Algebra] = object + version*: uint32 + header*: WitnessHeader + witnesses*: seq[Fr[Name]] + +func header*(wtns: WtnsBin): WitnessHeader = + result = wtns.sections.filterIt(it.sectionType == kHeader)[0].header + +func witnesses*(wtns: WtnsBin): seq[Witness] = + result = wtns.sections.filterIt(it.sectionType == kData)[0].wtns + +proc getWitnesses*[Name: static Algebra](witnesses: seq[Witness]): seq[Fr[Name]] = + result = newSeq[Fr[Name]](witnesses.len) + for i, w in witnesses: + when CM_WN or CMM_WN: + let isMont = false + elif CM_WM: + let isMont = true + else: + {.error: "One case must be active."} + result[i] = toFr[Name](w.data, isMont = isMont, isDoubleMont = false) ## Important: Witness does *not* store numbers in Montgomery rep + +proc toWtns*[Name: static Algebra](wtns: WtnsBin): Wtns[Name] = + result = Wtns[Name]( + version: wtns.version, + header: wtns.header(), + witnesses: wtns.witnesses().getWitnesses[:Name]() + ) + +proc initSection(kind: WtnsSectionKind, size: uint64): Section = + result = Section(sectionType: kind, size: size) + +template wtnsSection(sectionSize, body: untyped): untyped = + let startOffset = f.getFilePosition() + + body + + return sectionSize.int == f.getFilePosition() - startOffset + +proc parseMagicHeader(f: File, mh: var array[4, char]): bool = + result = f.readInto(mh) + +proc parseSectionKind(f: File, v: var WtnsSectionKind): bool = + var val: uint32 + result = f.parseInt(val, littleEndian) + v = WtnsSectionKind(val.int) + +proc parseWitnessHeader(f: File, h: var WitnessHeader): bool = + ?f.parseInt(h.n8, littleEndian) # byte size of the prime number + h.r.setLen(h.n8) + ?f.readInto(h.r) + ?f.parseInt(h.num, littleEndian) + result = true # would have returned before due to `?` otherwise + +proc parseWitnesses(f: File, s: var seq[Witness], sectionSize: uint64, elemSize: uint32): bool = + ## Parses the witnesses + let numElems = sectionSize div elemSize.uint64 + var buf = newSeq[byte](elemSize) + s.setLen(numElems) + for i in 0 ..< numElems: + ?f.readInto(buf) + s[i] = Witness(data: buf) ## XXX: fix me + result = true + +proc parseWitnesses(f: File, s: var Section, size: uint64, wtns: WtnsBin): bool = + let h = wtns.sections.filterIt(it.sectionType == kHeader)[0].header ## XXX: fixme + result = parseWitnesses(f, s.wtns, size, h.n8) + +proc parseSection(f: File, s: var Section, kind: WtnsSectionKind, size: uint64, wtns: var WtnsBin): bool = + # NOTE: The `wtns` object is there to provide the header information to + # the constraints section + s = initSection(kind, size) + case kind + of kHeader: ?f.parseWitnessHeader(s.header) + of kData: ?f.parseWitnesses(s, size, wtns) + else: raiseAssert "Invalid" + + result = true # would have returned otherwise due to `?` + +proc parseSection(f: File, wtns: var WtnsBin): Section = + var kind: WtnsSectionKind + var size: uint64 + doAssert f.parseSectionKind(kind), "Failed to read section type in section " + doAssert f.parseInt(size, littleEndian), "Failed to read section size in section " + + result = initSection(kHeader, size) + + doAssert f.parseSection(result, kind, size, wtns), "Failed to parse section: " & $kind + +proc parseWtnsFile*(path: string): WtnsBin = + var f = fileio.open(path, kRead) + + doAssert f.parseMagicHeader(result.magic), "Failed to read magic header" + doAssert f.parseInt(result.version, littleEndian), "Failed to read version" + doAssert f.parseInt(result.numberSections, littleEndian), "Failed to read number of sections" + + for i in 0 ..< result.numberSections: + let s = parseSection(f, result) + result.sections.add s + + fileio.close(f) diff --git a/constantine/proof_systems/constraint_systems/zkey_binary_parser.nim b/constantine/proof_systems/constraint_systems/zkey_binary_parser.nim new file mode 100644 index 00000000..1e2b5704 --- /dev/null +++ b/constantine/proof_systems/constraint_systems/zkey_binary_parser.nim @@ -0,0 +1,399 @@ +# Constantine +# Copyright (c) 2018-2019 Status Research & Development GmbH +# Copyright (c) 2020-Present Mamy André-Ratsimbazafy +# Licensed and distributed under either of +# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). +# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). +# at your option. This file may not be copied, modified, or distributed except according to those terms. + +# This file implements serialization +# +import + ../../serialization/[io_limbs, parsing], + constantine/platforms/[fileio, abstractions], + ../../named/algebras, # Fr, Fp + ../../math/extension_fields, # Fp2 + ../../math/elliptic/[ec_shortweierstrass_affine], # EC types + ../groth16_utils # to unmarshal data + +from std / sequtils import filterIt +from std / strutils import endsWith + +## Note on the parsing logic: +## +## A Zkey file is first parsed into the `ZkeyBin` type. This data type +## tries to mostly match the binary format of the `.zkey` files. +## *HOWEVER*, there is no specification for `.zkey` files. *BUT*, if we +## assume the same general specification holds as for the R1CS binary +## files (for which +## https://github.com/iden3/r1csfile/blob/master/doc/r1cs_bin_format.md +## exists), we must assume that each section can appear in arbitrary +## order in the binary file. Thus, we use an approach with variant objects +## and simply a `seq[Section]` for the `ZkeyBin` type. We could either +## disregard "spec compliance" and just assume the section order is +## based on the section numbering (i.e. like the `ZkeySectionKind` enum +## values) or handle different orders during parsing. +## +## In the end, we have a curve specific "typed" `Zkey[T]` type, which +## does away with this approach anyhow. + +type + ZkeySectionKind* = enum # `kInvalid` used to indicate unset & to make the enum work as field discriminator + kInvalid = 0 + kHeader = 1 + kGroth16Header = 2 + kIC = 3 + kCoeffs = 4 + kA = 5 + kB1 = 6 + kB2 = 7 + kC = 8 + kH = 9 + kContributions = 10 + + Header* = object + proverType*: uint32 ## Must be `1` for Groth16 + + Groth16Header_b* = object + n8q*: uint32 # Size of base field in bytes (4 bytes, unsigned integer) + q*: seq[byte] # Prime of the base field (n8q bytes) + n8r*: uint32 # Size of scalar field in bytes (4 bytes, unsigned integer) + r*: seq[byte] # Prime of the scalar field (n8r bytes) + nVars*: uint32 # Total number of variables (4 bytes, unsigned integer) + nPublic*: uint32 # Number of public variables (4 bytes, unsigned integer) + domainSize*: uint32 # Size of the domain (4 bytes, unsigned integer) + alpha1*: seq[byte] # alpha in G1 (2 * n8q bytes) + beta1*: seq[byte] # beta in G1 (2 * n8q bytes) + beta2*: seq[byte] # beta in G2 (4 * n8q bytes) + gamma2*: seq[byte] # gamma in G2 (4 * n8q bytes) + delta1*: seq[byte] # delta in G1 (2 * n8q bytes) + delta2*: seq[byte] # delta in G2 (4 * n8q bytes) + + Groth16Header*[Name: static Algebra] = object + n8q*: uint32 # Size of base field in bytes (4 bytes, unsigned integer) + q*: seq[byte] # Prime of the base field (n8q bytes) + n8r*: uint32 # Size of scalar field in bytes (4 bytes, unsigned integer) + r*: seq[byte] # Prime of the scalar field (n8r bytes) + nVars*: uint32 # Total number of variables (4 bytes, unsigned integer) + nPublic*: uint32 # Number of public variables (4 bytes, unsigned integer) + domainSize*: uint32 # Size of the domain (4 bytes, unsigned integer) + alpha1*: EC_ShortW_Aff[Fp[Name], G1] + beta1*: EC_ShortW_Aff[Fp[Name], G1] + beta2*: EC_ShortW_Aff[Fp2[Name], G2] + gamma2*: EC_ShortW_Aff[Fp2[Name], G2] + delta1*: EC_ShortW_Aff[Fp[Name], G1] + delta2*: EC_ShortW_Aff[Fp2[Name], G2] + + ## Generic section containing multiple points on one of the curves. Will be unmarshaled into + ## points on the correct curve afterwars. + DataSection* = object + points*: seq[seq[byte]] + + # `_b` suffix for raw binary + ## NOTE: Maybe a `Raw` suffix would be better + IC_b* = DataSection + A_b* = DataSection + B1_b* = DataSection + B2_b* = DataSection + C_b* = DataSection + H_b* = DataSection + + IC*[Name : static Algebra] = seq[EC_ShortW_Aff[Fp[Name], G1]] + A*[Name : static Algebra] = seq[EC_ShortW_Aff[Fp[Name], G1]] + B1*[Name : static Algebra] = seq[EC_ShortW_Aff[Fp[Name], G1]] + B2*[Name : static Algebra] = seq[EC_ShortW_Aff[Fp2[Name], G2]] + C*[Name : static Algebra] = seq[EC_ShortW_Aff[Fp[Name], G1]] + H*[Name : static Algebra] = seq[EC_ShortW_Aff[Fp[Name], G1]] + + Coefficient_b* = object + matrix*: uint32 + section*: uint32 + index*: uint32 + value*: seq[byte] # n8r bytes + + Coefficients_b* = object + num*: uint32 # number of coefficients + cs*: seq[Coefficient_b] + + Coefficient*[Name: static Algebra] = object + matrix*: uint32 + section*: uint32 + index*: uint32 + value*: Fr[Name] + + Coefficients*[Name: static Algebra] = object + num*: uint32 # number of coefficients + cs*: seq[Coefficient[Name]] + + Contributions* = object + hash*: array[64, byte] # hash of the circuit + num*: uint32 # number of contributions + # for each contribution has some data + + Section* = object + size*: uint64 # NOTE: in the real file the section type is *FIRST* and then the size + # But we cannot map this with a variant type in Nim (without using different + # names for each variant branch) + case sectionType*: ZkeySectionKind + of kInvalid: discard + of kHeader: header*: Header + of kGroth16Header: g16h: Groth16Header_b + of kIC: ic: IC_b + of kCoeffs: coeffs: Coefficients_b + of kA: a: A_b + of kB1: b1: B1_b + of kB2: b2: B2_b + of kC: c: C_b + of kH: h: H_b + of kContributions: contr: Contributions + + ## `ZkeyBin` is binary compatible with a `.zkey` binary file. Meaning it follows the structure + ## of the file (almost) exactly. The only difference is in the section header. The size comes + ## *after* the kind, which we don't reproduce in `Section` above + ZkeyBin* = object + magic*: array[4, char] + version*: uint32 + numberSections*: uint32 + sections*: seq[Section] # Note: Because of the unordered nature of the sections + # the `sections` seq won't follow the binary order of + # the data in the file. Instead, we first record (kind, file position) + # of each different section in the file and then parse them in increasing + # order of the section types + + ## `Zkey` is a "typed" version of the binary file, where all field elements have already been + ## unmarshalled according to the encoding spec used by SnarkJS. + Zkey*[Name: static Algebra] = object + version*: uint32 + header*: Header + g16h*: Groth16Header[Name] + ic*: IC[Name] + coeffs*: Coefficients[Name] + A*: A[Name] + B1*: B1[Name] + B2*: B2[Name] + C*: C[Name] + H*: H[Name] + contr*: Contributions + +## NOTE: These are rather ugly, but a result of the parsing approach we use. +## See note at the top of the file. +func header*(zkey: ZkeyBin): Header = + result = zkey.sections.filterIt(it.sectionType == kHeader)[0].header + +func Afield*(zkey: ZkeyBin): A_b = + result = zkey.sections.filterIt(it.sectionType == kA)[0].a + +func B1field*(zkey: ZkeyBin): B1_b = + result = zkey.sections.filterIt(it.sectionType == kB1)[0].b1 + +func B2field*(zkey: ZkeyBin): B2_b = + result = zkey.sections.filterIt(it.sectionType == kB2)[0].b2 + +func Cfield*(zkey: ZkeyBin): C_b = + result = zkey.sections.filterIt(it.sectionType == kC)[0].c + +func Hfield*(zkey: ZkeyBin): H_b = + result = zkey.sections.filterIt(it.sectionType == kH)[0].h + +func coeffs*(zkey: ZkeyBin): Coefficients_b = + result = zkey.sections.filterIt(it.sectionType == kCoeffs)[0].coeffs + +func groth16Header*(zkey: ZkeyBin): Groth16Header_b = + result = zkey.sections.filterIt(it.sectionType == kGroth16Header)[0].g16h + +func icField*(zkey: ZkeyBin): IC_b = + result = zkey.sections.filterIt(it.sectionType == kIC)[0].ic + +func contributions*(zkey: ZkeyBin): Contributions = + result = zkey.sections.filterIt(it.sectionType == kContributions)[0].contr + +func to*[Name: static Algebra](coefs: Coefficients_b, _: typedesc[Coefficients[Name]]): Coefficients[Name] = + result = Coefficients[Name](num: coefs.num, + cs: newSeq[Coefficient[Name]](coefs.num)) + for i in 0 ..< coefs.num: + let + m = coefs.cs[i].matrix + c = coefs.cs[i].section + s = coefs.cs[i].index + when CM_WN or CM_WM: + let isMont = true + let isDoubleMont = false + elif CMM_WN: + let isMont = false + let isDoubleMont = true + else: + {.error: "One case must be active.".} + result.cs[i] = Coefficient[Name]( + matrix: m, section: c, index: s, + value: toFr[Name](coefs.cs[i].value, isMont = isMont, isDoubleMont = isDoubleMont) + ) + +proc to*[Name: static Algebra](g16h: Groth16Header_b, _: typedesc[Groth16Header[Name]]): Groth16Header[Name] = + let alpha1 = g16h.alpha1.toEcG1[:Name]() + let beta1 = g16h.beta1.toEcG1[:Name]() + let beta2 = g16h.beta2.toEcG2[:Name]() + let gamma2 = g16h.gamma2.toEcG2[:Name]() + let delta1 = g16h.delta1.toEcG1[:Name]() + let delta2 = g16h.delta2.toEcG2[:Name]() + + result = Groth16Header[Name]( + n8q: g16h.n8q, + q: g16h.q, + n8r: g16h.n8r, + r: g16h.r, + nVars: g16h.nVars, + nPublic: g16h.nPublic, + domainSize: g16h.domainSize, + alpha1: alpha1, + beta1: beta1, + beta2: beta2, + gamma2: gamma2, + delta1: delta1, + delta2: delta2 + ) + +proc toZkey*[Name: static Algebra](zkey: ZkeyBin): Zkey[Name] = + result = Zkey[Name]( + version: zkey.version, + header: zkey.header(), + g16h: zkey.groth16header().to(Groth16Header[Name]), + ic: zkey.icField().points.asEC(Fp[Name]), + coeffs: zkey.coeffs().to(Coefficients[Name]), + A: zkey.AField().points.asEC(Fp[Name]), + B1: zkey.B1Field().points.asEC(Fp[Name]), + B2: zkey.B2Field().points.asEC2(Fp2[Name]), + C: zkey.CField().points.asEC(Fp[Name]), + H: zkey.HField().points.asEC(Fp[Name]), + contr: zkey.contributions() + ) + +proc initSection(kind: ZkeySectionKind, size: uint64): Section = + result = Section(sectionType: kind, size: size) + +template zkeySection(sectionSize, body: untyped): untyped = + let startOffset = f.getFilePosition() + + body + + return sectionSize.int == f.getFilePosition() - startOffset + +proc parseMagicHeader(f: File, mh: var array[4, char]): bool = + result = f.readInto(mh) + +proc parseSectionKind(f: File, v: var ZkeySectionKind): bool = + var val: uint32 + result = f.parseInt(val, littleEndian) + v = ZkeySectionKind(val.int) + +proc parseHeader(f: File, h: var Header): bool = + ?f.parseInt(h.proverType, littleEndian) # byte size of the prime number + doAssert h.proverType == 1, "Prover type must be `1` for Groth16, found: " & $h.proverType + result = true # would have returned before due to `?` otherwise + +proc parseGroth16Header(f: File, g16h: var Groth16Header_b): bool = + var buf: seq[byte] + for field, v in fieldPairs(g16h): + when typeof(v) is uint32: + ?f.parseInt(v, littleEndian) + elif typeof(v) is seq[byte]: + when field == "q": + doAssert g16h.n8q != 0, "Parsing failure, `n8q` not parsed." + buf.setLen(g16h.n8q) + elif field == "r": + doAssert g16h.n8r != 0, "Parsing failure, `n8r` not parsed." + buf.setLen(g16h.n8r) + elif field.endsWith("1"): # 2 * n8q bytes + doAssert g16h.n8q != 0, "Parsing failure, `n8q` not parsed." + buf.setLen(2 * g16h.n8q) + elif field.endsWith("2"): # 4 * n8q bytes + doAssert g16h.n8q != 0, "Parsing failure, `n8q` not parsed." + buf.setLen(4 * g16h.n8q) + else: + raiseAssert "Unsupported field: " & $field + ?f.readInto(buf) + v = buf + else: + raiseAssert "Unsupported type: " & $typeof(v) + result = true + +proc parseDataSection(f: File, d: var DataSection, sectionSize: uint64, elemSize: uint32): bool = + ## Parses a generic data section, each `elemSize` in size + let numElems = sectionSize div elemSize.uint64 + var buf = newSeq[byte](elemSize) + d.points.setLen(numElems.int) + for i in 0 ..< numElems: + ?f.readInto(buf) + d.points[i] = buf ## XXX: fix me + result = true + +proc parseDatasection(f: File, s: var Section, kind: ZkeySectionKind, size: uint64, zkey: var ZkeyBin): bool = + let g16h = zkey.sections.filterIt(it.sectionType == kGroth16Header)[0].g16h ## XXX: fixme + case kind + of kIC: ?f.parseDataSection(s.ic, size, 2 * g16h.n8q) + of kA: ?f.parseDataSection(s.a, size, 2 * g16h.n8q) + of kB1: ?f.parseDataSection(s.b1, size, 2 * g16h.n8q) + of kB2: ?f.parseDataSection(s.b2, size, 4 * g16h.n8q) + of kC: ?f.parseDataSection(s.c, size, 2 * g16h.n8q) + of kH: ?f.parseDataSection(s.h, size, 2 * g16h.n8q) + else: + raiseAssert "Not a data section: " & $kind + result = true + +proc parseCoefficient(f: File, s: var Coefficient_b, size: uint64): bool = + ?f.parseInt(s.matrix, littleEndian) + ?f.parseInt(s.section, littleEndian) + ?f.parseInt(s.index, littleEndian) + s.value = newSeq[byte](size) + ?f.readInto(s.value) + result = true + +proc parseCoefficients(f: File, s: var Coefficients_b, zkey: ZkeyBin): bool = + ?f.parseInt(s.num, littleEndian) + let g16h = zkey.sections.filterIt(it.sectionType == kGroth16Header)[0].g16h ## XXX: fixme + s.cs = newSeq[Coefficient_b](s.num) + for i in 0 ..< s.num: # parse coefficients + ?f.parseCoefficient(s.cs[i], g16h.n8r) + result = true + +proc parseContributions(f: File, s: var Contributions): bool = + ?f.readInto(s.hash) + ?f.parseInt(s.num, littleEndian) + # XXX: parse individual contributions + result = true + +proc parseSection(f: File, s: var Section, kind: ZkeySectionKind, size: uint64, zkey: var ZkeyBin): bool = + # NOTE: The `zkey` object is there to provide the header information to + # the constraints section + s = initSection(kind, size) + case kind + of kHeader: ?f.parseHeader(s.header) + of kGroth16Header: ?f.parseGroth16Header(s.g16h) + of kIC, kA .. kH: ?f.parseDataSection(s, kind, size, zkey) + of kCoeffs: ?f.parseCoefficients(s.coeffs, zkey) + of kContributions: ?f.parseContributions(s.contr) + else: raiseAssert "Invalid" + + result = true # would have returned otherwise due to `?` + +proc parseSection(f: File, zkey: var ZkeyBin): Section = + var kind: ZkeySectionKind + var size: uint64 + doAssert f.parseSectionKind(kind), "Failed to read section type in section " + doAssert f.parseInt(size, littleEndian), "Failed to read section size in section " + + result = initSection(kHeader, size) + + doAssert f.parseSection(result, kind, size, zkey), "Failed to parse section: " & $kind + +proc parseZkeyFile*(path: string): ZkeyBin = + var f = fileio.open(path, kRead) + + doAssert f.parseMagicHeader(result.magic), "Failed to read magic header" + doAssert f.parseInt(result.version, littleEndian), "Failed to read version" + doAssert f.parseInt(result.numberSections, littleEndian), "Failed to read number of sections" + + for i in 0 ..< result.numberSections: + let s = parseSection(f, result) + result.sections.add s + + fileio.close(f) diff --git a/constantine/proof_systems/groth16_utils.nim b/constantine/proof_systems/groth16_utils.nim new file mode 100644 index 00000000..6f8d9e48 --- /dev/null +++ b/constantine/proof_systems/groth16_utils.nim @@ -0,0 +1,87 @@ +import ../math/[arithmetic, extension_fields], + ../math/io/[io_bigints, io_fields, io_ec, io_extfields], + ../platforms/abstractions, + ../named/[algebras, properties_fields, properties_curves], + ../math/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_jacobian, ec_scalar_mul, ec_multi_scalar_mul, ec_scalar_mul_vartime], + ../csprngs/sysrand + +## Read zkey coefficients `C` as montgomery rep `M`, witness data `W` in normal rep `N` +## This produces the correct zkey coefficients, but wrong `C` in `buildABC`. +## Proof g^A, g^B are correct. +const CM_WN* {.booldefine.} = false +## Read zkey coefficients `C` as montgomery rep `M`, witness data `W` in montgomery rep `M` +## This produces the correct............ +## No proof is correct. +const CM_WM* {.booldefine.} = false +## Read zkey coefficients `C` as double montgomery rep `MM`, witness data `W` in normal rep `N` +## This produces the correct 3 proof values +const CMM_WN* {.booldefine.} = false + +## What about `CN_WM`? + +## Helper constructors for Fp / Fr elements used in Groth16 binary file parsers. +proc toFp*[Name: static Algebra](x: seq[byte], isMont = true): Fp[Name] = + let b = matchingBigInt(Name).unmarshal(x.toOpenArray(0, x.len - 1), littleEndian) + if isMont: + var bN: typeof(b) + bN.fromMont(b, Fp[Name].getModulus(), Fp[Name].getNegInvModWord(), Fp[Name].getSpareBits()) + result.fromBig(bN) + else: + result.fromBig(b) + +proc toFr*[Name: static Algebra](x: seq[byte], isMont = true, isDoubleMont = false): Fr[Name] = + let b = matchingOrderBigInt(Name).unmarshal(x.toOpenArray(0, x.len - 1), littleEndian) + if isMont: + var bN: typeof(b) + bN.fromMont(b, Fr[Name].getModulus(), Fr[Name].getNegInvModWord(), Fr[Name].getSpareBits()) + result.fromBig(bN) + elif isDoubleMont: + var bN: typeof(b) + bN.fromMont(b, Fr[Name].getModulus(), Fr[Name].getNegInvModWord(), Fr[Name].getSpareBits()) + var bNN: typeof(b) + bNN.fromMont(bN, Fr[Name].getModulus(), Fr[Name].getNegInvModWord(), Fr[Name].getSpareBits()) + result.fromBig(bNN) + else: + result.fromBig(b) + +proc toEcG1*[Name: static Algebra](s: seq[byte]): EC_ShortW_Aff[Fp[Name], G1] = + let x = toFp[Name](s[0 .. 31]) + let y = toFp[Name](s[32 .. ^1]) + result.x = x + result.y = y + if not bool(result.isNeutral()): + doAssert isOnCurve(result.x, result.y, G1).bool, "Input point is not on curve!" + +proc toFp2*[Name: static Algebra](x: seq[byte]): Fp2[Name] = + let c0 = toFp[Name](x[0 .. 31]) + let c1 = toFp[Name](x[32 .. 63]) + result.c0 = c0 + result.c1 = c1 + +proc toEcG2*[Name: static Algebra](s: seq[byte]): EC_ShortW_Aff[Fp2[Name], G2] = + let x = toFp2[Name](s[0 .. 63]) + let y = toFp2[Name](s[64 .. ^1]) + result.x = x + result.y = y + if not bool(result.isNeutral()): + doAssert isOnCurve(result.x, result.y, G2).bool, "Input point is not on curve!" + +proc asEC*[Name: static Algebra](pts: seq[seq[byte]], _: typedesc[Fp[Name]]): seq[EC_ShortW_Aff[Fp[Name], G1]] = + result = newSeq[EC_ShortW_Aff[Fp[Name], G1]](pts.len) + for i, el in pts: + result[i] = toEcG1[Name](el) + +proc asEC2*[Name: static Algebra](pts: seq[seq[byte]], _: typedesc[Fp2[Name]]): seq[EC_ShortW_Aff[Fp2[Name], G2]] = + result = newSeq[EC_ShortW_Aff[Fp2[Name], G2]](pts.len) + for i, el in pts: + result[i] = toEcG2[Name](el) + +proc randomFieldElement*[Name: static Algebra](_: typedesc[Fr[Name]]): Fr[Name] = + ## random element in ~Fr[Name]~ + let m = Fr[Name].getModulus() + var b: matchingOrderBigInt(Name) + + bind sysrand + while b.isZero().bool or (b > m).bool: ## XXX: or just truncate? + assert b.limbs.sysrand() + result.fromBig(b) diff --git a/constantine/proof_systems/manual_groth16.nim b/constantine/proof_systems/manual_groth16.nim new file mode 100644 index 00000000..027a51a9 --- /dev/null +++ b/constantine/proof_systems/manual_groth16.nim @@ -0,0 +1,374 @@ +import ./constraint_systems/r1cs_circom_parser, + ./constraint_systems/zkey_binary_parser, + ./constraint_systems/wtns_binary_parser + +import ../math/[arithmetic, extension_fields], + ../math/io/[io_bigints, io_fields, io_ec, io_extfields], + ../platforms/abstractions, + ../named/[algebras, properties_fields, properties_curves], + ../math/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_jacobian, ec_scalar_mul, ec_multi_scalar_mul, ec_scalar_mul_vartime], + ../named/zoo_generators + +import ../math/polynomials/[fft_fields, fft_lut] + +from std / math import log2 + +import ./groth16_utils + +# Export so users can parse files +export r1cs_circom_parser, zkey_binary_parser, wtns_binary_parser +export groth16_utils +export arithmetic, extension_fields, abstractions, + io_bigints, io_fields, io_ec, io_extfields, + ec_shortweierstrass_affine, ec_shortweierstrass_jacobian + +type + Groth16Prover*[Name: static Algebra] = object + ## XXX: In the future the below should be typed objects that are already unmarshalled! + zkey*: Zkey[Name] + wtns*: Wtns[Name] + r1cs*: R1CS + # secret random values `r`, `s` for the proof + ## XXX: These won't remain public of course + r*: Fr[Name] + s*: Fr[Name] + +proc init*[Name: static Algebra](G: typedesc[Groth16Prover[Name]], zkey: Zkey[Name], wtns: Wtns[Name], r1cs: R1CS): Groth16Prover[Name] = + result = Groth16Prover[Name]( + zkey: zkey, + wtns: wtns, + r1cs: r1cs, + r: randomFieldElement(Fr[Name]), ## XXX: do we want to do this in `init`? + s: randomFieldElement(Fr[Name]) + ) + +proc calcAp[Name: static Algebra](ctx: Groth16Prover[Name], wt: seq[Fr[Name]]): EC_ShortW_Jac[Fp[Name], G1] = + # A_p is defined as + # A_p = α_1 + (Σ_i [W]_i · A_i) + [r] · δ_1 + # A_p = alpha1 + sum(A[i] * witness[i] for i in range(zkey.g16h.nVars)) + r * delta1 + # where of course in principle `α_1` is `g_1^{α}` etc. + let g16h = ctx.zkey.g16h + + let alpha1 = g16h.alpha1 + let delta1 = g16h.delta1 + + echo "α1: ", alpha1.toHex() + echo "δ1: ", delta1.toHex() + + # Declare `A_p` for the result + var A_p: EC_ShortW_Jac[Fp[Name], G1] + + # Compute the terms independent of the witnesses + #A_p = alpha1.getJacobian + ctx.r * delta1 + #echo A_p.toHex() + + let As = ctx.zkey.A + doAssert As.len == wt.len + for i in 0 ..< As.len: + echo "i = ", i, " As[i] = ", As[i].toHex() + A_p += wt[i] * As[i] + + echo "g^A MSM via loop: ", A_p.toHex() + A_p += alpha1.getJacobian + ctx.r * delta1 + + + # Via MSM + var A_p_msm: EC_ShortW_Jac[Fp[Name], G1] + A_p_msm.multiScalarMul_vartime(wt, As) + echo "g^A MSM = ", A_p_msm.toHex() + A_p_msm += alpha1.getJacobian + ctx.r * delta1 + + doAssert (A_p == A_p_msm).bool + + result = A_p + +proc calcBp[Name: static Algebra](ctx: Groth16Prover[Name], wt: seq[Fr[Name]]): EC_ShortW_Jac[Fp2[Name], G2] = + # B_p = beta2 + sum(B2[i] * witness[i] for i in range(zkey.g16h.nVars)) + s * delta2 + # B_p = β_2 + (Σ_i [W]_i · B2_i) + [s] · δ_2 + # where of course in principle `β_1` is `g_1^{β}` etc. + let g16h = ctx.zkey.g16h + + let beta2 = g16h.beta2 + let delta2 = g16h.delta2 + + # Declare `B_p` for the result + var B_p: EC_ShortW_Jac[Fp2[Name], G2] + + # Compute the terms independent of the witnesses + B_p = beta2.getJacobian + ctx.s * delta2 + + let Bs = ctx.zkey.B2 + doAssert Bs.len == wt.len + # could compute via MSM + for i in 0 ..< Bs.len: + B_p += wt[i] * Bs[i] + + result = B_p + +proc calcB1[Name: static Algebra](ctx: Groth16Prover[Name], wt: seq[Fr[Name]]): EC_ShortW_Jac[Fp[Name], G1] = + let g16h = ctx.zkey.g16h + + let beta1 = g16h.beta1 + let delta1 = g16h.delta1 + result = beta1.getJacobian + ctx.s * delta1 + + # Get the B1 data + let Bs = ctx.zkey.B1 + doAssert Bs.len == wt.len + for i in 0 ..< Bs.len: + result += wt[i] * Bs[i] + +proc buildABC[Name: static Algebra](ctx: Groth16Prover[Name], wt: seq[Fr[Name]]): tuple[A, B, C: seq[Fr[Name]]] = + # Extract required data using accessors + let + coeffs = ctx.zkey.coeffs + g16h = ctx.zkey.g16h + domainSize = g16h.domainSize + nCoeff = coeffs.num + + # Initialize output sequences + var + outBuffA = newSeq[Fr[Name]](domainSize) + outBuffB = newSeq[Fr[Name]](domainSize) + outBuffC = newSeq[Fr[Name]](domainSize) + + template toPUA[Name](x: seq[Name]): untyped = cast[ptr UncheckedArray[Name]](addr x[0]) + + var outBuf = [toPUA outBuffA, toPUA outBuffB] + + # Build A and B polynomials + for i in 0 ..< nCoeff: + let + m = coeffs.cs[i].matrix + c = coeffs.cs[i].section + s = coeffs.cs[i].index + coef = coeffs.cs[i].value + assert s.int < wt.len + echo "Coef i = ", i, " = ", coef.toHex() + outBuf[m][c] = outBuf[m][c] + coef * wt[s] + + # Compute C polynomial + for i in 0 ..< domainSize: + ## XXX: Here this product yields numbers in SnarkJS I cannot reproduce + echo "OUTBUF A: ", outBuffA[i].toHex() + echo "OUTBUF B: ", outBuffB[i].toHex() + #outBuffC[i].prod(outBuffA[i], outBuffB[i]) + outBuffC[i] = outBuffA[i] * outBuffB[i] + echo "OUTBUF C: ", outBuffC[i].toHex() + + result = (outBuffA, outBuffB, outBuffC) + +proc transform[Name: static Algebra](args: seq[Fr[Name]], inc: Fr[Name]): seq[Fr[Name]] = + ## Applies (multiplies) increasing powers of `inc` to each element + ## of `args`, i.e. + ## + ## `{ a[0], a[1]·inc, a[2]·inc², a[3]·inc³, ... }`. + ## + ## In our case `inc` is usually a root of unity of the power given by + ## `log2( FFT order ) + 1`. + result = newSeq[Fr[Name]](args.len) + var cur = Fr[Name].fromUint(1.uint64) + for i in 0 ..< args.len: + result[i] = args[i] * cur + cur *= inc + +proc itf[Name: static Algebra](arg: seq[Fr[Name]]): seq[Fr[Name]] = + ## inverse FFT -> transform -> forward FFT + ## + ## Equivalent to SnarkJS (same for A and C): + ## ```js + ## const buffB = await Fr.ifft(buffB_T, "", "", logger, "IFFT_B"); + ## const buffBodd = await Fr.batchApplyKey(buffB, Fr.e(1), inc); + ## const buffBodd_T = await Fr.fft(buffBodd, "", "", logger, "FFT_B"); + ## ``` + let buffA = ifft_vartime(arg) + + let power = log2(arg.len.float).int # arg is power of 2 + let inc = scaleToRootOfUnity(Name)[power + 1] + let buffAodd = buffA.transform(inc) + + result = fft_vartime(buffAodd) + +proc calcCp[Name: static Algebra](ctx: Groth16Prover[Name], A_p, B1_p: EC_ShortW_Jac[Fp[Name], G1], wt: seq[Fr[Name]]): EC_ShortW_Jac[Fp[Name], G1] = + # # Compute C_p + # C_p = sum(C[i] * witness[i] for i in range(zkey.g16h.nVars)) + # C_p += A_p * s + r * B_p - r * s * delta1 + # C_p += sum(H[i] * (witness[i] * witness[j]) for i, j in r1cs.constraints) + + let abc = buildABC[Name](ctx, wt) + + let fftD = FFTDescriptor[Fr[Name]].init(abc[0].len * 2) + + let A = itf(abc[0]) + let B = itf(abc[1]) + let C = itf(abc[2]) + + for i in 0 ..< A.len: + echo "A after itf? ", A[i].toHex() + for i in 0 ..< B.len: + echo "B after itf? ", B[i].toHex() + for i in 0 ..< C.len: + echo "C after itf? ", C[i].toHex() + + # combine A, B, C again + var jabc = newSeq[Fr[Name]](A.len) + for i in 0 ..< jabc.len: + jabc[i] = A[i] * B[i] - C[i] + echo "JABC? ", jabc[i].toHex() + # Get the C data + let Cs = ctx.zkey.C + # Get private witnesses + let g16h = ctx.zkey.g16h + ## XXX: Why is `nPublic` `1` when `Cs.len` ends up as `4` and `nVars` is `6`? + echo "LEN ? ", Cs.len, " total witnesses? ", wt.len, " public? ", g16h.nPublic, " total? ", g16h.nVars + + var priv = newSeqOfCap[Fr[Name]](wt.len) + let nPub = g16h.nVars.int - Cs.len # g16h.nPublic.int + for i in nPub ..< g16h.nVars.int: + priv.add wt[i] + + doAssert Cs.len == priv.len, " Cs: " & $Cs.len & ", priv: " & $priv.len + var cw: EC_ShortW_Jac[Fp[Name], G1] + for i in 0 ..< Cs.len: + cw += priv[i] * Cs[i] + + let Hs = ctx.zkey.H + doAssert Hs.len == jabc.len + var resH: EC_ShortW_Jac[Fp[Name], G1] + for i in 0 ..< Hs.len: + resH += jabc[i] * Hs[i] + + let delta1 = g16h.delta1 + # Declare `C_p` for the result + var C_p: EC_ShortW_Jac[Fp[Name], G1] + C_p = ctx.s * A_p + ctx.r * B1_p - (ctx.r * ctx.s) * delta1 + cw + resH + result = C_p + +proc prove*[Name: static Algebra](ctx: Groth16Prover[Name]): tuple[A: EC_ShortW_Aff[Fp[Name], G1], + B: EC_ShortW_Aff[Fp2[Name], G2], + C: EC_ShortW_Aff[Fp[Name], G1]] = + #[ + XXX: fix up notation here! + r = random_scalar_field_element() + s = random_scalar_field_element() + + # Compute A_p + A_p = alpha1 + sum(A[i] * witness[i] for i in range(zkey.g16h.nVars)) + r * delta1 + + # Compute B_p + B_p = beta2 + sum(B2[i] * witness[i] for i in range(zkey.g16h.nVars)) + s * delta2 + + # Compute C_p + C_p = sum(C[i] * witness[i] for i in range(zkey.g16h.nVars)) + C_p += A_p * s + r * B_p - r * s * delta1 + C_p += sum(H[i] * (witness[i] * witness[j]) for i, j in r1cs.constraints) + + proof = (A_p, B_p, C_p) + ]# + + let wt = ctx.wtns.witnesses + echo "WITNESS DATA:" + for i, el in wt: + echo i, " = ", el.toHex() + + let A_p = ctx.calcAp(wt) + let B2_p = ctx.calcBp(wt) + let B1_p = ctx.calcB1(wt) + let C_p = ctx.calcCp(A_p, B1_p, wt) + + result = (A: A_p.getAffine(), B: B2_p.getAffine(), C: C_p.getAffine()) + +when isMainModule: + + const T = BN254_Snarks + + let wtns = parseWtnsFile("/home/basti/org/constantine/moonmath/circom/three_fac_js/witness.wtns") + .toWtns[:T]() + let zkey = parseZkeyFile("/home/basti/org/constantine/moonmath/snarkjs/three_fac/three_fac_final.zkey") + .toZkey[:T]() + let r1cs = parseR1csFile("/home/basti/org/constantine/moonmath/circom/three_fac.r1cs") + .toR1CS + + + let g16h = zkey.g16h + ## NOTE: We *expect* all these to be 0, because they are the respective moduli for + ## `Fp` and `Fr`! + ## XXX: move to a test case + echo "q = ", toFp[T](g16h.q, false).toDecimal() + echo "r = ", toFr[T](g16h.r, false).toDecimal() + echo "wtns r = ", toFr[T](wtns.header.r, false).toDecimal() + + var ctx = Groth16Prover[T].init(zkey, wtns, r1cs) + + ## Note: We will now calculate the proof using a fixed, non secret set of points + ## r, s (or r, t) ∈ 𝔽r in order to compare with a calculation of `snarkjs`. We + ## hacked in a print of the secret it randomly sampled. + # The 'secret' constants from + ## XXX: Move to a test case! + const rSJ = @[ + byte 143, 55, 118, 73, 42, 115, 60, 77, + 95, 209, 41, 144, 250, 137, 138, 71, + 176, 242, 186, 232, 179, 30, 88, 255, + 198, 161, 182, 150, 220, 149, 33, 19 + ] + const sSJ = @[ + byte 213, 105, 105, 27, 129, 249, 139, 158, + 221, 68, 37, 163, 59, 71, 19, 108, + 60, 153, 183, 156, 25, 148, 37, 9, + 85, 205, 250, 246, 132, 142, 244, 36 + ] + + # construct the random element `r` from snarkjs "secret" r + let r = toFr[BN254_Snarks](rSJ) + # and `s` + let s = toFr[BN254_Snarks](sSJ) + + ctx.r = r + ctx.s = s + + let (A_p, B2_p, C_p) = ctx.prove() + + echo "\n==============================\n" + echo "A_p#16 = ", A_p.toHex() + echo "A_p#10 = ", A_p.toDecimal() + echo "------------------------------" + echo "B_p#16 = ", B2_p.toHex() + echo "B_p#10 = ", B2_p.toDecimal() + echo "------------------------------" + echo "C_p#16 = ", C_p.toHex() + echo "C_p#10 = ", C_p.toDecimal() + + ## SnarkJS yields: + ## + ## `snarkjs groth16 prove three_fac_final.zkey ../../circom/three_fac_js/witness.wtns proof.json public.json` + ## + #[ +{ + "pi_a": [ + "5525629793372463776337933283524928112323589665400780041477380790923758613749", + "21229177076048503863699135039723099340209138028149442778064006577287317302601", + "1" + ], + "pi_b": [ + [ + "10113559933709853115219982658131344715329670532374721861173670433756614595086", + "748111067660143353202076805159132563350177510079329482395824347599610874338" + ], + [ + "14193926223452546125681093394065339196897041249946578591171606543100010486627", + "871256420758854731396810855688710623510558493821614150596755347032202324148" + ], + [ + "1", + "0" + ] + ], + "pi_c": [ + "18517653609733492682442099361591955563405567929398531111532682405176646276349", + "17315036348446251361273519572420522936369550153340386126725970444173389652255", + "1" + ], + "protocol": "groth16", + "curve": "bn128" +} + ]# diff --git a/examples/groth16_proof_example.nim b/examples/groth16_proof_example.nim new file mode 100644 index 00000000..ebded9a5 --- /dev/null +++ b/examples/groth16_proof_example.nim @@ -0,0 +1,43 @@ +import constantine/proof_systems/manual_groth16, + constantine/named/algebras + +const T = BN254_Snarks + +let wtns = parseWtnsFile("./groth16_files/three_fac_js/witness.wtns").toWtns[:T]() +let zkey = parseZkeyFile("./groth16_files/three_fac_final.zkey").toZkey[:T]() +let r1cs = parseR1csFile("./groth16_files/three_fac.r1cs").toR1CS() + +var ctx = Groth16Prover[T].init(zkey, wtns, r1cs) + +const rSJ = @[ + byte 143, 55, 118, 73, 42, 115, 60, 77, + 95, 209, 41, 144, 250, 137, 138, 71, + 176, 242, 186, 232, 179, 30, 88, 255, + 198, 161, 182, 150, 220, 149, 33, 19 +] +const sSJ = @[ + byte 213, 105, 105, 27, 129, 249, 139, 158, + 221, 68, 37, 163, 59, 71, 19, 108, + 60, 153, 183, 156, 25, 148, 37, 9, + 85, 205, 250, 246, 132, 142, 244, 36 +] + +# construct the random element `r` from snarkjs "secret" r +let r = toFr[BN254_Snarks](rSJ) +# and `s` +let s = toFr[BN254_Snarks](sSJ) + +ctx.r = r +ctx.s = s + +let (A_p, B2_p, C_p) = ctx.prove() + +echo "\n==============================\n" +echo "A_p#16 = ", A_p.toHex() +echo "A_p#10 = ", A_p.toDecimal() +echo "------------------------------" +echo "B_p#16 = ", B2_p.toHex() +echo "B_p#10 = ", B2_p.toDecimal() +echo "------------------------------" +echo "C_p#16 = ", C_p.toHex() +echo "C_p#10 = ", C_p.toDecimal() diff --git a/examples/groth16_prover.org b/examples/groth16_prover.org new file mode 100644 index 00000000..8549d54d --- /dev/null +++ b/examples/groth16_prover.org @@ -0,0 +1,398 @@ +* Groth16 prover / verifier example + +In this example we will go through the full construction of a Groth16 +prove and verification. The current Constantine Groth16 prover logic +depends on SnarkJS / Circom in order to produce an arithmetic circuit +of the statement to be proven, turn it into an R1CS, compute its +witnesses and set up the common reference string. Our logic uses the +SnarkJS produced ~.r1cs~, ~.zkey~ and ~.wtns~ files as inputs. + +** 3-factorization problem description + +We will use an example from the Moonmath manual, +https://github.com/LeastAuthority/moonmath-manual +namely the "3-factorization problem", starting from example 115 and +used in further examples building from it throughout the book. The +idea of the example is simply to build a SNARK, which proves the +knowledge of three factors of an element from $\mathcal{F}_{13}$, i.e. + +\[ +x_1·x_2·x_3 = x_4. +\] + +where $x_1, x_2, x_3$ are hidden witnesses and $x_4$ acts as a public +instance. In our explanation here we will skip over any manual +derivations of a possible R1CS, calculation of the QAP (Quadratic +Arithmetic Program) or algebraic circuits. Instead, we will define the +problem in Circom and have it generate the R1CS for us. + +Note that we will be light on details in some parts, those can be read +up on in the Moonmath manual. See the list of all related examples in +Moonmath in sec. [[#sec:3fac_examples]] if you wish to learn more about +different parts. + +** Circom definition + +The Circom code to express the problem can be written as follows +(example 137): +#+begin_src circom +template Multiplier() { + signal input a ; + signal input b ; + signal output c ; + c <== a*b ; +} +template three_fac () { + signal input x1 ; + signal input x2 ; + signal input x3 ; + signal output x4 ; + component mult1 = Multiplier() ; + component mult2 = Multiplier() ; + mult1.a <== x1 ; + mult1.b <== x2 ; + mult2.a <== mult1.c ; + mult2.b <== x3 ; + x4 <== mult2.c ; +} +component main = three_fac() ; +#+end_src + +If we save this as ~three_fac.circom~ we can use Circom to generate +the corresponding R1CS by: +#+begin_src sh :results code :exports both +cd groth16_files +circom three_fac.circom --r1cs --wasm --sym +#+end_src + +#+RESULTS: +#+begin_src sh +template instances: 2 +non-linear constraints: 2 +linear constraints: 0 +public inputs: 0 +private inputs: 3 +public outputs: 1 +wires: 6 +labels: 11 +Written successfully: ./three_fac.r1cs +Written successfully: ./three_fac.sym +Written successfully: ./three_fac_js/three_fac.wasm +Everything went okay +#+end_src + +This produces 3 output files, a ~.r1cs~ file and a ~.sym~ +file. Importantly, it also generates a new directory (in our case +called ~three_fac_js~, which contains a ~.wasm~ WebAssembly binary, as +well as 2 JavaScript source files. ~generate_witness.js~ will later be +used to generate a ~.wtns~ file from some inputs used in the proof and +~witness_calculator.js~ containing most of the actual logic. + +Using SnarkJS we can print information about the R1CS file: +#+begin_src sh :results code :exports both +snarkjs r1cs info groth16_files/three_fac.r1cs +#+end_src + +#+RESULTS: +#+begin_src sh +[INFO] snarkJS: Curve: bn-128 +[INFO] snarkJS: # of Wires: 6 +[INFO] snarkJS: # of Constraints: 2 +[INFO] snarkJS: # of Private Inputs: 3 +[INFO] snarkJS: # of Public Inputs: 0 +[INFO] snarkJS: # of Labels: 11 +[INFO] snarkJS: # of Outputs: 1 +#+end_src + +where we see the number of produced wires the corresponding algebraic +circuit has, the number of constraints in the R1CS as well as the +private and public inputs. + +** Common reference string / ~.zkey~ file construction + +From here we can use SnarkJS to (please read example 150 for more +details on these commands): +1. First perform a "powers of tau" ceremony, where optionally multiple + parties can contribute randomness to the common reference string of + the τ parameter. +2. Using the powers of tau, set up a Groth16 prove using our R1CS. +3. Again, multiple people can contribute randomness to the parameters + α, β, γ, δ that are part of the Groth16 common reference string. + +For part 1, we perform steps like the following: +#+begin_src sh +snarkjs powersoftau new bn128 4 pot4_0000.ptau -v +snarkjs powersoftau contribute pot4_0000.ptau pot4_0001.ptau -name="1st_cont" -v +snarkjs powersoftau verify pot4_0001.ptau +snarkjs powersoftau beacon pot4_0001.ptau pot4_beacon.ptau +snarkjs powersoftau prepare phase2 pot4_beacon.ptau pot4_final.ptau -v +snarkjs powersoftau verify pot4_final.ptau +#+end_src + +To set up the Groth16 proof, we run: +#+begin_src sh +snarkjs groth16 setup three_fac.r1cs pot4_final.ptau three_fac0000.zkey +#+end_src + +And finally we add randomness to the ~.zkey~ files, finalize, +verify it and export the verification key as JSON: +#+begin_src sh +snarkjs zkey contribute three_fac0000.zkey three_fac0001.zkey -name="1st Contributor Name" -v +snarkjs zkey verify three_fac.r1cs pot4_final.ptau three_fac0001.zkey +snarkjs zkey beacon three_fac0001.zkey three_fac_final.zkey 010203040506070809 10 -n="Final Beacon phase2" +snarkjs zkey verify three_fac.r1cs pot4_final.ptau three_fac_final.zkey +snarkjs zkey export verificationkey three_fac_final.zkey verification_key.json +#+end_src + +** Witness ~.wtns~ file construction + +With this we have 2 of the 3 required inputs to compute a Groth16 +proof in Constantine, the ~.r1cs~ file and the ~.zkey~ file. Now we +still need a Witness ~.wtns~ file. As alluded to earlier, +~generate_witness.js~ is used to generate that file. However, to +generate the file, we first and foremost need to define the private +witnesses that should be contained in it. In a normal context this +would be the values the prover intends to proof it as knowledge of. In +our case here, we will simply use 3 'randomly' chosen integers of the +field BN254 corresponding to the variables $x_1, x_2, x_3$ and store them in a JSON file: + +#+begin_src json +{ +"x1": 266454826700390499788624045644422204835838308568801104096964341478260924069, +"x2": 17022543691211744762566166588937408281011290768059146405469762658080007243141, +"x3": 2169708499392809782734482748125393322939898426476751716891099115492318742078 +} +#+end_src + +Stored as ~input.json~, we then run the JS code using ~node~: +#+begin_src sh +cd groth16_files/three_fac_js +node generate_witness.js three_fac.wasm input.json witness.wtns +#+end_src + +#+RESULTS: + +** SnarkJS as a Groth16 prover + +Now we can first use SnarkJS to run the Groth16 prover on our files: + +#+begin_src sh +cd groth16_files +snarkjs groth16 prove three_fac_final.zkey three_fac_js/witness.wtns proof.json public.json +#+end_src +which produces ~proof.json~: +#+begin_src json +{ + "pi_a": [ + "5525629793372463776337933283524928112323589665400780041477380790923758613749", + "21229177076048503863699135039723099340209138028149442778064006577287317302601", + "1" + ], + "pi_b": [ + [ + "10113559933709853115219982658131344715329670532374721861173670433756614595086", + "748111067660143353202076805159132563350177510079329482395824347599610874338" + ], + [ + "14193926223452546125681093394065339196897041249946578591171606543100010486627", + "871256420758854731396810855688710623510558493821614150596755347032202324148" + ], + [ + "1", + "0" + ] + ], + "pi_c": [ + "18517653609733492682442099361591955563405567929398531111532682405176646276349", + "17315036348446251361273519572420522936369550153340386126725970444173389652255", + "1" + ], + "protocol": "groth16", + "curve": "bn128" +} +#+end_src +and ~public.json~: +#+begin_src json +[ + "9539182767316925183286892436718181010853851464478187124330950611358943415507" +] +#+end_src + +** Constantine as a Groth16 prover + +The Groth16 prover of Constantine lives in +[[file:../constantine/proof_systems/manual_groth16.nim]]. If we use the +previously produced ~.r1cs~, ~.zkey~ and ~.wtns~ files to produce a +Groth16 proof with it, the output numbers will differ from the SnarkJS +results necessarily. This is because as part of the proving phase, the +prover chooses two additional random field elements ~r~, ~s~ (or +sometimes called ~r~ and ~t~). However, in order to make it +deterministic, we have extracted the random elements from SnarkJS and +will reuse them in Constantine now. + +Let's start by preparing all of our imports: +#+begin_src nim :tangle groth16_proof_example.nim +import constantine/proof_systems/manual_groth16, + constantine/named/algebras +#+end_src + +The name of the ~alt_bn128~ / ~BN254~ curve in Constantine is +~BN254_Snarks~. For convenience we will assign it to a shorter constant: + +#+begin_src nim :tangle groth16_proof_example.nim +const T = BN254_Snarks +#+end_src + +Next, we parse the three binary files. The first ~parseFooFile~ +command returns a "raw" binary data file, which contains all field +elements still as ~seq[byte]~ data. We can convert it to a more +"typed" expression using ~toFoo~ and handing the target curve: + +#+begin_src nim :tangle groth16_proof_example.nim +let wtns = parseWtnsFile("./groth16_files/three_fac_js/witness.wtns").toWtns[:T]() +let zkey = parseZkeyFile("./groth16_files/three_fac_final.zkey").toZkey[:T]() +let r1cs = parseR1csFile("./groth16_files/three_fac.r1cs").toR1CS() +#+end_src + +With these we can construct our Groth16 prover context object: + +#+begin_src nim :tangle groth16_proof_example.nim +var ctx = Groth16Prover[T].init(zkey, wtns, r1cs) +#+end_src + +Now we define the two constants ~r~ and ~s~ based on the bytes +extracted from SnarkJS and unmarshal them into field elements of the +G1 subgroup of BN254: +#+begin_src nim :tangle groth16_proof_example.nim +const rSJ = @[ + byte 143, 55, 118, 73, 42, 115, 60, 77, + 95, 209, 41, 144, 250, 137, 138, 71, + 176, 242, 186, 232, 179, 30, 88, 255, + 198, 161, 182, 150, 220, 149, 33, 19 +] +const sSJ = @[ + byte 213, 105, 105, 27, 129, 249, 139, 158, + 221, 68, 37, 163, 59, 71, 19, 108, + 60, 153, 183, 156, 25, 148, 37, 9, + 85, 205, 250, 246, 132, 142, 244, 36 +] + +# construct the random element `r` from snarkjs "secret" r +let r = toFr[BN254_Snarks](rSJ) +# and `s` +let s = toFr[BN254_Snarks](sSJ) +#+end_src + +Now all we need to do is overwrite the random field elements already +created. *IMPORTANT: IN A REAL USE CASE YOU WOULD NEVER DO THIS OF COURSE!* +#+begin_src nim :tangle groth16_proof_example.nim +ctx.r = r +ctx.s = s +#+end_src + +Let's run the proof and see if the output matches the SnarkJS output: +#+begin_src nim :tangle groth16_proof_example.nim :exports both +let (A_p, B2_p, C_p) = ctx.prove() + +echo "A_p#16 = ", A_p.toHex() +echo "A_p#10 = ", A_p.toDecimal() +echo "------------------------------" +echo "B_p#16 = ", B2_p.toHex() +echo "B_p#10 = ", B2_p.toDecimal() +echo "------------------------------" +echo "C_p#16 = ", C_p.toHex() +echo "C_p#10 = ", C_p.toDecimal() +#+end_src + +#+begin_src +A_p#16 = EC_ShortW_Jac[Fp[BN254_Snarks], G1]( + x: 0x0c37654828f4fa92099b6e6bc3ef1e233688e29775ad84a587e3e0a6b94734f5, + y: 0x2eef49d5d85978033554eeadf6a3af464fd201d8c6687fdb51fc7f1077622d49 +) +A_p#10 = EC_ShortW_Jac[Fp[BN254_Snarks], G1]( + x: 05525629793372463776337933283524928112323589665400780041477380790923758613749, + y: 21229177076048503863699135039723099340209138028149442778064006577287317302601 +) +------------------------------ +B_p#16 = EC_ShortW_Jac[Fp2[BN254_Snarks], G2]( + x: Fp2( + c0: 0x165c12731d58092618243a9a78339604e2c99771f27afb7f16083a1f5425920e, + c1: 0x01a76a75bc51d03cbf331de519dbe6b8b0d5115f4e3cbcc2c0833faba7cc89e2), + y: Fp2( + c0: 0x1f617a40811c35355866ab3987b0c87e39f84b749fe79489a2a8c1a33642db63, + c1: 0x01ed1d18bf3e70d11a0ec6c87da9c8296736fefa40f876ea6ae5d0df4520a8b4) +) +B_p#10 = EC_ShortW_Jac[Fp2[BN254_Snarks], G2]( + x: Fp2( + c0: 10113559933709853115219982658131344715329670532374721861173670433756614595086, + c1: 00748111067660143353202076805159132563350177510079329482395824347599610874338), + y: Fp2( + c0: 14193926223452546125681093394065339196897041249946578591171606543100010486627, + c1: 00871256420758854731396810855688710623510558493821614150596755347032202324148) +) +------------------------------ +C_p#16 = EC_ShortW_Jac[Fp[BN254_Snarks], G1]( + x: 0x1bb0c6fadbe2fe02b17d3da128f9a0aef119640c09f1ec52e17431115ddcedad, + y: 0x07439985d180eaa34951b45d430e02e5ba1bcc4d2b29dffd90171f1aa88c6a1e +) +C_p#10 = EC_ShortW_Jac[Fp[BN254_Snarks], G1]( + x: 12524785304069290509448413767699932740828656355192528146073107099016111123885, + y: 03285628268350284309020370879913195676683658549907819489051412512121524349470 +) +#+end_src + +As we can see when comparing the decimal outputs, the numbers match. + +*XXX*: RIGHT NOW C DOES NOT MATCH! + +This concludes this very quick introduction on the requirements and +how to compute a Groth16 proof with Constantine. + +** List of all 3-factorization examples in Moonmath +:PROPERTIES: +:CUSTOM_ID: sec:3fac_examples +:END: + +Just for reference, the full list of examples about the +3-factorization problem are (as of Moonmath of +<2024-08-14 Wed 18:44>) listed below. It is a useful overview to +understand the relevant steps. + +Examples: +- 115 (Language, L_3,fac) +- 118 (Decision function, R_3) + I_1 = W_1 · W_2 · W_3 +- 120 (R1CS), flatten equation: + W_1 · W_2 = W_4 + W_4 · W_3 = I_1 +- 122 (Explicit R1CS satisfying example) +- 124 (Algebraic circuit construction) + f_3,fac(x_1,x_2,x_3) = MUL(MUL(x_1, x_2), x_3) +- 126 (Example assignment to algebraic circuit) +- 128 (Example / note on circuit execution to find input witnesses & + compute W_4 by execution) +- 129 (Transform algebraic circuit back into R1CS following rules) +- 131 (QAP from R1CS): + QAP(R_3.fac_zk ) = {x2 + x + 9, {0, 0, 6x + 10, 0, 0, 7x + 4}, {0, 0, 0, 6x + 10, 7x + 4, 0}, {0, 7x + 4, 0, 0, 0, 6x + 10}} +- 137 (Circom definition) +- 139 (PAPER definition -> algebraic circuit) +- 147 (Groth16 parameters for 3-fac over BLS6-6): + Groth_16 − Param(R3. f ac_zk ) = (13, G1 [13], G2 [13], e(·, ·), (13, 15), (7v2 , 16v3 )) +- 148 (Groth16 via SnarkJS): + Statement to use BN254 / alt_bn128 in SnarkJS. +- 149 (CRS - Transformation of QAP into Common Reference String): + - choice of α, β, γ, δ, τ + #+begin_quote + | (27, 34), (26, 34), (38, 15), (13, 15), (33, 34) , O, (33, 9) | + CRS_G1 = | (33, 34), (26, 34), (38, 28), (27, 9) , (26, 34) | + + CRS_G2 = | (16v , 28v ), (37v , 27v ), (42v , 16v ), 7v , 16v ), (10v, 28v) | + #+end_quote +- 150 (SnarkJS setup for Groth16) +- 151 (Manual Groth16 prover phase over BLS6-6) +- 152 (Groth16 prover phase using SnarkJS) +- 153 (Manual Groth16 verification phase over BLS6-6) +- 154 (Groth16 verification phase in SnarkJS) +- 155 (Manual proof simulation trapdoor) +- 156 (Mention for SnarkJS simulation not implemented) + diff --git a/tests/math_polynomials/t_finite_field_fft.nim b/tests/math_polynomials/t_finite_field_fft.nim new file mode 100644 index 00000000..58a10e00 --- /dev/null +++ b/tests/math_polynomials/t_finite_field_fft.nim @@ -0,0 +1,100 @@ +import + unittest, + constantine/math/arithmetic, + constantine/math/io/[io_fields, io_bigints], + constantine/named/[algebras, properties_fields, properties_curves], + constantine/math/elliptic/[ec_shortweierstrass_affine, ec_shortweierstrass_jacobian, ec_scalar_mul, ec_multi_scalar_mul, ec_scalar_mul_vartime], + constantine/math/polynomials/[fft_fields, fft] + +## NOTE: This test must be compiled with `-d:CTT_TEST_CURVES` + +suite "FFT Tests (finite fields)": + test "FFT over Finite Field Fp[Fake13]": + + type F = Fp[Fake13] + + # Fr[Fake13] is GF(13) + let order = 4 # We'll use a small order for the test + # `5` is a generator for the order `4`, because `5^4 mod 13 = 1`: + # `5^0 mod 13 = 1 ` + # `5^1 mod 13 = 5 ` + # `5^2 mod 13 = 12` + # `5^3 mod 13 = 8 ` + # `5^4 mod 13 = 1 ` + let Gen = F.fromUInt(5'u64) + var fftDesc = FFTDescriptor[F].init(order, Gen) + defer: fftDesc.delete() + + # Input values + # Describes Polynomial: + # P(x) = a₀ + a₁·x + a₂·x² + a₃·x³ + # P(x) = 1 + 2·x + 3·x² + 4·x³ + var input = @[F.fromInt(1), F.fromInt(2), F.fromInt(3), F.fromInt(4)] + var output = newSeq[F](order) + + # Perform forward FFT + # The forward FFT returns the evaluation of polynomial P(x) at the + # roots of unity based on `Gen` + let status = fftDesc.fft_vartime(output, input) + check (status == FFTS_Success).bool + + # Expected output (calculated manually or with a known-good implementation) + let expected = @[ + # let `g(i) = 5^i mod 13` # where `5` is the generator, i.e. `5^4 mod 13 = 1` + F.fromUInt(10'u64), # P(g(0)) = (1 + 2·1 + 3·1² + 4·1³ ) mod 13 = 10 + F.fromUInt(1'u64), # P(g(1)) = (1 + 2·5 + 3·5² + 4·5³ ) mod 13 = 1 + F.fromUInt(11'u64), # P(g(2)) = (1 + 2·12 + 3·12² + 4·12³) mod 13 = 11 + F.fromUInt(8'u64) # P(g(3)) = (1 + 2·8 + 3·8² + 4·8³ ) mod 13 = 8 + ] + + for i in 0 ..< output.len: + check (output[i] == expected[i]).bool + + # Perform inverse FFT + var inverse_output = newSeq[F](order) + let inverse_status = fftDesc.ifft_vartime(inverse_output, output) + check (inverse_status == FFTS_Success).bool + + # Check if we get back the original input + for i in 0 ..< order: + check (inverse_output[i] == input[i]).bool + + test "FFT over finite field Fr[BN254_Snarks]": + type F = Fr[BN254_Snarks] + + const order = 4 + var fftDesc = FFTDescriptor[F].init(order) + defer: fftDesc.delete() + + # Input values + # Describes Polynomial: + # P(x) = a₀ + a₁·x + a₂·x² + a₃·x³ + # P(x) = 1 + 2·x + 3·x² + 4·x³ + var input = @[F.fromInt(1), F.fromInt(2), F.fromInt(3), F.fromInt(4)] + var output = newSeq[F](order) + + # Perform forward FFT + # The forward FFT returns the evaluation of polynomial P(x) at the + # roots of unity based on `Gen` + let status = fftDesc.fft_vartime(output, input) + check (status == FFTS_Success).bool + + template toFr(s: string): untyped = F.fromBig(matchingOrderBigInt(BN254_Snarks).fromHex(s, bigEndian)) + let expected = @[ + toFr "0x000000000000000000000000000000000000000000000000000000000000000a", + toFr "0x00000000000000016789af3a83522eb1969386a2f88c094a419fe246c11f9394", + toFr "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff", + toFr "0x30644e72e131a02850c6967bfe2f29ab91a061a5812d67470242134d2ee06c69" + ] + + for i in 0 ..< output.len: + check (output[i] == expected[i]).bool + + # Perform inverse FFT + var inverse_output = newSeq[F](order) + let inverse_status = fftDesc.ifft_vartime(inverse_output, output) + check (inverse_status == FFTS_Success).bool + + # Check if we get back the original input + for i in 0 ..< order: + check (inverse_output[i] == input[i]).bool diff --git a/tests/proof_systems/groth16_files/t_zkey.json b/tests/proof_systems/groth16_files/t_zkey.json new file mode 100644 index 00000000..9c170f3f --- /dev/null +++ b/tests/proof_systems/groth16_files/t_zkey.json @@ -0,0 +1,1257 @@ +{ + "version": 1, + "header": { + "proverType": 1 + }, + "g16h": { + "n8q": 32, + "q": [ + 71, + 253, + 124, + 216, + 22, + 140, + 32, + 60, + 141, + 202, + 113, + 104, + 145, + 106, + 129, + 151, + 93, + 88, + 129, + 129, + 182, + 69, + 80, + 184, + 41, + 160, + 49, + 225, + 114, + 78, + 100, + 48 + ], + "n8r": 32, + "r": [ + 1, + 0, + 0, + 240, + 147, + 245, + 225, + 67, + 145, + 112, + 185, + 121, + 72, + 232, + 51, + 40, + 93, + 88, + 129, + 129, + 182, + 69, + 80, + 184, + 41, + 160, + 49, + 225, + 114, + 78, + 100, + 48 + ], + "nVars": 6, + "nPublic": 1, + "domainSize": 4, + "alpha1": { + "x": { + "mres": { + "limbs": [ + 13925964088572246216, + 13742589492058863780, + 9476940806093235905, + 3362611766509840827 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 17481104537668069806, + 13950129170139171565, + 9579658453758972571, + 607686539175759096 + ] + } + } + }, + "beta1": { + "x": { + "mres": { + "limbs": [ + 14764516605348937795, + 9591755601364069681, + 14464396726792719788, + 2282336362458004519 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 16240601560751194657, + 12175426767587956416, + 16774016708564877740, + 2512680640774327453 + ] + } + } + }, + "beta2": { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 3270322030233528447, + 9016609499626425979, + 18106800030602388967, + 116589489467730433 + ] + } + }, + { + "mres": { + "limbs": [ + 7147804817778042924, + 13926681233934608099, + 2593579544344806394, + 125737815924096011 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 5042516886431319744, + 7330878682177452837, + 17764006822939567364, + 1100800799847715895 + ] + } + }, + { + "mres": { + "limbs": [ + 5054136446061530389, + 8582722777749975593, + 9606551017233391694, + 2051338976594960443 + ] + } + } + ] + } + }, + "gamma2": { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 10269251484633538598, + 15918845024527909234, + 18138289588161026783, + 1825990028691918907 + ] + } + }, + { + "mres": { + "limbs": [ + 12660871435976991040, + 6936631231174072516, + 714191060563144582, + 1512910971262892907 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 7034053747528165878, + 18338607757778656120, + 18419188534790028798, + 2953656481336934918 + ] + } + }, + { + "mres": { + "limbs": [ + 7208393106848765678, + 15877432936589245627, + 6195041853444001910, + 983087530859390082 + ] + } + } + ] + } + }, + "delta1": { + "x": { + "mres": { + "limbs": [ + 10002989469976289783, + 16477875574839359909, + 7857093606726690942, + 253235042624443816 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 12706341286799195498, + 18332677950483261025, + 4056129645201674314, + 2709618686330962747 + ] + } + } + }, + "delta2": { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 10780097336817227228, + 15308435357483846446, + 7827071303385416315, + 2976043425012853800 + ] + } + }, + { + "mres": { + "limbs": [ + 9926601593583141772, + 10478646357770173204, + 1886462824504761957, + 1113151453811462486 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 5681511216328213686, + 4772598523326543119, + 10918010982668516951, + 1146120876580835765 + ] + } + }, + { + "mres": { + "limbs": [ + 17633534535112326590, + 9010073426057181659, + 17827518104323058273, + 84099701317197459 + ] + } + } + ] + } + } + }, + "ic": [ + { + "x": { + "mres": { + "limbs": [ + 8498507250225182879, + 11414283414803093306, + 86313205369982859, + 1327344531303714366 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 17778271140205288861, + 14410647442925908475, + 2939680923049302282, + 3013237186062503147 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 144182305798471186, + 7063012362356384839, + 17960025875269118135, + 3398549968082205082 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 4256052102704685329, + 9656698010830347098, + 329749830419440050, + 3465742425856225961 + ] + } + } + } + ], + "coeffs": { + "num": 6, + "cs": [ + { + "matrix": 0, + "section": 0, + "index": 2, + "value": { + "mres": { + "limbs": [ + 2893861064349225562, + 15291318972085769902, + 3172436813243865047, + 3336461168475855748 + ] + } + } + }, + { + "matrix": 1, + "section": 0, + "index": 3, + "value": { + "mres": { + "limbs": [ + 1997599621687373223, + 6052339484930628067, + 10108755138030829701, + 150537098327114917 + ] + } + } + }, + { + "matrix": 0, + "section": 1, + "index": 5, + "value": { + "mres": { + "limbs": [ + 2893861064349225562, + 15291318972085769902, + 3172436813243865047, + 3336461168475855748 + ] + } + } + }, + { + "matrix": 1, + "section": 1, + "index": 4, + "value": { + "mres": { + "limbs": [ + 1997599621687373223, + 6052339484930628067, + 10108755138030829701, + 150537098327114917 + ] + } + } + }, + { + "matrix": 0, + "section": 2, + "index": 0, + "value": { + "mres": { + "limbs": [ + 1997599621687373223, + 6052339484930628067, + 10108755138030829701, + 150537098327114917 + ] + } + } + }, + { + "matrix": 0, + "section": 3, + "index": 1, + "value": { + "mres": { + "limbs": [ + 1997599621687373223, + 6052339484930628067, + 10108755138030829701, + 150537098327114917 + ] + } + } + } + ] + }, + "A": [ + { + "x": { + "mres": { + "limbs": [ + 8411991738497574394, + 4812842035064521053, + 3855093717410988801, + 1287704766722423230 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 17534872512909326999, + 13770361305464453420, + 15269905503833711964, + 1931340478843024912 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 3555393664030893055, + 2568631973890133638, + 6059213695816936250, + 2841044097474115818 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 18080596052961610625, + 640201565314638412, + 4330941541260809362, + 1309194338023650263 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 8640835477903764226, + 7170250978333027317, + 1669000963016125122, + 2642131684488230576 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 13120564344670812193, + 4283057503361257490, + 855506031648810442, + 1790915358084121621 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 8461714475464199279, + 1628129506834983000, + 14604041186392693521, + 1514287081098762343 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 14933225774167841338, + 4227186455304672631, + 9056987616518665933, + 2530337990259817207 + ] + } + } + } + ], + "B1": [ + { + "x": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 8640835477903764226, + 7170250978333027317, + 1669000963016125122, + 2642131684488230576 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 9658796600318395686, + 6634066641116625530, + 12425685919625884307, + 1696082908718849044 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 8461714475464199279, + 1628129506834983000, + 14604041186392693521, + 1514287081098762343 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 7846135170821366541, + 6689937689173210389, + 4224204334756028816, + 956660276543153458 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + } + ], + "B2": [ + { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + ] + } + }, + { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + ] + } + }, + { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + ] + } + }, + { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 2221700885755314967, + 3425978242931448249, + 8171574967084206717, + 1738847330961988603 + ] + } + }, + { + "mres": { + "limbs": [ + 11502417555104571261, + 924347463091524855, + 12612908236985029529, + 3139890662700272006 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 10794151037613322987, + 15417924285964247925, + 16373442397629755450, + 1792105613104675116 + ] + } + }, + { + "mres": { + "limbs": [ + 8676715419424595653, + 1880676684100826781, + 3263342836038281103, + 2961060771967373583 + ] + } + } + ] + } + }, + { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 5073919794795742742, + 6361026632926853030, + 7445452725032007412, + 1561155376839150292 + ] + } + }, + { + "mres": { + "limbs": [ + 1139168740997190592, + 5143868095894396947, + 6754363017172502520, + 1602846249401879083 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 11802952941633001010, + 5481908123034181950, + 1050247317803748989, + 3041787918381055263 + ] + } + }, + { + "mres": { + "limbs": [ + 6675958238158383638, + 6939027800843457296, + 18275187947023293659, + 1298244372613128771 + ] + } + } + ] + } + }, + { + "x": { + "coords": [ + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + ] + }, + "y": { + "coords": [ + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + }, + { + "mres": { + "limbs": [ + 0, + 0, + 0, + 0 + ] + } + } + ] + } + } + ], + "C": [ + { + "x": { + "mres": { + "limbs": [ + 4880452224974455006, + 12483256698092434454, + 16183853057546404034, + 1466982721751600570 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 519123567512150905, + 13648186404792563826, + 8575498277460323763, + 2929308687446985433 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 11776177061220625087, + 11144007132154920161, + 14832512964782604742, + 2743194658126172026 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 11868980184996092981, + 7770272586030593725, + 5901181197870207239, + 3301669830076875222 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 8072160774775660367, + 16049166721152470622, + 1427861074745032064, + 1743675877608078668 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 15537118409330861305, + 12808035235080574992, + 5774621647102064814, + 311370515709796437 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 9972454936968116998, + 9396835638540614424, + 15571406619042388759, + 820332184817920381 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 9145492366232960641, + 7659465626739688343, + 12362538444863847411, + 1133623199045677193 + ] + } + } + } + ], + "H": [ + { + "x": { + "mres": { + "limbs": [ + 1691797751783121018, + 9111041891540600823, + 5200379746596341433, + 482333683196747972 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 14064129055996645321, + 3313242041195738061, + 12698053057629697561, + 2257189183036650753 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 9665656393038420845, + 18406351292205921899, + 4173835129935986056, + 75303207305036905 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 13118481880823267632, + 10957880156592055544, + 1616085079919492922, + 1499623060997469833 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 5099446712988230797, + 4186623324024048365, + 14379833201355983657, + 1639864572092178152 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 10602679100874508323, + 18073232837032653440, + 1405969893897065789, + 990083763231913698 + ] + } + } + }, + { + "x": { + "mres": { + "limbs": [ + 12136017249349928552, + 12943707575502204642, + 6556358596293514448, + 3406940363828159493 + ] + } + }, + "y": { + "mres": { + "limbs": [ + 15023088725600681953, + 10270132635661047970, + 12703004539836373783, + 251945972054357677 + ] + } + } + } + ], + "contr": { + "hash": [ + 208, + 4, + 157, + 173, + 108, + 62, + 105, + 70, + 107, + 29, + 159, + 18, + 46, + 119, + 79, + 103, + 136, + 108, + 45, + 191, + 16, + 216, + 136, + 176, + 112, + 116, + 177, + 201, + 149, + 135, + 77, + 148, + 149, + 104, + 199, + 58, + 20, + 127, + 255, + 144, + 83, + 193, + 133, + 0, + 233, + 97, + 1, + 132, + 211, + 186, + 141, + 158, + 96, + 60, + 14, + 187, + 185, + 120, + 21, + 223, + 228, + 203, + 39, + 57 + ], + "num": 2 + } +} \ No newline at end of file diff --git a/tests/proof_systems/groth16_files/t_zkey_bin.json b/tests/proof_systems/groth16_files/t_zkey_bin.json new file mode 100644 index 00000000..32c9f431 --- /dev/null +++ b/tests/proof_systems/groth16_files/t_zkey_bin.json @@ -0,0 +1,3282 @@ +{ + "magic": [ + "z", + "k", + "e", + "y" + ], + "version": 1, + "numberSections": 10, + "sections": [ + { + "size": 4, + "sectionType": "kHeader", + "header": { + "proverType": 1 + } + }, + { + "size": 660, + "sectionType": "kGroth16Header", + "g16h": { + "n8q": 32, + "q": [ + 71, + 253, + 124, + 216, + 22, + 140, + 32, + 60, + 141, + 202, + 113, + 104, + 145, + 106, + 129, + 151, + 93, + 88, + 129, + 129, + 182, + 69, + 80, + 184, + 41, + 160, + 49, + 225, + 114, + 78, + 100, + 48 + ], + "n8r": 32, + "r": [ + 1, + 0, + 0, + 240, + 147, + 245, + 225, + 67, + 145, + 112, + 185, + 121, + 72, + 232, + 51, + 40, + 93, + 88, + 129, + 129, + 182, + 69, + 80, + 184, + 41, + 160, + 49, + 225, + 114, + 78, + 100, + 48 + ], + "nVars": 6, + "nPublic": 1, + "domainSize": 4, + "alpha1": [ + 200, + 248, + 49, + 62, + 142, + 246, + 66, + 193, + 164, + 56, + 47, + 232, + 84, + 124, + 183, + 190, + 193, + 102, + 151, + 192, + 115, + 219, + 132, + 131, + 187, + 41, + 94, + 244, + 149, + 101, + 170, + 46, + 174, + 169, + 44, + 73, + 245, + 91, + 153, + 242, + 237, + 202, + 245, + 188, + 145, + 208, + 152, + 193, + 155, + 18, + 223, + 197, + 155, + 200, + 241, + 132, + 248, + 140, + 91, + 14, + 176, + 239, + 110, + 8 + ], + "beta1": [ + 67, + 0, + 46, + 118, + 160, + 25, + 230, + 204, + 49, + 45, + 171, + 111, + 230, + 194, + 28, + 133, + 172, + 141, + 205, + 246, + 47, + 220, + 187, + 200, + 39, + 60, + 68, + 78, + 212, + 124, + 172, + 31, + 33, + 194, + 58, + 183, + 18, + 53, + 98, + 225, + 192, + 214, + 220, + 14, + 35, + 206, + 247, + 168, + 172, + 1, + 122, + 234, + 82, + 71, + 201, + 232, + 157, + 204, + 90, + 227, + 186, + 213, + 222, + 34 + ], + "beta2": [ + 127, + 40, + 52, + 65, + 143, + 132, + 98, + 45, + 123, + 78, + 165, + 95, + 145, + 110, + 33, + 125, + 231, + 101, + 22, + 163, + 177, + 70, + 72, + 251, + 1, + 170, + 68, + 224, + 133, + 53, + 158, + 1, + 44, + 104, + 124, + 111, + 156, + 26, + 50, + 99, + 227, + 102, + 197, + 168, + 203, + 130, + 69, + 193, + 250, + 67, + 100, + 173, + 218, + 62, + 254, + 35, + 11, + 60, + 253, + 156, + 224, + 181, + 190, + 1, + 192, + 198, + 165, + 77, + 100, + 158, + 250, + 69, + 37, + 187, + 171, + 30, + 82, + 131, + 188, + 101, + 4, + 165, + 237, + 116, + 22, + 110, + 134, + 246, + 55, + 148, + 99, + 195, + 126, + 212, + 70, + 15, + 21, + 77, + 218, + 217, + 81, + 230, + 35, + 70, + 41, + 242, + 112, + 165, + 228, + 244, + 27, + 119, + 78, + 32, + 221, + 162, + 64, + 83, + 81, + 133, + 59, + 32, + 219, + 69, + 238, + 209, + 119, + 28 + ], + "gamma2": [ + 38, + 32, + 188, + 2, + 209, + 181, + 131, + 142, + 114, + 1, + 123, + 73, + 53, + 25, + 235, + 220, + 223, + 26, + 129, + 151, + 71, + 38, + 184, + 251, + 59, + 80, + 150, + 175, + 65, + 56, + 87, + 25, + 64, + 97, + 76, + 168, + 125, + 115, + 180, + 175, + 196, + 216, + 2, + 88, + 90, + 221, + 67, + 96, + 134, + 47, + 160, + 82, + 252, + 80, + 233, + 9, + 107, + 123, + 234, + 58, + 131, + 240, + 254, + 20, + 246, + 233, + 107, + 136, + 157, + 250, + 157, + 97, + 120, + 155, + 158, + 245, + 151, + 210, + 127, + 254, + 254, + 125, + 27, + 35, + 98, + 26, + 158, + 255, + 6, + 66, + 158, + 174, + 235, + 126, + 253, + 40, + 238, + 86, + 24, + 199, + 86, + 91, + 9, + 100, + 187, + 60, + 125, + 50, + 34, + 249, + 87, + 220, + 118, + 16, + 53, + 51, + 190, + 53, + 249, + 85, + 130, + 100, + 253, + 147, + 230, + 160, + 164, + 13 + ], + "delta1": [ + 247, + 225, + 232, + 193, + 236, + 193, + 209, + 138, + 165, + 185, + 98, + 35, + 136, + 44, + 173, + 228, + 126, + 232, + 239, + 179, + 1, + 1, + 10, + 109, + 168, + 149, + 79, + 248, + 237, + 171, + 131, + 3, + 106, + 189, + 139, + 74, + 20, + 254, + 85, + 176, + 97, + 230, + 48, + 28, + 119, + 193, + 106, + 254, + 74, + 80, + 45, + 9, + 108, + 68, + 74, + 56, + 59, + 131, + 132, + 203, + 214, + 127, + 154, + 37 + ], + "delta2": [ + 220, + 21, + 32, + 149, + 105, + 153, + 154, + 149, + 46, + 203, + 218, + 157, + 217, + 124, + 114, + 212, + 123, + 2, + 241, + 125, + 225, + 87, + 159, + 108, + 40, + 44, + 7, + 48, + 186, + 7, + 77, + 41, + 140, + 131, + 89, + 84, + 142, + 95, + 194, + 137, + 20, + 251, + 212, + 25, + 88, + 161, + 107, + 145, + 101, + 30, + 150, + 168, + 241, + 15, + 46, + 26, + 86, + 81, + 164, + 121, + 89, + 181, + 114, + 15, + 182, + 4, + 164, + 108, + 89, + 200, + 216, + 78, + 15, + 181, + 110, + 22, + 18, + 173, + 59, + 66, + 87, + 62, + 197, + 131, + 36, + 145, + 132, + 151, + 181, + 145, + 66, + 102, + 221, + 214, + 231, + 15, + 190, + 253, + 243, + 179, + 59, + 230, + 182, + 244, + 219, + 57, + 83, + 40, + 11, + 54, + 10, + 125, + 97, + 106, + 5, + 251, + 66, + 17, + 104, + 247, + 147, + 94, + 186, + 150, + 59, + 200, + 42, + 1 + ] + } + }, + { + "size": 128, + "sectionType": "kIC", + "ic": { + "points": [ + [ + 159, + 248, + 192, + 42, + 81, + 195, + 240, + 117, + 58, + 151, + 248, + 80, + 74, + 174, + 103, + 158, + 139, + 207, + 128, + 41, + 103, + 165, + 50, + 1, + 62, + 78, + 234, + 146, + 209, + 172, + 107, + 18, + 157, + 221, + 131, + 160, + 104, + 27, + 185, + 246, + 251, + 113, + 253, + 154, + 127, + 231, + 252, + 199, + 10, + 153, + 88, + 115, + 57, + 216, + 203, + 40, + 235, + 204, + 64, + 131, + 65, + 43, + 209, + 41 + ], + [ + 18, + 190, + 3, + 16, + 11, + 61, + 0, + 2, + 71, + 228, + 113, + 118, + 82, + 220, + 4, + 98, + 183, + 104, + 61, + 148, + 96, + 212, + 62, + 249, + 154, + 121, + 244, + 142, + 48, + 19, + 42, + 47, + 17, + 97, + 68, + 127, + 214, + 136, + 16, + 59, + 90, + 155, + 195, + 110, + 173, + 123, + 3, + 134, + 178, + 97, + 229, + 66, + 185, + 129, + 147, + 4, + 169, + 50, + 41, + 192, + 95, + 202, + 24, + 48 + ] + ] + } + }, + { + "size": 268, + "sectionType": "kCoeffs", + "coeffs": { + "num": 6, + "cs": [ + { + "matrix": 0, + "section": 0, + "index": 2, + "value": [ + 90, + 146, + 222, + 65, + 78, + 15, + 41, + 40, + 174, + 22, + 93, + 150, + 150, + 173, + 53, + 212, + 215, + 215, + 197, + 45, + 121, + 194, + 6, + 44, + 132, + 91, + 227, + 97, + 193, + 125, + 77, + 46 + ] + }, + { + "matrix": 1, + "section": 0, + "index": 3, + "value": [ + 167, + 109, + 33, + 174, + 69, + 230, + 184, + 27, + 227, + 89, + 92, + 227, + 177, + 58, + 254, + 83, + 133, + 128, + 187, + 83, + 61, + 131, + 73, + 140, + 165, + 68, + 78, + 127, + 177, + 208, + 22, + 2 + ] + }, + { + "matrix": 0, + "section": 1, + "index": 5, + "value": [ + 90, + 146, + 222, + 65, + 78, + 15, + 41, + 40, + 174, + 22, + 93, + 150, + 150, + 173, + 53, + 212, + 215, + 215, + 197, + 45, + 121, + 194, + 6, + 44, + 132, + 91, + 227, + 97, + 193, + 125, + 77, + 46 + ] + }, + { + "matrix": 1, + "section": 1, + "index": 4, + "value": [ + 167, + 109, + 33, + 174, + 69, + 230, + 184, + 27, + 227, + 89, + 92, + 227, + 177, + 58, + 254, + 83, + 133, + 128, + 187, + 83, + 61, + 131, + 73, + 140, + 165, + 68, + 78, + 127, + 177, + 208, + 22, + 2 + ] + }, + { + "matrix": 0, + "section": 2, + "index": 0, + "value": [ + 167, + 109, + 33, + 174, + 69, + 230, + 184, + 27, + 227, + 89, + 92, + 227, + 177, + 58, + 254, + 83, + 133, + 128, + 187, + 83, + 61, + 131, + 73, + 140, + 165, + 68, + 78, + 127, + 177, + 208, + 22, + 2 + ] + }, + { + "matrix": 0, + "section": 3, + "index": 1, + "value": [ + 167, + 109, + 33, + 174, + 69, + 230, + 184, + 27, + 227, + 89, + 92, + 227, + 177, + 58, + 254, + 83, + 133, + 128, + 187, + 83, + 61, + 131, + 73, + 140, + 165, + 68, + 78, + 127, + 177, + 208, + 22, + 2 + ] + } + ] + } + }, + { + "size": 384, + "sectionType": "kA", + "a": { + "points": [ + [ + 250, + 37, + 171, + 226, + 234, + 101, + 189, + 116, + 93, + 193, + 22, + 212, + 84, + 166, + 202, + 66, + 1, + 67, + 31, + 146, + 79, + 11, + 128, + 53, + 190, + 57, + 17, + 172, + 169, + 216, + 222, + 17, + 151, + 186, + 184, + 145, + 165, + 97, + 88, + 243, + 44, + 57, + 254, + 24, + 166, + 38, + 26, + 191, + 92, + 101, + 202, + 253, + 38, + 154, + 233, + 211, + 16, + 118, + 197, + 205, + 238, + 127, + 205, + 26 + ], + [ + 255, + 99, + 60, + 61, + 179, + 75, + 87, + 49, + 134, + 142, + 110, + 202, + 43, + 157, + 165, + 35, + 58, + 171, + 89, + 213, + 192, + 166, + 22, + 84, + 234, + 56, + 42, + 221, + 141, + 106, + 109, + 39, + 129, + 75, + 27, + 168, + 80, + 46, + 235, + 250, + 76, + 98, + 58, + 133, + 238, + 115, + 226, + 8, + 146, + 84, + 213, + 173, + 98, + 152, + 26, + 60, + 215, + 51, + 218, + 99, + 80, + 49, + 43, + 18 + ], + [ + 2, + 235, + 11, + 7, + 22, + 106, + 234, + 119, + 245, + 235, + 239, + 113, + 70, + 217, + 129, + 99, + 194, + 106, + 92, + 122, + 136, + 123, + 41, + 23, + 176, + 182, + 185, + 183, + 196, + 188, + 170, + 36, + 33, + 120, + 194, + 120, + 193, + 155, + 21, + 182, + 18, + 80, + 107, + 80, + 27, + 122, + 112, + 59, + 202, + 165, + 161, + 255, + 51, + 94, + 223, + 11, + 21, + 68, + 101, + 141, + 7, + 156, + 218, + 24 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 111, + 12, + 211, + 188, + 123, + 12, + 110, + 117, + 88, + 40, + 59, + 7, + 41, + 71, + 152, + 22, + 17, + 91, + 28, + 96, + 21, + 250, + 171, + 202, + 103, + 216, + 218, + 196, + 19, + 212, + 3, + 21, + 58, + 90, + 251, + 211, + 184, + 121, + 61, + 207, + 119, + 21, + 199, + 59, + 175, + 251, + 169, + 58, + 205, + 246, + 180, + 81, + 64, + 226, + 176, + 125, + 247, + 186, + 28, + 239, + 254, + 144, + 29, + 35 + ] + ] + } + }, + { + "size": 384, + "sectionType": "kB1", + "b1": { + "points": [ + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 2, + 235, + 11, + 7, + 22, + 106, + 234, + 119, + 245, + 235, + 239, + 113, + 70, + 217, + 129, + 99, + 194, + 106, + 92, + 122, + 136, + 123, + 41, + 23, + 176, + 182, + 185, + 183, + 196, + 188, + 170, + 36, + 38, + 133, + 186, + 95, + 85, + 240, + 10, + 134, + 122, + 122, + 6, + 24, + 118, + 240, + 16, + 92, + 147, + 178, + 223, + 129, + 130, + 231, + 112, + 172, + 20, + 92, + 204, + 83, + 107, + 178, + 137, + 23 + ], + [ + 111, + 12, + 211, + 188, + 123, + 12, + 110, + 117, + 88, + 40, + 59, + 7, + 41, + 71, + 152, + 22, + 17, + 91, + 28, + 96, + 21, + 250, + 171, + 202, + 103, + 216, + 218, + 196, + 19, + 212, + 3, + 21, + 13, + 163, + 129, + 4, + 94, + 18, + 227, + 108, + 21, + 181, + 170, + 44, + 226, + 110, + 215, + 92, + 144, + 97, + 204, + 47, + 118, + 99, + 159, + 58, + 50, + 229, + 20, + 242, + 115, + 189, + 70, + 13 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + ] + } + }, + { + "size": 768, + "sectionType": "kB2", + "b2": { + "points": [ + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + [ + 23, + 195, + 157, + 60, + 47, + 17, + 213, + 30, + 185, + 89, + 37, + 114, + 15, + 133, + 139, + 47, + 125, + 74, + 135, + 64, + 35, + 68, + 103, + 113, + 251, + 27, + 160, + 72, + 111, + 160, + 33, + 24, + 125, + 159, + 57, + 238, + 210, + 203, + 160, + 159, + 247, + 148, + 154, + 202, + 30, + 241, + 211, + 12, + 153, + 83, + 23, + 42, + 55, + 13, + 10, + 175, + 134, + 201, + 208, + 255, + 235, + 33, + 147, + 43, + 235, + 50, + 219, + 200, + 45, + 135, + 204, + 149, + 117, + 99, + 196, + 78, + 115, + 120, + 247, + 213, + 58, + 176, + 10, + 69, + 28, + 39, + 58, + 227, + 44, + 121, + 22, + 101, + 143, + 214, + 222, + 24, + 197, + 30, + 245, + 231, + 179, + 226, + 105, + 120, + 157, + 118, + 48, + 221, + 122, + 129, + 25, + 26, + 143, + 167, + 96, + 250, + 4, + 185, + 73, + 45, + 15, + 145, + 117, + 111, + 21, + 205, + 23, + 41 + ], + [ + 22, + 134, + 159, + 171, + 43, + 47, + 106, + 70, + 166, + 119, + 103, + 62, + 5, + 232, + 70, + 88, + 244, + 54, + 127, + 67, + 206, + 143, + 83, + 103, + 212, + 74, + 176, + 59, + 139, + 86, + 170, + 21, + 192, + 211, + 93, + 50, + 239, + 35, + 207, + 15, + 19, + 32, + 64, + 163, + 199, + 176, + 98, + 71, + 248, + 71, + 154, + 194, + 96, + 81, + 188, + 93, + 43, + 138, + 42, + 237, + 43, + 116, + 62, + 22, + 50, + 214, + 175, + 106, + 42, + 131, + 204, + 163, + 62, + 225, + 199, + 188, + 100, + 166, + 19, + 76, + 125, + 206, + 255, + 255, + 94, + 58, + 147, + 14, + 31, + 125, + 45, + 229, + 254, + 153, + 54, + 42, + 22, + 234, + 197, + 152, + 165, + 196, + 165, + 92, + 16, + 147, + 213, + 51, + 5, + 97, + 76, + 96, + 219, + 220, + 210, + 153, + 156, + 130, + 158, + 253, + 67, + 98, + 65, + 229, + 96, + 74, + 4, + 18 + ], + [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ] + ] + } + }, + { + "size": 256, + "sectionType": "kC", + "c": { + "points": [ + [ + 222, + 0, + 222, + 220, + 112, + 217, + 186, + 67, + 22, + 180, + 208, + 233, + 211, + 111, + 61, + 173, + 194, + 64, + 44, + 48, + 156, + 152, + 152, + 224, + 186, + 205, + 163, + 85, + 3, + 197, + 91, + 20, + 121, + 99, + 24, + 92, + 34, + 76, + 52, + 7, + 114, + 248, + 249, + 85, + 57, + 25, + 104, + 189, + 179, + 45, + 255, + 151, + 63, + 74, + 2, + 119, + 217, + 74, + 190, + 40, + 187, + 254, + 166, + 40 + ], + [ + 191, + 170, + 40, + 242, + 164, + 98, + 109, + 163, + 225, + 124, + 131, + 118, + 113, + 119, + 167, + 154, + 198, + 65, + 62, + 152, + 244, + 171, + 215, + 205, + 122, + 15, + 92, + 235, + 1, + 201, + 17, + 38, + 53, + 156, + 215, + 252, + 151, + 22, + 183, + 164, + 189, + 54, + 79, + 146, + 191, + 141, + 213, + 107, + 7, + 145, + 68, + 158, + 8, + 53, + 229, + 81, + 214, + 65, + 91, + 171, + 55, + 227, + 209, + 45 + ], + [ + 79, + 95, + 88, + 222, + 116, + 19, + 6, + 112, + 94, + 78, + 80, + 203, + 25, + 24, + 186, + 222, + 128, + 253, + 200, + 20, + 21, + 200, + 208, + 19, + 76, + 61, + 63, + 232, + 248, + 199, + 50, + 24, + 249, + 36, + 89, + 197, + 223, + 238, + 158, + 215, + 16, + 24, + 170, + 185, + 47, + 72, + 191, + 177, + 174, + 184, + 204, + 243, + 202, + 147, + 35, + 80, + 85, + 160, + 105, + 150, + 213, + 53, + 82, + 4 + ], + [ + 6, + 59, + 147, + 200, + 237, + 70, + 101, + 138, + 24, + 187, + 241, + 7, + 58, + 68, + 104, + 130, + 23, + 27, + 48, + 181, + 209, + 191, + 24, + 216, + 125, + 105, + 228, + 153, + 198, + 103, + 98, + 11, + 129, + 190, + 92, + 41, + 218, + 80, + 235, + 126, + 151, + 67, + 34, + 235, + 103, + 227, + 75, + 106, + 243, + 59, + 56, + 80, + 55, + 143, + 144, + 171, + 137, + 112, + 66, + 24, + 75, + 112, + 187, + 15 + ] + ] + } + }, + { + "size": 256, + "sectionType": "kH", + "h": { + "points": [ + [ + 122, + 96, + 214, + 122, + 23, + 121, + 122, + 23, + 247, + 7, + 251, + 246, + 83, + 236, + 112, + 126, + 185, + 198, + 2, + 165, + 211, + 117, + 43, + 72, + 196, + 224, + 37, + 234, + 237, + 151, + 177, + 6, + 201, + 15, + 238, + 137, + 220, + 210, + 45, + 195, + 205, + 179, + 92, + 8, + 21, + 0, + 251, + 45, + 25, + 182, + 20, + 143, + 250, + 139, + 56, + 176, + 1, + 245, + 192, + 85, + 154, + 37, + 83, + 31 + ], + [ + 109, + 219, + 9, + 84, + 71, + 79, + 35, + 134, + 107, + 118, + 252, + 174, + 250, + 126, + 112, + 255, + 136, + 125, + 225, + 206, + 239, + 112, + 236, + 57, + 105, + 68, + 221, + 57, + 222, + 135, + 11, + 1, + 48, + 129, + 222, + 18, + 196, + 53, + 14, + 182, + 248, + 164, + 248, + 240, + 241, + 53, + 18, + 152, + 58, + 255, + 104, + 91, + 209, + 124, + 109, + 22, + 137, + 246, + 188, + 135, + 58, + 187, + 207, + 20 + ], + [ + 141, + 100, + 178, + 118, + 196, + 223, + 196, + 70, + 237, + 138, + 209, + 68, + 187, + 223, + 25, + 58, + 41, + 11, + 49, + 232, + 27, + 110, + 143, + 199, + 232, + 94, + 77, + 211, + 35, + 248, + 193, + 22, + 35, + 72, + 21, + 131, + 119, + 72, + 36, + 147, + 128, + 178, + 65, + 90, + 130, + 5, + 209, + 250, + 61, + 213, + 160, + 90, + 43, + 2, + 131, + 19, + 226, + 246, + 81, + 221, + 239, + 123, + 189, + 13 + ], + [ + 104, + 6, + 132, + 143, + 99, + 203, + 107, + 168, + 226, + 110, + 87, + 18, + 118, + 73, + 161, + 179, + 208, + 84, + 39, + 0, + 104, + 221, + 252, + 90, + 5, + 228, + 234, + 204, + 53, + 226, + 71, + 47, + 225, + 191, + 154, + 166, + 153, + 187, + 124, + 208, + 162, + 24, + 185, + 248, + 55, + 215, + 134, + 142, + 23, + 27, + 31, + 89, + 83, + 35, + 74, + 176, + 173, + 246, + 227, + 214, + 134, + 23, + 127, + 3 + ] + ] + } + }, + { + "size": 908, + "sectionType": "kContributions", + "contr": { + "hash": [ + 208, + 4, + 157, + 173, + 108, + 62, + 105, + 70, + 107, + 29, + 159, + 18, + 46, + 119, + 79, + 103, + 136, + 108, + 45, + 191, + 16, + 216, + 136, + 176, + 112, + 116, + 177, + 201, + 149, + 135, + 77, + 148, + 149, + 104, + 199, + 58, + 20, + 127, + 255, + 144, + 83, + 193, + 133, + 0, + 233, + 97, + 1, + 132, + 211, + 186, + 141, + 158, + 96, + 60, + 14, + 187, + 185, + 120, + 21, + 223, + 228, + 203, + 39, + 57 + ], + "num": 2 + } + } + ] +} \ No newline at end of file diff --git a/tests/proof_systems/groth16_files/three_fac.r1cs b/tests/proof_systems/groth16_files/three_fac.r1cs new file mode 100644 index 00000000..8b4aaef9 Binary files /dev/null and b/tests/proof_systems/groth16_files/three_fac.r1cs differ diff --git a/tests/proof_systems/groth16_files/three_fac_final.zkey b/tests/proof_systems/groth16_files/three_fac_final.zkey new file mode 100644 index 00000000..9e68e1bc Binary files /dev/null and b/tests/proof_systems/groth16_files/three_fac_final.zkey differ diff --git a/tests/proof_systems/groth16_files/witness.wtns b/tests/proof_systems/groth16_files/witness.wtns new file mode 100644 index 00000000..b49c6d01 Binary files /dev/null and b/tests/proof_systems/groth16_files/witness.wtns differ diff --git a/tests/proof_systems/t_groth16_prover.nim b/tests/proof_systems/t_groth16_prover.nim new file mode 100644 index 00000000..3e41addf --- /dev/null +++ b/tests/proof_systems/t_groth16_prover.nim @@ -0,0 +1,104 @@ +import std/[os, unittest, strutils], + constantine/proof_systems/manual_groth16, + constantine/named/algebras + +#[ +For information about the data files used in this test case, see +`examples/groth16_prover.org`. +]# + +suite "Groth16 prover": + test "Proving 3-factorization example": + const T = BN254_Snarks + # parse binary files + let wtns = parseWtnsFile("./groth16_files/witness.wtns").toWtns[:T]() + let zkey = parseZkeyFile("./groth16_files/three_fac_final.zkey").toZkey[:T]() + let r1cs = parseR1csFile("./groth16_files/three_fac.r1cs").toR1CS() + # construct mutable prover (to overwrite r, s) + var ctx = Groth16Prover[T].init(zkey, wtns, r1cs) + # definition of `r` and `s` values that produced expected proof + const rSJ = @[ + byte 143, 55, 118, 73, 42, 115, 60, 77, + 95, 209, 41, 144, 250, 137, 138, 71, + 176, 242, 186, 232, 179, 30, 88, 255, + 198, 161, 182, 150, 220, 149, 33, 19 + ] + const sSJ = @[ + byte 213, 105, 105, 27, 129, 249, 139, 158, + 221, 68, 37, 163, 59, 71, 19, 108, + 60, 153, 183, 156, 25, 148, 37, 9, + 85, 205, 250, 246, 132, 142, 244, 36 + ] + + # construct the random element `r` from snarkjs "secret" r + let r = toFr[BN254_Snarks](rSJ) + # and `s` + let s = toFr[BN254_Snarks](sSJ) + # overwrite context's random values + ctx.r = r + ctx.s = s + + echo "r = ", ctx.r.toHex() + echo "s = ", ctx.s.toHex() + + # expected values produced by SnarkJS with these `r`, `s` values + # x/y coordinates of Fp point on G1 subgroup of EC, corresponding to `g^A_1` + const ax = "5525629793372463776337933283524928112323589665400780041477380790923758613749" + const ay = "21229177076048503863699135039723099340209138028149442778064006577287317302601" + # x/y cooridnates of Fp2 point on G2 subgroup, corresponding to `g^B_2` + const bxc0 = "10113559933709853115219982658131344715329670532374721861173670433756614595086" + const bxc1 = "748111067660143353202076805159132563350177510079329482395824347599610874338" + const byc0 = "14193926223452546125681093394065339196897041249946578591171606543100010486627" + const byc1 = "871256420758854731396810855688710623510558493821614150596755347032202324148" + # x/y coordinates of Fp point on G1 subgroup, corresponding to `g^C_1` + const cx = "18517653609733492682442099361591955563405567929398531111532682405176646276349" + const cy = "17315036348446251361273519572420522936369550153340386126725970444173389652255" + + proc toECG1(x, y: string): EC_ShortW_Aff[Fp[T], G1] {.noinit.} = + let xF = Fp[T].fromDecimal(x) + let yF = Fp[T].fromDecimal(y) + result.x = xF + result.y = yF + + proc toFp2(c0, c1: string): Fp2[T] {.noinit.} = + let c0F = Fp[T].fromDecimal(c0) + let c1F = Fp[T].fromDecimal(c1) + result.c0 = c0F + result.c1 = c1F + + proc toECG2(xc0, xc1, yc0, yc1: string): EC_ShortW_Aff[Fp2[T], G2] {.noinit.} = + let xF2 = toFp2(xc0, xc1) + let yF2 = toFp2(yc0, yc1) + result.x = xF2 + result.y = yF2 + + let aExp = toECG1(ax, ay) + let bExp = toECG2(bxc0, bxc1, byc0, byc1) + let cExp = toECG1(cx, cy) + + # call the proof and... + let (A_p, B2_p, C_p) = ctx.prove() + + echo aExp.toDecimal() + echo bExp.toDecimal() + echo cExp.toDecimal() + + echo "\n==============================\n" + echo "A_p#16 = ", A_p.toHex() + echo "A_p#10 = ", A_p.toDecimal() + echo "------------------------------" + echo "B_p#16 = ", B2_p.toHex() + echo "B_p#10 = ", B2_p.toDecimal() + echo "------------------------------" + echo "C_p#16 = ", C_p.toHex() + echo "C_p#10 = ", C_p.toDecimal() + + #check (A_p == aExp.getJacobian).bool + #check (B2_p == bExp.getJacobian).bool + ### XXX: C currently fails! + #check (C_p == cExp.getJacobian).bool + + check (A_p == aExp).bool + check (B2_p == bExp).bool + ## XXX: C currently fails! + check (C_p == cExp).bool diff --git a/tests/proof_systems/t_wtns_parser.nim b/tests/proof_systems/t_wtns_parser.nim new file mode 100644 index 00000000..9931d7fb --- /dev/null +++ b/tests/proof_systems/t_wtns_parser.nim @@ -0,0 +1,69 @@ +import + std/[os, unittest, strutils], + constantine/proof_systems/constraint_systems/wtns_binary_parser, + constantine/named/algebras, + constantine/math/io/io_fields, + constantine/math/arithmetic, + constantine/proof_systems/groth16_utils + +const TestDir = currentSourcePath.rsplit(DirSep, 1)[0] +suite "Witness (.wtns) binary file parser": + + test "Parse Moonmath 3-factorization `.wtns` file": + const path = TestDir / "groth16_files/witness.wtns" + let wtns = parseWtnsFile(path) + + block CheckRaw: + check wtns.magic == ['w', 't', 'n', 's'] + check wtns.version == 2 + check wtns.numberSections == 2 + + let expHeader = WitnessHeader( + n8: 32, + r: @[1, 0, 0, 240, 147, 245, 225, 67, 145, 112, 185, 121, 72, 232, 51, 40, 93, 88, 129, 129, 182, 69, 80, 184, 41, 160, 49, 225, 114, 78, 100, 48], + num: 6 + ) + + check wtns.sections.len == 2 + + let h = wtns.sections[0] + check h.size == 40 + check h.sectionType == kHeader + check h.header == expHeader + # check header `r` field is indeed field modulus + let rb = toFr[BN254_Snarks](h.header.r) + check rb.isZero().bool + + let w = wtns.sections[1] + check w.size == 192 + check w.sectionType == kData + + func toWitness(s: seq[byte]): Witness = Witness(data: s) + let expWtns = @[ + toWitness @[byte 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + toWitness @[byte 211, 12, 174, 248, 219, 66, 74, 118, 56, 33, 96, 120, 222, 127, 224, 3, 223, 23, 92, 96, 143, 206, 39, 36, 252, 6, 62, 238, 113, 252, 22, 21], + toWitness @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 69, 30, 210, 222, 206, 150, 0], + toWitness @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 164, 14, 153, 21, 107, 162, 37], + toWitness @[byte 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 6, 137, 28, 241, 2, 204, 4], + toWitness @[byte 192, 241, 127, 208, 49, 238, 224, 192, 158, 180, 48, 145, 210, 42, 166, 8, 7, 60, 29, 157, 205, 128, 142, 152, 99, 179, 236, 248, 41, 210, 204, 45] + ] + + check w.wtns == expWtns + + block CheckTyped: + const T = BN254_Snarks + let wtns = wtns.toWtns[:T]() + + echo wtns + + let expW = @[ + Fr[T].fromHex("0x0000000000000000000000000000000000000000000000000000000000000001"), + Fr[T].fromHex("0x1516fc71ee3e06fc2427ce8f605c17df03e07fde78602138764a42dbf8ae0cd3"), + Fr[T].fromHex("0x0096ceded21e4548000000000000000000000000000000000000000000000000"), + Fr[T].fromHex("0x25a26b15990ea400000000000000000000000000000000000000000000000000"), + Fr[T].fromHex("0x04cc02f11c8906c0000000000000000000000000000000000000000000000000"), + Fr[T].fromHex("0x2dccd229f8ecb363988e80cd9d1d3c0708a62ad29130b49ec0e0ee31d07ff1c0") + ] + + for i, w in wtns.witnesses: + check (w == expW[i]).bool diff --git a/tests/proof_systems/t_zkey_parser.nim b/tests/proof_systems/t_zkey_parser.nim new file mode 100644 index 00000000..2d8e867d --- /dev/null +++ b/tests/proof_systems/t_zkey_parser.nim @@ -0,0 +1,56 @@ +import + std/[os, unittest, strutils, json], + constantine/proof_systems/constraint_systems/zkey_binary_parser, + constantine/named/algebras, + constantine/platforms/abstractions, + constantine/math/io/[io_bigints, io_fields], + constantine/math/[arithmetic, extension_fields], + constantine/proof_systems/groth16_utils + +## The two procs below are to serialize `ZkeyBin` and `Zkey` to JSON. +proc `%`(c: char): JsonNode = % ($c) +proc `%`(c: SecretWord): JsonNode = % (c.uint64) + +const UpdateTestVectors = false +const RawVec = "groth16_files/t_zkey_bin.json" +const TypedVec = "groth16_files/t_zkey.json" + +const TestDir = currentSourcePath.rsplit(DirSep, 1)[0] +suite "Zkey (.zkey) binary file parser": + + + ## NOTE: We perform the check of whether we parse the `.zkey` binary file correctly + ## by writing the Zkey types as JSON data. Ideally, we would explicitly check each + ## field, but given the large number of fields, it would make for a pretty big + ## test case. Given that the parsed data has been utilized for a successful Groth16 + ## proof, we consider the stored JSON files as 'correct'. + test "Parse Moonmath 3-factorization `.zkey` file": + const path = TestDir / "groth16_files/three_fac_final.zkey" + let zkey = parseZkeyFile(path) + + let zj = % zkey + + when UpdateTestVectors: + writeFile(RawVec, zj.pretty()) + block CheckRaw: + check zkey.magic == ['z', 'k', 'e', 'y'] + check zkey.version == 1 + check zkey.header().proverType == 1 + check zkey.numberSections == 10 + + # read expected test vector + let exp = RawVec.readFile().parseJson() + check zj == exp + + block CheckTyped: + const T = BN254_Snarks + # convert to 'typed' (unmarshalled) type + let zkey = zkey.toZkey[:T]() + + let zj = % zkey + when UpdateTestVectors: + writeFile(TypedVec, zj.pretty()) + + # read expected test vector + let exp = TypedVec.readFile().parseJson() + check zj == exp