github.com/consensys/gnark@v0.11.0/backend/plonk/bn254/solidity.go (about)

     1  package plonk
     2  
     3  const tmplSolidityVerifier = `// SPDX-License-Identifier: Apache-2.0
     4  
     5  // Copyright 2023 Consensys Software Inc.
     6  //
     7  // Licensed under the Apache License, Version 2.0 (the "License");
     8  // you may not use this file except in compliance with the License.
     9  // You may obtain a copy of the License at
    10  //
    11  //     http://www.apache.org/licenses/LICENSE-2.0
    12  //
    13  // Unless required by applicable law or agreed to in writing, software
    14  // distributed under the License is distributed on an "AS IS" BASIS,
    15  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  // See the License for the specific language governing permissions and
    17  // limitations under the License.
    18  
    19  // Code generated by gnark DO NOT EDIT
    20  
    21  pragma solidity {{ .Cfg.PragmaVersion }};
    22  
    23  contract PlonkVerifier {
    24  
    25    uint256 private constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
    26    uint256 private constant R_MOD_MINUS_ONE = 21888242871839275222246405745257275088548364400416034343698204186575808495616;
    27    uint256 private constant P_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
    28    {{ range $index, $element := .Vk.Kzg.G2 }}
    29    uint256 private constant G2_SRS_{{ $index }}_X_0 = {{ (fpstr $element.X.A1) }};
    30    uint256 private constant G2_SRS_{{ $index }}_X_1 = {{ (fpstr $element.X.A0) }};
    31    uint256 private constant G2_SRS_{{ $index }}_Y_0 = {{ (fpstr $element.Y.A1) }};
    32    uint256 private constant G2_SRS_{{ $index }}_Y_1 = {{ (fpstr $element.Y.A0) }};
    33    {{ end }}
    34    uint256 private constant G1_SRS_X = {{ fpstr .Vk.Kzg.G1.X }};
    35    uint256 private constant G1_SRS_Y = {{ fpstr .Vk.Kzg.G1.Y }};
    36  
    37    // ----------------------- vk ---------------------
    38    uint256 private constant VK_NB_PUBLIC_INPUTS = {{ .Vk.NbPublicVariables }};
    39    uint256 private constant VK_DOMAIN_SIZE = {{ .Vk.Size }};
    40    uint256 private constant VK_INV_DOMAIN_SIZE = {{ (frstr .Vk.SizeInv) }};
    41    uint256 private constant VK_OMEGA = {{ (frstr .Vk.Generator) }};
    42    uint256 private constant VK_QL_COM_X = {{ (fpstr .Vk.Ql.X) }};
    43    uint256 private constant VK_QL_COM_Y = {{ (fpstr .Vk.Ql.Y) }};
    44    uint256 private constant VK_QR_COM_X = {{ (fpstr .Vk.Qr.X) }};
    45    uint256 private constant VK_QR_COM_Y = {{ (fpstr .Vk.Qr.Y) }};
    46    uint256 private constant VK_QM_COM_X = {{ (fpstr .Vk.Qm.X) }};
    47    uint256 private constant VK_QM_COM_Y = {{ (fpstr .Vk.Qm.Y) }};
    48    uint256 private constant VK_QO_COM_X = {{ (fpstr .Vk.Qo.X) }};
    49    uint256 private constant VK_QO_COM_Y = {{ (fpstr .Vk.Qo.Y) }};
    50    uint256 private constant VK_QK_COM_X = {{ (fpstr .Vk.Qk.X) }};
    51    uint256 private constant VK_QK_COM_Y = {{ (fpstr .Vk.Qk.Y) }};
    52    {{ range $index, $element := .Vk.S }}
    53    uint256 private constant VK_S{{ inc $index }}_COM_X = {{ (fpstr $element.X) }};
    54    uint256 private constant VK_S{{ inc $index }}_COM_Y = {{ (fpstr $element.Y) }};
    55    {{ end }}
    56    uint256 private constant VK_COSET_SHIFT = {{ frstr .Vk.CosetShift }};
    57    
    58    {{ range $index, $element := .Vk.Qcp}}
    59    uint256 private constant VK_QCP_{{ $index }}_X = {{ (fpstr $element.X) }};
    60    uint256 private constant VK_QCP_{{ $index }}_Y = {{ (fpstr $element.Y) }};
    61    {{ end }}
    62    
    63    {{ range $index, $element := .Vk.CommitmentConstraintIndexes -}}
    64    uint256 private constant VK_INDEX_COMMIT_API_{{ $index }} = {{ $element }};
    65    {{ end -}}
    66    uint256 private constant VK_NB_CUSTOM_GATES = {{ len .Vk.CommitmentConstraintIndexes }};
    67  
    68    // ------------------------------------------------
    69  
    70    // size of the proof without call custom gate
    71    uint256 private constant FIXED_PROOF_SIZE = 0x300;
    72  
    73    // offset proof
    74    {{ $offset := 0 }}
    75    uint256 private constant PROOF_L_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    76    uint256 private constant PROOF_L_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    77    uint256 private constant PROOF_R_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    78    uint256 private constant PROOF_R_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    79    uint256 private constant PROOF_O_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    80    uint256 private constant PROOF_O_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    81  
    82    // h = h_0 + x^{n+2}h_1 + x^{2(n+2)}h_2
    83    uint256 private constant PROOF_H_0_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    84    uint256 private constant PROOF_H_0_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    85    uint256 private constant PROOF_H_1_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    86    uint256 private constant PROOF_H_1_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    87    uint256 private constant PROOF_H_2_COM_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    88    uint256 private constant PROOF_H_2_COM_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    89  
    90    // "evaluations of wire polynomials at zeta
    91    uint256 private constant PROOF_L_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    92    uint256 private constant PROOF_R_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    93    uint256 private constant PROOF_O_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}}
    94  
    95    // S1(zeta),S2(zeta)
    96    uint256 private constant PROOF_S1_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} // Sσ1(zeta)
    97    uint256 private constant PROOF_S2_AT_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}} // Sσ2(zeta)
    98  
    99    // [Z]
   100    uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   101    uint256 private constant PROOF_GRAND_PRODUCT_COMMITMENT_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   102  
   103    uint256 private constant PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA = {{ hex $offset }};{{ $offset = add $offset 0x20}} // z(w*zeta)
   104  
   105    // Folded proof for the opening of linearised poly, l, r, o, s_1, s_2, qcp
   106    uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   107    uint256 private constant PROOF_BATCH_OPENING_AT_ZETA_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   108  
   109    uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   110    uint256 private constant PROOF_OPENING_AT_ZETA_OMEGA_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   111  
   112    uint256 private constant PROOF_OPENING_QCP_AT_ZETA = {{ hex $offset }};
   113    uint256 private constant PROOF_BSB_COMMITMENTS = {{ hex (add $offset (mul (len .Vk.CommitmentConstraintIndexes) 32 ) )}};
   114  
   115    // -------- offset state
   116  
   117    // challenges to check the claimed quotient
   118    {{ $offset = 0 }}
   119    uint256 private constant STATE_ALPHA = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   120    uint256 private constant STATE_BETA = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   121    uint256 private constant STATE_GAMMA = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   122    uint256 private constant STATE_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   123    uint256 private constant STATE_ALPHA_SQUARE_LAGRANGE_0 = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   124    uint256 private constant STATE_FOLDED_H_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   125    uint256 private constant STATE_FOLDED_H_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   126    uint256 private constant STATE_LINEARISED_POLYNOMIAL_X = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   127    uint256 private constant STATE_LINEARISED_POLYNOMIAL_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   128    uint256 private constant STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   129    uint256 private constant STATE_FOLDED_CLAIMED_VALUES = {{ hex $offset }};{{ $offset = add $offset 0x20}} // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp
   130    uint256 private constant STATE_FOLDED_DIGESTS_X = {{ hex $offset }};{{ $offset = add $offset 0x20}} // linearised poly, l, r, o, s_1, s_2, qcp
   131    uint256 private constant STATE_FOLDED_DIGESTS_Y = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   132    uint256 private constant STATE_PI = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   133    uint256 private constant STATE_ZETA_POWER_N_MINUS_ONE = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   134    uint256 private constant STATE_GAMMA_KZG = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   135    uint256 private constant STATE_SUCCESS = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   136    uint256 private constant STATE_CHECK_VAR = {{ hex $offset }};{{ $offset = add $offset 0x20}} // /!\ this slot is used for debugging only
   137    uint256 private constant STATE_LAST_MEM = {{ hex $offset }};{{ $offset = add $offset 0x20}}
   138  
   139    // -------- utils (for Fiat Shamir)
   140    uint256 private constant FS_ALPHA = 0x616C706861; // "alpha"
   141    uint256 private constant FS_BETA = 0x62657461; // "beta"
   142    uint256 private constant FS_GAMMA = 0x67616d6d61; // "gamma"
   143    uint256 private constant FS_ZETA = 0x7a657461; // "zeta"
   144    uint256 private constant FS_GAMMA_KZG = 0x67616d6d61; // "gamma"
   145  
   146    // -------- errors
   147    uint256 private constant ERROR_STRING_ID = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string)
   148  
   149    {{ if (gt (len .Vk.CommitmentConstraintIndexes) 0 )}}
   150    // -------- utils (for hash_fr)
   151  	uint256 private constant HASH_FR_BB = 340282366920938463463374607431768211456; // 2**128
   152  	uint256 private constant HASH_FR_ZERO_UINT256 = 0;
   153  	uint8 private constant HASH_FR_LEN_IN_BYTES = 48;
   154  	uint8 private constant HASH_FR_SIZE_DOMAIN = 11;
   155  	uint8 private constant HASH_FR_ONE = 1;
   156  	uint8 private constant HASH_FR_TWO = 2;
   157    {{ end }}
   158  
   159    // -------- precompiles
   160    uint8 private constant SHA2 = 0x2;
   161    uint8 private constant MOD_EXP = 0x5;
   162    uint8 private constant EC_ADD = 0x6;
   163    uint8 private constant EC_MUL = 0x7;
   164    uint8 private constant EC_PAIR = 0x8;
   165    
   166    /// Verify a Plonk proof.
   167    /// Reverts if the proof or the public inputs are malformed.
   168    /// @param proof serialised plonk proof (using gnark's MarshalSolidity)
   169    /// @param public_inputs (must be reduced)
   170    /// @return success true if the proof passes false otherwise
   171    function Verify(bytes calldata proof, uint256[] calldata public_inputs) 
   172    public view returns(bool success) {
   173  
   174      assembly {
   175  
   176        let mem := mload(0x40)
   177        let freeMem := add(mem, STATE_LAST_MEM)
   178  
   179        // sanity checks
   180        check_number_of_public_inputs(public_inputs.length)
   181        check_inputs_size(public_inputs.length, public_inputs.offset)
   182        check_proof_size(proof.length)
   183        check_proof_openings_size(proof.offset)
   184  
   185        // compute the challenges
   186        let prev_challenge_non_reduced
   187        prev_challenge_non_reduced := derive_gamma(proof.offset, public_inputs.length, public_inputs.offset)
   188        prev_challenge_non_reduced := derive_beta(prev_challenge_non_reduced)
   189        prev_challenge_non_reduced := derive_alpha(proof.offset, prev_challenge_non_reduced)
   190        derive_zeta(proof.offset, prev_challenge_non_reduced)
   191  
   192        // evaluation of Z=Xⁿ-1 at ζ, we save this value
   193        let zeta := mload(add(mem, STATE_ZETA))
   194        let zeta_power_n_minus_one := addmod(pow(zeta, VK_DOMAIN_SIZE, freeMem), sub(R_MOD, 1), R_MOD)
   195        mstore(add(mem, STATE_ZETA_POWER_N_MINUS_ONE), zeta_power_n_minus_one)
   196  
   197        // public inputs contribution
   198        let l_pi := sum_pi_wo_api_commit(public_inputs.offset, public_inputs.length, freeMem)
   199        {{ if (gt (len .Vk.CommitmentConstraintIndexes) 0 ) -}}
   200        let l_pi_commit := sum_pi_commit(proof.offset, public_inputs.length, freeMem)
   201        l_pi := addmod(l_pi_commit, l_pi, R_MOD)
   202        {{ end -}}
   203        mstore(add(mem, STATE_PI), l_pi)
   204  
   205        compute_alpha_square_lagrange_0()
   206        compute_opening_linearised_polynomial(proof.offset)
   207        fold_h(proof.offset)
   208        compute_commitment_linearised_polynomial(proof.offset)
   209        compute_gamma_kzg(proof.offset)
   210        fold_state(proof.offset)
   211        batch_verify_multi_points(proof.offset)
   212  
   213        success := mload(add(mem, STATE_SUCCESS))
   214  
   215        // Beginning errors -------------------------------------------------
   216  
   217        function error_nb_public_inputs() {
   218          let ptError := mload(0x40)
   219          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   220          mstore(add(ptError, 0x4), 0x20)
   221          mstore(add(ptError, 0x24), 0x1d)
   222          mstore(add(ptError, 0x44), "wrong number of public inputs")
   223          revert(ptError, 0x64)
   224        }
   225  
   226        /// Called when an exponentiation mod r fails
   227        function error_mod_exp() {
   228          let ptError := mload(0x40)
   229          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   230          mstore(add(ptError, 0x4), 0x20)
   231          mstore(add(ptError, 0x24), 0xc)
   232          mstore(add(ptError, 0x44), "error mod exp")
   233          revert(ptError, 0x64)
   234        }
   235  
   236        /// Called when an operation on Bn254 fails
   237        /// @dev for instance when calling EcMul on a point not on Bn254.
   238        function error_ec_op() {
   239          let ptError := mload(0x40)
   240          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   241          mstore(add(ptError, 0x4), 0x20)
   242          mstore(add(ptError, 0x24), 0x12)
   243          mstore(add(ptError, 0x44), "error ec operation")
   244          revert(ptError, 0x64)
   245        }
   246  
   247        /// Called when one of the public inputs is not reduced.
   248        function error_inputs_size() {
   249          let ptError := mload(0x40)
   250          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   251          mstore(add(ptError, 0x4), 0x20)
   252          mstore(add(ptError, 0x24), 0x18)
   253          mstore(add(ptError, 0x44), "inputs are bigger than r")
   254          revert(ptError, 0x64)
   255        }
   256  
   257        /// Called when the size proof is not as expected
   258        /// @dev to avoid overflow attack for instance
   259        function error_proof_size() {
   260          let ptError := mload(0x40)
   261          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   262          mstore(add(ptError, 0x4), 0x20)
   263          mstore(add(ptError, 0x24), 0x10)
   264          mstore(add(ptError, 0x44), "wrong proof size")
   265          revert(ptError, 0x64)
   266        }
   267  
   268        /// Called when one the openings is bigger than r
   269        /// The openings are the claimed evalutions of a polynomial
   270        /// in a Kzg proof.
   271        function error_proof_openings_size() {
   272          let ptError := mload(0x40)
   273          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   274          mstore(add(ptError, 0x4), 0x20)
   275          mstore(add(ptError, 0x24), 0x16)
   276          mstore(add(ptError, 0x44), "openings bigger than r")
   277          revert(ptError, 0x64)
   278        }
   279  
   280        function error_pairing() {
   281          let ptError := mload(0x40)
   282          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   283          mstore(add(ptError, 0x4), 0x20)
   284          mstore(add(ptError, 0x24), 0xd)
   285          mstore(add(ptError, 0x44), "error pairing")
   286          revert(ptError, 0x64)
   287        }
   288  
   289        function error_verify() {
   290          let ptError := mload(0x40)
   291          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   292          mstore(add(ptError, 0x4), 0x20)
   293          mstore(add(ptError, 0x24), 0xc)
   294          mstore(add(ptError, 0x44), "error verify")
   295          revert(ptError, 0x64)
   296        }
   297  
   298        function error_random_generation() {
   299          let ptError := mload(0x40)
   300          mstore(ptError, ERROR_STRING_ID) // selector for function Error(string)
   301          mstore(add(ptError, 0x4), 0x20)
   302          mstore(add(ptError, 0x24), 0x14)
   303          mstore(add(ptError, 0x44), "error random gen kzg")
   304          revert(ptError, 0x64)
   305        }
   306        // end errors -------------------------------------------------
   307  
   308        // Beginning checks -------------------------------------------------
   309        
   310        /// @param s actual number of public inputs
   311        function check_number_of_public_inputs(s) {
   312          if iszero(eq(s, VK_NB_PUBLIC_INPUTS)) {
   313            error_nb_public_inputs()
   314          }
   315        }
   316      
   317        /// Checks that the public inputs are < R_MOD.
   318        /// @param s number of public inputs
   319        /// @param p pointer to the public inputs array
   320        function check_inputs_size(s, p) {
   321          for {let i} lt(i, s) {i:=add(i,1)}
   322          {
   323            if gt(calldataload(p), R_MOD_MINUS_ONE) {
   324              error_inputs_size()
   325            }
   326            p := add(p, 0x20)
   327          }
   328        }
   329  
   330        /// Checks if the proof is of the correct size
   331        /// @param actual_proof_size size of the proof (not the expected size)
   332        function check_proof_size(actual_proof_size) {
   333          let expected_proof_size := add(FIXED_PROOF_SIZE, mul(VK_NB_CUSTOM_GATES,0x60))
   334          if iszero(eq(actual_proof_size, expected_proof_size)) {
   335           error_proof_size() 
   336          }
   337        }
   338      
   339        /// Checks if the multiple openings of the polynomials are < R_MOD.
   340        /// @param aproof pointer to the beginning of the proof
   341        /// @dev the 'a' prepending proof is to have a local name
   342        function check_proof_openings_size(aproof) {
   343          
   344          // PROOF_L_AT_ZETA
   345          let p := add(aproof, PROOF_L_AT_ZETA)
   346          if gt(calldataload(p), R_MOD_MINUS_ONE) {
   347            error_proof_openings_size()
   348          }
   349  
   350          // PROOF_R_AT_ZETA
   351          p := add(aproof, PROOF_R_AT_ZETA)
   352          if gt(calldataload(p), R_MOD_MINUS_ONE) {
   353            error_proof_openings_size()
   354          }
   355  
   356          // PROOF_O_AT_ZETA
   357          p := add(aproof, PROOF_O_AT_ZETA)
   358          if gt(calldataload(p), R_MOD_MINUS_ONE) {
   359            error_proof_openings_size()
   360          }
   361  
   362          // PROOF_S1_AT_ZETA
   363          p := add(aproof, PROOF_S1_AT_ZETA)
   364          if gt(calldataload(p), R_MOD_MINUS_ONE) {
   365            error_proof_openings_size()
   366          }
   367          
   368          // PROOF_S2_AT_ZETA
   369          p := add(aproof, PROOF_S2_AT_ZETA)
   370          if gt(calldataload(p), R_MOD_MINUS_ONE) {
   371            error_proof_openings_size()
   372          }
   373  
   374          // PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA
   375          p := add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)
   376          if gt(calldataload(p), R_MOD_MINUS_ONE) {
   377            error_proof_openings_size()
   378          }
   379  
   380          // PROOF_OPENING_QCP_AT_ZETA
   381          
   382          p := add(aproof, PROOF_OPENING_QCP_AT_ZETA)
   383          for {let i:=0} lt(i, VK_NB_CUSTOM_GATES) {i:=add(i,1)}
   384          {
   385            if gt(calldataload(p), R_MOD_MINUS_ONE) {
   386              error_proof_openings_size()
   387            }
   388            p := add(p, 0x20)
   389          }
   390  
   391        }
   392        // end checks -------------------------------------------------
   393  
   394        // Beginning challenges -------------------------------------------------
   395  
   396        /// Derive gamma as Sha256(<transcript>)
   397        /// @param aproof pointer to the proof
   398        /// @param nb_pi number of public inputs
   399        /// @param pi pointer to the array of public inputs
   400        /// @return the challenge gamma, not reduced
   401        /// @notice The transcript is the concatenation (in this order) of:
   402        /// * the word "gamma" in ascii, equal to [0x67,0x61,0x6d, 0x6d, 0x61] and encoded as a uint256.
   403        /// * the commitments to the permutation polynomials S1, S2, S3, where we concatenate the coordinates of those points
   404        /// * the commitments of Ql, Qr, Qm, Qo, Qk
   405        /// * the public inputs
   406        /// * the commitments of the wires related to the custom gates (commitments_wires_commit_api)
   407        /// * commitments to L, R, O (proof_<l,r,o>_com_<x,y>)
   408        /// The data described above is written starting at mPtr. "gamma" lies on 5 bytes,
   409        /// and is encoded as a uint256 number n. In basis b = 256, the number looks like this
   410        /// [0 0 0 .. 0x67 0x61 0x6d, 0x6d, 0x61]. The first non zero entry is at position 27=0x1b
   411        /// Gamma reduced (the actual challenge) is stored at add(state, state_gamma)
   412        function derive_gamma(aproof, nb_pi, pi)->gamma_not_reduced {
   413          
   414          let state := mload(0x40)
   415          let mPtr := add(state, STATE_LAST_MEM)
   416  
   417          mstore(mPtr, FS_GAMMA) // "gamma"
   418  
   419          {{ $offset = 0x20 }}
   420          mstore(add(mPtr, {{ hex $offset }}), VK_S1_COM_X) {{ $offset = add $offset 0x20}}
   421          mstore(add(mPtr, {{ hex $offset }}), VK_S1_COM_Y) {{ $offset = add $offset 0x20}}
   422          mstore(add(mPtr, {{ hex $offset }}), VK_S2_COM_X) {{ $offset = add $offset 0x20}}
   423          mstore(add(mPtr, {{ hex $offset }}), VK_S2_COM_Y) {{ $offset = add $offset 0x20}}
   424          mstore(add(mPtr, {{ hex $offset }}), VK_S3_COM_X) {{ $offset = add $offset 0x20}}
   425          mstore(add(mPtr, {{ hex $offset }}), VK_S3_COM_Y) {{ $offset = add $offset 0x20}}
   426          mstore(add(mPtr, {{ hex $offset }}), VK_QL_COM_X) {{ $offset = add $offset 0x20}}
   427          mstore(add(mPtr, {{ hex $offset }}), VK_QL_COM_Y) {{ $offset = add $offset 0x20}}
   428          mstore(add(mPtr, {{ hex $offset }}), VK_QR_COM_X) {{ $offset = add $offset 0x20}}
   429          mstore(add(mPtr, {{ hex $offset }}), VK_QR_COM_Y) {{ $offset = add $offset 0x20}}
   430          mstore(add(mPtr, {{ hex $offset }}), VK_QM_COM_X) {{ $offset = add $offset 0x20}}
   431          mstore(add(mPtr, {{ hex $offset }}), VK_QM_COM_Y) {{ $offset = add $offset 0x20}}
   432          mstore(add(mPtr, {{ hex $offset }}), VK_QO_COM_X) {{ $offset = add $offset 0x20}}
   433          mstore(add(mPtr, {{ hex $offset }}), VK_QO_COM_Y) {{ $offset = add $offset 0x20}}
   434          mstore(add(mPtr, {{ hex $offset }}), VK_QK_COM_X) {{ $offset = add $offset 0x20}}
   435          mstore(add(mPtr, {{ hex $offset }}), VK_QK_COM_Y) {{ $offset = add $offset 0x20}}
   436          {{ range $index, $element := .Vk.CommitmentConstraintIndexes}}
   437          mstore(add(mPtr, {{ hex $offset }}), VK_QCP_{{ $index }}_X) {{ $offset = add $offset 0x20}}
   438          mstore(add(mPtr, {{ hex $offset }}), VK_QCP_{{ $index }}_Y) {{ $offset = add $offset 0x20}}
   439          {{ end }}
   440          // public inputs
   441          let _mPtr := add(mPtr, {{ hex (add (mul (len .Vk.CommitmentConstraintIndexes) 64) 544) }})
   442          let size_pi_in_bytes := mul(nb_pi, 0x20)
   443          calldatacopy(_mPtr, pi, size_pi_in_bytes)
   444          _mPtr := add(_mPtr, size_pi_in_bytes)
   445  
   446          // commitments to l, r, o
   447          let size_commitments_lro_in_bytes := 0xc0
   448          calldatacopy(_mPtr, aproof, size_commitments_lro_in_bytes)
   449          _mPtr := add(_mPtr, size_commitments_lro_in_bytes)
   450  
   451          // total size is :
   452          // sizegamma(=0x5) + 11*64(=0x2c0)
   453          // + nb_public_inputs*0x20
   454          // + nb_custom gates*0x40
   455          let size := add(0x2c5, size_pi_in_bytes)
   456          {{ if (gt (len .Vk.CommitmentConstraintIndexes) 0 )}}
   457          size := add(size, mul(VK_NB_CUSTOM_GATES, 0x40))
   458          {{ end -}}
   459          let l_success := staticcall(gas(), SHA2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma"
   460          if iszero(l_success) {
   461            error_verify()
   462          }
   463          gamma_not_reduced := mload(mPtr)
   464          mstore(add(state, STATE_GAMMA), mod(gamma_not_reduced, R_MOD))
   465        }
   466  
   467        /// derive beta as Sha256<transcript>
   468        /// @param gamma_not_reduced the previous challenge (gamma) not reduced
   469        /// @return beta_not_reduced the next challenge, beta, not reduced
   470        /// @notice the transcript consists of the previous challenge only.
   471        /// The reduced version of beta is stored at add(state, state_beta)
   472        function derive_beta(gamma_not_reduced)->beta_not_reduced{
   473          
   474          let state := mload(0x40)
   475          let mPtr := add(mload(0x40), STATE_LAST_MEM)
   476  
   477          // beta
   478          mstore(mPtr, FS_BETA) // "beta"
   479          mstore(add(mPtr, 0x20), gamma_not_reduced)
   480          let l_success := staticcall(gas(), SHA2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma"
   481          if iszero(l_success) {
   482            error_verify()
   483          }
   484          beta_not_reduced := mload(mPtr)
   485          mstore(add(state, STATE_BETA), mod(beta_not_reduced, R_MOD))
   486        }
   487  
   488        /// derive alpha as sha256<transcript>
   489        /// @param aproof pointer to the proof object
   490        /// @param beta_not_reduced the previous challenge (beta) not reduced
   491        /// @return alpha_not_reduced the next challenge, alpha, not reduced
   492        /// @notice the transcript consists of the previous challenge (beta)
   493        /// not reduced, the commitments to the wires associated to the QCP_i,
   494        /// and the commitment to the grand product polynomial 
   495        function derive_alpha(aproof, beta_not_reduced)->alpha_not_reduced {
   496          
   497          let state := mload(0x40)
   498          let mPtr := add(mload(0x40), STATE_LAST_MEM)
   499          let full_size := 0x65 // size("alpha") + 0x20 (previous challenge)
   500  
   501          // alpha
   502          mstore(mPtr, FS_ALPHA) // "alpha"
   503          let _mPtr := add(mPtr, 0x20)
   504          mstore(_mPtr, beta_not_reduced)
   505          _mPtr := add(_mPtr, 0x20)
   506          {{ if (gt (len .Vk.CommitmentConstraintIndexes) 0 )}}
   507          // Bsb22Commitments
   508          let proof_bsb_commitments := add(aproof, PROOF_BSB_COMMITMENTS)
   509          let size_bsb_commitments := mul(0x40, VK_NB_CUSTOM_GATES)
   510          calldatacopy(_mPtr, proof_bsb_commitments, size_bsb_commitments)
   511          _mPtr := add(_mPtr, size_bsb_commitments)
   512          full_size := add(full_size, size_bsb_commitments)
   513          {{ end }}
   514          // [Z], the commitment to the grand product polynomial
   515          calldatacopy(_mPtr, add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X), 0x40)
   516          let l_success := staticcall(gas(), SHA2, add(mPtr, 0x1b), full_size, mPtr, 0x20)
   517          if iszero(l_success) {
   518            error_verify()
   519          }
   520  
   521          alpha_not_reduced := mload(mPtr)
   522          mstore(add(state, STATE_ALPHA), mod(alpha_not_reduced, R_MOD))
   523        }
   524  
   525        /// derive zeta as sha256<transcript>
   526        /// @param aproof pointer to the proof object
   527        /// @param alpha_not_reduced the previous challenge (alpha) not reduced
   528        /// The transcript consists of the previous challenge and the commitment to
   529        /// the quotient polynomial h.
   530        function derive_zeta(aproof, alpha_not_reduced) {
   531          
   532          let state := mload(0x40)
   533          let mPtr := add(mload(0x40), STATE_LAST_MEM)
   534  
   535          // zeta
   536          mstore(mPtr, FS_ZETA) // "zeta"
   537          mstore(add(mPtr, 0x20), alpha_not_reduced)
   538          calldatacopy(add(mPtr, 0x40), add(aproof, PROOF_H_0_COM_X), 0xc0)
   539          let l_success := staticcall(gas(), SHA2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20)
   540          if iszero(l_success) {
   541            error_verify()
   542          }
   543          let zeta_not_reduced := mload(mPtr)
   544          mstore(add(state, STATE_ZETA), mod(zeta_not_reduced, R_MOD))
   545        }
   546        // END challenges -------------------------------------------------
   547  
   548        // BEGINNING compute_pi -------------------------------------------------
   549  
   550        /// sum_pi_wo_api_commit computes the public inputs contributions,
   551        /// except for the public inputs coming from the custom gate
   552        /// @param ins pointer to the public inputs
   553        /// @param n number of public inputs
   554        /// @param mPtr free memory
   555        /// @return pi_wo_commit public inputs contribution (except the public inputs coming from the custom gate)
   556        function sum_pi_wo_api_commit(ins, n, mPtr)->pi_wo_commit {
   557          
   558          let state := mload(0x40)
   559          let z := mload(add(state, STATE_ZETA))
   560          let zpnmo := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE))
   561  
   562          let li := mPtr
   563          batch_compute_lagranges_at_z(z, zpnmo, n, li)
   564  
   565          let tmp := 0
   566          for {let i:=0} lt(i,n) {i:=add(i,1)}
   567          {
   568            tmp := mulmod(mload(li), calldataload(ins), R_MOD)
   569            pi_wo_commit := addmod(pi_wo_commit, tmp, R_MOD)
   570            li := add(li, 0x20)
   571            ins := add(ins, 0x20)
   572          }
   573          
   574        }
   575  
   576        /// batch_compute_lagranges_at_z computes [L_0(z), .., L_{n-1}(z)]
   577        /// @param z point at which the Lagranges are evaluated
   578        /// @param zpnmo ζⁿ-1
   579        /// @param n_pub number of public inputs (number of Lagranges to compute)
   580        /// @param mPtr pointer to which the results are stored
   581        function batch_compute_lagranges_at_z(z, zpnmo, n_pub, mPtr) {
   582  
   583          let zn := mulmod(zpnmo, VK_INV_DOMAIN_SIZE, R_MOD) // 1/n * (ζⁿ - 1)
   584          
   585          let _w := 1
   586          let _mPtr := mPtr
   587          for {let i:=0} lt(i,n_pub) {i:=add(i,1)}
   588          {
   589            mstore(_mPtr, addmod(z,sub(R_MOD, _w), R_MOD))
   590            _w := mulmod(_w, VK_OMEGA, R_MOD)
   591            _mPtr := add(_mPtr, 0x20)
   592          }
   593          batch_invert(mPtr, n_pub, _mPtr)
   594          _mPtr := mPtr
   595          _w := 1
   596          for {let i:=0} lt(i,n_pub) {i:=add(i,1)}
   597          {
   598            mstore(_mPtr, mulmod(mulmod(mload(_mPtr), zn , R_MOD), _w, R_MOD))
   599            _mPtr := add(_mPtr, 0x20)
   600            _w := mulmod(_w, VK_OMEGA, R_MOD)
   601          }
   602        } 
   603  
   604        /// @notice Montgomery trick for batch inversion mod R_MOD
   605        /// @param ins pointer to the data to batch invert
   606        /// @param number of elements to batch invert
   607        /// @param mPtr free memory
   608        function batch_invert(ins, nb_ins, mPtr) {
   609          mstore(mPtr, 1)
   610          let offset := 0
   611          for {let i:=0} lt(i, nb_ins) {i:=add(i,1)}
   612          {
   613            let prev := mload(add(mPtr, offset))
   614            let cur := mload(add(ins, offset))
   615            cur := mulmod(prev, cur, R_MOD)
   616            offset := add(offset, 0x20)
   617            mstore(add(mPtr, offset), cur)
   618          }
   619          ins := add(ins, sub(offset, 0x20))
   620          mPtr := add(mPtr, offset)
   621          let inv := pow(mload(mPtr), sub(R_MOD,2), add(mPtr, 0x20))
   622          for {let i:=0} lt(i, nb_ins) {i:=add(i,1)}
   623          {
   624            mPtr := sub(mPtr, 0x20)
   625            let tmp := mload(ins)
   626            let cur := mulmod(inv, mload(mPtr), R_MOD)
   627            mstore(ins, cur)
   628            inv := mulmod(inv, tmp, R_MOD)
   629            ins := sub(ins, 0x20)
   630          }
   631        }
   632  
   633        {{ if (gt (len .Vk.CommitmentConstraintIndexes) 0 )}}
   634        /// Public inputs (the ones coming from the custom gate) contribution
   635        /// @param aproof pointer to the proof
   636        /// @param nb_public_inputs number of public inputs
   637        /// @param mPtr pointer to free memory
   638        /// @return pi_commit custom gate public inputs contribution
   639        function sum_pi_commit(aproof, nb_public_inputs, mPtr)->pi_commit {
   640  
   641          let state := mload(0x40)
   642          let z := mload(add(state, STATE_ZETA))
   643          let zpnmo := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE))
   644  
   645          let p := add(aproof, PROOF_BSB_COMMITMENTS)
   646  
   647          let h_fr, ith_lagrange
   648         
   649          {{ range $index, $element := .Vk.CommitmentConstraintIndexes}}
   650          h_fr := hash_fr(calldataload(p), calldataload(add(p, 0x20)), mPtr)
   651          ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, VK_INDEX_COMMIT_API_{{ $index }}), mPtr)
   652          pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, R_MOD), R_MOD)
   653          {{ if (lt (inc $index) (len $.Vk.CommitmentConstraintIndexes) )}}
   654          p := add(p, 0x40)
   655          {{ end }}
   656          {{ end }}
   657  
   658        }
   659  
   660        /// Computes L_i(zeta) =  ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where:
   661        /// @param z zeta
   662        /// @param zpmno ζⁿ-1
   663        /// @param i i-th lagrange
   664        /// @param mPtr free memory
   665        /// @return res = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) 
   666        function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr)->res {
   667  
   668          let w := pow(VK_OMEGA, i, mPtr) // w**i
   669          i := addmod(z, sub(R_MOD, w), R_MOD) // z-w**i
   670          w := mulmod(w, VK_INV_DOMAIN_SIZE, R_MOD) // w**i/n
   671          i := pow(i, sub(R_MOD,2), mPtr) // (z-w**i)**-1
   672          w := mulmod(w, i, R_MOD) // w**i/n*(z-w)**-1
   673          res := mulmod(w, zpnmo, R_MOD)
   674        
   675        }
   676  
   677        /// @dev https://tools.ietf.org/html/draft-irtf-cfrg-hash-to-curve-06#section-5.2
   678        /// @param x x coordinate of a point on Bn254(𝔽_p)
   679        /// @param y y coordinate of a point on Bn254(𝔽_p)
   680        /// @param mPtr free memory
   681        /// @return res an element mod R_MOD
   682        function hash_fr(x, y, mPtr)->res {
   683  
   684          // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, HASH_FR_SIZE_DOMAIN]
   685          // <-  64 bytes  ->  <-64b -> <-       1 bytes each     ->
   686  
   687          // [0x00, .., 0x00] 64 bytes of zero
   688          mstore(mPtr, HASH_FR_ZERO_UINT256)
   689          mstore(add(mPtr, 0x20), HASH_FR_ZERO_UINT256)
   690      
   691          // msg =  x || y , both on 32 bytes
   692          mstore(add(mPtr, 0x40), x)
   693          mstore(add(mPtr, 0x60), y)
   694  
   695          // 0 || 48 || 0 all on 1 byte
   696          mstore8(add(mPtr, 0x80), 0)
   697          mstore8(add(mPtr, 0x81), HASH_FR_LEN_IN_BYTES)
   698          mstore8(add(mPtr, 0x82), 0)
   699  
   700          // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,]
   701          mstore8(add(mPtr, 0x83), 0x42)
   702          mstore8(add(mPtr, 0x84), 0x53)
   703          mstore8(add(mPtr, 0x85), 0x42)
   704          mstore8(add(mPtr, 0x86), 0x32)
   705          mstore8(add(mPtr, 0x87), 0x32)
   706          mstore8(add(mPtr, 0x88), 0x2d)
   707          mstore8(add(mPtr, 0x89), 0x50)
   708          mstore8(add(mPtr, 0x8a), 0x6c)
   709          mstore8(add(mPtr, 0x8b), 0x6f)
   710          mstore8(add(mPtr, 0x8c), 0x6e)
   711          mstore8(add(mPtr, 0x8d), 0x6b)
   712  
   713          // size domain
   714          mstore8(add(mPtr, 0x8e), HASH_FR_SIZE_DOMAIN)
   715  
   716          let l_success := staticcall(gas(), SHA2, mPtr, 0x8f, mPtr, 0x20)
   717          if iszero(l_success) {
   718            error_verify()
   719          }
   720  
   721          let b0 := mload(mPtr)
   722  
   723          // [b0         || one || dst || HASH_FR_SIZE_DOMAIN]
   724          // <-64bytes ->  <-    1 byte each      ->
   725          mstore8(add(mPtr, 0x20), HASH_FR_ONE) // 1
   726          
   727          mstore8(add(mPtr, 0x21), 0x42) // dst
   728          mstore8(add(mPtr, 0x22), 0x53)
   729          mstore8(add(mPtr, 0x23), 0x42)
   730          mstore8(add(mPtr, 0x24), 0x32)
   731          mstore8(add(mPtr, 0x25), 0x32)
   732          mstore8(add(mPtr, 0x26), 0x2d)
   733          mstore8(add(mPtr, 0x27), 0x50)
   734          mstore8(add(mPtr, 0x28), 0x6c)
   735          mstore8(add(mPtr, 0x29), 0x6f)
   736          mstore8(add(mPtr, 0x2a), 0x6e)
   737          mstore8(add(mPtr, 0x2b), 0x6b)
   738  
   739          mstore8(add(mPtr, 0x2c), HASH_FR_SIZE_DOMAIN) // size domain
   740          l_success := staticcall(gas(), SHA2, mPtr, 0x2d, mPtr, 0x20)
   741          if iszero(l_success) {
   742            error_verify()
   743          }
   744  
   745          // b1 is located at mPtr. We store b2 at add(mPtr, 0x20)
   746  
   747          // [b0^b1      || two || dst || HASH_FR_SIZE_DOMAIN]
   748          // <-64bytes ->  <-    1 byte each      ->
   749          mstore(add(mPtr, 0x20), xor(mload(mPtr), b0))
   750          mstore8(add(mPtr, 0x40), HASH_FR_TWO)
   751  
   752          mstore8(add(mPtr, 0x41), 0x42) // dst
   753          mstore8(add(mPtr, 0x42), 0x53)
   754          mstore8(add(mPtr, 0x43), 0x42)
   755          mstore8(add(mPtr, 0x44), 0x32)
   756          mstore8(add(mPtr, 0x45), 0x32)
   757          mstore8(add(mPtr, 0x46), 0x2d)
   758          mstore8(add(mPtr, 0x47), 0x50)
   759          mstore8(add(mPtr, 0x48), 0x6c)
   760          mstore8(add(mPtr, 0x49), 0x6f)
   761          mstore8(add(mPtr, 0x4a), 0x6e)
   762          mstore8(add(mPtr, 0x4b), 0x6b)
   763  
   764          mstore8(add(mPtr, 0x4c), HASH_FR_SIZE_DOMAIN) // size domain
   765  
   766          let offset := add(mPtr, 0x20)
   767          l_success := staticcall(gas(), SHA2, offset, 0x2d, offset, 0x20)
   768          if iszero(l_success) {
   769            error_verify()
   770          }
   771  
   772          // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes.
   773          // we interpret it as a big integer mod r in big endian (similar to regular decimal notation)
   774          // the result is then 2**(8*16)*mPtr[:32] + mPtr[32:48]
   775          res := mulmod(mload(mPtr), HASH_FR_BB, R_MOD) // <- res = 2**128 * mPtr[:32]
   776          let b1 := shr(128, mload(add(mPtr, 0x20))) // b1 <- [0, 0, .., 0 ||  b2[:16] ]
   777          res := addmod(res, b1, R_MOD)
   778  
   779        }
   780        {{ end }}
   781        // END compute_pi -------------------------------------------------
   782  
   783        /// @notice compute α² * 1/n * (ζ{n}-1)/(ζ - 1) where
   784        /// *  α = challenge derived in derive_gamma_beta_alpha_zeta
   785        /// * n = vk_domain_size
   786        /// * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*)
   787        /// * ζ = zeta (challenge derived with Fiat Shamir)
   788        function compute_alpha_square_lagrange_0() {   
   789          let state := mload(0x40)
   790          let mPtr := add(mload(0x40), STATE_LAST_MEM)
   791  
   792          let res := mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE))
   793          let den := addmod(mload(add(state, STATE_ZETA)), sub(R_MOD, 1), R_MOD)
   794          den := pow(den, sub(R_MOD, 2), mPtr)
   795          den := mulmod(den, VK_INV_DOMAIN_SIZE, R_MOD)
   796          res := mulmod(den, res, R_MOD)
   797  
   798          let l_alpha := mload(add(state, STATE_ALPHA))
   799          res := mulmod(res, l_alpha, R_MOD)
   800          res := mulmod(res, l_alpha, R_MOD)
   801          mstore(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0), res)
   802        }
   803  
   804        /// @notice follows alg. p.13 of https://eprint.iacr.org/2019/953.pdf
   805        /// with t₁ = t₂ = 1, and the proofs are ([digest] + [quotient] +purported evaluation):
   806        /// * [state_folded_state_digests], [proof_batch_opening_at_zeta_x], state_folded_evals
   807        /// * [proof_grand_product_commitment], [proof_opening_at_zeta_omega_x], [proof_grand_product_at_zeta_omega]
   808        /// @param aproof pointer to the proof
   809        function batch_verify_multi_points(aproof) {
   810          let state := mload(0x40)
   811          let mPtr := add(state, STATE_LAST_MEM)
   812  
   813          // derive a random number. As there is no random generator, we
   814          // do an FS like challenge derivation, depending on both digests and
   815          // ζ to ensure that the prover cannot control the random number.
   816          // Note: adding the other point ζω is not needed, as ω is known beforehand.
   817          mstore(mPtr, mload(add(state, STATE_FOLDED_DIGESTS_X)))
   818          mstore(add(mPtr, 0x20), mload(add(state, STATE_FOLDED_DIGESTS_Y)))
   819          mstore(add(mPtr, 0x40), calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X)))
   820          mstore(add(mPtr, 0x60), calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_Y)))
   821          mstore(add(mPtr, 0x80), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X)))
   822          mstore(add(mPtr, 0xa0), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_Y)))
   823          mstore(add(mPtr, 0xc0), calldataload(add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X)))
   824          mstore(add(mPtr, 0xe0), calldataload(add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_Y)))
   825          mstore(add(mPtr, 0x100), mload(add(state, STATE_ZETA)))
   826          mstore(add(mPtr, 0x120), mload(add(state, STATE_GAMMA_KZG)))
   827          let random := staticcall(gas(), SHA2, mPtr, 0x140, mPtr, 0x20)
   828          if iszero(random){
   829            error_random_generation()
   830          }
   831          random := mod(mload(mPtr), R_MOD) // use the same variable as we are one variable away from getting stack-too-deep error...
   832  
   833          let folded_quotients := mPtr
   834          mPtr := add(folded_quotients, 0x40)
   835          mstore(folded_quotients, calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X)))
   836          mstore(add(folded_quotients, 0x20), calldataload(add(aproof, PROOF_BATCH_OPENING_AT_ZETA_Y)))
   837          point_acc_mul_calldata(folded_quotients, add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X), random, mPtr)
   838  
   839          let folded_digests := add(state, STATE_FOLDED_DIGESTS_X)
   840          point_acc_mul_calldata(folded_digests, add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X), random, mPtr)
   841  
   842          let folded_evals := add(state, STATE_FOLDED_CLAIMED_VALUES)
   843          fr_acc_mul_calldata(folded_evals, add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA), random)
   844  
   845          let folded_evals_commit := mPtr
   846          mPtr := add(folded_evals_commit, 0x40)
   847          mstore(folded_evals_commit, G1_SRS_X)
   848          mstore(add(folded_evals_commit, 0x20), G1_SRS_Y)
   849          mstore(add(folded_evals_commit, 0x40), mload(folded_evals))
   850          let check_staticcall := staticcall(gas(), 7, folded_evals_commit, 0x60, folded_evals_commit, 0x40)
   851          if iszero(check_staticcall) {
   852            error_verify()
   853          }
   854  
   855          let folded_evals_commit_y := add(folded_evals_commit, 0x20)
   856          mstore(folded_evals_commit_y, sub(P_MOD, mload(folded_evals_commit_y)))
   857          point_add(folded_digests, folded_digests, folded_evals_commit, mPtr)
   858  
   859          let folded_points_quotients := mPtr
   860          mPtr := add(mPtr, 0x40)
   861          point_mul_calldata(
   862            folded_points_quotients,
   863            add(aproof, PROOF_BATCH_OPENING_AT_ZETA_X),
   864            mload(add(state, STATE_ZETA)),
   865            mPtr
   866          )
   867          let zeta_omega := mulmod(mload(add(state, STATE_ZETA)), VK_OMEGA, R_MOD)
   868          random := mulmod(random, zeta_omega, R_MOD)
   869          point_acc_mul_calldata(folded_points_quotients, add(aproof, PROOF_OPENING_AT_ZETA_OMEGA_X), random, mPtr)
   870  
   871          point_add(folded_digests, folded_digests, folded_points_quotients, mPtr)
   872  
   873          let folded_quotients_y := add(folded_quotients, 0x20)
   874          mstore(folded_quotients_y, sub(P_MOD, mload(folded_quotients_y)))
   875  
   876          mstore(mPtr, mload(folded_digests))
   877          {{ $offset = 0x20 }}
   878          mstore(add(mPtr, {{ hex $offset }}), mload(add(folded_digests, 0x20))) {{ $offset = add $offset 0x20 }}
   879          mstore(add(mPtr, {{ hex $offset }}), G2_SRS_0_X_0) {{ $offset = add $offset 0x20 }} // the 4 lines are the canonical G2 point on BN254
   880          mstore(add(mPtr, {{ hex $offset }}), G2_SRS_0_X_1) {{ $offset = add $offset 0x20 }}
   881          mstore(add(mPtr, {{ hex $offset }}), G2_SRS_0_Y_0) {{ $offset = add $offset 0x20 }}
   882          mstore(add(mPtr, {{ hex $offset }}), G2_SRS_0_Y_1) {{ $offset = add $offset 0x20 }}
   883          mstore(add(mPtr, {{ hex $offset }}), mload(folded_quotients)) {{ $offset = add $offset 0x20 }}
   884          mstore(add(mPtr, {{ hex $offset }}), mload(add(folded_quotients, 0x20))) {{ $offset = add $offset 0x20 }}
   885          mstore(add(mPtr, {{ hex $offset }}), G2_SRS_1_X_0) {{ $offset = add $offset 0x20 }}
   886          mstore(add(mPtr, {{ hex $offset }}), G2_SRS_1_X_1) {{ $offset = add $offset 0x20 }}
   887          mstore(add(mPtr, {{ hex $offset }}), G2_SRS_1_Y_0) {{ $offset = add $offset 0x20 }}
   888          mstore(add(mPtr, {{ hex $offset }}), G2_SRS_1_Y_1) {{ $offset = add $offset 0x20 }}
   889          check_pairing_kzg(mPtr)
   890        }
   891  
   892        /// @notice check_pairing_kzg checks the result of the final pairing product of the batched
   893        /// kzg verification. The purpose of this function is to avoid exhausting the stack
   894        /// in the function batch_verify_multi_points.
   895        /// @param mPtr pointer storing the tuple of pairs
   896        function check_pairing_kzg(mPtr) {
   897          let state := mload(0x40)
   898  
   899          let l_success := staticcall(gas(), 8, mPtr, 0x180, 0x00, 0x20)
   900          if iszero(l_success) {
   901            error_pairing()
   902          }
   903          let res_pairing := mload(0x00)
   904          mstore(add(state, STATE_SUCCESS), res_pairing)
   905        }
   906  
   907        /// @notice Fold the opening proofs at ζ:
   908        /// * at state+state_folded_digest we store: [Linearised_polynomial]+γ[L] + γ²[R] + γ³[O] + γ⁴[S₁] +γ⁵[S₂] + ∑ᵢγ⁵⁺ⁱ[Pi_{i}]
   909        /// * at state+state_folded_claimed_values we store: Linearised_polynomial(ζ)+γL(ζ) + γ²R(ζ)+ γ³O(ζ) + γ⁴S₁(ζ) +γ⁵S₂(ζ) + ∑ᵢγ⁵⁺ⁱPi_{i}(ζ)
   910        /// @param aproof pointer to the proof
   911        /// acc_gamma stores the γⁱ
   912        function fold_state(aproof) {
   913  
   914          let state := mload(0x40)
   915          let mPtr := add(mload(0x40), STATE_LAST_MEM)
   916          let mPtr20 := add(mPtr, 0x20)
   917          let mPtr40 := add(mPtr, 0x40)
   918  
   919          let l_gamma_kzg := mload(add(state, STATE_GAMMA_KZG))
   920          let acc_gamma := l_gamma_kzg
   921          let state_folded_digests := add(state, STATE_FOLDED_DIGESTS_X)
   922  
   923          mstore(state_folded_digests, mload(add(state, STATE_LINEARISED_POLYNOMIAL_X)))
   924          mstore(add(state, STATE_FOLDED_DIGESTS_Y), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y)))
   925          mstore(add(state, STATE_FOLDED_CLAIMED_VALUES), mload(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA)))
   926  
   927          point_acc_mul_calldata(state_folded_digests, add(aproof, PROOF_L_COM_X), acc_gamma, mPtr)
   928          fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_L_AT_ZETA), acc_gamma)
   929  
   930          acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
   931          point_acc_mul_calldata(state_folded_digests, add(aproof, PROOF_R_COM_X), acc_gamma, mPtr)
   932          fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_R_AT_ZETA), acc_gamma)
   933  
   934          acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
   935          point_acc_mul_calldata(state_folded_digests, add(aproof, PROOF_O_COM_X), acc_gamma, mPtr)
   936          fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_O_AT_ZETA), acc_gamma)
   937  
   938          acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
   939          mstore(mPtr, VK_S1_COM_X)
   940          mstore(mPtr20, VK_S1_COM_Y)
   941          point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40)
   942          fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_S1_AT_ZETA), acc_gamma)
   943  
   944          acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
   945          mstore(mPtr, VK_S2_COM_X)
   946          mstore(mPtr20, VK_S2_COM_Y)
   947          point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40)
   948          fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), add(aproof, PROOF_S2_AT_ZETA), acc_gamma)
   949  
   950          {{- if (gt (len .Vk.CommitmentConstraintIndexes) 0 ) }}
   951          let poqaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA)
   952          {{ range $index, $element := .Vk.CommitmentConstraintIndexes }}
   953          acc_gamma := mulmod(acc_gamma, l_gamma_kzg, R_MOD)
   954          mstore(mPtr, VK_QCP_{{ $index }}_X)
   955          mstore(mPtr20, VK_QCP_{{ $index }}_Y)
   956          point_acc_mul(state_folded_digests, mPtr, acc_gamma, mPtr40)
   957          fr_acc_mul_calldata(add(state, STATE_FOLDED_CLAIMED_VALUES), poqaz, acc_gamma)
   958          poqaz := add(poqaz, 0x20)
   959          {{ end }}
   960          {{ end -}}
   961        }
   962  
   963        /// @notice generate the challenge (using Fiat Shamir) to fold the opening proofs
   964        /// at ζ.
   965        /// The process for deriving γ is the same as in derive_gamma but this time the inputs are
   966        /// in this order (the [] means it's a commitment):
   967        /// * ζ
   968        /// * [Linearised polynomial]
   969        /// * [L], [R], [O]
   970        /// * [S₁] [S₂]
   971        /// * [Pi_{i}] (wires associated to custom gates)
   972        /// Then there are the purported evaluations of the previous committed polynomials:
   973        /// * Linearised_polynomial(ζ)
   974        /// * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ)
   975        /// * Pi_{i}(ζ)
   976        /// * Z(ζω)
   977        /// @param aproof pointer to the proof
   978        function compute_gamma_kzg(aproof) {
   979  
   980          let state := mload(0x40)
   981          let mPtr := add(mload(0x40), STATE_LAST_MEM)
   982          mstore(mPtr, FS_GAMMA_KZG) // "gamma"
   983          mstore(add(mPtr, 0x20), mload(add(state, STATE_ZETA)))
   984          mstore(add(mPtr,0x40), mload(add(state, STATE_LINEARISED_POLYNOMIAL_X)))
   985          mstore(add(mPtr,0x60), mload(add(state, STATE_LINEARISED_POLYNOMIAL_Y)))
   986          calldatacopy(add(mPtr, 0x80), add(aproof, PROOF_L_COM_X), 0xc0)
   987          mstore(add(mPtr,0x140), VK_S1_COM_X)
   988          mstore(add(mPtr,0x160), VK_S1_COM_Y)
   989          mstore(add(mPtr,0x180), VK_S2_COM_X)
   990          mstore(add(mPtr,0x1a0), VK_S2_COM_Y)
   991          
   992          let offset := 0x1c0
   993          
   994          {{ range $index, $element := .Vk.CommitmentConstraintIndexes -}}
   995          mstore(add(mPtr,offset), VK_QCP_{{ $index }}_X)
   996          mstore(add(mPtr,add(offset, 0x20)), VK_QCP_{{ $index }}_Y)
   997          offset := add(offset, 0x40)
   998          {{ end -}}
   999          
  1000          mstore(add(mPtr, offset), mload(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA)))
  1001          mstore(add(mPtr, add(offset, 0x20)), calldataload(add(aproof, PROOF_L_AT_ZETA)))
  1002          mstore(add(mPtr, add(offset, 0x40)), calldataload(add(aproof, PROOF_R_AT_ZETA)))
  1003          mstore(add(mPtr, add(offset, 0x60)), calldataload(add(aproof, PROOF_O_AT_ZETA)))
  1004          mstore(add(mPtr, add(offset, 0x80)), calldataload(add(aproof, PROOF_S1_AT_ZETA)))
  1005          mstore(add(mPtr, add(offset, 0xa0)), calldataload(add(aproof, PROOF_S2_AT_ZETA)))
  1006  
  1007          let _mPtr := add(mPtr, add(offset, 0xc0))
  1008  
  1009          {{ if (gt (len .Vk.CommitmentConstraintIndexes) 0 )}}
  1010          let _poqaz := add(aproof, PROOF_OPENING_QCP_AT_ZETA)
  1011          calldatacopy(_mPtr, _poqaz, mul(VK_NB_CUSTOM_GATES, 0x20))
  1012          _mPtr := add(_mPtr, mul(VK_NB_CUSTOM_GATES, 0x20))
  1013          {{ end }}
  1014  
  1015          mstore(_mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)))
  1016  
  1017          let start_input := 0x1b // 00.."gamma"
  1018          let size_input := add(0x14, mul(VK_NB_CUSTOM_GATES,3)) // number of 32bytes elmts = 0x14 (zeta+3*6 for the digests+openings) + 3*VK_NB_CUSTOM_GATES (for the commitments of the selectors) + 1 (opening of Z at ζω)
  1019          size_input := add(0x5, mul(size_input, 0x20)) // size in bytes: 15*32 bytes + 5 bytes for gamma
  1020          let check_staticcall := staticcall(gas(), SHA2, add(mPtr,start_input), size_input, add(state, STATE_GAMMA_KZG), 0x20)
  1021          if iszero(check_staticcall) {
  1022            error_verify()
  1023          }
  1024          mstore(add(state, STATE_GAMMA_KZG), mod(mload(add(state, STATE_GAMMA_KZG)), R_MOD))
  1025        }
  1026  
  1027        function compute_commitment_linearised_polynomial_ec(aproof, s1, s2) {
  1028          
  1029          let state := mload(0x40)
  1030          let mPtr := add(mload(0x40), STATE_LAST_MEM)
  1031  
  1032          mstore(mPtr, VK_QL_COM_X)
  1033          mstore(add(mPtr, 0x20), VK_QL_COM_Y)
  1034          point_mul(
  1035            add(state, STATE_LINEARISED_POLYNOMIAL_X),
  1036            mPtr,
  1037            calldataload(add(aproof, PROOF_L_AT_ZETA)),
  1038            add(mPtr, 0x40)
  1039          )
  1040  
  1041          mstore(mPtr, VK_QR_COM_X)
  1042          mstore(add(mPtr, 0x20), VK_QR_COM_Y)
  1043          point_acc_mul(
  1044            add(state, STATE_LINEARISED_POLYNOMIAL_X),
  1045            mPtr,
  1046            calldataload(add(aproof, PROOF_R_AT_ZETA)),
  1047            add(mPtr, 0x40)
  1048          )
  1049  
  1050          let rl := mulmod(calldataload(add(aproof, PROOF_L_AT_ZETA)), calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)
  1051          mstore(mPtr, VK_QM_COM_X)
  1052          mstore(add(mPtr, 0x20), VK_QM_COM_Y)
  1053          point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, rl, add(mPtr, 0x40))
  1054  
  1055          mstore(mPtr, VK_QO_COM_X)
  1056          mstore(add(mPtr, 0x20), VK_QO_COM_Y)
  1057          point_acc_mul(
  1058            add(state, STATE_LINEARISED_POLYNOMIAL_X),
  1059            mPtr,
  1060            calldataload(add(aproof, PROOF_O_AT_ZETA)),
  1061            add(mPtr, 0x40)
  1062          )
  1063  
  1064          mstore(mPtr, VK_QK_COM_X)
  1065          mstore(add(mPtr, 0x20), VK_QK_COM_Y)
  1066          point_add(
  1067            add(state, STATE_LINEARISED_POLYNOMIAL_X),
  1068            add(state, STATE_LINEARISED_POLYNOMIAL_X),
  1069            mPtr,
  1070            add(mPtr, 0x40)
  1071          )
  1072  
  1073          {{ if (gt (len .Vk.CommitmentConstraintIndexes) 0 )}}
  1074          let qcp_opening_at_zeta := add(aproof, PROOF_OPENING_QCP_AT_ZETA)
  1075          let bsb_commitments := add(aproof, PROOF_BSB_COMMITMENTS)
  1076          for {
  1077            let i := 0
  1078          } lt(i, VK_NB_CUSTOM_GATES) {
  1079            i := add(i, 1)
  1080          } {
  1081            mstore(mPtr, calldataload(bsb_commitments))
  1082            mstore(add(mPtr, 0x20), calldataload(add(bsb_commitments, 0x20)))
  1083            point_acc_mul(
  1084              add(state, STATE_LINEARISED_POLYNOMIAL_X),
  1085              mPtr,
  1086              calldataload(qcp_opening_at_zeta),
  1087              add(mPtr, 0x40)
  1088            )
  1089            qcp_opening_at_zeta := add(qcp_opening_at_zeta, 0x20)
  1090            bsb_commitments := add(bsb_commitments, 0x40)
  1091          }
  1092          {{ end }}
  1093  
  1094          mstore(mPtr, VK_S3_COM_X)
  1095          mstore(add(mPtr, 0x20), VK_S3_COM_Y)
  1096          point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, s1, add(mPtr, 0x40))
  1097  
  1098          mstore(mPtr, calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_X)))
  1099          mstore(add(mPtr, 0x20), calldataload(add(aproof, PROOF_GRAND_PRODUCT_COMMITMENT_Y)))
  1100          point_acc_mul(add(state, STATE_LINEARISED_POLYNOMIAL_X), mPtr, s2, add(mPtr, 0x40))
  1101  
  1102          point_add(
  1103            add(state, STATE_LINEARISED_POLYNOMIAL_X), 
  1104            add(state, STATE_LINEARISED_POLYNOMIAL_X), 
  1105            add(state, STATE_FOLDED_H_X), 
  1106            mPtr)
  1107        }
  1108  
  1109        /// @notice Compute the commitment to the linearized polynomial equal to
  1110        ///	L(ζ)[Qₗ]+r(ζ)[Qᵣ]+R(ζ)L(ζ)[Qₘ]+O(ζ)[Qₒ]+[Qₖ]+Σᵢqc'ᵢ(ζ)[BsbCommitmentᵢ] +
  1111        ///	α*( Z(μζ)(L(ζ)+β*S₁(ζ)+γ)*(R(ζ)+β*S₂(ζ)+γ)[S₃]-[Z](L(ζ)+β*id_{1}(ζ)+γ)*(R(ζ)+β*id_{2}(ζ)+γ)*(O(ζ)+β*id_{3}(ζ)+γ) ) +
  1112        ///	α²*L₁(ζ)[Z] - Z_{H}(ζ)*(([H₀] + ζᵐ⁺²*[H₁] + ζ²⁽ᵐ⁺²⁾*[H₂])
  1113        /// where
  1114        /// * id_1 = id, id_2 = vk_coset_shift*id, id_3 = vk_coset_shift^{2}*id
  1115        /// * the [] means that it's a commitment (i.e. a point on Bn254(F_p))
  1116        /// * Z_{H}(ζ) = ζ^n-1
  1117        /// @param aproof pointer to the proof
  1118        function compute_commitment_linearised_polynomial(aproof) {
  1119          let state := mload(0x40)
  1120          let l_beta := mload(add(state, STATE_BETA))
  1121          let l_gamma := mload(add(state, STATE_GAMMA))
  1122          let l_zeta := mload(add(state, STATE_ZETA))
  1123          let l_alpha := mload(add(state, STATE_ALPHA))
  1124  
  1125          let u := mulmod(calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), l_beta, R_MOD)
  1126          let v := mulmod(l_beta, calldataload(add(aproof, PROOF_S1_AT_ZETA)), R_MOD)
  1127          v := addmod(v, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)
  1128          v := addmod(v, l_gamma, R_MOD)
  1129  
  1130          let w := mulmod(l_beta, calldataload(add(aproof, PROOF_S2_AT_ZETA)), R_MOD)
  1131          w := addmod(w, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)
  1132          w := addmod(w, l_gamma, R_MOD)
  1133  
  1134          let s1 := mulmod(u, v, R_MOD)
  1135          s1 := mulmod(s1, w, R_MOD)
  1136          s1 := mulmod(s1, l_alpha, R_MOD)
  1137  
  1138          let coset_square := mulmod(VK_COSET_SHIFT, VK_COSET_SHIFT, R_MOD)
  1139          let betazeta := mulmod(l_beta, l_zeta, R_MOD)
  1140          u := addmod(betazeta, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)
  1141          u := addmod(u, l_gamma, R_MOD)
  1142  
  1143          v := mulmod(betazeta, VK_COSET_SHIFT, R_MOD)
  1144          v := addmod(v, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)
  1145          v := addmod(v, l_gamma, R_MOD)
  1146  
  1147          w := mulmod(betazeta, coset_square, R_MOD)
  1148          w := addmod(w, calldataload(add(aproof, PROOF_O_AT_ZETA)), R_MOD)
  1149          w := addmod(w, l_gamma, R_MOD)
  1150  
  1151          let s2 := mulmod(u, v, R_MOD)
  1152          s2 := mulmod(s2, w, R_MOD)
  1153          s2 := sub(R_MOD, s2)
  1154          s2 := mulmod(s2, l_alpha, R_MOD)
  1155          s2 := addmod(s2, mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0)), R_MOD)
  1156  
  1157          // at this stage:
  1158          // * s₁ = α*Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β
  1159          // * s₂ = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + α²*L₁(ζ)
  1160  
  1161          compute_commitment_linearised_polynomial_ec(aproof, s1, s2)
  1162        }
  1163  
  1164        /// @notice compute -z_h(ζ)*([H₁] + ζⁿ⁺²[H₂] + ζ²⁽ⁿ⁺²⁾[H₃]) and store the result at
  1165        /// state + state_folded_h
  1166        /// @param aproof pointer to the proof
  1167        function fold_h(aproof) {
  1168          let state := mload(0x40)
  1169          let n_plus_two := add(VK_DOMAIN_SIZE, 2)
  1170          let mPtr := add(mload(0x40), STATE_LAST_MEM)
  1171          let zeta_power_n_plus_two := pow(mload(add(state, STATE_ZETA)), n_plus_two, mPtr)
  1172          point_mul_calldata(add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_2_COM_X), zeta_power_n_plus_two, mPtr)
  1173          point_add_calldata(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_1_COM_X), mPtr)
  1174          point_mul(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), zeta_power_n_plus_two, mPtr)
  1175          point_add_calldata(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), add(aproof, PROOF_H_0_COM_X), mPtr)
  1176            point_mul(add(state, STATE_FOLDED_H_X), add(state, STATE_FOLDED_H_X), mload(add(state, STATE_ZETA_POWER_N_MINUS_ONE)), mPtr)
  1177          let folded_h_y := mload(add(state, STATE_FOLDED_H_Y))
  1178          folded_h_y := sub(P_MOD, folded_h_y)
  1179          mstore(add(state, STATE_FOLDED_H_Y), folded_h_y)
  1180        }
  1181  
  1182        /// @notice check that the opening of the linearised polynomial at zeta is equal to
  1183        /// - [ PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ) ]
  1184        /// @param aproof pointer to the proof
  1185        function compute_opening_linearised_polynomial(aproof) {
  1186          
  1187          let state := mload(0x40)
  1188  
  1189          // (l(ζ)+β*s1(ζ)+γ)
  1190          let s1
  1191          s1 := mulmod(calldataload(add(aproof, PROOF_S1_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)
  1192          s1 := addmod(s1, mload(add(state, STATE_GAMMA)), R_MOD)
  1193          s1 := addmod(s1, calldataload(add(aproof, PROOF_L_AT_ZETA)), R_MOD)
  1194  
  1195          // (r(ζ)+β*s2(ζ)+γ)
  1196          let s2
  1197          s2 := mulmod(calldataload(add(aproof, PROOF_S2_AT_ZETA)), mload(add(state, STATE_BETA)), R_MOD)
  1198          s2 := addmod(s2, mload(add(state, STATE_GAMMA)), R_MOD)
  1199          s2 := addmod(s2, calldataload(add(aproof, PROOF_R_AT_ZETA)), R_MOD)
  1200  
  1201          // (o(ζ)+γ)
  1202          let o
  1203          o := addmod(calldataload(add(aproof, PROOF_O_AT_ZETA)), mload(add(state, STATE_GAMMA)), R_MOD)
  1204  
  1205          //  α*Z(μζ)*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ)
  1206          s1 := mulmod(s1, s2, R_MOD)
  1207          s1 := mulmod(s1, o, R_MOD)
  1208          s1 := mulmod(s1, mload(add(state, STATE_ALPHA)), R_MOD)
  1209          s1 := mulmod(s1, calldataload(add(aproof, PROOF_GRAND_PRODUCT_AT_ZETA_OMEGA)), R_MOD)
  1210  
  1211          // PI(ζ) - α²*L₁(ζ) + α(l(ζ)+β*s1(ζ)+γ)(r(ζ)+β*s2(ζ)+γ)(o(ζ)+γ)*z(ωζ)
  1212          s1 := addmod(s1, mload(add(state, STATE_PI)), R_MOD)
  1213          s2 := mload(add(state, STATE_ALPHA_SQUARE_LAGRANGE_0))
  1214          s2 := sub(R_MOD, s2)
  1215          s1 := addmod(s1, s2, R_MOD)
  1216          s1 := sub(R_MOD, s1)
  1217  
  1218          mstore(add(state, STATE_OPENING_LINEARISED_POLYNOMIAL_ZETA), s1)
  1219        }
  1220  
  1221        // BEGINNING utils math functions -------------------------------------------------
  1222        
  1223        /// @param dst pointer storing the result
  1224        /// @param p pointer to the first point
  1225        /// @param q pointer to the second point
  1226        /// @param mPtr pointer to free memory
  1227        function point_add(dst, p, q, mPtr) {
  1228          mstore(mPtr, mload(p))
  1229          mstore(add(mPtr, 0x20), mload(add(p, 0x20)))
  1230          mstore(add(mPtr, 0x40), mload(q))
  1231          mstore(add(mPtr, 0x60), mload(add(q, 0x20)))
  1232          let l_success := staticcall(gas(),EC_ADD,mPtr,0x80,dst,0x40)
  1233          if iszero(l_success) {
  1234            error_ec_op()
  1235          }
  1236        }
  1237  
  1238        /// @param dst pointer storing the result
  1239        /// @param p pointer to the first point (calldata)
  1240        /// @param q pointer to the second point (calladata)
  1241        /// @param mPtr pointer to free memory
  1242        function point_add_calldata(dst, p, q, mPtr) {
  1243          mstore(mPtr, mload(p))
  1244          mstore(add(mPtr, 0x20), mload(add(p, 0x20)))
  1245          mstore(add(mPtr, 0x40), calldataload(q))
  1246          mstore(add(mPtr, 0x60), calldataload(add(q, 0x20)))
  1247          let l_success := staticcall(gas(), EC_ADD, mPtr, 0x80, dst, 0x40)
  1248          if iszero(l_success) {
  1249            error_ec_op()
  1250          }
  1251        }
  1252  
  1253        /// @parma dst pointer storing the result
  1254        /// @param src pointer to a point on Bn254(𝔽_p)
  1255        /// @param s scalar
  1256        /// @param mPtr free memory
  1257        function point_mul(dst,src,s, mPtr) {
  1258          mstore(mPtr,mload(src))
  1259          mstore(add(mPtr,0x20),mload(add(src,0x20)))
  1260          mstore(add(mPtr,0x40),s)
  1261          let l_success := staticcall(gas(),EC_MUL,mPtr,0x60,dst,0x40)
  1262          if iszero(l_success) {
  1263            error_ec_op()
  1264          }
  1265        }
  1266  
  1267        /// @parma dst pointer storing the result
  1268        /// @param src pointer to a point on Bn254(𝔽_p) on calldata
  1269        /// @param s scalar
  1270        /// @param mPtr free memory
  1271        function point_mul_calldata(dst, src, s, mPtr) {
  1272          mstore(mPtr, calldataload(src))
  1273          mstore(add(mPtr, 0x20), calldataload(add(src, 0x20)))
  1274          mstore(add(mPtr, 0x40), s)
  1275          let l_success := staticcall(gas(), EC_MUL, mPtr, 0x60, dst, 0x40)
  1276          if iszero(l_success) {
  1277            error_ec_op()
  1278          }
  1279        }
  1280  
  1281        /// @notice dst <- dst + [s]src (Elliptic curve)
  1282        /// @param dst pointer accumulator point storing the result
  1283        /// @param src pointer to the point to multiply and add
  1284        /// @param s scalar
  1285        /// @param mPtr free memory
  1286        function point_acc_mul(dst,src,s, mPtr) {
  1287          mstore(mPtr,mload(src))
  1288          mstore(add(mPtr,0x20),mload(add(src,0x20)))
  1289          mstore(add(mPtr,0x40),s)
  1290          let l_success := staticcall(gas(),7,mPtr,0x60,mPtr,0x40)
  1291          mstore(add(mPtr,0x40),mload(dst))
  1292          mstore(add(mPtr,0x60),mload(add(dst,0x20)))
  1293          l_success := and(l_success, staticcall(gas(),EC_ADD,mPtr,0x80,dst, 0x40))
  1294          if iszero(l_success) {
  1295            error_ec_op()
  1296          }
  1297        }
  1298  
  1299        /// @notice dst <- dst + [s]src (Elliptic curve)
  1300        /// @param dst pointer accumulator point storing the result
  1301        /// @param src pointer to the point to multiply and add (on calldata)
  1302        /// @param s scalar
  1303        /// @mPtr free memory
  1304        function point_acc_mul_calldata(dst, src, s, mPtr) {
  1305          mstore(mPtr, calldataload(src))
  1306          mstore(add(mPtr, 0x20), calldataload(add(src, 0x20)))
  1307          mstore(add(mPtr, 0x40), s)
  1308          let l_success := staticcall(gas(), 7, mPtr, 0x60, mPtr, 0x40)
  1309          mstore(add(mPtr, 0x40), mload(dst))
  1310          mstore(add(mPtr, 0x60), mload(add(dst, 0x20)))
  1311          l_success := and(l_success, staticcall(gas(), EC_ADD, mPtr, 0x80, dst, 0x40))
  1312          if iszero(l_success) {
  1313            error_ec_op()
  1314          }
  1315        }
  1316  
  1317        /// @notice dst <- dst + src*s (Fr) dst,src are addresses, s is a value
  1318        /// @param dst pointer storing the result
  1319        /// @param src pointer to the scalar to multiply and add (on calldata)
  1320        /// @param s scalar
  1321        function fr_acc_mul_calldata(dst, src, s) {
  1322          let tmp :=  mulmod(calldataload(src), s, R_MOD)
  1323          mstore(dst, addmod(mload(dst), tmp, R_MOD))
  1324        }
  1325  
  1326        /// @param x element to exponentiate
  1327        /// @param e exponent
  1328        /// @param mPtr free memory
  1329        /// @return res x ** e mod r
  1330        function pow(x, e, mPtr)->res {
  1331          mstore(mPtr, 0x20)
  1332          mstore(add(mPtr, 0x20), 0x20)
  1333          mstore(add(mPtr, 0x40), 0x20)
  1334          mstore(add(mPtr, 0x60), x)
  1335          mstore(add(mPtr, 0x80), e)
  1336          mstore(add(mPtr, 0xa0), R_MOD)
  1337          let check_staticcall := staticcall(gas(),MOD_EXP,mPtr,0xc0,mPtr,0x20)
  1338          if eq(check_staticcall, 0) {
  1339              error_mod_exp()
  1340          }
  1341          res := mload(mPtr)
  1342        }
  1343      }
  1344    }
  1345  }
  1346  `
  1347  
  1348  // MarshalSolidity converts a proof to a byte array that can be used in a
  1349  // Solidity contract.
  1350  func (proof *Proof) MarshalSolidity() []byte {
  1351  
  1352  	res := make([]byte, 0, 1024)
  1353  
  1354  	// uint256 l_com_x;
  1355  	// uint256 l_com_y;
  1356  	// uint256 r_com_x;
  1357  	// uint256 r_com_y;
  1358  	// uint256 o_com_x;
  1359  	// uint256 o_com_y;
  1360  	var tmp64 [64]byte
  1361  	for i := 0; i < 3; i++ {
  1362  		tmp64 = proof.LRO[i].RawBytes()
  1363  		res = append(res, tmp64[:]...)
  1364  	}
  1365  
  1366  	// uint256 h_0_x;
  1367  	// uint256 h_0_y;
  1368  	// uint256 h_1_x;
  1369  	// uint256 h_1_y;
  1370  	// uint256 h_2_x;
  1371  	// uint256 h_2_y;
  1372  	for i := 0; i < 3; i++ {
  1373  		tmp64 = proof.H[i].RawBytes()
  1374  		res = append(res, tmp64[:]...)
  1375  	}
  1376  	var tmp32 [32]byte
  1377  
  1378  	// uint256 l_at_zeta;
  1379  	// uint256 r_at_zeta;
  1380  	// uint256 o_at_zeta;
  1381  	// uint256 s1_at_zeta;
  1382  	// uint256 s2_at_zeta;
  1383  	for i := 1; i < 6; i++ {
  1384  		tmp32 = proof.BatchedProof.ClaimedValues[i].Bytes()
  1385  		res = append(res, tmp32[:]...)
  1386  	}
  1387  
  1388  	// uint256 grand_product_commitment_x;
  1389  	// uint256 grand_product_commitment_y;
  1390  	tmp64 = proof.Z.RawBytes()
  1391  	res = append(res, tmp64[:]...)
  1392  
  1393  	// uint256 grand_product_at_zeta_omega;
  1394  	tmp32 = proof.ZShiftedOpening.ClaimedValue.Bytes()
  1395  	res = append(res, tmp32[:]...)
  1396  
  1397  	// we skip the claimed value of the linearised polynomial at zeta because it
  1398  	// is recomputed by the verifier and plugged in the batch opening proof directly
  1399  
  1400  	// uint256 opening_at_zeta_proof_x;
  1401  	// uint256 opening_at_zeta_proof_y;
  1402  	tmp64 = proof.BatchedProof.H.RawBytes()
  1403  	res = append(res, tmp64[:]...)
  1404  
  1405  	// uint256 opening_at_zeta_omega_proof_x;
  1406  	// uint256 opening_at_zeta_omega_proof_y;
  1407  	tmp64 = proof.ZShiftedOpening.H.RawBytes()
  1408  	res = append(res, tmp64[:]...)
  1409  
  1410  	// uint256[] selector_commit_api_at_zeta;
  1411  	// uint256[] wire_committed_commitments;
  1412  	for i := 0; i < len(proof.Bsb22Commitments); i++ {
  1413  		tmp32 = proof.BatchedProof.ClaimedValues[6+i].Bytes()
  1414  		res = append(res, tmp32[:]...)
  1415  	}
  1416  
  1417  	for _, bc := range proof.Bsb22Commitments {
  1418  		tmp64 = bc.RawBytes()
  1419  		res = append(res, tmp64[:]...)
  1420  	}
  1421  
  1422  	return res
  1423  }