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