github.com/consensys/gnark-crypto@v0.14.0/ecc/bls12-378/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-378/fr"
    30  	"github.com/consensys/gnark-crypto/ecc/bls12-378/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  }