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