mirror of
https://github.com/logos-storage/nim-groth16.git
synced 2026-07-30 20:33:22 +00:00
unify the two projection term MSM-s into a single one
This commit is contained in:
parent
257e2e20f0
commit
2e2a8bcb35
@ -77,6 +77,17 @@ func selectTrues*[T]( bs: seq[bool] , xs: seq[T] ): seq[T] =
|
||||
j += 1
|
||||
return ys
|
||||
|
||||
func selectFalses*[T]( bs: seq[bool] , xs: seq[T] ): seq[T] =
|
||||
assert( bs.len == xs.len )
|
||||
let k = countFalses(bs)
|
||||
var ys: seq[T] = newSeq[T]( k )
|
||||
var j = 0
|
||||
for (i,b) in bs.pairs:
|
||||
if not b:
|
||||
ys[j] = xs[i]
|
||||
j += 1
|
||||
return ys
|
||||
|
||||
func notBoolSeq*( us: seq[bool]): seq[bool] =
|
||||
let n = us.len
|
||||
var ws: seq[bool] = newSeq[bool]( n )
|
||||
|
||||
@ -11,4 +11,11 @@ See also [this write-up](https://hackmd.io/@bkomuves/HyBL5V5xMe) (mostly followi
|
||||
Here some details are different from the paper though, and our implementation
|
||||
is noticeably more efficient in practice.
|
||||
|
||||
### Different versions
|
||||
|
||||
We provide several slightly different versions with different tradeoffs
|
||||
|
||||
- `dynamic/v1`: This is a version mostly following the Dynark paper (TODO: sparse convolution algorithm)
|
||||
- `dyanmic/v2`: This introduces some of our optimizations:
|
||||
- the projection term updates become essentially free
|
||||
- we reorder the circuit so that the changes are in a subgroup (TODO)
|
||||
|
||||
@ -13,7 +13,7 @@ import groth16/math/domain
|
||||
import groth16/math/ntt
|
||||
import groth16/math/group_fft
|
||||
import groth16/math/poly
|
||||
#import groth16/math/convolution
|
||||
import groth16/math/convolution
|
||||
|
||||
import groth16/zkey_types
|
||||
|
||||
@ -225,3 +225,110 @@ func evalLagrangeProductViaLemma*(D: Domain, i: int, k: int, tau: F): F =
|
||||
return ztau * evalPhiAt(D, i, k, tau)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# *** diagonal "phi_ii" points
|
||||
|
||||
# the points `delta^{-1} * phi_ii(tau) * (tau^N - 1) * g1` as in the Dynark paper
|
||||
#
|
||||
# where
|
||||
#
|
||||
# > phi_ii = - sum_j phi_ij = - sum_j ( W[j-i]*L[i] + W[i-j]*L[j] )
|
||||
# > phi_ij = W[j-i]*L[i] + W[i-j]*L[j]
|
||||
#
|
||||
func calculateDiagPhiFFT*( wvec: seq[F], deltaLZ: seq[G1] ): seq[G1] =
|
||||
let N = wvec.len
|
||||
assert( N == deltaLZ.len )
|
||||
|
||||
let sumW = sumSeqFr( wvec )
|
||||
|
||||
var hs: seq[G1] = groupConvolution( wvec , deltaLZ )
|
||||
for i in 0..<N:
|
||||
hs[i] += (sumW ** deltaLZ[i])
|
||||
hs[i] = negG1(hs[i])
|
||||
|
||||
return hs
|
||||
|
||||
# NOTE: this is EXTREMELY SLOW
|
||||
proc calculateDiagPhiNaive*( wvec: seq[F], deltaLZ: seq[G1], pool: Taskpool ): seq[G1] =
|
||||
let N = wvec.len
|
||||
assert( N == deltaLZ.len )
|
||||
|
||||
let sumW = sumSeqFr( wvec )
|
||||
|
||||
var diagPhi: seq[G1] = newSeq[G1]( N )
|
||||
for i in 0..<N:
|
||||
var ws: seq[F] = newSeq[F]( N )
|
||||
for j in 0..<N:
|
||||
if i != j:
|
||||
ws[j] = wvec[ safeMod(i-j , N) ]
|
||||
else:
|
||||
ws[j] = sumW
|
||||
diagPhi[i] = negG1( msmMultiThreadedG1( ws, deltaLZ, pool ) )
|
||||
|
||||
return diagPhi
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# *** cross-term coefficients
|
||||
|
||||
#
|
||||
# Computes the expansion `f(x)` where `f(x) = A(x)B(x) mod (x^N - 1)` in
|
||||
# terms of `(x^N-1)*L_i(x)`
|
||||
#
|
||||
# The inputs `As` and `Bs` are the Lagrange-basis coefficients of A(x) and B(x).
|
||||
#
|
||||
# Note: the remainder modulo `Z(x) := x^N-1` is `sum_k a[k]*b[k]*L_k(x)`
|
||||
#
|
||||
func crossTermCoeffs*(D: Domain, As: seq[F], Bs: seq[F]) : seq[F] =
|
||||
|
||||
let N = D.domainSize
|
||||
assert( N == As.len )
|
||||
assert( N == Bs.len )
|
||||
|
||||
let ABs = pointwiseProdFr( As, Bs )
|
||||
|
||||
let Aconv = fieldConvolveWithWVecBar( D , As )
|
||||
let Bconv = fieldConvolveWithWVecBar( D , Bs )
|
||||
let ABconv = fieldConvolveWithWVecBar( D , ABs )
|
||||
|
||||
let sumW = sumOfWVec( N )
|
||||
|
||||
var output: seq[F] = newSeq[F]( N )
|
||||
|
||||
for k in 0..<N:
|
||||
output[k] = As[k]*Bconv[k] + Bs[k]*Aconv[k] - ABconv[k] - ABs[k]*sumW
|
||||
|
||||
return output
|
||||
|
||||
#---------------------------------------
|
||||
|
||||
proc testCrossTermCoeffs*(N : int): bool =
|
||||
|
||||
let D = createDomain( N )
|
||||
|
||||
let As = randFrSeq(N)
|
||||
let Bs = randFrSeq(N)
|
||||
|
||||
let tau = randFr()
|
||||
let ztau = smallPowFr(tau,N) - oneFr # Z(tau) = tau^N - 1
|
||||
|
||||
# reference `A(tau)*B(tau)`
|
||||
var Atau: F = zeroFr
|
||||
var Btau: F = zeroFr
|
||||
for i in 0..<N:
|
||||
Atau += As[i] * evalLagrangePolyAt( D , i , tau ) # A(x) = sum_i A_i * L_i(x)
|
||||
Btau += Bs[i] * evalLagrangePolyAt( D , i , tau )
|
||||
var reference = Atau * Btau
|
||||
|
||||
# correction term (because of the modulo Z(x) behaviour of what we test)
|
||||
for k in 0..<N:
|
||||
reference -= As[k] * Bs[k] * evalLagrangePolyAt( D , k , tau )
|
||||
|
||||
# the thing we want to test
|
||||
let coeffs = crossTermCoeffs( D , As , Bs )
|
||||
var smart: F = zeroFr
|
||||
for i in 0..<N:
|
||||
let lztau = zTau * evalLagrangePolyAt( D , i , tau )
|
||||
smart += coeffs[i] * lztau
|
||||
|
||||
return (smart === reference)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
@ -2,13 +2,7 @@
|
||||
#
|
||||
# Finishing a Dynark-style proof
|
||||
#
|
||||
# We are implementing the cross-term differently from the paper:
|
||||
#
|
||||
# - in the paper there are only two convolutions (4 field FFT-s), but 2 MSM-s
|
||||
# - we have three convolutions (6 field FFT-s), but only 1 MSM.
|
||||
#
|
||||
# In practice our version is significantly faster, as MSM-s are much more expensive
|
||||
# than field FFTs (at least for practical sizes)
|
||||
# This version more-or-less follows the Dynark paper
|
||||
#
|
||||
|
||||
import std/options
|
||||
@ -40,70 +34,6 @@ import groth16/dynamic/v1/setup
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
# Computes the expansion `f(x)` where `f(x) = A(x)B(x) mod (x^N - 1)` in
|
||||
# terms of `(x^N-1)*L_i(x)`
|
||||
#
|
||||
# The inputs `As` and `Bs` are the Lagrange-basis coefficients of A(x) and B(x).
|
||||
#
|
||||
# Note: the remainder modulo `Z(x) := x^N-1` is `sum_k a[k]*b[k]*L_k(x)`
|
||||
#
|
||||
func crossTermCoeffs*(D: Domain, As: seq[F], Bs: seq[F]) : seq[F] =
|
||||
|
||||
let N = D.domainSize
|
||||
assert( N == As.len )
|
||||
assert( N == Bs.len )
|
||||
|
||||
let ABs = pointwiseProdFr( As, Bs )
|
||||
|
||||
let Aconv = fieldConvolveWithWVecBar( D , As )
|
||||
let Bconv = fieldConvolveWithWVecBar( D , Bs )
|
||||
let ABconv = fieldConvolveWithWVecBar( D , ABs )
|
||||
|
||||
let sumW = sumOfWVec( N )
|
||||
|
||||
var output: seq[F] = newSeq[F]( N )
|
||||
|
||||
for k in 0..<N:
|
||||
output[k] = As[k]*Bconv[k] + Bs[k]*Aconv[k] - ABconv[k] - ABs[k]*sumW
|
||||
|
||||
return output
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
proc testCrossTermCoeffs*(N : int): bool =
|
||||
|
||||
let D = createDomain( N )
|
||||
|
||||
let As = randFrSeq(N)
|
||||
let Bs = randFrSeq(N)
|
||||
|
||||
let tau = randFr()
|
||||
let ztau = smallPowFr(tau,N) - oneFr # Z(tau) = tau^N - 1
|
||||
|
||||
# reference `A(tau)*B(tau)`
|
||||
var Atau: F = zeroFr
|
||||
var Btau: F = zeroFr
|
||||
for i in 0..<N:
|
||||
Atau += As[i] * evalLagrangePolyAt( D , i , tau ) # A(x) = sum_i A_i * L_i(x)
|
||||
Btau += Bs[i] * evalLagrangePolyAt( D , i , tau )
|
||||
var reference = Atau * Btau
|
||||
|
||||
# correction term (because of the modulo Z(x) behaviour of what we test)
|
||||
for k in 0..<N:
|
||||
reference -= As[k] * Bs[k] * evalLagrangePolyAt( D , k , tau )
|
||||
|
||||
# the thing we want to test
|
||||
let coeffs = crossTermCoeffs( D , As , Bs )
|
||||
var smart: F = zeroFr
|
||||
for i in 0..<N:
|
||||
let lztau = zTau * evalLagrangePolyAt( D , i , tau )
|
||||
smart += coeffs[i] * lztau
|
||||
|
||||
return (smart === reference)
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
proc finishDynaProofWithMaskV1*( zkey: ZKey, wtns: Witness, dynaPreProof: DynaPreProofV1, mask: Mask, pool: Taskpool, printTimings: bool): Proof =
|
||||
|
||||
let N = zkey.header.domainSize
|
||||
@ -131,23 +61,6 @@ proc finishDynaProofWithMaskV1*( zkey: ZKey, wtns: Witness, dynaPreProof: DynaPr
|
||||
|
||||
var nonlin: G1 = infG1
|
||||
|
||||
#[
|
||||
|
||||
--- this only makes sense if we can restrict to a subgroup ---
|
||||
|
||||
var cs: seq[F]
|
||||
withMeasureTime(printTimings,"computing the nonlinear \"cross\" term"):
|
||||
withMeasureTime(printTimings," - computing the cross coeffs took"):
|
||||
cs = crossTermCoeffs(D , deltaAB.valuesAz , deltaAB.valuesBz )
|
||||
withMeasureTime(printTimings," - computing the cross MSM took"):
|
||||
nonlin = msmMultiThreadedG1( cs , dynaPreProof.dynaSetup.pointsDeltaLZ , pool )
|
||||
|
||||
echo " - nonzero coefficients in deltaA = " & $countNonZerosFr(deltaAB.valuesAz)
|
||||
echo " - nonzero coefficients in deltaB = " & $countNonZerosFr(deltaAB.valuesBz)
|
||||
echo " - nonzero coefficients in the cross-term = " & $countNonZerosFr(cs)
|
||||
|
||||
]#
|
||||
|
||||
# following the Dynark paper
|
||||
withMeasureTime(printTimings,"computing the nonlinear \"cross\" term (Dynark-style)"):
|
||||
|
||||
@ -175,21 +88,6 @@ proc finishDynaProofWithMaskV1*( zkey: ZKey, wtns: Witness, dynaPreProof: DynaPr
|
||||
let ls = selectTrues( dynaPreProof.deltaImages.imageAB , dynaPreProof.dynaSetup.pointsDeltaLZ )
|
||||
nonlin += msmMultiThreadedG1( ds , ls , pool )
|
||||
|
||||
#[
|
||||
var ds: seq[F] = newSeq[F ]( K )
|
||||
var qs: seq[G1] = newSeq[G1]( K )
|
||||
for (k,i) in idxs.pairs:
|
||||
var x: F = zeroFr
|
||||
for j in idxs:
|
||||
if (i != j):
|
||||
let ab = deltaAB.valuesAz[i] * deltaAB.valuesBz[j] +
|
||||
deltaAB.valuesAz[j] * deltaAB.valuesBz[i]
|
||||
x += ab * wvec[ safeMod( j - i , N ) ]
|
||||
ds[k] = x
|
||||
qs[k] = dynaPreProof.dynaSetup.pointsDeltaLZ[i]
|
||||
nonlin += msmMultiThreadedG1( ds , qs , pool )
|
||||
]#
|
||||
|
||||
withMeasureTime(printTimings,"computing the nonlinear \"projection\" terms"):
|
||||
let comprAz = selectTrues( dynaPreProof.deltaImages.imageA , deltaAB.valuesAz )
|
||||
let comprBz = selectTrues( dynaPreProof.deltaImages.imageB , deltaAB.valuesBz )
|
||||
|
||||
@ -41,9 +41,6 @@ func projectionElementsV1*(setup: DynaSetupV1, D: Domain, As: seq[F], complImage
|
||||
let N = D.domainSize
|
||||
var Us: seq[G1] = newSeq[G1]( N )
|
||||
|
||||
# # reverse indexed wvec: wvecBar[i] = wvec[-i]
|
||||
# let wvecBar: seq[F] = fftReverseVec( setup.weightVec )
|
||||
|
||||
let fldN : F = intToFr( N )
|
||||
let sumW : F = sumOfWVec( N )
|
||||
|
||||
@ -66,14 +63,9 @@ func projectionElementsV1*(setup: DynaSetupV1, D: Domain, As: seq[F], complImage
|
||||
func simulateProjectionElementsV1*( D: Domain, tau: F, delta: F, As: seq[F], complImage: seq[bool] ): seq[G1] =
|
||||
|
||||
let N = D.domainSize
|
||||
|
||||
let setup = simulateDynaSetupV1( D, tau, delta )
|
||||
|
||||
let deltaInv : F = invFr(delta)
|
||||
|
||||
# # reverse indexed wvec: wvecBar[i] = wvec[-i]
|
||||
# let wvecBar: seq[F] = fftReverseVec( calculateWVec( D ) )
|
||||
|
||||
# sum_i A[i]*L_i(tau)
|
||||
var asLTau: F = zeroFr
|
||||
for i in 0..<N:
|
||||
@ -139,31 +131,6 @@ proc dynaPreProofV1*(zkey: ZKey, setup: DynaSetupV1, partialWitness: PartialWitn
|
||||
let N = zkey.header.domainSize
|
||||
let D = createDomain(N)
|
||||
|
||||
#[
|
||||
import groth16/math/matrix
|
||||
import std/tables # TODO: temporary!
|
||||
|
||||
# TODO: just tmp testing
|
||||
let M = zkey.header.nvars
|
||||
let matABC = zkeyToSparseMatrices(zkey)
|
||||
let dmask = notBoolSeq( partialWitnessMask(partialWitness) )
|
||||
let cntA = selectTrues( dmask , sparseMatrixColumnCounts(matABC.A) )
|
||||
let cntB = selectTrues( dmask , sparseMatrixColumnCounts(matABC.B) )
|
||||
# echo $cntA
|
||||
# echo $cntB
|
||||
echo $sumIntSeq(cntA)
|
||||
echo $sumIntSeq(cntB)
|
||||
echo $countTrues(dmask)
|
||||
for j in 0..<M:
|
||||
if dmask[j]:
|
||||
for (i,x) in matABC.A.columns[j].pairs:
|
||||
echo toDecimalFr(x)
|
||||
for j in 0..<M:
|
||||
if dmask[j]:
|
||||
for (i,x) in matABC.B.columns[j].pairs:
|
||||
echo toDecimalFr(x)
|
||||
]#
|
||||
|
||||
var partialAB : PartialAB
|
||||
withMeasureTime(printTimings,"build partial AB"):
|
||||
partialAB = buildPartialAB( zkey, partialWitness.values )
|
||||
|
||||
@ -23,53 +23,6 @@ import groth16/dynamic/v1/types
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# let deltaImgA = sparseMatrixImage( A , delta_mask )
|
||||
# let deltaImgB = sparseMatrixImage( B , delta_mask )
|
||||
# let deltaImgAB = orBoolSeqs( deltaImgA , deltaImgB )
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# the points `delta^{-1} * phi_ii(tau) * (tau^N - 1) * g1` as in the Dynark paper
|
||||
#
|
||||
# where
|
||||
#
|
||||
# > phi_ii = - sum_j phi_ij = - sum_j ( W[j-i]*L[i] + W[i-j]*L[j] )
|
||||
# > phi_ij = W[j-i]*L[i] + W[i-j]*L[j]
|
||||
#
|
||||
func calculateDiagPhiFFT*( wvec: seq[F], deltaLZ: seq[G1] ): seq[G1] =
|
||||
let N = wvec.len
|
||||
assert( N == deltaLZ.len )
|
||||
|
||||
let sumW = sumSeqFr( wvec )
|
||||
|
||||
var hs: seq[G1] = groupConvolution( wvec , deltaLZ )
|
||||
for i in 0..<N:
|
||||
hs[i] += (sumW ** deltaLZ[i])
|
||||
hs[i] = negG1(hs[i])
|
||||
|
||||
return hs
|
||||
|
||||
# NOTE: this is EXTREMELY SLOW
|
||||
proc calculateDiagPhiNaive*( wvec: seq[F], deltaLZ: seq[G1], pool: Taskpool ): seq[G1] =
|
||||
let N = wvec.len
|
||||
assert( N == deltaLZ.len )
|
||||
|
||||
let sumW = sumSeqFr( wvec )
|
||||
|
||||
var diagPhi: seq[G1] = newSeq[G1]( N )
|
||||
for i in 0..<N:
|
||||
var ws: seq[F] = newSeq[F]( N )
|
||||
for j in 0..<N:
|
||||
if i != j:
|
||||
ws[j] = wvec[ safeMod(i-j , N) ]
|
||||
else:
|
||||
ws[j] = sumW
|
||||
diagPhi[i] = negG1( msmMultiThreadedG1( ws, deltaLZ, pool ) )
|
||||
|
||||
return diagPhi
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# does the setup from the ZKey (prover key)
|
||||
proc dynaSetupV1FromZKey*(zkey: Zkey, pool: Taskpool ): DynaSetupV1 =
|
||||
|
||||
@ -93,12 +46,9 @@ proc dynaSetupV1FromZKey*(zkey: Zkey, pool: Taskpool ): DynaSetupV1 =
|
||||
let conv = groupConvolution( wvec , deltaLZ )
|
||||
|
||||
var diagPhi : seq[G1]
|
||||
var diagPhi2 : seq[G1]
|
||||
withMeasureTime(true,"phi diagonal took"):
|
||||
diagPhi = calculateDiagPhiFFT( wvec , deltaLZ )
|
||||
|
||||
echo "agree = " & $isEqualG1Seq( diagPhi , diagPhi2 )
|
||||
|
||||
return DynaSetupV1( weightVec : wvec ,
|
||||
pointsDeltaLZ : deltaLZ ,
|
||||
wConvDeltaLZ : conv ,
|
||||
|
||||
141
groth16/dynamic/v2/finish.nim
Normal file
141
groth16/dynamic/v2/finish.nim
Normal file
@ -0,0 +1,141 @@
|
||||
|
||||
#
|
||||
# Finishing a Dynark-style proof
|
||||
#
|
||||
# We are implementing the cross-term differently from the paper:
|
||||
#
|
||||
# - in the paper there are only two convolutions (4 field FFT-s), but 2 MSM-s
|
||||
# - we have three convolutions (6 field FFT-s), but only 1 MSM.
|
||||
#
|
||||
# In practice our version is significantly faster, as MSM-s are much more expensive
|
||||
# than field FFTs (at least for practical sizes)
|
||||
#
|
||||
|
||||
import std/options
|
||||
|
||||
import taskpools
|
||||
|
||||
import constantine/math/arithmetic
|
||||
import constantine/named/properties_fields
|
||||
|
||||
import groth16/bn128
|
||||
import groth16/bn128/arrays
|
||||
import groth16/misc
|
||||
|
||||
import groth16/zkey_types
|
||||
import groth16/files/witness
|
||||
|
||||
import groth16/math/domain
|
||||
import groth16/math/ntt
|
||||
import groth16/math/poly
|
||||
import groth16/math/convolution
|
||||
|
||||
import groth16/prover/types
|
||||
import groth16/prover/shared
|
||||
import groth16/partial/finish
|
||||
|
||||
import groth16/dynamic/shared
|
||||
import groth16/dynamic/v2/types
|
||||
import groth16/dynamic/v2/setup
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
proc finishDynaProofWithMaskV2*( zkey: ZKey, wtns: Witness, dynaPreProof: DynaPreProofV2, mask: Mask, pool: Taskpool, printTimings: bool): Proof =
|
||||
|
||||
let N = zkey.header.domainSize
|
||||
let M = zkey.header.nvars
|
||||
let D = createDomain( N )
|
||||
|
||||
let partialMask = dynaPreProof.partialProof.partial_mask
|
||||
|
||||
let wvec = dynaPreProof.dynaSetup.weightVec
|
||||
let wvecRev = fftReverseVec( wvec )
|
||||
|
||||
var deltaAB: OnlyAB
|
||||
var witnessDelta: seq[Option[F]] = newSeq[Option[F]]( M )
|
||||
withMeasureTime(printTimings,"building deltaAz, deltaBz"):
|
||||
for j in 0..<M:
|
||||
if partialMask[j]:
|
||||
witnessDelta[j] = none(F)
|
||||
else:
|
||||
witnessDelta[j] = some(wtns.values[j])
|
||||
deltaAB = buildOnlyAB( zkey , witnessDelta )
|
||||
|
||||
var proof: Proof
|
||||
withMeasureTime(printTimings,"finishing the linear terms"):
|
||||
proof = finishPartialProofWithMaskGeneric( false, zkey, wtns, dynaPreProof.partialProof, mask, pool, false )
|
||||
|
||||
var nonlin: G1 = infG1
|
||||
|
||||
#[
|
||||
|
||||
--- this only makes sense if we can restrict to a subgroup ---
|
||||
|
||||
var cs: seq[F]
|
||||
withMeasureTime(printTimings,"computing the nonlinear \"cross\" term"):
|
||||
withMeasureTime(printTimings," - computing the cross coeffs took"):
|
||||
cs = crossTermCoeffs(D , deltaAB.valuesAz , deltaAB.valuesBz )
|
||||
withMeasureTime(printTimings," - computing the cross MSM took"):
|
||||
nonlin = msmMultiThreadedG1( cs , dynaPreProof.dynaSetup.pointsDeltaLZ , pool )
|
||||
|
||||
echo " - nonzero coefficients in deltaA = " & $countNonZerosFr(deltaAB.valuesAz)
|
||||
echo " - nonzero coefficients in deltaB = " & $countNonZerosFr(deltaAB.valuesBz)
|
||||
echo " - nonzero coefficients in the cross-term = " & $countNonZerosFr(cs)
|
||||
|
||||
]#
|
||||
|
||||
# following the Dynark paper
|
||||
withMeasureTime(printTimings,"computing the nonlinear \"cross\" term (Dynark-style)"):
|
||||
|
||||
let idxs = trueIndices( dynaPreProof.deltaImages.imageAB )
|
||||
let K = idxs.len
|
||||
|
||||
withMeasureTime(printTimings," - computing the diagonal part of the cross-term"):
|
||||
var cs: seq[F] = newSeq[F ]( K )
|
||||
var ps: seq[G1] = newSeq[G1]( K )
|
||||
for (k,i) in idxs.pairs:
|
||||
cs[k] = deltaAB.valuesAz[i] * deltaAB.valuesBz[i]
|
||||
ps[k] = dynaPreProof.dynaSetup.diagPhiPoints[i]
|
||||
nonlin += msmMultiThreadedG1( cs , ps , pool )
|
||||
|
||||
withMeasureTime(printTimings," - computing the off-diagonal part of the cross-term"):
|
||||
let As = deltaAB.valuesAz
|
||||
let Bs = deltaAB.valuesBz
|
||||
let AstarW = fieldConvolution( As , wvecRev )
|
||||
let BstarW = fieldConvolution( Bs , wvecRev ) # TODO: make this sparse somehow??!
|
||||
|
||||
var ds: seq[F] = newSeq[F]( K )
|
||||
for (k,i) in idxs.pairs:
|
||||
ds[k] = As[i] * BstarW[i] + Bs[i] * AstarW[i]
|
||||
|
||||
let ls = selectTrues( dynaPreProof.deltaImages.imageAB , dynaPreProof.dynaSetup.pointsDeltaLZ )
|
||||
nonlin += msmMultiThreadedG1( ds , ls , pool )
|
||||
|
||||
#[
|
||||
var ds: seq[F] = newSeq[F ]( K )
|
||||
var qs: seq[G1] = newSeq[G1]( K )
|
||||
for (k,i) in idxs.pairs:
|
||||
var x: F = zeroFr
|
||||
for j in idxs:
|
||||
if (i != j):
|
||||
let ab = deltaAB.valuesAz[i] * deltaAB.valuesBz[j] +
|
||||
deltaAB.valuesAz[j] * deltaAB.valuesBz[i]
|
||||
x += ab * wvec[ safeMod( j - i , N ) ]
|
||||
ds[k] = x
|
||||
qs[k] = dynaPreProof.dynaSetup.pointsDeltaLZ[i]
|
||||
nonlin += msmMultiThreadedG1( ds , qs , pool )
|
||||
]#
|
||||
|
||||
withMeasureTime(printTimings,"computing the nonlinear \"projection\" terms"):
|
||||
let zs = selectFalses( partialMask , wtns.values )
|
||||
nonlin += msmMultiThreadedG1( zs , dynaPreProof.dynaPreprocess.unified , pool)
|
||||
|
||||
proof.pi_c += nonlin
|
||||
|
||||
return proof
|
||||
|
||||
proc finishDynaProofV2*( zkey: ZKey, wtns: Witness, dynaPreProof: DynaPreProofV2 , pool: Taskpool, printTimings = false ): Proof =
|
||||
let mask = randomMask()
|
||||
return finishDynaProofWithMaskV2( zkey, wtns, dynaPreProof, mask, pool, printTimings )
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
150
groth16/dynamic/v2/preprocess.nim
Normal file
150
groth16/dynamic/v2/preprocess.nim
Normal file
@ -0,0 +1,150 @@
|
||||
|
||||
#
|
||||
# this is V2, we compute the projection terms differently
|
||||
#
|
||||
|
||||
import std/options
|
||||
import std/tables
|
||||
|
||||
import taskpools
|
||||
|
||||
import constantine/math/arithmetic
|
||||
import constantine/named/properties_fields
|
||||
|
||||
import groth16/bn128
|
||||
import groth16/bn128/arrays
|
||||
import groth16/misc
|
||||
|
||||
import groth16/zkey_types
|
||||
|
||||
import groth16/math/domain
|
||||
import groth16/math/convolution
|
||||
import groth16/math/poly
|
||||
import groth16/math/matrix
|
||||
|
||||
import groth16/partial/precalc
|
||||
|
||||
import groth16/dynamic/shared
|
||||
import groth16/dynamic/v2/types
|
||||
import groth16/dynamic/v2/setup
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
func projectionElementsV2*(setup: DynaSetupV2, D: Domain, As: seq[F], complImage: seq[bool]): seq[G1] =
|
||||
let N = D.domainSize
|
||||
var Us: seq[G1] = newSeq[G1]( N )
|
||||
|
||||
# # reverse indexed wvec: wvecBar[i] = wvec[-i]
|
||||
# let wvecBar: seq[F] = fftReverseVec( setup.weightVec )
|
||||
|
||||
let fldN : F = intToFr( N )
|
||||
let sumW : F = sumOfWVec( N )
|
||||
|
||||
let WBarStarA : seq[F] = fieldConvolveWithWVecBar( D , As )
|
||||
let ALstarW : seq[G1] = groupConvolveWithWVec( D , pointwiseScaleG1( As , setup.pointsDeltaLZ ) )
|
||||
|
||||
# 2*d scalar multiplications + the group convolution above
|
||||
for k in 0..<N:
|
||||
|
||||
# we only compute for the _image of_ the complementer of the partial witness
|
||||
if complImage[k]:
|
||||
let cf : F = WBarStarA[k] - As[k] * sumW
|
||||
Us[k] = ALstarW[k] - (As[k] ** setup.wConvDeltaLZ[k]) + (cf ** setup.pointsDeltaLZ[k])
|
||||
|
||||
return Us
|
||||
|
||||
#---------------------------------------
|
||||
|
||||
proc dynaPreprocessV2*(zkey: ZKey, setup: DynaSetupV2, partialAB: PartialAB, zdeltaMask: seq[bool] ): DynaPreprocessV2 =
|
||||
let N = zkey.header.domainSize
|
||||
let D = createDomain(N)
|
||||
|
||||
var projA_full: seq[G1]
|
||||
var projB_full: seq[G1]
|
||||
withMeasureTime(true," + the \"projection\" points U_k, V_k"):
|
||||
# note: as we want to compute A0(x)*B'(x), the image masks should be the other ones!!
|
||||
projA_full = projectionElementsV2( setup , D , partialAB.valuesAz , partialAB.complImageB )
|
||||
projB_full = projectionElementsV2( setup , D , partialAB.valuesBz , partialAB.complImageA )
|
||||
|
||||
let m = countTrues(zdeltaMask)
|
||||
var Xs: seq[G1] = newSeq[G1]( m )
|
||||
|
||||
withMeasureTime(true," + the \"unified\" points X_j"):
|
||||
|
||||
let matABC = zkeyToSparseMatrices(zkey)
|
||||
let colsA = selectTrues( zdeltaMask , matABC.A.columns )
|
||||
let colsB = selectTrues( zdeltaMask , matABC.B.columns )
|
||||
|
||||
for j in 0..<m:
|
||||
var h : G1 = infG1
|
||||
for (k,a) in colsA[j].pairs: h += (a ** projB_full[k])
|
||||
for (k,b) in colsB[j].pairs: h += (b ** projA_full[k])
|
||||
Xs[j] = h
|
||||
|
||||
return DynaPreprocessV2( unified: Xs )
|
||||
|
||||
#[
|
||||
|
||||
# just tmp testing
|
||||
let M = zkey.header.nvars
|
||||
let matABC = zkeyToSparseMatrices(zkey)
|
||||
let dmask = notBoolSeq( partialWitnessMask(partialWitness) )
|
||||
let cntA = selectTrues( dmask , sparseMatrixColumnCounts(matABC.A) )
|
||||
let cntB = selectTrues( dmask , sparseMatrixColumnCounts(matABC.B) )
|
||||
# echo $cntA
|
||||
# echo $cntB
|
||||
echo $sumIntSeq(cntA)
|
||||
echo $sumIntSeq(cntB)
|
||||
echo $countTrues(dmask)
|
||||
for j in 0..<M:
|
||||
if dmask[j]:
|
||||
for (i,x) in matABC.A.columns[j].pairs:
|
||||
echo toDecimalFr(x)
|
||||
for j in 0..<M:
|
||||
if dmask[j]:
|
||||
for (i,x) in matABC.B.columns[j].pairs:
|
||||
echo toDecimalFr(x)
|
||||
]#
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
proc dynaPreProofV2*(zkey: ZKey, setup: DynaSetupV2, partialWitness: PartialWitness, pool: Taskpool, printTimings: bool): DynaPreProofV2 =
|
||||
|
||||
let N = zkey.header.domainSize
|
||||
let D = createDomain(N)
|
||||
|
||||
let zdeltaMask = notBoolSeq( partialWitnessMask( partialWitness ) )
|
||||
|
||||
var partialAB : PartialAB
|
||||
withMeasureTime(printTimings,"build partial AB"):
|
||||
partialAB = buildPartialAB( zkey, partialWitness.values )
|
||||
|
||||
let imageAB = orBoolSeqs( partialAB.complImageA , partialAB.complImageB )
|
||||
let deltaImages = DeltaImages( imageA : partialAB.complImageA ,
|
||||
imageB : partialAB.complImageB ,
|
||||
imageAB : imageAB )
|
||||
|
||||
var partialProof : PartialProof
|
||||
withMeasureTime(printTimings,"precomputing the linear terms"):
|
||||
partialProof = generatePartialProof( zkey, partialWitness, pool, false )
|
||||
|
||||
var nonlin: G1
|
||||
withMeasureTime(printTimings,"precomputing the nonlinear term `A0(tau)*B0(tau)` "):
|
||||
let cs = crossTermCoeffs(D , partialAB.valuesAz , partialAB.valuesBz )
|
||||
nonlin = msmMultiThreadedG1( cs , setup.pointsDeltaLZ , pool )
|
||||
|
||||
partialProof.partial_pi_c += nonlin
|
||||
|
||||
var preprocess : DynaPreprocessV2
|
||||
withMeasureTime(printTimings,"precomputing the \"projection\" points"):
|
||||
preprocess = dynaPreprocessV2( zkey, setup, partialAB, zdeltaMask )
|
||||
|
||||
return DynaPreProofV2( partialProof : partialProof ,
|
||||
deltaImages : deltaImages ,
|
||||
dynaSetup : setup ,
|
||||
dynaPreprocess : preprocess )
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
82
groth16/dynamic/v2/setup.nim
Normal file
82
groth16/dynamic/v2/setup.nim
Normal file
@ -0,0 +1,82 @@
|
||||
|
||||
{.push raises:[].}
|
||||
|
||||
# import constantine/named/properties_fields
|
||||
|
||||
import taskpools
|
||||
|
||||
import groth16/bn128
|
||||
import groth16/bn128/arrays
|
||||
import groth16/misc
|
||||
|
||||
import groth16/math/domain
|
||||
import groth16/math/group_fft
|
||||
import groth16/math/poly
|
||||
import groth16/math/convolution
|
||||
import groth16/math/convert
|
||||
# import groth16/math/ntt
|
||||
|
||||
import groth16/zkey_types
|
||||
|
||||
import groth16/dynamic/shared
|
||||
import groth16/dynamic/v2/types
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# does the setup from the ZKey (prover key)
|
||||
proc dynaSetupV2FromZKey*(zkey: Zkey, pool: Taskpool ): DynaSetupV2 =
|
||||
|
||||
let N = zkey.header.domainSize
|
||||
let D = createDomain(N)
|
||||
|
||||
var deltaZTau : seq[G1] # the points `delta^-1 * (tau^N-1) * tau^i * g1`
|
||||
case zkey.header.flavour
|
||||
|
||||
of JensGroth:
|
||||
deltaZTau = zkey.pPoints.pointsH1
|
||||
|
||||
of Snarkjs:
|
||||
echo "Jordi-style .zkey detected; converting points! (slow...)"
|
||||
withMeasureTime(true,"Jordi-to-Jens conversion"):
|
||||
deltaZTau = convertPointsFromJordi(D , zkey.pPoints.pointsH1)
|
||||
|
||||
let sumW = sumOfWVec( N )
|
||||
let wvec = calculateWVec( D )
|
||||
let deltaLZ = inverseGroupFFT( deltaZTau , D )
|
||||
let conv = groupConvolution( wvec , deltaLZ )
|
||||
|
||||
var diagPhi : seq[G1]
|
||||
withMeasureTime(true,"phi diagonal took"):
|
||||
diagPhi = calculateDiagPhiFFT( wvec , deltaLZ )
|
||||
|
||||
return DynaSetupV2( weightVec : wvec ,
|
||||
pointsDeltaLZ : deltaLZ ,
|
||||
wConvDeltaLZ : conv ,
|
||||
diagPhiPoints : diagPhi )
|
||||
|
||||
#---------------------------------------
|
||||
|
||||
# simulates a setup (from the "toxic waste" values `tau` and `delta`), so that
|
||||
# we can test components of the system
|
||||
func simulateDynaSetupV2*( D: Domain, tau: F, delta: F ): DynaSetupV2 =
|
||||
|
||||
let N = D.domainSize
|
||||
let ztau: F = smallPowFr(tau,N) - oneFr
|
||||
let deltaZTau: F = ztau / delta
|
||||
|
||||
# compute `delta^-1 * L_i(tau) * (tau^N - 1) ** g1
|
||||
var deltaLZ: seq[G1] = newSeq[G1]( N )
|
||||
for i in 0..<N:
|
||||
let y: F = deltaZTau * evalLagrangePolyAt( D, i, tau )
|
||||
deltaLZ[i] = y ** gen1
|
||||
|
||||
let wvec = calculateWVec( D )
|
||||
let conv = groupConvolution( wvec , deltaLZ )
|
||||
let diagPhi = calculateDiagPhiFFT( wvec , deltaLZ )
|
||||
|
||||
return DynaSetupV2( weightVec : wvec ,
|
||||
pointsDeltaLZ : deltaLZ ,
|
||||
wConvDeltaLZ : conv ,
|
||||
diagPhiPoints : diagPhi )
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
39
groth16/dynamic/v2/types.nim
Normal file
39
groth16/dynamic/v2/types.nim
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
{.push raises:[].}
|
||||
|
||||
#import constantine/named/properties_fields
|
||||
|
||||
import groth16/bn128
|
||||
import groth16/math/domain
|
||||
#import groth16/math/ntt
|
||||
#import groth16/math/poly
|
||||
|
||||
import groth16/partial/types as ptypes
|
||||
import groth16/dynamic/types as dtypes
|
||||
|
||||
export ptypes
|
||||
export dtypes
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
type
|
||||
|
||||
# things we can compute at circuit setup time
|
||||
DynaSetupV2* = object
|
||||
weightVec* : seq[F] # the values `W_k` (it's surprisingly slow to re-compute in the finish part!)
|
||||
pointsDeltaLZ* : seq[G1] # the points `delta^-1 * L_i(tau) * Z(tau) * g1` where `Z(x) = x^N-1`
|
||||
wConvDeltaLZ* : seq[G1] # the convolution of `W` and `pointsDeltaLZ`
|
||||
diagPhiPoints* : seq[G1] # the points `delta^-1 * phi_ii(tau) * Z(tau) * g1` from the Dynark paper
|
||||
|
||||
# things we can compute from the partial witness
|
||||
DynaPreprocessV2* = object
|
||||
unified* : seq[G1] # the points `X_j := sum_k ( B_{kj} * U_k + A_{kj} * V_k )
|
||||
|
||||
# "pre-proof" of the Dynark paper, which we can finish
|
||||
DynaPreProofV2* = object
|
||||
partialProof* : PartialProof # linear part of the partial proof
|
||||
deltaImages* : DeltaImages # image of the witness delta under A and B (where can update happen)
|
||||
dynaSetup* : DynaSetupV2 # circuit-setup time calculations
|
||||
dynaPreprocess* : DynaPreprocessV2 # "Dynark"-style preprocessing
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
Loading…
x
Reference in New Issue
Block a user