github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-381/fr/fri/fri.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 fri 18 19 import ( 20 "bytes" 21 "errors" 22 "fmt" 23 "hash" 24 "math/big" 25 "math/bits" 26 27 "github.com/consensys/gnark-crypto/accumulator/merkletree" 28 "github.com/consensys/gnark-crypto/ecc" 29 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr" 30 "github.com/consensys/gnark-crypto/ecc/bls12-381/fr/fft" 31 fiatshamir "github.com/consensys/gnark-crypto/fiat-shamir" 32 ) 33 34 var ( 35 ErrLowDegree = errors.New("the fully folded polynomial in not of degree 1") 36 ErrProximityTestFolding = errors.New("one round of interaction failed") 37 ErrOddSize = errors.New("the size should be even") 38 ErrMerkleRoot = errors.New("merkle roots of the opening and the proof of proximity don't coincide") 39 ErrMerklePath = errors.New("merkle path proof is wrong") 40 ErrRangePosition = errors.New("the asked opening position is out of range") 41 ) 42 43 const rho = 8 44 45 const nbRounds = 1 46 47 // 2^{-1}, used several times 48 var twoInv fr.Element 49 50 // Digest commitment of a polynomial. 51 type Digest []byte 52 53 // merkleProof helper structure to build the merkle proof 54 // At each round, two contiguous values from the evaluated polynomial 55 // are queried. For one value, the full Merkle path will be provided. 56 // For the neighbor value, only the leaf is provided (so ProofSet will 57 // be empty), since the Merkle path is the same as for the first value. 58 type MerkleProof struct { 59 60 // Merkle root 61 MerkleRoot []byte 62 63 // ProofSet stores [leaf ∥ node_1 ∥ .. ∥ merkleRoot ], where the leaf is not 64 // hashed. 65 ProofSet [][]byte 66 67 // number of leaves of the tree. 68 numLeaves uint64 69 } 70 71 // MerkleProof used to open a polynomial 72 type OpeningProof struct { 73 74 // those fields are private since they are only needed for 75 // the verification, which is abstracted in the VerifyOpening 76 // method. 77 merkleRoot []byte 78 ProofSet [][]byte 79 numLeaves uint64 80 index uint64 81 82 // ClaimedValue value of the leaf. This field is exported 83 // because it's needed for protocols using polynomial commitment 84 // schemes (to verify an algebraic relation). 85 ClaimedValue fr.Element 86 } 87 88 // IOPP Interactive Oracle Proof of Proximity 89 type IOPP uint 90 91 const ( 92 // Multiplicative version of FRI, using the map x->x², on a 93 // power of 2 subgroup of Fr^{*}. 94 RADIX_2_FRI IOPP = iota 95 ) 96 97 // round contains the data corresponding to a single round 98 // of fri. 99 // It consists of a list of Interactions between the prover and the verifier, 100 // where each interaction contains a challenge provided by the verifier, as 101 // well as MerkleProofs for the queries of the verifier. The Merkle proofs 102 // correspond to the openings of the i-th folded polynomial at 2 points that 103 // belong to the same fiber of x -> x². 104 type Round struct { 105 106 // stores the Interactions between the prover and the verifier. 107 // Each interaction results in a set or merkle proofs, corresponding 108 // to the queries of the verifier. 109 Interactions [][2]MerkleProof 110 111 // evaluation stores the evaluation of the fully folded polynomial. 112 // The fully folded polynomial is constant, and is evaluated on a 113 // a set of size \rho. Since the polynomial is supposed to be constant, 114 // only one evaluation, corresponding to the polynomial, is given. Since 115 // the prover cannot know in advance which entry the verifier will query, 116 // providing a single evaluation 117 Evaluation fr.Element 118 } 119 120 // ProofOfProximity proof of proximity, attesting that 121 // a function is d-close to a low degree polynomial. 122 // 123 // It is composed of a series of Interactions, emulated with Fiat Shamir, 124 type ProofOfProximity struct { 125 126 // ID unique ID attached to the proof of proximity. It's needed for 127 // protocols using Fiat Shamir for instance, where challenges are derived 128 // from the proof of proximity. 129 ID []byte 130 131 // round contains the data corresponding to a single round 132 // of fri. There are nbRounds rounds of Interactions. 133 Rounds []Round 134 } 135 136 // Iopp interface that an iopp should implement 137 type Iopp interface { 138 139 // BuildProofOfProximity creates a proof of proximity that p is d-close to a polynomial 140 // of degree len(p). The proof is built non interactively using Fiat Shamir. 141 BuildProofOfProximity(p []fr.Element) (ProofOfProximity, error) 142 143 // VerifyProofOfProximity verifies the proof of proximity. It returns an error if the 144 // verification fails. 145 VerifyProofOfProximity(proof ProofOfProximity) error 146 147 // Opens a polynomial at gⁱ where i = position. 148 Open(p []fr.Element, position uint64) (OpeningProof, error) 149 150 // Verifies the opening of a polynomial at gⁱ where i = position. 151 VerifyOpening(position uint64, openingProof OpeningProof, pp ProofOfProximity) error 152 } 153 154 // GetRho returns the factor ρ = size_code_word/size_polynomial 155 func GetRho() int { 156 return rho 157 } 158 159 func init() { 160 twoInv.SetUint64(2).Inverse(&twoInv) 161 } 162 163 // New creates a new IOPP capable to handle degree(size) polynomials. 164 func (iopp IOPP) New(size uint64, h hash.Hash) Iopp { 165 switch iopp { 166 case RADIX_2_FRI: 167 return newRadixTwoFri(size, h) 168 default: 169 panic("iopp name is not recognized") 170 } 171 } 172 173 // radixTwoFri empty structs implementing compressionFunction for 174 // the squaring function. 175 type radixTwoFri struct { 176 177 // hash function that is used for Fiat Shamir and for committing to 178 // the oracles. 179 h hash.Hash 180 181 // nbSteps number of Interactions between the prover and the verifier 182 nbSteps int 183 184 // domain used to build the Reed Solomon code from the given polynomial. 185 // The size of the domain is ρ*size_polynomial. 186 domain *fft.Domain 187 } 188 189 func newRadixTwoFri(size uint64, h hash.Hash) radixTwoFri { 190 191 var res radixTwoFri 192 193 // computing the number of steps 194 n := ecc.NextPowerOfTwo(size) 195 nbSteps := bits.TrailingZeros(uint(n)) 196 res.nbSteps = nbSteps 197 198 // extending the domain 199 n = n * rho 200 201 // building the domains 202 res.domain = fft.NewDomain(n) 203 204 // hash function 205 res.h = h 206 207 return res 208 } 209 210 // convertCanonicalSorted convert the index i, an entry in a 211 // sorted polynomial, to the corresponding entry in canonical 212 // representation. n is the size of the polynomial. 213 func convertCanonicalSorted(i, n int) int { 214 215 if i < n/2 { 216 return 2 * i 217 } else { 218 l := n - (i + 1) 219 l = 2 * l 220 return n - l - 1 221 } 222 223 } 224 225 // deriveQueriesPositions derives the indices of the oracle 226 // function that the verifier has to pick, in sorted form. 227 // * pos is the initial position, i.e. the logarithm of the first challenge 228 // * size is the size of the initial polynomial 229 // * The result is a slice of []int, where each entry is a tuple (iₖ), such that 230 // the verifier needs to evaluate ∑ₖ oracle(iₖ)xᵏ to build 231 // the folded function. 232 func (s radixTwoFri) deriveQueriesPositions(pos int, size int) []int { 233 234 _s := size / 2 235 res := make([]int, s.nbSteps) 236 res[0] = pos 237 for i := 1; i < s.nbSteps; i++ { 238 t := (res[i-1] - (res[i-1] % 2)) / 2 239 res[i] = convertCanonicalSorted(t, _s) 240 _s = _s / 2 241 } 242 243 return res 244 } 245 246 // sort orders the evaluation of a polynomial on a domain 247 // such that contiguous entries are in the same fiber: 248 // {q(g⁰), q(g^{n/2}), q(g¹), q(g^{1+n/2}),...,q(g^{n/2-1}), q(gⁿ⁻¹)} 249 func sort(evaluations []fr.Element) []fr.Element { 250 q := make([]fr.Element, len(evaluations)) 251 n := len(evaluations) / 2 252 for i := 0; i < n; i++ { 253 q[2*i].Set(&evaluations[i]) 254 q[2*i+1].Set(&evaluations[i+n]) 255 } 256 return q 257 } 258 259 // Opens a polynomial at gⁱ where i = position. 260 func (s radixTwoFri) Open(p []fr.Element, position uint64) (OpeningProof, error) { 261 262 // check that position is in the correct range 263 if position >= s.domain.Cardinality { 264 return OpeningProof{}, ErrRangePosition 265 } 266 267 // put q in evaluation form 268 q := make([]fr.Element, s.domain.Cardinality) 269 copy(q, p) 270 s.domain.FFT(q, fft.DIF) 271 fft.BitReverse(q) 272 273 // sort q to have fibers in contiguous entries. The goal is to have one 274 // Merkle path for both openings of entries which are in the same fiber. 275 q = sort(q) 276 277 // build the Merkle proof, we the position is converted to fit the sorted polynomial 278 pos := convertCanonicalSorted(int(position), len(q)) 279 280 tree := merkletree.New(s.h) 281 err := tree.SetIndex(uint64(pos)) 282 if err != nil { 283 return OpeningProof{}, err 284 } 285 for i := 0; i < len(q); i++ { 286 tree.Push(q[i].Marshal()) 287 } 288 var res OpeningProof 289 res.merkleRoot, res.ProofSet, res.index, res.numLeaves = tree.Prove() 290 291 // set the claimed value, which is the first entry of the Merkle proof 292 res.ClaimedValue.SetBytes(res.ProofSet[0]) 293 294 return res, nil 295 } 296 297 // Verifies the opening of a polynomial. 298 // * position the point at which the proof is opened (the point is gⁱ where i = position) 299 // * openingProof Merkle path proof 300 // * pp proof of proximity, needed because before opening Merkle path proof one should be sure that the 301 // committed values come from a polynomial. During the verification of the Merkle path proof, the root 302 // hash of the Merkle path is compared to the root hash of the first interaction of the proof of proximity, 303 // those should be equal, if not an error is raised. 304 func (s radixTwoFri) VerifyOpening(position uint64, openingProof OpeningProof, pp ProofOfProximity) error { 305 306 // To query the Merkle path, we look at the first series of Interactions, and check whether it's the point 307 // at 'position' or its neighbor that contains the full Merkle path. 308 var fullMerkleProof int 309 if len(pp.Rounds[0].Interactions[0][0].ProofSet) > len(pp.Rounds[0].Interactions[0][1].ProofSet) { 310 fullMerkleProof = 0 311 } else { 312 fullMerkleProof = 1 313 } 314 315 // check that the merkle roots coincide 316 if !bytes.Equal(openingProof.merkleRoot, pp.Rounds[0].Interactions[0][fullMerkleProof].MerkleRoot) { 317 return ErrMerkleRoot 318 } 319 320 // convert position to the sorted version 321 sizePoly := s.domain.Cardinality 322 pos := convertCanonicalSorted(int(position), int(sizePoly)) 323 324 // check the Merkle proof 325 res := merkletree.VerifyProof(s.h, openingProof.merkleRoot, openingProof.ProofSet, uint64(pos), openingProof.numLeaves) 326 if !res { 327 return ErrMerklePath 328 } 329 return nil 330 331 } 332 333 // foldPolynomialLagrangeBasis folds a polynomial p, expressed in Lagrange basis. 334 // 335 // Fᵣ[X]/(Xⁿ-1) is a free module of rank 2 on Fᵣ[Y]/(Y^{n/2}-1). If 336 // p∈ Fᵣ[X]/(Xⁿ-1), expressed in Lagrange basis, the function finds the coordinates 337 // p₁, p₂ of p in Fᵣ[Y]/(Y^{n/2}-1), expressed in Lagrange basis. Finally, it computes 338 // p₁ + x*p₂ and returns it. 339 // 340 // * p is the polynomial to fold, in Lagrange basis, sorted like this: p = [p(1),p(-1),p(g),p(-g),p(g²),p(-g²),...] 341 // * g is a generator of the subgroup of Fᵣ^{*} of size len(p) 342 // * x is the folding challenge x, used to return p₁+x*p₂ 343 func foldPolynomialLagrangeBasis(pSorted []fr.Element, gInv, x fr.Element) []fr.Element { 344 345 // we have the following system 346 // p₁(g²ⁱ)+gⁱp₂(g²ⁱ) = p(gⁱ) 347 // p₁(g²ⁱ)-gⁱp₂(g²ⁱ) = p(-gⁱ) 348 // we solve the system for p₁(g²ⁱ),p₂(g²ⁱ) 349 s := len(pSorted) 350 res := make([]fr.Element, s/2) 351 352 var p1, p2, acc fr.Element 353 acc.SetOne() 354 355 for i := 0; i < s/2; i++ { 356 357 p1.Add(&pSorted[2*i], &pSorted[2*i+1]) 358 p2.Sub(&pSorted[2*i], &pSorted[2*i+1]).Mul(&p2, &acc) 359 res[i].Mul(&p2, &x).Add(&res[i], &p1).Mul(&res[i], &twoInv) 360 361 acc.Mul(&acc, &gInv) 362 363 } 364 365 return res 366 } 367 368 // buildProofOfProximitySingleRound generates a proof that a function, given as an oracle from 369 // the verifier point of view, is in fact δ-close to a polynomial. 370 // * salt is a variable for multi rounds, it allows to generate different challenges using Fiat Shamir 371 // * p is in evaluation form 372 func (s radixTwoFri) buildProofOfProximitySingleRound(salt fr.Element, p []fr.Element) (Round, error) { 373 374 // the proof will contain nbSteps Interactions 375 var res Round 376 res.Interactions = make([][2]MerkleProof, s.nbSteps) 377 378 // Fiat Shamir transcript to derive the challenges. The xᵢ are used to fold the 379 // polynomials. 380 // During the i-th round, the prover has a polynomial P of degree n. The verifier sends 381 // xᵢ∈ Fᵣ to the prover. The prover expresses F in Fᵣ[X,Y]/<Y-X²> as 382 // P₀(Y)+X P₁(Y) where P₀, P₁ are of degree n/2, and he then folds the polynomial 383 // by replacing x by xᵢ. 384 xis := make([]string, s.nbSteps+1) 385 for i := 0; i < s.nbSteps; i++ { 386 xis[i] = fmt.Sprintf("x%d", i) 387 } 388 xis[s.nbSteps] = "s0" 389 fs := fiatshamir.NewTranscript(s.h, xis...) 390 391 // the salt is binded to the first challenge, to ensure the challenges 392 // are different at each round. 393 err := fs.Bind(xis[0], salt.Marshal()) 394 if err != nil { 395 return Round{}, err 396 } 397 398 // step 1 : fold the polynomial using the xi 399 400 // evalsAtRound stores the list of the nbSteps polynomial evaluations, each evaluation 401 // corresponds to the evaluation o the folded polynomial at round i. 402 evalsAtRound := make([][]fr.Element, s.nbSteps) 403 404 // evaluate p and sort the result 405 _p := make([]fr.Element, s.domain.Cardinality) 406 copy(_p, p) 407 408 // gInv inverse of the generator of the cyclic group of size the size of the polynomial. 409 // The size of the cyclic group is ρ*s.domainSize, and not s.domainSize. 410 var gInv fr.Element 411 gInv.Set(&s.domain.GeneratorInv) 412 413 for i := 0; i < s.nbSteps; i++ { 414 415 evalsAtRound[i] = sort(_p) 416 417 // compute the root hash, needed to derive xi 418 t := merkletree.New(s.h) 419 for k := 0; k < len(_p); k++ { 420 t.Push(evalsAtRound[i][k].Marshal()) 421 } 422 rh := t.Root() 423 err := fs.Bind(xis[i], rh) 424 if err != nil { 425 return res, err 426 } 427 428 // derive the challenge 429 bxi, err := fs.ComputeChallenge(xis[i]) 430 if err != nil { 431 return res, err 432 } 433 var xi fr.Element 434 xi.SetBytes(bxi) 435 436 // fold _p, reusing its memory 437 _p = foldPolynomialLagrangeBasis(evalsAtRound[i], gInv, xi) 438 439 // g <- g² 440 gInv.Square(&gInv) 441 442 } 443 444 // last round, provide the evaluation. The fully folded polynomial is of size rho. It should 445 // correspond to the evaluation of a polynomial of degree 1 on ρ points, so those points 446 // are supposed to be on a line. 447 res.Evaluation.Set(&_p[0]) 448 449 // step 2: provide the Merkle proofs of the queries 450 451 // derive the verifier queries 452 err = fs.Bind(xis[s.nbSteps], res.Evaluation.Marshal()) 453 if err != nil { 454 return res, err 455 } 456 binSeed, err := fs.ComputeChallenge(xis[s.nbSteps]) 457 if err != nil { 458 return res, err 459 } 460 var bPos, bCardinality big.Int 461 bPos.SetBytes(binSeed) 462 bCardinality.SetUint64(s.domain.Cardinality) 463 bPos.Mod(&bPos, &bCardinality) 464 si := s.deriveQueriesPositions(int(bPos.Uint64()), int(s.domain.Cardinality)) 465 466 for i := 0; i < s.nbSteps; i++ { 467 468 // build proofs of queries at s[i] 469 t := merkletree.New(s.h) 470 err := t.SetIndex(uint64(si[i])) 471 if err != nil { 472 return res, err 473 } 474 for k := 0; k < len(evalsAtRound[i]); k++ { 475 t.Push(evalsAtRound[i][k].Marshal()) 476 } 477 mr, ProofSet, _, numLeaves := t.Prove() 478 479 // c denotes the entry that contains the full Merkle proof. The entry 1-c will 480 // only contain 2 elements, which are the neighbor point, and the hash of the 481 // first point. The remaining of the Merkle path is common to both the original 482 // point and its neighbor. 483 c := si[i] % 2 484 res.Interactions[i][c] = MerkleProof{mr, ProofSet, numLeaves} 485 res.Interactions[i][1-c] = MerkleProof{ 486 mr, 487 make([][]byte, 2), 488 numLeaves, 489 } 490 res.Interactions[i][1-c].ProofSet[0] = evalsAtRound[i][si[i]+1-2*c].Marshal() 491 s.h.Reset() 492 _, err = s.h.Write(res.Interactions[i][c].ProofSet[0]) 493 if err != nil { 494 return res, err 495 } 496 res.Interactions[i][1-c].ProofSet[1] = s.h.Sum(nil) 497 498 } 499 500 return res, nil 501 502 } 503 504 // BuildProofOfProximity generates a proof that a function, given as an oracle from 505 // the verifier point of view, is in fact δ-close to a polynomial. 506 func (s radixTwoFri) BuildProofOfProximity(p []fr.Element) (ProofOfProximity, error) { 507 508 // the proof will contain nbSteps Interactions 509 var proof ProofOfProximity 510 proof.Rounds = make([]Round, nbRounds) 511 512 // evaluate p 513 // evaluate p and sort the result 514 _p := make([]fr.Element, s.domain.Cardinality) 515 copy(_p, p) 516 s.domain.FFT(_p, fft.DIF) 517 fft.BitReverse(_p) 518 519 var err error 520 var salt, one fr.Element 521 one.SetOne() 522 for i := 0; i < nbRounds; i++ { 523 proof.Rounds[i], err = s.buildProofOfProximitySingleRound(salt, _p) 524 if err != nil { 525 return proof, err 526 } 527 salt.Add(&salt, &one) 528 } 529 530 return proof, nil 531 } 532 533 // verifyProofOfProximitySingleRound verifies the proof of proximity. It returns an error if the 534 // verification fails. 535 func (s radixTwoFri) verifyProofOfProximitySingleRound(salt fr.Element, proof Round) error { 536 537 // Fiat Shamir transcript to derive the challenges 538 xis := make([]string, s.nbSteps+1) 539 for i := 0; i < s.nbSteps; i++ { 540 xis[i] = fmt.Sprintf("x%d", i) 541 } 542 xis[s.nbSteps] = "s0" 543 fs := fiatshamir.NewTranscript(s.h, xis...) 544 545 xi := make([]fr.Element, s.nbSteps) 546 547 // the salt is binded to the first challenge, to ensure the challenges 548 // are different at each round. 549 err := fs.Bind(xis[0], salt.Marshal()) 550 if err != nil { 551 return err 552 } 553 554 for i := 0; i < s.nbSteps; i++ { 555 err := fs.Bind(xis[i], proof.Interactions[i][0].MerkleRoot) 556 if err != nil { 557 return err 558 } 559 bxi, err := fs.ComputeChallenge(xis[i]) 560 if err != nil { 561 return err 562 } 563 xi[i].SetBytes(bxi) 564 } 565 566 // derive the verifier queries 567 // for i := 0; i < len(proof.evaluation); i++ { 568 // err := fs.Bind(xis[s.nbSteps], proof.evaluation[i].Marshal()) 569 // if err != nil { 570 // return err 571 // } 572 // } 573 err = fs.Bind(xis[s.nbSteps], proof.Evaluation.Marshal()) 574 if err != nil { 575 return err 576 } 577 binSeed, err := fs.ComputeChallenge(xis[s.nbSteps]) 578 if err != nil { 579 return err 580 } 581 var bPos, bCardinality big.Int 582 bPos.SetBytes(binSeed) 583 bCardinality.SetUint64(s.domain.Cardinality) 584 bPos.Mod(&bPos, &bCardinality) 585 si := s.deriveQueriesPositions(int(bPos.Uint64()), int(s.domain.Cardinality)) 586 587 // for each round check the Merkle proof and the correctness of the folding 588 589 // current size of the polynomial 590 var accGInv fr.Element 591 accGInv.Set(&s.domain.GeneratorInv) 592 for i := 0; i < s.nbSteps; i++ { 593 594 // correctness of Merkle proof 595 // c is the entry containing the full Merkle proof. 596 c := si[i] % 2 597 res := merkletree.VerifyProof( 598 s.h, 599 proof.Interactions[i][c].MerkleRoot, 600 proof.Interactions[i][c].ProofSet, 601 uint64(si[i]), 602 proof.Interactions[i][c].numLeaves, 603 ) 604 if !res { 605 return ErrMerklePath 606 } 607 608 // we verify the Merkle proof for the neighbor query, to do that we have 609 // to pick the full Merkle proof of the first entry, stripped off of the leaf and 610 // the first node. We replace the leaf and the first node by the leaf and the first 611 // node of the partial Merkle proof, since the leaf and the first node of both proofs 612 // are the only entries that differ. 613 ProofSet := make([][]byte, len(proof.Interactions[i][c].ProofSet)) 614 copy(ProofSet[2:], proof.Interactions[i][c].ProofSet[2:]) 615 ProofSet[0] = proof.Interactions[i][1-c].ProofSet[0] 616 ProofSet[1] = proof.Interactions[i][1-c].ProofSet[1] 617 res = merkletree.VerifyProof( 618 s.h, 619 proof.Interactions[i][1-c].MerkleRoot, 620 ProofSet, 621 uint64(si[i]+1-2*c), 622 proof.Interactions[i][1-c].numLeaves, 623 ) 624 if !res { 625 return ErrMerklePath 626 } 627 628 // correctness of the folding 629 if i < s.nbSteps-1 { 630 631 var fe, fo, l, r, fn fr.Element 632 633 // l = P(gⁱ), r = P(g^{i+n/2}) 634 l.SetBytes(proof.Interactions[i][0].ProofSet[0]) 635 r.SetBytes(proof.Interactions[i][1].ProofSet[0]) 636 637 // (g^{si[i]}, g^{si[i]+1}) is the fiber of g^{2*si[i]}. The system to solve 638 // (for P₀(g^{2si[i]}), P₀(g^{2si[i]}) ) is: 639 // P(g^{si[i]}) = P₀(g^{2si[i]}) + g^{si[i]/2}*P₀(g^{2si[i]}) 640 // P(g^{si[i]+1}) = P₀(g^{2si[i]}) - g^{si[i]/2}*P₀(g^{2si[i]}) 641 bm := big.NewInt(int64(si[i] / 2)) 642 var ginv fr.Element 643 ginv.Exp(accGInv, bm) 644 fe.Add(&l, &r) // P₁(g²ⁱ) (to be multiplied by 2⁻¹) 645 fo.Sub(&l, &r).Mul(&fo, &ginv) // P₀(g²ⁱ) (to be multiplied by 2⁻¹) 646 fo.Mul(&fo, &xi[i]).Add(&fo, &fe).Mul(&fo, &twoInv) // P₀(g²ⁱ) + xᵢ * P₁(g²ⁱ) 647 648 fn.SetBytes(proof.Interactions[i+1][si[i+1]%2].ProofSet[0]) 649 650 if !fo.Equal(&fn) { 651 return ErrProximityTestFolding 652 } 653 654 // next inverse generator 655 accGInv.Square(&accGInv) 656 } 657 658 } 659 660 // last transition 661 var fe, fo, l, r fr.Element 662 663 l.SetBytes(proof.Interactions[s.nbSteps-1][0].ProofSet[0]) 664 r.SetBytes(proof.Interactions[s.nbSteps-1][1].ProofSet[0]) 665 666 _si := si[s.nbSteps-1] / 2 667 668 accGInv.Exp(accGInv, big.NewInt(int64(_si))) 669 670 fe.Add(&l, &r) // P₁(g²ⁱ) (to be multiplied by 2⁻¹) 671 fo.Sub(&l, &r).Mul(&fo, &accGInv) // P₀(g²ⁱ) (to be multiplied by 2⁻¹) 672 fo.Mul(&fo, &xi[s.nbSteps-1]).Add(&fo, &fe).Mul(&fo, &twoInv) // P₀(g²ⁱ) + xᵢ * P₁(g²ⁱ) 673 674 // Last step: the final evaluation should be the evaluation of a degree 0 polynomial, 675 // so it must be constant. 676 if !fo.Equal(&proof.Evaluation) { 677 return ErrProximityTestFolding 678 } 679 680 return nil 681 } 682 683 // VerifyProofOfProximity verifies the proof, by checking each interaction one 684 // by one. 685 func (s radixTwoFri) VerifyProofOfProximity(proof ProofOfProximity) error { 686 687 var salt, one fr.Element 688 one.SetOne() 689 for i := 0; i < nbRounds; i++ { 690 err := s.verifyProofOfProximitySingleRound(salt, proof.Rounds[i]) 691 if err != nil { 692 return err 693 } 694 salt.Add(&salt, &one) 695 } 696 return nil 697 698 }