github.com/consensys/gnark-crypto@v0.14.0/internal/generator/plookup/template/table.go.tmpl (about) 1 import ( 2 "crypto/sha256" 3 "errors" 4 "math/big" 5 "sort" 6 7 {{ .CurvePackage }} "github.com/consensys/gnark-crypto/ecc/{{ .Name }}" 8 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr" 9 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr/fft" 10 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/kzg" 11 "github.com/consensys/gnark-crypto/ecc/{{ .Name }}/fr/permutation" 12 fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" 13 ) 14 15 var ( 16 ErrIncompatibleSize = errors.New("the tables in f and t are not of the same size") 17 ErrFoldedCommitment = errors.New("the folded commitment is malformed") 18 ErrNumberDigests = errors.New("proof.ts and proof.fs are not of the same length") 19 ) 20 21 // ProofLookupTables proofs that a list of tables 22 type ProofLookupTables struct { 23 24 // commitments to the rows f 25 fs []kzg.Digest 26 27 // commitments to the rows of t 28 ts []kzg.Digest 29 30 // lookup proof for the f and t folded 31 foldedProof ProofLookupVector 32 33 // proof that the ts folded correspond to t in the folded proof 34 permutationProof permutation.Proof 35 } 36 37 // ProveLookupTables generates a proof that f, seen as a multi dimensional table, 38 // consists of vectors that are in t. In other words for each i, f[:][i] must be one 39 // of the t[:][j]. 40 // 41 // For instance, if t is the truth table of the XOR function, t will be populated such 42 // that t[:][i] contains the i-th entry of the truth table, so t[0][i] XOR t[1][i] = t[2][i]. 43 // 44 // The fr.Vector in f and t are supposed to be of the same size constant size. 45 func ProveLookupTables(pk kzg.ProvingKey, f, t []fr.Vector) (ProofLookupTables, error) { 46 47 // res 48 proof := ProofLookupTables{} 49 var err error 50 51 // hash function used for Fiat Shamir 52 hFunc := sha256.New() 53 54 // transcript to derive the challenge 55 fs := fiatshamir.NewTranscript(hFunc, "lambda") 56 57 // check the sizes 58 if len(f) != len(t) { 59 return proof, ErrIncompatibleSize 60 } 61 s := len(f[0]) 62 for i := 1; i < len(f); i++ { 63 if len(f[i]) != s { 64 return proof, ErrIncompatibleSize 65 } 66 } 67 s = len(t[0]) 68 for i := 1; i < len(t); i++ { 69 if len(t[i]) != s { 70 return proof, ErrIncompatibleSize 71 } 72 } 73 74 // commit to the tables in f and t 75 nbRows := len(t) 76 proof.fs = make([]kzg.Digest, nbRows) 77 proof.ts = make([]kzg.Digest, nbRows) 78 _nbColumns := len(f[0]) + 1 79 if _nbColumns < len(t[0]) { 80 _nbColumns = len(t[0]) 81 } 82 d := fft.NewDomain(uint64(_nbColumns)) 83 nbColumns := d.Cardinality 84 lfs := make([][]fr.Element, nbRows) 85 cfs := make([][]fr.Element, nbRows) 86 lts := make([][]fr.Element, nbRows) 87 cts := make([][]fr.Element, nbRows) 88 89 for i := 0; i < nbRows; i++ { 90 91 cfs[i] = make([]fr.Element, nbColumns) 92 lfs[i] = make([]fr.Element, nbColumns) 93 copy(cfs[i], f[i]) 94 copy(lfs[i], f[i]) 95 for j := len(f[i]); j < int(nbColumns); j++ { 96 cfs[i][j] = f[i][len(f[i])-1] 97 lfs[i][j] = f[i][len(f[i])-1] 98 } 99 d.FFTInverse(cfs[i], fft.DIF) 100 fft.BitReverse(cfs[i]) 101 proof.fs[i], err = kzg.Commit(cfs[i], pk) 102 if err != nil { 103 return proof, err 104 } 105 106 cts[i] = make([]fr.Element, nbColumns) 107 lts[i] = make([]fr.Element, nbColumns) 108 copy(cts[i], t[i]) 109 copy(lts[i], t[i]) 110 for j := len(t[i]); j < int(d.Cardinality); j++ { 111 cts[i][j] = t[i][len(t[i])-1] 112 lts[i][j] = t[i][len(t[i])-1] 113 } 114 d.FFTInverse(cts[i], fft.DIF) 115 fft.BitReverse(cts[i]) 116 proof.ts[i], err = kzg.Commit(cts[i], pk) 117 if err != nil { 118 return proof, err 119 } 120 } 121 122 // fold f and t 123 comms := make([]*kzg.Digest, 2*nbRows) 124 for i := 0; i < nbRows; i++ { 125 comms[i] = new(kzg.Digest) 126 comms[i].Set(&proof.fs[i]) 127 comms[nbRows+i] = new(kzg.Digest) 128 comms[nbRows+i].Set(&proof.ts[i]) 129 } 130 lambda, err := deriveRandomness(fs, "lambda", comms...) 131 if err != nil { 132 return proof, err 133 } 134 foldedf := make(fr.Vector, nbColumns) 135 foldedt := make(fr.Vector, nbColumns) 136 for i := 0; i < int(nbColumns); i++ { 137 for j := nbRows - 1; j >= 0; j-- { 138 foldedf[i].Mul(&foldedf[i], &lambda). 139 Add(&foldedf[i], &lfs[j][i]) 140 foldedt[i].Mul(&foldedt[i], &lambda). 141 Add(&foldedt[i], <s[j][i]) 142 } 143 } 144 145 // generate a proof of permutation of the foldedt and sort(foldedt) 146 foldedtSorted := make(fr.Vector, nbColumns) 147 copy(foldedtSorted, foldedt) 148 sort.Sort(foldedtSorted) 149 proof.permutationProof, err = permutation.Prove(pk, foldedt, foldedtSorted) 150 if err != nil { 151 return proof, err 152 } 153 154 // call plookupVector, on foldedf[:len(foldedf)-1] to ensure that the domain size 155 // in ProveLookupVector is the same as d's 156 proof.foldedProof, err = ProveLookupVector(pk, foldedf[:len(foldedf)-1], foldedt) 157 158 return proof, err 159 } 160 161 // VerifyLookupTables verifies that a ProofLookupTables proof is correct. 162 func VerifyLookupTables(vk kzg.VerifyingKey, proof ProofLookupTables) error { 163 164 // hash function used for Fiat Shamir 165 hFunc := sha256.New() 166 167 // transcript to derive the challenge 168 fs := fiatshamir.NewTranscript(hFunc, "lambda") 169 170 // check that the number of digests is the same 171 if len(proof.fs) != len(proof.ts) { 172 return ErrNumberDigests 173 } 174 175 // fold the commitments fs and ts 176 nbRows := len(proof.fs) 177 comms := make([]*kzg.Digest, 2*nbRows) 178 for i := 0; i < nbRows; i++ { 179 comms[i] = &proof.fs[i] 180 comms[i+nbRows] = &proof.ts[i] 181 } 182 lambda, err := deriveRandomness(fs, "lambda", comms...) 183 if err != nil { 184 return err 185 } 186 187 // fold the commitments of the rows of t and f 188 var comf, comt kzg.Digest 189 comf.Set(&proof.fs[nbRows-1]) 190 comt.Set(&proof.ts[nbRows-1]) 191 var blambda big.Int 192 lambda.BigInt(&blambda) 193 for i := nbRows - 2; i >= 0; i-- { 194 comf.ScalarMultiplication(&comf, &blambda). 195 Add(&comf, &proof.fs[i]) 196 comt.ScalarMultiplication(&comt, &blambda). 197 Add(&comt, &proof.ts[i]) 198 } 199 200 // check that the folded commitment of the fs correspond to foldedProof.f 201 if !comf.Equal(&proof.foldedProof.f) { 202 return ErrFoldedCommitment 203 } 204 205 // check that the folded commitment of the ts is a permutation of proof.FoldedProof.t 206 err = permutation.Verify(vk, proof.permutationProof) 207 if err != nil { 208 return err 209 } 210 211 // verify the inner proof 212 return VerifyLookupVector(vk, proof.foldedProof) 213 } 214 215 // TODO put that in fiat-shamir package 216 func deriveRandomness(fs *fiatshamir.Transcript, challenge string, points ...*{{ .CurvePackage }}.G1Affine) (fr.Element, error) { 217 218 var buf [{{ .CurvePackage }}.SizeOfG1AffineUncompressed]byte 219 var r fr.Element 220 221 for _, p := range points { 222 buf = p.RawBytes() 223 if err := fs.Bind(challenge, buf[:]); err != nil { 224 return r, err 225 } 226 } 227 228 b, err := fs.ComputeChallenge(challenge) 229 if err != nil { 230 return r, err 231 } 232 r.SetBytes(b) 233 return r, nil 234 }