Conversation
|
I just had another look at SnarkJS to see if I can figure out the double Montgomery encoding situation. As it turns out at least I could identify the SnarkJS code that writes the coefficients as doubly Montgomery encoded. This happens here: const nR2 = curve.Fr.mul(n, R2r);
curve.Fr.toRprLE(buffCoeff, 12, nR2);where const R2r = curve.Fr.e(Scalar.mod(Scalar.mul(Rr,Rr), primeR));Which at the very least finally ends the question of whether I'm imagining things or not. I still have no clue why this is done to be honest. The commits touching these lines are not very illuminating either. |
2be3509 to
47ba6f9
Compare
Otherwise when performing operations on EC points in Jacobian, depending on import these can be missing.
Previously these were generated from the wrong branch of the
`{.booldefine.}` variables we had in the code when the PR was still a
draft. Instead of parsing the data as doubly Montgomery encoded as
they actually are, we parsed them as regular Montgomery encoded
resulting in wrong test vectors.
29f1a4e to
8efc5bb
Compare
|
I guess the code at the moment only works on the 64-bit backend. That rings a bell, it was probably due to the parsing of the binary files. Need to check. |
mratsim
left a comment
There was a problem hiding this comment.
LGTM.
I have added several notes for further refactoring and will follow-up with an issue once that is merged. I think the PR is too big at the moment to add further non-critical fix.
Critical if possible would be to fix the 32-bit tests.
The FFT part is not worth changing at the moment but is important to merge now because the following future work use FFT:
- FRI for zkVM acceleration
- Whir for Lean Ethereum
- PeerDAS. Ethereum 2D KZG commitments with Error Correcting Codes
| 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 |
There was a problem hiding this comment.
| ## Stringify an elliptic curve point to Hex | |
| ## Stringify an elliptic curve point to decimal |
| ## Note. Leading zeros are not removed. | ||
| ## Output as decimal. | ||
| ## | ||
| ## WARNING: NOT constant time! |
There was a problem hiding this comment.
Actually it is constant-time see low-level impl:
constantine/constantine/math/io/io_bigints.nim
Lines 371 to 385 in b3f4ebd
The mid-level impl is wrong:
constantine/constantine/math/io/io_fields.nim
Lines 110 to 117 in b3f4ebd
But it's fine, afaik, it's only used for debugging purposes.
| accum.add toDecimal(f) | ||
|
|
||
| func appendDecimal*(accum: var string, f: ExtensionField, indent = 0, order: static Endianness = bigEndian) = | ||
| ## Stringify a tower field element to hex. |
There was a problem hiding this comment.
| ## Stringify a tower field element to hex. | |
| ## Stringify a tower field element to decimal. |
| 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]) |
There was a problem hiding this comment.
Curious the PR that introduced scalarMul_vartime also rewrote this part of the FFT and the argument order should be scalarMul_vartime(EC, scalar).
There was a problem hiding this comment.
Ah my mistake, the argument order when out-of-place is correct. (EC out, scalar, EC in)
| 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 |
There was a problem hiding this comment.
on fields.
Note that there was an implementation here: https://github.com/mratsim/constantine/blob/v0.2.0/research/kzg/fft_fr.nim
| beta2*: EC_ShortW_Aff[Fp2[Name], G2] ## g₂^β | ||
| gamma2*: EC_ShortW_Aff[Fp2[Name], G2] ## g₂^γ | ||
| delta2*: EC_ShortW_Aff[Fp2[Name], G2] ## g₂^δ | ||
| gamma_abc*: seq[EC_ShortW_Aff[Fp[Name], G1]] ## == `zkey.ic` == `[g₁^{ \frac{β·A_i(τ) + α·B_i(τ) + C_0(τ)}{ γ }}]` |
There was a problem hiding this comment.
TODO: we will probably want to remove all seqs here in the future to unify with GPU impl.
Also we just don't use seq in Constantine except at the very edge for easy integration with Nim
| r1cs: r1cs | ||
| ) | ||
|
|
||
| proc calcAp[Name: static Algebra](ctx: Groth16Prover[Name], wt: seq[Fr[Name]]): EC_ShortW_Jac[Fp[Name], G1] {.noinit.} = |
There was a problem hiding this comment.
All non-var parameters should be openArray instead of seq
| result = B1_p | ||
|
|
||
|
|
||
| proc buildABC[Name: static Algebra](ctx: Groth16Prover[Name], wt: seq[Fr[Name]]): tuple[A, B, C: seq[Fr[Name]]] = |
There was a problem hiding this comment.
TODO: remove seq creation and prefer the caller to pass allocated buffers if possible
| 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].prod(args[i], cur) | ||
| cur *= inc |
There was a problem hiding this comment.
Note: This patterns also exist in KZG and IPA and other commitments:
See
constantine/tests/t_ethereum_verkle_ipa_test_helper.nim
Lines 36 to 52 in b3f4ebd
So there is a computePowers proc, though it does allocate more.
constantine/constantine/math/arithmetic/finite_fields.nim
Lines 841 to 854 in b3f4ebd
| pairing(proof.C, vk.delta2) | ||
|
|
||
| # 4. Check pairing equality | ||
| result = bool(lhs == rhs) |
There was a problem hiding this comment.
TODO: This can be rewritten by negating either proof.A or proof.B then use multipairing and check that the result is the pairing group neutral element (i.e. 1 in GT/Fp12)
Edit: The PR previously contained a long description of my debugging of the Groth16 prover implementation. For anyone interested, it can still be found here.
This PR implements a Groth16 prover and verifier, which requires SnarkJS produced
.wtns,.zkeyand.r1csbinary files.