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  }