github.com/consensys/gnark@v0.11.0/backend/groth16/bn254/verify.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 gnark DO NOT EDIT 16 17 package groth16 18 19 import ( 20 "bytes" 21 "crypto/sha256" 22 "errors" 23 "fmt" 24 "github.com/consensys/gnark-crypto/ecc/bn254/fp" 25 "golang.org/x/crypto/sha3" 26 "io" 27 "math/big" 28 "text/template" 29 "time" 30 31 "github.com/consensys/gnark-crypto/ecc" 32 curve "github.com/consensys/gnark-crypto/ecc/bn254" 33 "github.com/consensys/gnark-crypto/ecc/bn254/fr" 34 "github.com/consensys/gnark-crypto/ecc/bn254/fr/hash_to_field" 35 "github.com/consensys/gnark-crypto/ecc/bn254/fr/pedersen" 36 "github.com/consensys/gnark-crypto/utils" 37 "github.com/consensys/gnark/backend" 38 "github.com/consensys/gnark/backend/solidity" 39 "github.com/consensys/gnark/constraint" 40 "github.com/consensys/gnark/logger" 41 ) 42 43 var ( 44 errPairingCheckFailed = errors.New("pairing doesn't match") 45 errCorrectSubgroupCheckFailed = errors.New("points in the proof are not in the correct subgroup") 46 ) 47 48 // Verify verifies a proof with given VerifyingKey and publicWitness 49 func Verify(proof *Proof, vk *VerifyingKey, publicWitness fr.Vector, opts ...backend.VerifierOption) error { 50 opt, err := backend.NewVerifierConfig(opts...) 51 if err != nil { 52 return fmt.Errorf("new verifier config: %w", err) 53 } 54 if opt.HashToFieldFn == nil { 55 opt.HashToFieldFn = hash_to_field.New([]byte(constraint.CommitmentDst)) 56 } 57 58 nbPublicVars := len(vk.G1.K) - len(vk.PublicAndCommitmentCommitted) 59 60 if len(publicWitness) != nbPublicVars-1 { 61 return fmt.Errorf("invalid witness size, got %d, expected %d (public - ONE_WIRE)", len(publicWitness), len(vk.G1.K)-1) 62 } 63 log := logger.Logger().With().Str("curve", vk.CurveID().String()).Str("backend", "groth16").Logger() 64 start := time.Now() 65 66 // check that the points in the proof are in the correct subgroup 67 if !proof.isValid() { 68 return errCorrectSubgroupCheckFailed 69 } 70 71 var doubleML curve.GT 72 chDone := make(chan error, 1) 73 74 // compute (eKrsδ, eArBs) 75 go func() { 76 var errML error 77 doubleML, errML = curve.MillerLoop([]curve.G1Affine{proof.Krs, proof.Ar}, []curve.G2Affine{vk.G2.deltaNeg, proof.Bs}) 78 chDone <- errML 79 close(chDone) 80 }() 81 82 maxNbPublicCommitted := 0 83 for _, s := range vk.PublicAndCommitmentCommitted { // iterate over commitments 84 maxNbPublicCommitted = utils.Max(maxNbPublicCommitted, len(s)) 85 } 86 commitmentsSerialized := make([]byte, len(vk.PublicAndCommitmentCommitted)*fr.Bytes) 87 commitmentPrehashSerialized := make([]byte, curve.SizeOfG1AffineUncompressed+maxNbPublicCommitted*fr.Bytes) 88 for i := range vk.PublicAndCommitmentCommitted { // solveCommitmentWire 89 copy(commitmentPrehashSerialized, proof.Commitments[i].Marshal()) 90 offset := curve.SizeOfG1AffineUncompressed 91 for j := range vk.PublicAndCommitmentCommitted[i] { 92 copy(commitmentPrehashSerialized[offset:], publicWitness[vk.PublicAndCommitmentCommitted[i][j]-1].Marshal()) 93 offset += fr.Bytes 94 } 95 opt.HashToFieldFn.Write(commitmentPrehashSerialized[:offset]) 96 hashBts := opt.HashToFieldFn.Sum(nil) 97 opt.HashToFieldFn.Reset() 98 nbBuf := fr.Bytes 99 if opt.HashToFieldFn.Size() < fr.Bytes { 100 nbBuf = opt.HashToFieldFn.Size() 101 } 102 var res fr.Element 103 res.SetBytes(hashBts[:nbBuf]) 104 publicWitness = append(publicWitness, res) 105 copy(commitmentsSerialized[i*fr.Bytes:], res.Marshal()) 106 } 107 if len(vk.CommitmentKeys) > 0 { 108 challenge, err := fr.Hash(commitmentsSerialized, []byte("G16-BSB22"), 1) 109 if err != nil { 110 return err 111 } 112 if err = pedersen.BatchVerifyMultiVk(vk.CommitmentKeys, proof.Commitments, []curve.G1Affine{proof.CommitmentPok}, challenge[0]); err != nil { 113 return err 114 } 115 } 116 117 // compute e(Σx.[Kvk(t)]1, -[γ]2) 118 var kSum curve.G1Jac 119 if _, err := kSum.MultiExp(vk.G1.K[1:], publicWitness, ecc.MultiExpConfig{}); err != nil { 120 return err 121 } 122 kSum.AddMixed(&vk.G1.K[0]) 123 124 for i := range proof.Commitments { 125 kSum.AddMixed(&proof.Commitments[i]) 126 } 127 128 var kSumAff curve.G1Affine 129 kSumAff.FromJacobian(&kSum) 130 131 right, err := curve.MillerLoop([]curve.G1Affine{kSumAff}, []curve.G2Affine{vk.G2.gammaNeg}) 132 if err != nil { 133 return err 134 } 135 136 // wait for (eKrsδ, eArBs) 137 if err := <-chDone; err != nil { 138 return err 139 } 140 141 right = curve.FinalExponentiation(&right, &doubleML) 142 if !vk.e.Equal(&right) { 143 return errPairingCheckFailed 144 } 145 146 log.Debug().Dur("took", time.Since(start)).Msg("verifier done") 147 return nil 148 } 149 150 // ExportSolidity writes a solidity Verifier contract on provided writer. 151 // This is an experimental feature and gnark solidity generator as not been thoroughly tested. 152 // 153 // See https://github.com/ConsenSys/gnark-tests for example usage. 154 func (vk *VerifyingKey) ExportSolidity(w io.Writer, exportOpts ...solidity.ExportOption) error { 155 cfg, err := solidity.NewExportConfig(exportOpts...) 156 log := logger.Logger() 157 if err != nil { 158 return err 159 } 160 if cfg.HashToFieldFn == nil { 161 // set the target hash function to legacy keccak256 as it is the default for `solidity.WithTargetSolidityVerifier`` 162 cfg.HashToFieldFn = sha3.NewLegacyKeccak256() 163 log.Debug().Msg("hash to field function not set, using keccak256 as default") 164 } 165 // a bit hacky way to understand what hash function is provided. We already 166 // receive instance of hash function but it is difficult to compare it with 167 // sha256.New() or sha3.NewLegacyKeccak256() directly. 168 // 169 // So, we hash an empty input and compare the outputs. 170 cfg.HashToFieldFn.Reset() 171 hashBts := cfg.HashToFieldFn.Sum(nil) 172 var hashFnName string 173 if bytes.Equal(hashBts, sha256.New().Sum(nil)) { 174 hashFnName = "sha256" 175 } else if bytes.Equal(hashBts, sha3.NewLegacyKeccak256().Sum(nil)) { 176 hashFnName = "keccak256" 177 } else { 178 return fmt.Errorf("unsupported hash function used, only supported sha256 and legacy keccak256") 179 } 180 cfg.HashToFieldFn.Reset() 181 helpers := template.FuncMap{ 182 "sum": func(a, b int) int { 183 return a + b 184 }, 185 "sub": func(a, b int) int { 186 return a - b 187 }, 188 "mul": func(a, b int) int { 189 return a * b 190 }, 191 "intRange": func(max int) []int { 192 out := make([]int, max) 193 for i := 0; i < max; i++ { 194 out[i] = i 195 } 196 return out 197 }, 198 "fpstr": func(x fp.Element) string { 199 bv := new(big.Int) 200 x.BigInt(bv) 201 return bv.String() 202 }, 203 "hashFnName": func() string { 204 return hashFnName 205 }, 206 } 207 208 if len(vk.PublicAndCommitmentCommitted) > 1 { 209 log.Warn().Msg("exporting solidity verifier with more than one commitment is not supported") 210 } else if len(vk.PublicAndCommitmentCommitted) == 1 { 211 log.Warn().Msg("exporting solidity verifier only supports `sha256` as `HashToField`. The generated contract may not work for proofs generated with other hash functions.") 212 } 213 214 tmpl, err := template.New("").Funcs(helpers).Parse(solidityTemplate) 215 if err != nil { 216 return err 217 } 218 219 // negate Beta, Gamma and Delta, to avoid negating proof elements in the verifier 220 var betaNeg curve.G2Affine 221 betaNeg.Neg(&vk.G2.Beta) 222 beta := vk.G2.Beta 223 vk.G2.Beta = betaNeg 224 vk.G2.Gamma, vk.G2.gammaNeg = vk.G2.gammaNeg, vk.G2.Gamma 225 vk.G2.Delta, vk.G2.deltaNeg = vk.G2.deltaNeg, vk.G2.Delta 226 227 // execute template 228 err = tmpl.Execute(w, struct { 229 Cfg solidity.ExportConfig 230 Vk VerifyingKey 231 }{ 232 Cfg: cfg, 233 Vk: *vk, 234 }) 235 236 // restore Beta, Gamma and Delta 237 vk.G2.Beta = beta 238 vk.G2.Gamma, vk.G2.gammaNeg = vk.G2.gammaNeg, vk.G2.Gamma 239 vk.G2.Delta, vk.G2.deltaNeg = vk.G2.deltaNeg, vk.G2.Delta 240 241 return err 242 }