unify the two projection term MSM-s into a single one

This commit is contained in:
Balazs Komuves 2026-06-16 13:14:25 +02:00
parent 257e2e20f0
commit 2e2a8bcb35
No known key found for this signature in database
GPG Key ID: F63B7AEF18435562
10 changed files with 539 additions and 187 deletions

View File

@ -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 )

View File

@ -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)

View File

@ -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)
#-------------------------------------------------------------------------------

View File

@ -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 )

View File

@ -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 )

View File

@ -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 ,

View 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 )
#-------------------------------------------------------------------------------

View 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 )
#-------------------------------------------------------------------------------

View 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 )
#-------------------------------------------------------------------------------

View 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
#-------------------------------------------------------------------------------