From 6a143a1e04fe04a6f52d634d6472403cfb85ee8d Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 18:41:00 +0200 Subject: [PATCH 01/13] feat: compute_pi assembly ok --- .../bn254/solidity/contracts/Verifier.sol | 1424 +++++++++++++++++ 1 file changed, 1424 insertions(+) create mode 100644 backend/plonk/bn254/solidity/contracts/Verifier.sol diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol new file mode 100644 index 0000000000..64b17cadc4 --- /dev/null +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -0,0 +1,1424 @@ +// SPDX-License-Identifier: Apache-2.0 + +// Copyright 2023 Consensys Software Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by gnark DO NOT EDIT + +pragma solidity ^0.8.19; + +contract PlonkVerifier { + + uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 private constant p_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + uint256 private constant g2_srs_0_x_0 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; + uint256 private constant g2_srs_0_x_1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; + uint256 private constant g2_srs_0_y_0 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; + uint256 private constant g2_srs_0_y_1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; + + uint256 private constant g2_srs_1_x_0 = 47713196425749429575943217444118312942213353489131971952158393057073394709; + uint256 private constant g2_srs_1_x_1 = 14082566465881351121005025145697241888648303397746826167017579314219467944174; + uint256 private constant g2_srs_1_y_0 = 2701615586724206752938529831863939849831470165250987450603179980450952505250; + uint256 private constant g2_srs_1_y_1 = 4826080465536228920237187461721988550508671189557677442630659399549457339706; + + // ----------------------- vk --------------------- + uint256 private constant vk_domain_size = 64; + uint256 private constant vk_inv_domain_size = 21546239076966786546898805655487630165289796206659533807077919746160561487873; + uint256 private constant vk_omega = 9088801421649573101014283686030284801466796108869023335878462724291607593530; + uint256 private constant vk_ql_com_x = 10868848045043734703276297354523927435135720685817894190739415177156976922804; + uint256 private constant vk_ql_com_y = 20843608126201315617190154936873147978207810863848578062491214103973954353900; + uint256 private constant vk_qr_com_x = 2679235231493861237565984579160056368873174851272447016820965771639185884129; + uint256 private constant vk_qr_com_y = 5769890692747333821981369609828300566137216559866495823241488516555117850765; + uint256 private constant vk_qm_com_x = 12174728365463593868597410524635068192072309684162556252866030467398704224320; + uint256 private constant vk_qm_com_y = 2601873741960857746398047572147634701494442710834991645015027130583495490073; + uint256 private constant vk_qo_com_x = 2679235231493861237565984579160056368873174851272447016820965771639185884129; + uint256 private constant vk_qo_com_y = 16118352179091941400265036135428974522559094597431327839447549378090108357818; + uint256 private constant vk_qk_com_x = 12174728365463593868597410524635068192072309684162556252866030467398704224320; + uint256 private constant vk_qk_com_y = 19286369129878417475848358173109640387201868446462832017674010764061730718510; + + uint256 private constant vk_s1_com_x = 3732030616711959612213933484143551003153419855408787100769579029855683361349; + uint256 private constant vk_s1_com_y = 8863350200848143456467367762048138762160747177984624877332042811932646213158; + + uint256 private constant vk_s2_com_x = 14978594494098749485501410444126109880601380191003862373946945147295051518552; + uint256 private constant vk_s2_com_y = 14609132512541969816702917650350145292194838347590364588003909602629760651344; + + uint256 private constant vk_s3_com_x = 20661969894449877091914268765653852026526548709497569785432654520121872287907; + uint256 private constant vk_s3_com_y = 7147432922287329972047167657582942187695189639450447765447794162301168023303; + + uint256 private constant vk_coset_shift = 5; + + + uint256 private constant vk_selector_commitments_commit_api_0_x = 17939871339693758397907567934363234620791071268920919277161314430004366183253; + uint256 private constant vk_selector_commitments_commit_api_0_y = 17021664810853884023109619590156222347987157544435252377102168154366528134123; + + uint256 private constant vk_selector_commitments_commit_api_1_x = 10428549206826812980966537226094112662425214493145610768212486078263283139225; + uint256 private constant vk_selector_commitments_commit_api_1_y = 7981313614471682011874166235075241848872806781904931743163227627908232767976; + + uint256 private constant vk_selector_commitments_commit_api_2_x = 15547614805428943792721823383916476247217088537196428817825813781338621128228; + uint256 private constant vk_selector_commitments_commit_api_2_y = 9707207482676214718109065159968174936261689123781781812609918340958746242815; + + uint256 private constant vk_index_commit_api_0 = 3; + uint256 private constant vk_index_commit_api_1 = 7; + uint256 private constant vk_index_commit_api_2 = 11; + + function load_vk_commitments_indices_commit_api(uint256[] memory v) + internal pure { + assembly { + let _v := add(v, 0x20) + + mstore(_v, 3) + _v := add(_v, 0x20) + + mstore(_v, 7) + _v := add(_v, 0x20) + + mstore(_v, 11) + _v := add(_v, 0x20) + + } + } + + uint256 private constant vk_nb_commitments_commit_api = 3; + + // ------------------------------------------------ + + // offset proof + uint256 private constant proof_l_com_x = 0x20; + uint256 private constant proof_l_com_y = 0x40; + uint256 private constant proof_r_com_x = 0x60; + uint256 private constant proof_r_com_y = 0x80; + uint256 private constant proof_o_com_x = 0xa0; + uint256 private constant proof_o_com_y = 0xc0; + + // h = h_0 + x^{n+2}h_1 + x^{2(n+2)}h_2 + uint256 private constant proof_h_0_x = 0xe0; + uint256 private constant proof_h_0_y = 0x100; + uint256 private constant proof_h_1_x = 0x120; + uint256 private constant proof_h_1_y = 0x140; + uint256 private constant proof_h_2_x = 0x160; + uint256 private constant proof_h_2_y = 0x180; + + // wire values at zeta + uint256 private constant proof_l_at_zeta = 0x1a0; + uint256 private constant proof_r_at_zeta = 0x1c0; + uint256 private constant proof_o_at_zeta = 0x1e0; + + //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) + uint256 private constant proof_s1_at_zeta = 0x200; // Sσ1(zeta) + uint256 private constant proof_s2_at_zeta = 0x220; // Sσ2(zeta) + + //Bn254.G1Point grand_product_commitment; // [z(x)] + uint256 private constant proof_grand_product_commitment_x = 0x240; + uint256 private constant proof_grand_product_commitment_y = 0x260; + + uint256 private constant proof_grand_product_at_zeta_omega = 0x280; // z(w*zeta) + uint256 private constant proof_quotient_polynomial_at_zeta = 0x2a0; // t(zeta) + uint256 private constant proof_linearised_polynomial_at_zeta = 0x2c0; // r(zeta) + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant proof_batch_opening_at_zeta_x = 0x2e0; // [Wzeta] + uint256 private constant proof_batch_opening_at_zeta_y = 0x300; + + //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] + uint256 private constant proof_opening_at_zeta_omega_x = 0x320; + uint256 private constant proof_opening_at_zeta_omega_y = 0x340; + + uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x360; + // -> next part of proof is + // [ openings_selector_commits || commitments_wires_commit_api] + + // -------- offset state + + // challenges to check the claimed quotient + uint256 private constant state_alpha = 0x00; + uint256 private constant state_beta = 0x20; + uint256 private constant state_gamma = 0x40; + uint256 private constant state_zeta = 0x60; + + // reusable value + uint256 private constant state_alpha_square_lagrange_0 = 0x80; + + // commitment to H + uint256 private constant state_folded_h_x = 0xa0; + uint256 private constant state_folded_h_y = 0xc0; + + // commitment to the linearised polynomial + uint256 private constant state_linearised_polynomial_x = 0xe0; + uint256 private constant state_linearised_polynomial_y = 0x100; + + // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp + uint256 private constant state_folded_claimed_values = 0x120; + + // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp + // Bn254.G1Point folded_digests; + uint256 private constant state_folded_digests_x = 0x140; + uint256 private constant state_folded_digests_y = 0x160; + + uint256 private constant state_pi = 0x180; + + uint256 private constant state_zeta_power_n_minus_one = 0x1a0; + + uint256 private constant state_gamma_kzg = 0x1c0; + + uint256 private constant state_success = 0x1e0; + uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only + + uint256 private constant state_last_mem = 0x220; + + // -------- errors + uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) + + + // -------- utils (for hash_fr) + uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 + uint256 private constant zero_uint256 = 0; + + uint8 private constant lenInBytes = 48; + uint8 private constant sizeDomain = 11; + uint8 private constant one = 1; + uint8 private constant two = 2; + + + // read the commitments to the wires related to the commit api and store them in wire_commitments. + // The commitments are points on Bn254(Fp) so they are stored on 2 uint256. + function load_wire_commitments_commit_api(uint256[] memory wire_commitments, bytes memory proof) + internal pure { + assembly { + let w := add(wire_commitments, 0x20) + let p := add(proof, proof_openings_selector_commit_api_at_zeta) + p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + // x coordinate + mstore(w, mload(p)) + w := add(w,0x20) + p := add(p,0x20) + + // y coordinate + mstore(w, mload(p)) + w := add(w,0x20) + p := add(p,0x20) + } + } + } + + + function derive_gamma_beta_alpha_zeta(bytes memory proof, uint256[] memory public_inputs) + internal view returns(uint256 gamma, uint256 beta, uint256 alpha, uint256 zeta) { + assembly { + + let mem := mload(0x40) + + derive_gamma(proof, public_inputs) + gamma := mload(mem) + + derive_beta(proof, gamma) + beta := mload(mem) + + derive_alpha(proof, beta) + alpha := mload(mem) + + derive_zeta(proof, alpha) + zeta := mload(mem) + + gamma := mod(gamma, r_mod) + beta := mod(beta, r_mod) + alpha := mod(alpha, r_mod) + zeta := mod(zeta, r_mod) + + function error_sha2_256() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x19) + mstore(add(ptError, 0x44), "error staticcall sha2-256") + revert(ptError, 0x64) + } + + // Derive gamma as Sha256() + // where transcript is the concatenation (in this order) of: + // * the word "gamma" in ascii, equal to [0x67,0x61,0x6d, 0x6d, 0x61] and encoded as a uint256. + // * the commitments to the permutation polynomials S1, S2, S3, where we concatenate the coordinates of those points + // * the commitments of Ql, Qr, Qm, Qo, Qk + // * the public inputs + // * the commitments of the wires related to the custom gates (commitments_wires_commit_api) + // * commitments to L, R, O (proof__com_) + // The data described above is written starting at mPtr. "gamma" lies on 5 bytes, + // and is encoded as a uint256 number n. In basis b = 256, the number looks like this + // [0 0 0 .. 0x67 0x61 0x6d, 0x6d, 0x61]. The first non zero entry is at position 27=0x1b + function derive_gamma(aproof, pub_inputs) { + + let mPtr := mload(0x40) + + // gamma + // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61] + // (same for alpha, beta, zeta) + mstore(mPtr, 0x67616d6d61) // "gamma" + + mstore(add(mPtr, 0x20), vk_s1_com_x) + mstore(add(mPtr, 0x40), vk_s1_com_y) + mstore(add(mPtr, 0x60), vk_s2_com_x) + mstore(add(mPtr, 0x80), vk_s2_com_y) + mstore(add(mPtr, 0xa0), vk_s3_com_x) + mstore(add(mPtr, 0xc0), vk_s3_com_y) + mstore(add(mPtr, 0xe0), vk_ql_com_x) + mstore(add(mPtr, 0x100), vk_ql_com_y) + mstore(add(mPtr, 0x120), vk_qr_com_x) + mstore(add(mPtr, 0x140), vk_qr_com_y) + mstore(add(mPtr, 0x160), vk_qm_com_x) + mstore(add(mPtr, 0x180), vk_qm_com_y) + mstore(add(mPtr, 0x1a0), vk_qo_com_x) + mstore(add(mPtr, 0x1c0), vk_qo_com_y) + mstore(add(mPtr, 0x1e0), vk_qk_com_x) + mstore(add(mPtr, 0x200), vk_qk_com_y) + + let pi := add(pub_inputs, 0x20) + let _mPtr := add(mPtr, 0x220) + for {let i:=0} lt(i, mload(pub_inputs)) {i:=add(i,1)} + { + mstore(_mPtr, mload(pi)) + pi := add(pi, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + mstore(_mPtr, mload(_proof)) + mstore(add(_mPtr, 0x20), mload(add(_proof, 0x20))) + _mPtr := add(_mPtr, 0x40) + _proof := add(_proof, 0x40) + } + + mstore(_mPtr, mload(add(aproof, proof_l_com_x))) + mstore(add(_mPtr, 0x20), mload(add(aproof, proof_l_com_y))) + mstore(add(_mPtr, 0x40), mload(add(aproof, proof_r_com_x))) + mstore(add(_mPtr, 0x60), mload(add(aproof, proof_r_com_y))) + mstore(add(_mPtr, 0x80), mload(add(aproof, proof_o_com_x))) + mstore(add(_mPtr, 0xa0), mload(add(aproof, proof_o_com_y))) + + let size := add(0x2c5, mul(mload(pub_inputs), 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 0x40)) + let success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(success) { + error_sha2_256() + } + } + + function derive_beta(aproof, prev_challenge){ + let mPtr := mload(0x40) + // beta + mstore(mPtr, 0x62657461) // "beta" + mstore(add(mPtr, 0x20), prev_challenge) + let success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(success) { + error_sha2_256() + } + } + + // alpha depends on the previous challenge (beta) and on the commitment to the grand product polynomial + function derive_alpha(aproof, prev_challenge){ + let mPtr := mload(0x40) + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), prev_challenge) + mstore(add(mPtr, 0x40), mload(add(aproof, proof_grand_product_commitment_x))) + mstore(add(mPtr, 0x60), mload(add(aproof, proof_grand_product_commitment_y))) + let success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(success) { + error_sha2_256() + } + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, prev_challenge) { + let mPtr := mload(0x40) + // zeta + mstore(mPtr, 0x7a657461) // "zeta" + mstore(add(mPtr, 0x20), prev_challenge) + mstore(add(mPtr, 0x40), mload(add(aproof, proof_h_0_x))) + mstore(add(mPtr, 0x60), mload(add(aproof, proof_h_0_y))) + mstore(add(mPtr, 0x80), mload(add(aproof, proof_h_1_x))) + mstore(add(mPtr, 0xa0), mload(add(aproof, proof_h_1_y))) + mstore(add(mPtr, 0xc0), mload(add(aproof, proof_h_2_x))) + mstore(add(mPtr, 0xe0), mload(add(aproof, proof_h_2_y))) + let success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) + if iszero(success) { + error_sha2_256() + } + } + } + } + + + // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = zeta (challenge derived with Fiat Shamir) + // * zpnmo = ζⁿ-1 + function compute_ith_lagrange_at_z(uint256 zeta, uint256 zpnmo, uint256 i) + internal view returns (uint256 res) { + assembly { + + function error_pow_local() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x17) + mstore(add(ptError, 0x44), "error staticcall modexp") + revert(ptError, 0x64) + } + + // _n^_i [r] + function pow_local(x, e, mPtr)->result { + // let mPtr := mload(0x40) + mstore(mPtr, 0x20) + mstore(add(mPtr, 0x20), 0x20) + mstore(add(mPtr, 0x40), 0x20) + mstore(add(mPtr, 0x60), x) + mstore(add(mPtr, 0x80), e) + mstore(add(mPtr, 0xa0), r_mod) + let success := staticcall(gas(),0x05,mPtr,0xc0,0x00,0x20) + if iszero(success) { + error_pow_local() + } + result := mload(0x00) + } + + let freeMem := mload(0x40) + let w := pow_local(vk_omega, i, freeMem) // w**i + i := addmod(zeta, sub(r_mod, w), r_mod) // z-w**i + w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n + i := pow_local(i, sub(r_mod,2), freeMem) // (z-w**i)**-1 + w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1 + res := mulmod(w, zpnmo, r_mod) + } + } + + + + // corresponds to https://github.com/ConsenSys/gnark-crypto/blob/develop/ecc/bn254/fr/element.go + // function used to hash points resulting from calls to Commit() in the circuit. + function hash_fr(uint256 x, uint256 y) internal view returns (uint256 res) { + + assembly { + + function error_sha2_256() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x19) + mstore(add(ptError, 0x44), "error staticcall sha2-256") + revert(ptError, 0x64) + } + + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> + let mPtr := mload(0x40) + + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, zero_uint256) + mstore(add(mPtr, 0x20), zero_uint256) + + // msg = x || y , both on 32 bytes + mstore(add(mPtr, 0x40), x) + mstore(add(mPtr, 0x60), y) + + // 0 || 48 || 0 all on 1 byte + mstore8(add(mPtr, 0x80), 0) + mstore8(add(mPtr, 0x81), lenInBytes) + mstore8(add(mPtr, 0x82), 0) + + // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] + mstore8(add(mPtr, 0x83), 0x42) + mstore8(add(mPtr, 0x84), 0x53) + mstore8(add(mPtr, 0x85), 0x42) + mstore8(add(mPtr, 0x86), 0x32) + mstore8(add(mPtr, 0x87), 0x32) + mstore8(add(mPtr, 0x88), 0x2d) + mstore8(add(mPtr, 0x89), 0x50) + mstore8(add(mPtr, 0x8a), 0x6c) + mstore8(add(mPtr, 0x8b), 0x6f) + mstore8(add(mPtr, 0x8c), 0x6e) + mstore8(add(mPtr, 0x8d), 0x6b) + + // size domain + mstore8(add(mPtr, 0x8e), sizeDomain) + + let success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(success) { + error_sha2_256() + } + + let b0 := mload(mPtr) + + // [b0 || one || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), one) // 1 + + mstore8(add(mPtr, 0x21), 0x42) // dst + mstore8(add(mPtr, 0x22), 0x53) + mstore8(add(mPtr, 0x23), 0x42) + mstore8(add(mPtr, 0x24), 0x32) + mstore8(add(mPtr, 0x25), 0x32) + mstore8(add(mPtr, 0x26), 0x2d) + mstore8(add(mPtr, 0x27), 0x50) + mstore8(add(mPtr, 0x28), 0x6c) + mstore8(add(mPtr, 0x29), 0x6f) + mstore8(add(mPtr, 0x2a), 0x6e) + mstore8(add(mPtr, 0x2b), 0x6b) + + mstore8(add(mPtr, 0x2c), sizeDomain) // size domain + success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) + if iszero(success) { + error_sha2_256() + } + + // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) + + // [b0^b1 || two || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), two) + + mstore8(add(mPtr, 0x41), 0x42) // dst + mstore8(add(mPtr, 0x42), 0x53) + mstore8(add(mPtr, 0x43), 0x42) + mstore8(add(mPtr, 0x44), 0x32) + mstore8(add(mPtr, 0x45), 0x32) + mstore8(add(mPtr, 0x46), 0x2d) + mstore8(add(mPtr, 0x47), 0x50) + mstore8(add(mPtr, 0x48), 0x6c) + mstore8(add(mPtr, 0x49), 0x6f) + mstore8(add(mPtr, 0x4a), 0x6e) + mstore8(add(mPtr, 0x4b), 0x6b) + + mstore8(add(mPtr, 0x4c), sizeDomain) // size domain + + let offset := add(mPtr, 0x20) + success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) + if iszero(success) { + error_sha2_256() + } + + // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. + // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) + // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] + res := mulmod(mload(mPtr), bb, r_mod) // <- res = 2**128 * mPtr[:32] + offset := add(mPtr, 0x10) + for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + { + mstore8(offset, 0x00) + offset := add(offset, 0x1) + } + let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, r_mod) + + } + + } + + event PrintUint256(uint256 a); + + // returns the contribution of the public inputs + ζⁿ-1 which will + // be reused several times + function compute_pi( + uint256[] memory public_inputs, + uint256 zeta, + bytes memory proof + ) internal returns (uint256 pi, uint256 zeta_power_n_minus_one) { + + uint256 check; + + assembly { + + function error_pow() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x17) + mstore(add(ptError, 0x44), "error staticcall modexp") + revert(ptError, 0x64) + } + + // evaluation of Z=Xⁿ-1 at ζ, we save this value + zeta_power_n_minus_one := addmod(pow(zeta, vk_domain_size, mload(0x40)), sub(r_mod, 1), r_mod) + + // free mem + let freeMem := mload(0x40) + + pi := sum_pi_wo_api_commit(add(public_inputs,0x20), mload(public_inputs), zeta, zeta_power_n_minus_one, freeMem) + + check := sum_pi_commit(proof, mload(public_inputs), zeta, zeta_power_n_minus_one, freeMem) + check := addmod(check, pi, r_mod) + + function sum_pi_wo_api_commit(ins, n, z, zpnmo, mPtr)->pi_wo_commit { + + let li := mPtr + batch_compute_lagranges_at_z(z, zpnmo, n, li) + + let tmp := 0 + for {let i:=0} lt(i,n) {i:=add(i,1)} + { + tmp := mulmod(mload(li), mload(ins), r_mod) + pi_wo_commit := addmod(pi_wo_commit, tmp, r_mod) + li := add(li, 0x20) + ins := add(ins, 0x20) + } + + } + + // mPtr free memory. Computes the public input contribution related to the commit + function sum_pi_commit(aproof, nb_public_inputs, z, zpnmo, mPtr)->pi_commit { + + let p := add(aproof, proof_openings_selector_commit_api_at_zeta) + p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire commitments + + let h_fr, ith_lagrange + + h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) + ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_0), mPtr) + pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) + p := add(p, 0x40) + + h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) + ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_1), mPtr) + pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) + p := add(p, 0x40) + + h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) + ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_2), mPtr) + pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) + p := add(p, 0x40) + + } + + // z zeta + // zpmno ζⁿ-1 + // i i-th lagrange + // mPtr free memory + function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr)->res { + + let w := pow(vk_omega, i, mPtr) // w**i + i := addmod(z, sub(r_mod, w), r_mod) // z-w**i + w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n + i := pow(i, sub(r_mod,2), mPtr) // (z-w**i)**-1 + w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1 + res := mulmod(w, zpnmo, r_mod) + + } + + function error_sha2_256() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x19) + mstore(add(ptError, 0x44), "error staticcall sha2-256") + revert(ptError, 0x64) + } + + // (x, y) point on bn254, both on 32bytes + // mPtr free memory + function hash_fr(x, y, mPtr)->res { + + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> + + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, zero_uint256) + mstore(add(mPtr, 0x20), zero_uint256) + + // msg = x || y , both on 32 bytes + mstore(add(mPtr, 0x40), x) + mstore(add(mPtr, 0x60), y) + + // 0 || 48 || 0 all on 1 byte + mstore8(add(mPtr, 0x80), 0) + mstore8(add(mPtr, 0x81), lenInBytes) + mstore8(add(mPtr, 0x82), 0) + + // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] + mstore8(add(mPtr, 0x83), 0x42) + mstore8(add(mPtr, 0x84), 0x53) + mstore8(add(mPtr, 0x85), 0x42) + mstore8(add(mPtr, 0x86), 0x32) + mstore8(add(mPtr, 0x87), 0x32) + mstore8(add(mPtr, 0x88), 0x2d) + mstore8(add(mPtr, 0x89), 0x50) + mstore8(add(mPtr, 0x8a), 0x6c) + mstore8(add(mPtr, 0x8b), 0x6f) + mstore8(add(mPtr, 0x8c), 0x6e) + mstore8(add(mPtr, 0x8d), 0x6b) + + // size domain + mstore8(add(mPtr, 0x8e), sizeDomain) + + let success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(success) { + error_sha2_256() + } + + let b0 := mload(mPtr) + + // [b0 || one || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), one) // 1 + + mstore8(add(mPtr, 0x21), 0x42) // dst + mstore8(add(mPtr, 0x22), 0x53) + mstore8(add(mPtr, 0x23), 0x42) + mstore8(add(mPtr, 0x24), 0x32) + mstore8(add(mPtr, 0x25), 0x32) + mstore8(add(mPtr, 0x26), 0x2d) + mstore8(add(mPtr, 0x27), 0x50) + mstore8(add(mPtr, 0x28), 0x6c) + mstore8(add(mPtr, 0x29), 0x6f) + mstore8(add(mPtr, 0x2a), 0x6e) + mstore8(add(mPtr, 0x2b), 0x6b) + + mstore8(add(mPtr, 0x2c), sizeDomain) // size domain + success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) + if iszero(success) { + error_sha2_256() + } + + // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) + + // [b0^b1 || two || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), two) + + mstore8(add(mPtr, 0x41), 0x42) // dst + mstore8(add(mPtr, 0x42), 0x53) + mstore8(add(mPtr, 0x43), 0x42) + mstore8(add(mPtr, 0x44), 0x32) + mstore8(add(mPtr, 0x45), 0x32) + mstore8(add(mPtr, 0x46), 0x2d) + mstore8(add(mPtr, 0x47), 0x50) + mstore8(add(mPtr, 0x48), 0x6c) + mstore8(add(mPtr, 0x49), 0x6f) + mstore8(add(mPtr, 0x4a), 0x6e) + mstore8(add(mPtr, 0x4b), 0x6b) + + mstore8(add(mPtr, 0x4c), sizeDomain) // size domain + + let offset := add(mPtr, 0x20) + success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) + if iszero(success) { + error_sha2_256() + } + + // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. + // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) + // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] + res := mulmod(mload(mPtr), bb, r_mod) // <- res = 2**128 * mPtr[:32] + offset := add(mPtr, 0x10) + for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + { + mstore8(offset, 0x00) + offset := add(offset, 0x1) + } + let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, r_mod) + + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + function batch_compute_lagranges_at_z(z, zpnmo, n, mPtr) { + + let zn := mulmod(zpnmo, vk_inv_domain_size, r_mod) // 1/n * (ζⁿ - 1) + + let _w := 1 + let _mPtr := mPtr + for {let i:=0} lt(i,n) {i:=add(i,1)} + { + mstore(_mPtr, addmod(z,sub(r_mod, _w), r_mod)) + _w := mulmod(_w, vk_omega, r_mod) + _mPtr := add(_mPtr, 0x20) + } + batch_invert(mPtr, n, _mPtr) + _mPtr := mPtr + _w := 1 + for {let i:=0} lt(i,n) {i:=add(i,1)} + { + mstore(_mPtr, mulmod(mulmod(mload(_mPtr), zn , r_mod), _w, r_mod)) + _mPtr := add(_mPtr, 0x20) + _w := mulmod(_w, vk_omega, r_mod) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + function batch_invert(ins, nb_ins, mPtr) { + mstore(mPtr, 1) + let offset := 0 + for {let i:=0} lt(i, nb_ins) {i:=add(i,1)} + { + let prev := mload(add(mPtr, offset)) + let cur := mload(add(ins, offset)) + cur := mulmod(prev, cur, r_mod) + offset := add(offset, 0x20) + mstore(add(mPtr, offset), cur) + } + ins := add(ins, sub(offset, 0x20)) + mPtr := add(mPtr, offset) + let inv := pow(mload(mPtr), sub(r_mod,2), add(mPtr, 0x20)) + for {let i:=0} lt(i, nb_ins) {i:=add(i,1)} + { + mPtr := sub(mPtr, 0x20) + let tmp := mload(ins) + let cur := mulmod(inv, mload(mPtr), r_mod) + mstore(ins, cur) + inv := mulmod(inv, tmp, r_mod) + ins := sub(ins, 0x20) + } + } + + // res <- x^e mod r, mPtr = free memory + function pow(x, e, mPtr)->res { + mstore(mPtr, 0x20) + mstore(add(mPtr, 0x20), 0x20) + mstore(add(mPtr, 0x40), 0x20) + mstore(add(mPtr, 0x60), x) + mstore(add(mPtr, 0x80), e) + mstore(add(mPtr, 0xa0), r_mod) + let success := staticcall(gas(),0x05,mPtr,0xc0,mPtr,0x20) + if iszero(success) { + error_pow() + } + res := mload(mPtr) + } + + } + emit PrintUint256(check); + + + // compute the contribution of the public inputs whose indices are in commitment_indices, + // and whose value is hash_fr of the corresponding commitme + uint256[] memory commitment_indices = new uint256[](vk_nb_commitments_commit_api); + load_vk_commitments_indices_commit_api(commitment_indices); + + unchecked { + uint256[] memory wire_committed_commitments = new uint256[](2 * vk_nb_commitments_commit_api); + + load_wire_commitments_commit_api(wire_committed_commitments, proof); + uint256 hash_res; + uint256 ith_lagrange_at_z; + for (uint256 i; i < vk_nb_commitments_commit_api; ) { + hash_res = hash_fr(wire_committed_commitments[2 * i], wire_committed_commitments[2 * i + 1]); + ith_lagrange_at_z = compute_ith_lagrange_at_z( + zeta, + zeta_power_n_minus_one, + commitment_indices[i] + public_inputs.length + ); + assembly { + ith_lagrange_at_z := mulmod(hash_res, ith_lagrange_at_z, r_mod) + pi := addmod(pi, ith_lagrange_at_z, r_mod) + } + ++i; + } + } + + } + + function check_inputs_size(uint256[] memory public_inputs) + internal pure { + + bool input_checks = true; + assembly { + let s := mload(public_inputs) + let p := add(public_inputs, 0x20) + for {let i} lt(i, s) {i:=add(i,1)} + { + input_checks := and(input_checks,lt(mload(p), r_mod)) + p := add(p, 0x20) + } + } + require(input_checks, "some inputs are bigger than r"); + + } + + function check_proof_size(bytes memory proof) + internal pure { + unchecked { + uint256 expected_proof_size = 0x340+vk_nb_commitments_commit_api*0x60; + uint256 actual_proof_size; + assembly { + actual_proof_size := mload(proof) + } + require(actual_proof_size==expected_proof_size, "wrong proof size"); + } + } + + function check_proof_openings_size(bytes memory proof) + internal pure { + bool openings_check = true; + assembly { + + // linearised polynomial at zeta + let p := add(proof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // quotient polynomial at zeta + p := add(proof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_l_at_zeta + p := add(proof, proof_l_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_r_at_zeta + p := add(proof, proof_r_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_o_at_zeta + p := add(proof, proof_o_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_s1_at_zeta + p := add(proof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_s2_at_zeta + p := add(proof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(proof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(proof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + openings_check := and(openings_check, lt(mload(p), r_mod)) + p := add(p, 0x20) + } + + + } + require(openings_check, "some openings are bigger than r"); + } + + function Verify(bytes memory proof, uint256[] memory public_inputs) + public returns(bool success) { + check_inputs_size(public_inputs); + check_proof_size(proof); + check_proof_openings_size(proof); + + (uint256 gamma, uint256 beta, uint256 alpha, uint256 zeta) = derive_gamma_beta_alpha_zeta(proof, public_inputs); + + + (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta, proof); + emit PrintUint256(pi); + + + uint256 check; + + assembly { + + let mem := mload(0x40) + mstore(add(mem, state_alpha), alpha) + mstore(add(mem, state_gamma), gamma) + mstore(add(mem, state_zeta), zeta) + mstore(add(mem, state_beta), beta) + + mstore(add(mem, state_pi), pi) + + mstore(add(mem, state_zeta_power_n_minus_one), zeta_power_n_minus_one) + + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(proof) + fold_h(proof) + compute_commitment_linearised_polynomial(proof) + compute_gamma_kzg(proof) + fold_state(proof) + batch_verify_multi_points(proof) + + success := mload(add(mem, state_success)) + + check := mload(add(mem, state_check_var)) + + function error_verify() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0xc) + mstore(add(ptError, 0x44), "error verify") + revert(ptError, 0x64) + } + + // compute α² * 1/n * (ζ{n}-1)/(ζ - 1) where + // * α = challenge derived in derive_gamma_beta_alpha_zeta + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = zeta (challenge derived with Fiat Shamir) + function compute_alpha_square_lagrange_0() { + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let res := mload(add(state, state_zeta_power_n_minus_one)) + let den := addmod(mload(add(state, state_zeta)), sub(r_mod, 1), r_mod) + den := pow(den, sub(r_mod, 2), mPtr) + den := mulmod(den, vk_inv_domain_size, r_mod) + res := mulmod(den, res, r_mod) + + let l_alpha := mload(add(state, state_alpha)) + res := mulmod(res, l_alpha, r_mod) + res := mulmod(res, l_alpha, r_mod) + mstore(add(state, state_alpha_square_lagrange_0), res) + } + + // follows alg. p.13 of https://eprint.iacr.org/2019/953.pdf + // with t₁ = t₂ = 1, and the proofs are ([digest] + [quotient] +purported evaluation): + // * [state_folded_state_digests], [proof_batch_opening_at_zeta_x], state_folded_evals + // * [proof_grand_product_commitment], [proof_opening_at_zeta_omega_x], [proof_grand_product_at_zeta_omega] + function batch_verify_multi_points(aproof) { + + let state := mload(0x40) + let mPtr := add(state, state_last_mem) + + // here the random is not a challenge, hence no need to use Fiat Shamir, we just + // need an unpredictible result. + let random := mod(keccak256(state, 0x20), r_mod) + + let folded_quotients := mPtr + mPtr := add(folded_quotients, 0x40) + mstore(folded_quotients, mload(add(aproof, proof_batch_opening_at_zeta_x))) + mstore(add(folded_quotients, 0x20), mload(add(aproof, proof_batch_opening_at_zeta_y))) + point_acc_mul(folded_quotients, add(aproof, proof_opening_at_zeta_omega_x), random, mPtr) + + let folded_digests := add(state, state_folded_digests_x) + point_acc_mul(folded_digests, add(aproof, proof_grand_product_commitment_x), random, mPtr) + + let folded_evals := add(state, state_folded_claimed_values) + fr_acc_mul(folded_evals, add(aproof, proof_grand_product_at_zeta_omega), random) + + let folded_evals_commit := mPtr + mPtr := add(folded_evals_commit, 0x40) + mstore(folded_evals_commit, 1) + mstore(add(folded_evals_commit, 0x20), 2) + mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) + let check_staticcall := staticcall(gas(),7,folded_evals_commit,0x60,folded_evals_commit,0x40) + if eq(check_staticcall, 0) { + error_verify() + } + + let folded_evals_commit_y := add(folded_evals_commit, 0x20) + mstore(folded_evals_commit_y, sub(p_mod, mload(folded_evals_commit_y))) + point_add(folded_digests, folded_digests, folded_evals_commit, mPtr) + + let folded_points_quotients := mPtr + mPtr := add(mPtr, 0x40) + point_mul(folded_points_quotients, add(aproof, proof_batch_opening_at_zeta_x), mload(add(state, state_zeta)), mPtr) + let zeta_omega := mulmod(mload(add(state, state_zeta)), vk_omega, r_mod) + random := mulmod(random, zeta_omega, r_mod) + point_acc_mul(folded_points_quotients, add(aproof, proof_opening_at_zeta_omega_x), random, mPtr) + + point_add(folded_digests, folded_digests, folded_points_quotients, mPtr) + + let folded_quotients_y := add(folded_quotients, 0x20) + mstore(folded_quotients_y, sub(p_mod, mload(folded_quotients_y))) + + mstore(mPtr, mload(folded_digests)) + mstore(add(mPtr, 0x20), mload(add(folded_digests, 0x20))) + mstore(add(mPtr, 0x40), g2_srs_0_x_0) // the 4 lines are the canonical G2 point on BN254 + mstore(add(mPtr, 0x60), g2_srs_0_x_1) + mstore(add(mPtr, 0x80), g2_srs_0_y_0) + mstore(add(mPtr, 0xa0), g2_srs_0_y_1) + mstore(add(mPtr, 0xc0), mload(folded_quotients)) + mstore(add(mPtr, 0xe0), mload(add(folded_quotients, 0x20))) + mstore(add(mPtr, 0x100), g2_srs_1_x_0) + mstore(add(mPtr, 0x120), g2_srs_1_x_1) + mstore(add(mPtr, 0x140), g2_srs_1_y_0) + mstore(add(mPtr, 0x160), g2_srs_1_y_1) + check_pairing_kzg(mPtr) + } + + // check_pairing_kzg checks the result of the final pairing product of the batched + // kzg verification. The purpose of this function is too avoid exhausting the stack + // in the function batch_verify_multi_points. + // mPtr: pointer storing the tuple of pairs + function check_pairing_kzg(mPtr) { + + let state := mload(0x40) + + // TODO test the staticcall using the method from audit_4-5 + let l_success := staticcall(gas(),8,mPtr,0x180,0x00,0x20) + let res_pairing := mload(0x00) + let s_success := mload(add(state, state_success)) + res_pairing := and(and(res_pairing, l_success), s_success) + mstore(add(state, state_success), res_pairing) + } + + // Fold the opening proofs at ζ: + // * at state+state_folded_digest we store: [H] + γ[Linearised_polynomial]+γ²[L] + γ³[R] + γ⁴[O] + γ⁵[S₁] +γ⁶[S₂] + ∑ᵢγ⁶⁺ⁱ[Pi_{i}] + // * at state+state_folded_claimed_values we store: H(ζ) + γLinearised_polynomial(ζ)+γ²L(ζ) + γ³R(ζ)+ γ⁴O(ζ) + γ⁵S₁(ζ) +γ⁶S₂(ζ) + ∑ᵢγ⁶⁺ⁱPi_{i}(ζ) + // acc_gamma stores the γⁱ + function fold_state(aproof) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + let l_gamma_kzg := mload(add(state, state_gamma_kzg)) + let acc_gamma := l_gamma_kzg + + let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 + let mPtrOffset := add(mPtr, offset) + + mstore(add(state, state_folded_digests_x), mload(add(mPtr,0x40))) + mstore(add(state, state_folded_digests_y), mload(add(mPtr,0x60))) + mstore(add(state, state_folded_claimed_values), mload(add(aproof, proof_quotient_polynomial_at_zeta))) + + point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x80), acc_gamma, mPtrOffset) + fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) + mstore(add(state, state_check_var), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0xc0), acc_gamma, mPtrOffset) + fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_l_at_zeta), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x100), acc_gamma, add(mPtr, offset)) + fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_r_at_zeta), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x140), acc_gamma, add(mPtr, offset)) + fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_o_at_zeta), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x180), acc_gamma, add(mPtr, offset)) + fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_s1_at_zeta), acc_gamma) + + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x1c0), acc_gamma, add(mPtr, offset)) + fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) + + let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + let opca := add(mPtr, 0x200) // offset_proof_commits_api + for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) + point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) + fr_acc_mul(add(state, state_folded_claimed_values), poscaz, acc_gamma) + poscaz := add(poscaz, 0x20) + opca := add(opca, 0x40) + } + + } + + // generate the challenge (using Fiat Shamir) to fold the opening proofs + // at ζ. + // The process for deriving γ is the same as in derive_gamma but this time the inputs are + // in this order (the [] means it's a commitment): + // * ζ + // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) + // * [Linearised polynomial] + // * [L], [R], [O] + // * [S₁] [S₂] + // * [Pi_{i}] (wires associated to custom gates) + // Then there are the purported evaluations of the previous committed polynomials: + // * H(ζ) + // * Linearised_polynomial(ζ) + // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) + // * Pi_{i}(ζ) + function compute_gamma_kzg(aproof) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + mstore(mPtr, 0x67616d6d61) // "gamma" + mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) + mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) + mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) + mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) + mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) + mstore(add(mPtr,0xc0), mload(add(aproof, proof_l_com_x))) + mstore(add(mPtr,0xe0), mload(add(aproof, proof_l_com_y))) + mstore(add(mPtr,0x100), mload(add(aproof, proof_r_com_x))) + mstore(add(mPtr,0x120), mload(add(aproof, proof_r_com_y))) + mstore(add(mPtr,0x140), mload(add(aproof, proof_o_com_x))) + mstore(add(mPtr,0x160), mload(add(aproof, proof_o_com_y))) + mstore(add(mPtr,0x180), vk_s1_com_x) + mstore(add(mPtr,0x1a0), vk_s1_com_y) + mstore(add(mPtr,0x1c0), vk_s2_com_x) + mstore(add(mPtr,0x1e0), vk_s2_com_y) + + let offset := 0x200 + + mstore(add(mPtr,offset), vk_selector_commitments_commit_api_0_x) + mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_0_y) + offset := add(offset, 0x40) + + mstore(add(mPtr,offset), vk_selector_commitments_commit_api_1_x) + mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_1_y) + offset := add(offset, 0x40) + + mstore(add(mPtr,offset), vk_selector_commitments_commit_api_2_x) + mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_2_y) + offset := add(offset, 0x40) + + + mstore(add(mPtr, offset), mload(add(aproof, proof_quotient_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x20)), mload(add(aproof, proof_linearised_polynomial_at_zeta))) + mstore(add(mPtr, add(offset, 0x40)), mload(add(aproof, proof_l_at_zeta))) + mstore(add(mPtr, add(offset, 0x60)), mload(add(aproof, proof_r_at_zeta))) + mstore(add(mPtr, add(offset, 0x80)), mload(add(aproof, proof_o_at_zeta))) + mstore(add(mPtr, add(offset, 0xa0)), mload(add(aproof, proof_s1_at_zeta))) + mstore(add(mPtr, add(offset, 0xc0)), mload(add(aproof, proof_s2_at_zeta))) + + + let _mPtr := add(mPtr, add(offset, 0xe0)) + let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + mstore(_mPtr, mload(_poscaz)) + _poscaz := add(_poscaz, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + + let start_input := 0x1b // 00.."gamma" + let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) + size_input := add(0x5, mul(size_input, 0x20)) // size in bytes: 15*32 bytes + 5 bytes for gamma + let check_staticcall := staticcall(gas(), 0x2, add(mPtr,start_input), size_input, add(state, state_gamma_kzg), 0x20) + if eq(check_staticcall, 0) { + error_verify() + } + mstore(add(state, state_gamma_kzg), mod(mload(add(state, state_gamma_kzg)), r_mod)) + } + + function compute_commitment_linearised_polynomial_ec(aproof, s1, s2) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + mstore(mPtr, vk_ql_com_x) + mstore(add(mPtr,0x20), vk_ql_com_y) + point_mul(add(state, state_linearised_polynomial_x), mPtr, mload(add(aproof, proof_l_at_zeta)), add(mPtr,0x40)) + + mstore(mPtr, vk_qr_com_x) + mstore(add(mPtr,0x20), vk_qr_com_y) + point_acc_mul(add(state, state_linearised_polynomial_x),mPtr,mload(add(aproof, proof_r_at_zeta)),add(mPtr,0x40)) + + let rl := mulmod(mload(add(aproof, proof_l_at_zeta)), mload(add(aproof, proof_r_at_zeta)), r_mod) + mstore(mPtr, vk_qm_com_x) + mstore(add(mPtr,0x20), vk_qm_com_y) + point_acc_mul(add(state, state_linearised_polynomial_x),mPtr,rl,add(mPtr,0x40)) + + mstore(mPtr, vk_qo_com_x) + mstore(add(mPtr,0x20), vk_qo_com_y) + point_acc_mul(add(state, state_linearised_polynomial_x),mPtr,mload(add(aproof, proof_o_at_zeta)),add(mPtr,0x40)) + + mstore(mPtr, vk_qk_com_x) + mstore(add(mPtr, 0x20), vk_qk_com_y) + point_add(add(state, state_linearised_polynomial_x),add(state, state_linearised_polynomial_x),mPtr,add(mPtr, 0x40)) + + let commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) + let commits_api := add(aproof, add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20))) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + mstore(mPtr, mload(commits_api)) + mstore(add(mPtr, 0x20), mload(add(commits_api, 0x20))) + point_acc_mul(add(state, state_linearised_polynomial_x),mPtr,mload(commits_api_at_zeta),add(mPtr,0x40)) + commits_api_at_zeta := add(commits_api_at_zeta, 0x20) + commits_api := add(commits_api, 0x40) + } + + mstore(mPtr, vk_s3_com_x) + mstore(add(mPtr, 0x20), vk_s3_com_y) + point_acc_mul(add(state, state_linearised_polynomial_x), mPtr, s1, add(mPtr, 0x40)) + + mstore(mPtr, mload(add(aproof, proof_grand_product_commitment_x))) + mstore(add(mPtr, 0x20), mload(add(aproof, proof_grand_product_commitment_y))) + point_acc_mul(add(state, state_linearised_polynomial_x), mPtr, s2, add(mPtr, 0x40)) + + } + + // Compute the commitment to the linearized polynomial equal to + // L(ζ)[Qₗ]+r(ζ)[Qᵣ]+R(ζ)L(ζ)[Qₘ]+O(ζ)[Qₒ]+[Qₖ]+Σᵢqc'ᵢ(ζ)[BsbCommitmentᵢ] + + // α*( Z(μζ)(L(ζ)+β*S₁(ζ)+γ)*(R(ζ)+β*S₂(ζ)+γ)[S₃]-[Z](L(ζ)+β*id_{1}(ζ)+γ)*(R(ζ)+β*id_{2(ζ)+γ)*(O(ζ)+β*id_{3}(ζ)+γ) ) + + // α²*L₁(ζ)[Z] + // where + // * id_1 = id, id_2 = vk_coset_shift*id, id_3 = vk_coset_shift^{2}*id + // * the [] means that it's a commitment (i.e. a point on Bn254(F_p)) + function compute_commitment_linearised_polynomial(aproof) { + + let state := mload(0x40) + let l_beta := mload(add(state, state_beta)) + let l_gamma := mload(add(state, state_gamma)) + let l_zeta := mload(add(state, state_zeta)) + let l_alpha := mload(add(state, state_alpha)) + + let u := mulmod(mload(add(aproof,proof_grand_product_at_zeta_omega)), l_beta, r_mod) + let v := mulmod(l_beta, mload(add(aproof, proof_s1_at_zeta)), r_mod) + v := addmod(v, mload(add(aproof, proof_l_at_zeta)), r_mod) + v := addmod(v, l_gamma, r_mod) + + let w := mulmod(l_beta, mload(add(aproof, proof_s2_at_zeta)), r_mod) + w := addmod(w, mload(add(aproof, proof_r_at_zeta)), r_mod) + w := addmod(w, l_gamma, r_mod) + + let s1 := mulmod(u, v, r_mod) + s1 := mulmod(s1, w, r_mod) + s1 := mulmod(s1, l_alpha, r_mod) + + let coset_square := mulmod(vk_coset_shift, vk_coset_shift, r_mod) + let betazeta := mulmod(l_beta, l_zeta, r_mod) + u := addmod(betazeta, mload(add(aproof, proof_l_at_zeta)), r_mod) + u := addmod(u, l_gamma, r_mod) + + v := mulmod(betazeta, vk_coset_shift, r_mod) + v := addmod(v, mload(add(aproof, proof_r_at_zeta)), r_mod) + v := addmod(v, l_gamma, r_mod) + + w := mulmod(betazeta, coset_square, r_mod) + w := addmod(w, mload(add(aproof, proof_o_at_zeta)), r_mod) + w := addmod(w, l_gamma, r_mod) + + let s2 := mulmod(u, v, r_mod) + s2 := mulmod(s2, w, r_mod) + s2 := sub(r_mod, s2) + s2 := mulmod(s2, l_alpha, r_mod) + s2 := addmod(s2, mload(add(state, state_alpha_square_lagrange_0)), r_mod) + + // at this stage: + // * s₁ = α*Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β + // * s₂ = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + α²*L₁(ζ) + + compute_commitment_linearised_polynomial_ec(aproof, s1, s2) + } + + // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at + // state + state_folded_h + function fold_h(aproof) { + let state := mload(0x40) + let n_plus_two := add(vk_domain_size, 2) + let mPtr := add(mload(0x40), state_last_mem) + let zeta_power_n_plus_two := pow(mload(add(state, state_zeta)), n_plus_two, mPtr) + point_mul(add(state, state_folded_h_x), add(aproof, proof_h_2_x), zeta_power_n_plus_two, mPtr) + point_add(add(state, state_folded_h_x), add(state, state_folded_h_x), add(aproof, proof_h_1_x), mPtr) + point_mul(add(state, state_folded_h_x), add(state, state_folded_h_x), zeta_power_n_plus_two, mPtr) + point_add(add(state, state_folded_h_x), add(state, state_folded_h_x), add(aproof, proof_h_0_x), mPtr) + } + + // check that + // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + + // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) + // + α²*L₁(ζ) = + // (ζⁿ-1)H(ζ) + function verify_quotient_poly_eval_at_zeta(aproof) { + + let state := mload(0x40) + + // (l(ζ)+β*s1(ζ)+γ) + let s1 := add(mload(0x40), state_last_mem) + mstore(s1, mulmod(mload(add(aproof,proof_s1_at_zeta)),mload(add(state, state_beta)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) + mstore(s1, addmod(mload(s1), mload(add(aproof, proof_l_at_zeta)), r_mod)) + + // (r(ζ)+β*s2(ζ)+γ) + let s2 := add(s1,0x20) + mstore(s2, mulmod(mload(add(aproof,proof_s2_at_zeta)),mload(add(state, state_beta)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) + mstore(s2, addmod(mload(s2), mload(add(aproof, proof_r_at_zeta)), r_mod)) + // _s2 := mload(s2) + + // (o(ζ)+γ) + let o := add(s1,0x40) + mstore(o, addmod(mload(add(aproof,proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) + + // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) + mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) + mstore(s1, mulmod(mload(s1), mload(o), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) + mstore(s1, mulmod(mload(s1), mload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) + + let computed_quotient := add(s1,0x60) + + // linearizedpolynomial + pi(zeta) + mstore(computed_quotient, addmod(mload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) + mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) + mstore(computed_quotient, addmod(mload(computed_quotient), sub(r_mod,mload(add(state, state_alpha_square_lagrange_0))), r_mod)) + mstore(s2, mulmod(mload(add(aproof,proof_quotient_polynomial_at_zeta)), mload(add(state, state_zeta_power_n_minus_one)), r_mod)) + + mstore(add(state, state_success),eq(mload(computed_quotient), mload(s2))) + } + + function point_add(dst, p, q, mPtr) { + // let mPtr := add(mload(0x40), state_last_mem) + let state := mload(0x40) + mstore(mPtr, mload(p)) + mstore(add(mPtr, 0x20), mload(add(p, 0x20))) + mstore(add(mPtr, 0x40), mload(q)) + mstore(add(mPtr, 0x60), mload(add(q, 0x20))) + let l_success := staticcall(gas(),6,mPtr,0x80,dst,0x40) + mstore(add(state, state_success), and(l_success,mload(add(state, state_success)))) + } + + // dst <- [s]src + function point_mul(dst,src,s, mPtr) { + // let mPtr := add(mload(0x40), state_last_mem) + let state := mload(0x40) + mstore(mPtr,mload(src)) + mstore(add(mPtr,0x20),mload(add(src,0x20))) + mstore(add(mPtr,0x40),s) + let l_success := staticcall(gas(),7,mPtr,0x60,dst,0x40) + mstore(add(state, state_success), and(l_success,mload(add(state, state_success)))) + } + + // dst <- dst + [s]src (Elliptic curve) + function point_acc_mul(dst,src,s, mPtr) { + let state := mload(0x40) + mstore(mPtr,mload(src)) + mstore(add(mPtr,0x20),mload(add(src,0x20))) + mstore(add(mPtr,0x40),s) + let l_success := staticcall(gas(),7,mPtr,0x60,mPtr,0x40) + mstore(add(mPtr,0x40),mload(dst)) + mstore(add(mPtr,0x60),mload(add(dst,0x20))) + l_success := and(l_success, staticcall(gas(),6,mPtr,0x80,dst, 0x40)) + mstore(add(state, state_success), and(l_success,mload(add(state, state_success)))) + } + + // dst <- dst + src (Fr) dst,src are addresses, s is a value + function fr_acc_mul(dst, src, s) { + let tmp := mulmod(mload(src), s, r_mod) + mstore(dst, addmod(mload(dst), tmp, r_mod)) + } + + // dst <- x ** e mod r (x, e are values, not pointers) + function pow(x, e, mPtr)->res { + mstore(mPtr, 0x20) + mstore(add(mPtr, 0x20), 0x20) + mstore(add(mPtr, 0x40), 0x20) + mstore(add(mPtr, 0x60), x) + mstore(add(mPtr, 0x80), e) + mstore(add(mPtr, 0xa0), r_mod) + let check_staticcall := staticcall(gas(),0x05,mPtr,0xc0,mPtr,0x20) + if eq(check_staticcall, 0) { + error_verify() + } + res := mload(mPtr) + } + } + } +} From f78c84a594a091b28ee4c5bdf025891972591f47 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 18:57:42 +0200 Subject: [PATCH 02/13] clean: removed dead code --- .../bn254/solidity/contracts/Verifier.sol | 42 ++++--------------- 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index 64b17cadc4..a873d97ed6 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -539,9 +539,9 @@ contract PlonkVerifier { uint256[] memory public_inputs, uint256 zeta, bytes memory proof - ) internal returns (uint256 pi, uint256 zeta_power_n_minus_one) { + ) internal view returns (uint256 pi, uint256 zeta_power_n_minus_one) { - uint256 check; + // uint256 check; assembly { @@ -560,10 +560,10 @@ contract PlonkVerifier { // free mem let freeMem := mload(0x40) + let wocommit pi := sum_pi_wo_api_commit(add(public_inputs,0x20), mload(public_inputs), zeta, zeta_power_n_minus_one, freeMem) - - check := sum_pi_commit(proof, mload(public_inputs), zeta, zeta_power_n_minus_one, freeMem) - check := addmod(check, pi, r_mod) + wocommit := sum_pi_commit(proof, mload(public_inputs), zeta, zeta_power_n_minus_one, freeMem) + pi := addmod(wocommit, pi, r_mod) function sum_pi_wo_api_commit(ins, n, z, zpnmo, mPtr)->pi_wo_commit { @@ -809,34 +809,6 @@ contract PlonkVerifier { } } - emit PrintUint256(check); - - - // compute the contribution of the public inputs whose indices are in commitment_indices, - // and whose value is hash_fr of the corresponding commitme - uint256[] memory commitment_indices = new uint256[](vk_nb_commitments_commit_api); - load_vk_commitments_indices_commit_api(commitment_indices); - - unchecked { - uint256[] memory wire_committed_commitments = new uint256[](2 * vk_nb_commitments_commit_api); - - load_wire_commitments_commit_api(wire_committed_commitments, proof); - uint256 hash_res; - uint256 ith_lagrange_at_z; - for (uint256 i; i < vk_nb_commitments_commit_api; ) { - hash_res = hash_fr(wire_committed_commitments[2 * i], wire_committed_commitments[2 * i + 1]); - ith_lagrange_at_z = compute_ith_lagrange_at_z( - zeta, - zeta_power_n_minus_one, - commitment_indices[i] + public_inputs.length - ); - assembly { - ith_lagrange_at_z := mulmod(hash_res, ith_lagrange_at_z, r_mod) - pi := addmod(pi, ith_lagrange_at_z, r_mod) - } - ++i; - } - } } @@ -921,7 +893,7 @@ contract PlonkVerifier { } function Verify(bytes memory proof, uint256[] memory public_inputs) - public returns(bool success) { + public view returns(bool success) { check_inputs_size(public_inputs); check_proof_size(proof); check_proof_openings_size(proof); @@ -930,7 +902,7 @@ contract PlonkVerifier { (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta, proof); - emit PrintUint256(pi); + // emit PrintUint256(pi); uint256 check; From 7cc87a7eec008433a3a8140c3232690a4ef2f5eb Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 18:59:17 +0200 Subject: [PATCH 03/13] clean: removed more dead code --- .../bn254/solidity/contracts/Verifier.sol | 44 +------------------ 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index a873d97ed6..df9349da8b 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -73,23 +73,6 @@ contract PlonkVerifier { uint256 private constant vk_index_commit_api_1 = 7; uint256 private constant vk_index_commit_api_2 = 11; - function load_vk_commitments_indices_commit_api(uint256[] memory v) - internal pure { - assembly { - let _v := add(v, 0x20) - - mstore(_v, 3) - _v := add(_v, 0x20) - - mstore(_v, 7) - _v := add(_v, 0x20) - - mstore(_v, 11) - _v := add(_v, 0x20) - - } - } - uint256 private constant vk_nb_commitments_commit_api = 3; // ------------------------------------------------ @@ -188,32 +171,7 @@ contract PlonkVerifier { uint8 private constant lenInBytes = 48; uint8 private constant sizeDomain = 11; uint8 private constant one = 1; - uint8 private constant two = 2; - - - // read the commitments to the wires related to the commit api and store them in wire_commitments. - // The commitments are points on Bn254(Fp) so they are stored on 2 uint256. - function load_wire_commitments_commit_api(uint256[] memory wire_commitments, bytes memory proof) - internal pure { - assembly { - let w := add(wire_commitments, 0x20) - let p := add(proof, proof_openings_selector_commit_api_at_zeta) - p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - // x coordinate - mstore(w, mload(p)) - w := add(w,0x20) - p := add(p,0x20) - - // y coordinate - mstore(w, mload(p)) - w := add(w,0x20) - p := add(p,0x20) - } - } - } - + uint8 private constant two = 2; function derive_gamma_beta_alpha_zeta(bytes memory proof, uint256[] memory public_inputs) internal view returns(uint256 gamma, uint256 beta, uint256 alpha, uint256 zeta) { From 1bf6d08ea1a13d39772f0849f1f70959e03cbfec Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 19:03:06 +0200 Subject: [PATCH 04/13] clean: even more deadcode --- .../bn254/solidity/contracts/Verifier.sol | 168 +----------------- 1 file changed, 1 insertion(+), 167 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index df9349da8b..844c1917d4 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -321,173 +321,6 @@ contract PlonkVerifier { } } - - // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = zeta (challenge derived with Fiat Shamir) - // * zpnmo = ζⁿ-1 - function compute_ith_lagrange_at_z(uint256 zeta, uint256 zpnmo, uint256 i) - internal view returns (uint256 res) { - assembly { - - function error_pow_local() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x17) - mstore(add(ptError, 0x44), "error staticcall modexp") - revert(ptError, 0x64) - } - - // _n^_i [r] - function pow_local(x, e, mPtr)->result { - // let mPtr := mload(0x40) - mstore(mPtr, 0x20) - mstore(add(mPtr, 0x20), 0x20) - mstore(add(mPtr, 0x40), 0x20) - mstore(add(mPtr, 0x60), x) - mstore(add(mPtr, 0x80), e) - mstore(add(mPtr, 0xa0), r_mod) - let success := staticcall(gas(),0x05,mPtr,0xc0,0x00,0x20) - if iszero(success) { - error_pow_local() - } - result := mload(0x00) - } - - let freeMem := mload(0x40) - let w := pow_local(vk_omega, i, freeMem) // w**i - i := addmod(zeta, sub(r_mod, w), r_mod) // z-w**i - w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n - i := pow_local(i, sub(r_mod,2), freeMem) // (z-w**i)**-1 - w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1 - res := mulmod(w, zpnmo, r_mod) - } - } - - - - // corresponds to https://github.com/ConsenSys/gnark-crypto/blob/develop/ecc/bn254/fr/element.go - // function used to hash points resulting from calls to Commit() in the circuit. - function hash_fr(uint256 x, uint256 y) internal view returns (uint256 res) { - - assembly { - - function error_sha2_256() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x19) - mstore(add(ptError, 0x44), "error staticcall sha2-256") - revert(ptError, 0x64) - } - - // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] - // <- 64 bytes -> <-64b -> <- 1 bytes each -> - let mPtr := mload(0x40) - - // [0x00, .., 0x00] 64 bytes of zero - mstore(mPtr, zero_uint256) - mstore(add(mPtr, 0x20), zero_uint256) - - // msg = x || y , both on 32 bytes - mstore(add(mPtr, 0x40), x) - mstore(add(mPtr, 0x60), y) - - // 0 || 48 || 0 all on 1 byte - mstore8(add(mPtr, 0x80), 0) - mstore8(add(mPtr, 0x81), lenInBytes) - mstore8(add(mPtr, 0x82), 0) - - // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] - mstore8(add(mPtr, 0x83), 0x42) - mstore8(add(mPtr, 0x84), 0x53) - mstore8(add(mPtr, 0x85), 0x42) - mstore8(add(mPtr, 0x86), 0x32) - mstore8(add(mPtr, 0x87), 0x32) - mstore8(add(mPtr, 0x88), 0x2d) - mstore8(add(mPtr, 0x89), 0x50) - mstore8(add(mPtr, 0x8a), 0x6c) - mstore8(add(mPtr, 0x8b), 0x6f) - mstore8(add(mPtr, 0x8c), 0x6e) - mstore8(add(mPtr, 0x8d), 0x6b) - - // size domain - mstore8(add(mPtr, 0x8e), sizeDomain) - - let success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) - if iszero(success) { - error_sha2_256() - } - - let b0 := mload(mPtr) - - // [b0 || one || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore8(add(mPtr, 0x20), one) // 1 - - mstore8(add(mPtr, 0x21), 0x42) // dst - mstore8(add(mPtr, 0x22), 0x53) - mstore8(add(mPtr, 0x23), 0x42) - mstore8(add(mPtr, 0x24), 0x32) - mstore8(add(mPtr, 0x25), 0x32) - mstore8(add(mPtr, 0x26), 0x2d) - mstore8(add(mPtr, 0x27), 0x50) - mstore8(add(mPtr, 0x28), 0x6c) - mstore8(add(mPtr, 0x29), 0x6f) - mstore8(add(mPtr, 0x2a), 0x6e) - mstore8(add(mPtr, 0x2b), 0x6b) - - mstore8(add(mPtr, 0x2c), sizeDomain) // size domain - success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) - if iszero(success) { - error_sha2_256() - } - - // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) - - // [b0^b1 || two || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) - mstore8(add(mPtr, 0x40), two) - - mstore8(add(mPtr, 0x41), 0x42) // dst - mstore8(add(mPtr, 0x42), 0x53) - mstore8(add(mPtr, 0x43), 0x42) - mstore8(add(mPtr, 0x44), 0x32) - mstore8(add(mPtr, 0x45), 0x32) - mstore8(add(mPtr, 0x46), 0x2d) - mstore8(add(mPtr, 0x47), 0x50) - mstore8(add(mPtr, 0x48), 0x6c) - mstore8(add(mPtr, 0x49), 0x6f) - mstore8(add(mPtr, 0x4a), 0x6e) - mstore8(add(mPtr, 0x4b), 0x6b) - - mstore8(add(mPtr, 0x4c), sizeDomain) // size domain - - let offset := add(mPtr, 0x20) - success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) - if iszero(success) { - error_sha2_256() - } - - // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. - // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) - // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] - res := mulmod(mload(mPtr), bb, r_mod) // <- res = 2**128 * mPtr[:32] - offset := add(mPtr, 0x10) - for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] - { - mstore8(offset, 0x00) - offset := add(offset, 0x1) - } - let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] - res := addmod(res, b1, r_mod) - - } - - } event PrintUint256(uint256 a); @@ -568,6 +401,7 @@ contract PlonkVerifier { // zpmno ζⁿ-1 // i i-th lagrange // mPtr free memory + // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr)->res { let w := pow(vk_omega, i, mPtr) // w**i From 39a57a9cb12e73f1531f8d5cdf842882eefc17db Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 19:23:43 +0200 Subject: [PATCH 05/13] feat: compute_pi in main assembly block ok --- .../bn254/solidity/contracts/Verifier.sol | 256 +++++++++++++++++- 1 file changed, 249 insertions(+), 7 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index 844c1917d4..960fe0c0fd 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -332,8 +332,6 @@ contract PlonkVerifier { bytes memory proof ) internal view returns (uint256 pi, uint256 zeta_power_n_minus_one) { - // uint256 check; - assembly { function error_pow() { @@ -693,7 +691,7 @@ contract PlonkVerifier { (uint256 gamma, uint256 beta, uint256 alpha, uint256 zeta) = derive_gamma_beta_alpha_zeta(proof, public_inputs); - (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta, proof); + // (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta, proof); // emit PrintUint256(pi); @@ -707,10 +705,7 @@ contract PlonkVerifier { mstore(add(mem, state_zeta), zeta) mstore(add(mem, state_beta), beta) - mstore(add(mem, state_pi), pi) - - mstore(add(mem, state_zeta_power_n_minus_one), zeta_power_n_minus_one) - + compute_pi(public_inputs, proof) compute_alpha_square_lagrange_0() verify_quotient_poly_eval_at_zeta(proof) fold_h(proof) @@ -732,6 +727,253 @@ contract PlonkVerifier { revert(ptError, 0x64) } + // BEGINNING ------------------------------------------------- + function compute_pi(ins, aproof) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // evaluation of Z=Xⁿ-1 at ζ, we save this value + let z := mload(add(state, state_zeta)) + let zpnmo := addmod(pow(z, vk_domain_size, mPtr), sub(r_mod, 1), r_mod) + mstore(add(state, state_zeta_power_n_minus_one), zpnmo) + + let l_pi := sum_pi_wo_api_commit(add(ins,0x20), mload(ins), mPtr) + let l_wocommit := sum_pi_commit(aproof, mload(ins), mPtr) + l_pi := addmod(l_wocommit, l_pi, r_mod) + mstore(add(state, state_pi), l_pi) + + } + + function sum_pi_wo_api_commit(ins, n, mPtr)->pi_wo_commit { + + let state := mload(0x40) + let z := mload(add(state, state_zeta)) + let zpnmo := mload(add(state, state_zeta_power_n_minus_one)) + + let li := mPtr + batch_compute_lagranges_at_z(z, zpnmo, n, li) + + let tmp := 0 + for {let i:=0} lt(i,n) {i:=add(i,1)} + { + tmp := mulmod(mload(li), mload(ins), r_mod) + pi_wo_commit := addmod(pi_wo_commit, tmp, r_mod) + li := add(li, 0x20) + ins := add(ins, 0x20) + } + + } + + // mPtr free memory. Computes the public input contribution related to the commit + function sum_pi_commit(aproof, nb_public_inputs, mPtr)->pi_commit { + + let state := mload(0x40) + let z := mload(add(state, state_zeta)) + let zpnmo := mload(add(state, state_zeta_power_n_minus_one)) + + let p := add(aproof, proof_openings_selector_commit_api_at_zeta) + p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire commitments + + let h_fr, ith_lagrange + + h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) + ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_0), mPtr) + pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) + p := add(p, 0x40) + + h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) + ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_1), mPtr) + pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) + p := add(p, 0x40) + + h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) + ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_2), mPtr) + pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) + p := add(p, 0x40) + + } + + // z zeta + // zpmno ζⁿ-1 + // i i-th lagrange + // mPtr free memory + // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr)->res { + + let w := pow(vk_omega, i, mPtr) // w**i + i := addmod(z, sub(r_mod, w), r_mod) // z-w**i + w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n + i := pow(i, sub(r_mod,2), mPtr) // (z-w**i)**-1 + w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1 + res := mulmod(w, zpnmo, r_mod) + + } + + // (x, y) point on bn254, both on 32bytes + // mPtr free memory + function hash_fr(x, y, mPtr)->res { + + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> + + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, zero_uint256) + mstore(add(mPtr, 0x20), zero_uint256) + + // msg = x || y , both on 32 bytes + mstore(add(mPtr, 0x40), x) + mstore(add(mPtr, 0x60), y) + + // 0 || 48 || 0 all on 1 byte + mstore8(add(mPtr, 0x80), 0) + mstore8(add(mPtr, 0x81), lenInBytes) + mstore8(add(mPtr, 0x82), 0) + + // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] + mstore8(add(mPtr, 0x83), 0x42) + mstore8(add(mPtr, 0x84), 0x53) + mstore8(add(mPtr, 0x85), 0x42) + mstore8(add(mPtr, 0x86), 0x32) + mstore8(add(mPtr, 0x87), 0x32) + mstore8(add(mPtr, 0x88), 0x2d) + mstore8(add(mPtr, 0x89), 0x50) + mstore8(add(mPtr, 0x8a), 0x6c) + mstore8(add(mPtr, 0x8b), 0x6f) + mstore8(add(mPtr, 0x8c), 0x6e) + mstore8(add(mPtr, 0x8d), 0x6b) + + // size domain + mstore8(add(mPtr, 0x8e), sizeDomain) + + let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(l_success) { + error_verify() + } + + let b0 := mload(mPtr) + + // [b0 || one || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), one) // 1 + + mstore8(add(mPtr, 0x21), 0x42) // dst + mstore8(add(mPtr, 0x22), 0x53) + mstore8(add(mPtr, 0x23), 0x42) + mstore8(add(mPtr, 0x24), 0x32) + mstore8(add(mPtr, 0x25), 0x32) + mstore8(add(mPtr, 0x26), 0x2d) + mstore8(add(mPtr, 0x27), 0x50) + mstore8(add(mPtr, 0x28), 0x6c) + mstore8(add(mPtr, 0x29), 0x6f) + mstore8(add(mPtr, 0x2a), 0x6e) + mstore8(add(mPtr, 0x2b), 0x6b) + + mstore8(add(mPtr, 0x2c), sizeDomain) // size domain + l_success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) + if iszero(l_success) { + error_verify() + } + + // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) + + // [b0^b1 || two || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), two) + + mstore8(add(mPtr, 0x41), 0x42) // dst + mstore8(add(mPtr, 0x42), 0x53) + mstore8(add(mPtr, 0x43), 0x42) + mstore8(add(mPtr, 0x44), 0x32) + mstore8(add(mPtr, 0x45), 0x32) + mstore8(add(mPtr, 0x46), 0x2d) + mstore8(add(mPtr, 0x47), 0x50) + mstore8(add(mPtr, 0x48), 0x6c) + mstore8(add(mPtr, 0x49), 0x6f) + mstore8(add(mPtr, 0x4a), 0x6e) + mstore8(add(mPtr, 0x4b), 0x6b) + + mstore8(add(mPtr, 0x4c), sizeDomain) // size domain + + let offset := add(mPtr, 0x20) + l_success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) + if iszero(l_success) { + error_verify() + } + + // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. + // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) + // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] + res := mulmod(mload(mPtr), bb, r_mod) // <- res = 2**128 * mPtr[:32] + offset := add(mPtr, 0x10) + for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + { + mstore8(offset, 0x00) + offset := add(offset, 0x1) + } + let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, r_mod) + + } + + // mPtr <- [L_0(z), .., L_{n-1}(z)] + // + // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + // * n = vk_domain_size + // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) + // * ζ = z (challenge derived with Fiat Shamir) + // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed + function batch_compute_lagranges_at_z(z, zpnmo, n, mPtr) { + + let zn := mulmod(zpnmo, vk_inv_domain_size, r_mod) // 1/n * (ζⁿ - 1) + + let _w := 1 + let _mPtr := mPtr + for {let i:=0} lt(i,n) {i:=add(i,1)} + { + mstore(_mPtr, addmod(z,sub(r_mod, _w), r_mod)) + _w := mulmod(_w, vk_omega, r_mod) + _mPtr := add(_mPtr, 0x20) + } + batch_invert(mPtr, n, _mPtr) + _mPtr := mPtr + _w := 1 + for {let i:=0} lt(i,n) {i:=add(i,1)} + { + mstore(_mPtr, mulmod(mulmod(mload(_mPtr), zn , r_mod), _w, r_mod)) + _mPtr := add(_mPtr, 0x20) + _w := mulmod(_w, vk_omega, r_mod) + } + } + + // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. + function batch_invert(ins, nb_ins, mPtr) { + mstore(mPtr, 1) + let offset := 0 + for {let i:=0} lt(i, nb_ins) {i:=add(i,1)} + { + let prev := mload(add(mPtr, offset)) + let cur := mload(add(ins, offset)) + cur := mulmod(prev, cur, r_mod) + offset := add(offset, 0x20) + mstore(add(mPtr, offset), cur) + } + ins := add(ins, sub(offset, 0x20)) + mPtr := add(mPtr, offset) + let inv := pow(mload(mPtr), sub(r_mod,2), add(mPtr, 0x20)) + for {let i:=0} lt(i, nb_ins) {i:=add(i,1)} + { + mPtr := sub(mPtr, 0x20) + let tmp := mload(ins) + let cur := mulmod(inv, mload(mPtr), r_mod) + mstore(ins, cur) + inv := mulmod(inv, tmp, r_mod) + ins := sub(ins, 0x20) + } + } + // END ------------------------------------------------- + // compute α² * 1/n * (ζ{n}-1)/(ζ - 1) where // * α = challenge derived in derive_gamma_beta_alpha_zeta // * n = vk_domain_size From 69102f392bd280c62cc89f89faf18f209402d647 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 19:29:07 +0200 Subject: [PATCH 06/13] clean: removed dead code --- .../bn254/solidity/contracts/Verifier.sol | 284 +----------------- 1 file changed, 4 insertions(+), 280 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index 960fe0c0fd..8db2699d88 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -321,286 +321,7 @@ contract PlonkVerifier { } } - event PrintUint256(uint256 a); - - // returns the contribution of the public inputs + ζⁿ-1 which will - // be reused several times - function compute_pi( - uint256[] memory public_inputs, - uint256 zeta, - bytes memory proof - ) internal view returns (uint256 pi, uint256 zeta_power_n_minus_one) { - - assembly { - - function error_pow() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x17) - mstore(add(ptError, 0x44), "error staticcall modexp") - revert(ptError, 0x64) - } - - // evaluation of Z=Xⁿ-1 at ζ, we save this value - zeta_power_n_minus_one := addmod(pow(zeta, vk_domain_size, mload(0x40)), sub(r_mod, 1), r_mod) - - // free mem - let freeMem := mload(0x40) - - let wocommit - pi := sum_pi_wo_api_commit(add(public_inputs,0x20), mload(public_inputs), zeta, zeta_power_n_minus_one, freeMem) - wocommit := sum_pi_commit(proof, mload(public_inputs), zeta, zeta_power_n_minus_one, freeMem) - pi := addmod(wocommit, pi, r_mod) - - function sum_pi_wo_api_commit(ins, n, z, zpnmo, mPtr)->pi_wo_commit { - - let li := mPtr - batch_compute_lagranges_at_z(z, zpnmo, n, li) - - let tmp := 0 - for {let i:=0} lt(i,n) {i:=add(i,1)} - { - tmp := mulmod(mload(li), mload(ins), r_mod) - pi_wo_commit := addmod(pi_wo_commit, tmp, r_mod) - li := add(li, 0x20) - ins := add(ins, 0x20) - } - - } - - // mPtr free memory. Computes the public input contribution related to the commit - function sum_pi_commit(aproof, nb_public_inputs, z, zpnmo, mPtr)->pi_commit { - - let p := add(aproof, proof_openings_selector_commit_api_at_zeta) - p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire commitments - - let h_fr, ith_lagrange - - h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) - ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_0), mPtr) - pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) - p := add(p, 0x40) - - h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) - ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_1), mPtr) - pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) - p := add(p, 0x40) - - h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) - ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_2), mPtr) - pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) - p := add(p, 0x40) - - } - - // z zeta - // zpmno ζⁿ-1 - // i i-th lagrange - // mPtr free memory - // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr)->res { - - let w := pow(vk_omega, i, mPtr) // w**i - i := addmod(z, sub(r_mod, w), r_mod) // z-w**i - w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n - i := pow(i, sub(r_mod,2), mPtr) // (z-w**i)**-1 - w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1 - res := mulmod(w, zpnmo, r_mod) - - } - - function error_sha2_256() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x19) - mstore(add(ptError, 0x44), "error staticcall sha2-256") - revert(ptError, 0x64) - } - - // (x, y) point on bn254, both on 32bytes - // mPtr free memory - function hash_fr(x, y, mPtr)->res { - - // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] - // <- 64 bytes -> <-64b -> <- 1 bytes each -> - - // [0x00, .., 0x00] 64 bytes of zero - mstore(mPtr, zero_uint256) - mstore(add(mPtr, 0x20), zero_uint256) - - // msg = x || y , both on 32 bytes - mstore(add(mPtr, 0x40), x) - mstore(add(mPtr, 0x60), y) - - // 0 || 48 || 0 all on 1 byte - mstore8(add(mPtr, 0x80), 0) - mstore8(add(mPtr, 0x81), lenInBytes) - mstore8(add(mPtr, 0x82), 0) - - // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] - mstore8(add(mPtr, 0x83), 0x42) - mstore8(add(mPtr, 0x84), 0x53) - mstore8(add(mPtr, 0x85), 0x42) - mstore8(add(mPtr, 0x86), 0x32) - mstore8(add(mPtr, 0x87), 0x32) - mstore8(add(mPtr, 0x88), 0x2d) - mstore8(add(mPtr, 0x89), 0x50) - mstore8(add(mPtr, 0x8a), 0x6c) - mstore8(add(mPtr, 0x8b), 0x6f) - mstore8(add(mPtr, 0x8c), 0x6e) - mstore8(add(mPtr, 0x8d), 0x6b) - - // size domain - mstore8(add(mPtr, 0x8e), sizeDomain) - - let success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) - if iszero(success) { - error_sha2_256() - } - - let b0 := mload(mPtr) - - // [b0 || one || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore8(add(mPtr, 0x20), one) // 1 - - mstore8(add(mPtr, 0x21), 0x42) // dst - mstore8(add(mPtr, 0x22), 0x53) - mstore8(add(mPtr, 0x23), 0x42) - mstore8(add(mPtr, 0x24), 0x32) - mstore8(add(mPtr, 0x25), 0x32) - mstore8(add(mPtr, 0x26), 0x2d) - mstore8(add(mPtr, 0x27), 0x50) - mstore8(add(mPtr, 0x28), 0x6c) - mstore8(add(mPtr, 0x29), 0x6f) - mstore8(add(mPtr, 0x2a), 0x6e) - mstore8(add(mPtr, 0x2b), 0x6b) - - mstore8(add(mPtr, 0x2c), sizeDomain) // size domain - success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) - if iszero(success) { - error_sha2_256() - } - - // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) - - // [b0^b1 || two || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) - mstore8(add(mPtr, 0x40), two) - - mstore8(add(mPtr, 0x41), 0x42) // dst - mstore8(add(mPtr, 0x42), 0x53) - mstore8(add(mPtr, 0x43), 0x42) - mstore8(add(mPtr, 0x44), 0x32) - mstore8(add(mPtr, 0x45), 0x32) - mstore8(add(mPtr, 0x46), 0x2d) - mstore8(add(mPtr, 0x47), 0x50) - mstore8(add(mPtr, 0x48), 0x6c) - mstore8(add(mPtr, 0x49), 0x6f) - mstore8(add(mPtr, 0x4a), 0x6e) - mstore8(add(mPtr, 0x4b), 0x6b) - - mstore8(add(mPtr, 0x4c), sizeDomain) // size domain - - let offset := add(mPtr, 0x20) - success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) - if iszero(success) { - error_sha2_256() - } - - // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. - // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) - // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] - res := mulmod(mload(mPtr), bb, r_mod) // <- res = 2**128 * mPtr[:32] - offset := add(mPtr, 0x10) - for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] - { - mstore8(offset, 0x00) - offset := add(offset, 0x1) - } - let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] - res := addmod(res, b1, r_mod) - - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - function batch_compute_lagranges_at_z(z, zpnmo, n, mPtr) { - - let zn := mulmod(zpnmo, vk_inv_domain_size, r_mod) // 1/n * (ζⁿ - 1) - - let _w := 1 - let _mPtr := mPtr - for {let i:=0} lt(i,n) {i:=add(i,1)} - { - mstore(_mPtr, addmod(z,sub(r_mod, _w), r_mod)) - _w := mulmod(_w, vk_omega, r_mod) - _mPtr := add(_mPtr, 0x20) - } - batch_invert(mPtr, n, _mPtr) - _mPtr := mPtr - _w := 1 - for {let i:=0} lt(i,n) {i:=add(i,1)} - { - mstore(_mPtr, mulmod(mulmod(mload(_mPtr), zn , r_mod), _w, r_mod)) - _mPtr := add(_mPtr, 0x20) - _w := mulmod(_w, vk_omega, r_mod) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - function batch_invert(ins, nb_ins, mPtr) { - mstore(mPtr, 1) - let offset := 0 - for {let i:=0} lt(i, nb_ins) {i:=add(i,1)} - { - let prev := mload(add(mPtr, offset)) - let cur := mload(add(ins, offset)) - cur := mulmod(prev, cur, r_mod) - offset := add(offset, 0x20) - mstore(add(mPtr, offset), cur) - } - ins := add(ins, sub(offset, 0x20)) - mPtr := add(mPtr, offset) - let inv := pow(mload(mPtr), sub(r_mod,2), add(mPtr, 0x20)) - for {let i:=0} lt(i, nb_ins) {i:=add(i,1)} - { - mPtr := sub(mPtr, 0x20) - let tmp := mload(ins) - let cur := mulmod(inv, mload(mPtr), r_mod) - mstore(ins, cur) - inv := mulmod(inv, tmp, r_mod) - ins := sub(ins, 0x20) - } - } - - // res <- x^e mod r, mPtr = free memory - function pow(x, e, mPtr)->res { - mstore(mPtr, 0x20) - mstore(add(mPtr, 0x20), 0x20) - mstore(add(mPtr, 0x40), 0x20) - mstore(add(mPtr, 0x60), x) - mstore(add(mPtr, 0x80), e) - mstore(add(mPtr, 0xa0), r_mod) - let success := staticcall(gas(),0x05,mPtr,0xc0,mPtr,0x20) - if iszero(success) { - error_pow() - } - res := mload(mPtr) - } - - } - - } function check_inputs_size(uint256[] memory public_inputs) internal pure { @@ -684,6 +405,7 @@ contract PlonkVerifier { function Verify(bytes memory proof, uint256[] memory public_inputs) public view returns(bool success) { + check_inputs_size(public_inputs); check_proof_size(proof); check_proof_openings_size(proof); @@ -713,10 +435,11 @@ contract PlonkVerifier { compute_gamma_kzg(proof) fold_state(proof) batch_verify_multi_points(proof) + // check := mload(add(mem, state_pi)) success := mload(add(mem, state_success)) - check := mload(add(mem, state_check_var)) + // check := mload(add(mem, state_check_var)) function error_verify() { let ptError := mload(0x40) @@ -1426,5 +1149,6 @@ contract PlonkVerifier { res := mload(mPtr) } } + // emit PrintUint256(check); } } From 6018cceb9c5395edfe255c7ed3b3c4587aa90287 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 21:18:36 +0200 Subject: [PATCH 07/13] feat: challenges derivation in the main block --- .../bn254/solidity/contracts/Verifier.sol | 156 ++++++++++++++++-- 1 file changed, 146 insertions(+), 10 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index 8db2699d88..3d6087bcd2 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -410,24 +410,25 @@ contract PlonkVerifier { check_proof_size(proof); check_proof_openings_size(proof); - (uint256 gamma, uint256 beta, uint256 alpha, uint256 zeta) = derive_gamma_beta_alpha_zeta(proof, public_inputs); - - // (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta, proof); // emit PrintUint256(pi); - uint256 check; assembly { let mem := mload(0x40) - mstore(add(mem, state_alpha), alpha) - mstore(add(mem, state_gamma), gamma) - mstore(add(mem, state_zeta), zeta) - mstore(add(mem, state_beta), beta) + // compute the challenges + let gamma_nr := derive_gamma(proof, public_inputs) + let beta_nr := derive_beta(proof, gamma_nr) + let alpha_nr := derive_alpha(proof, beta_nr) + derive_zeta(proof, alpha_nr) + // check := mload(add(mem, state_zeta)) + + // public inputs contribution compute_pi(public_inputs, proof) + compute_alpha_square_lagrange_0() verify_quotient_poly_eval_at_zeta(proof) fold_h(proof) @@ -450,7 +451,142 @@ contract PlonkVerifier { revert(ptError, 0x64) } - // BEGINNING ------------------------------------------------- + // END challenges ------------------------------------------------- + + // Derive gamma as Sha256() + // where transcript is the concatenation (in this order) of: + // * the word "gamma" in ascii, equal to [0x67,0x61,0x6d, 0x6d, 0x61] and encoded as a uint256. + // * the commitments to the permutation polynomials S1, S2, S3, where we concatenate the coordinates of those points + // * the commitments of Ql, Qr, Qm, Qo, Qk + // * the public inputs + // * the commitments of the wires related to the custom gates (commitments_wires_commit_api) + // * commitments to L, R, O (proof__com_) + // The data described above is written starting at mPtr. "gamma" lies on 5 bytes, + // and is encoded as a uint256 number n. In basis b = 256, the number looks like this + // [0 0 0 .. 0x67 0x61 0x6d, 0x6d, 0x61]. The first non zero entry is at position 27=0x1b + function derive_gamma(aproof, ins)->gamma_not_reduced { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // gamma + // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61] + // (same for alpha, beta, zeta) + mstore(mPtr, 0x67616d6d61) // "gamma" + + mstore(add(mPtr, 0x20), vk_s1_com_x) + mstore(add(mPtr, 0x40), vk_s1_com_y) + mstore(add(mPtr, 0x60), vk_s2_com_x) + mstore(add(mPtr, 0x80), vk_s2_com_y) + mstore(add(mPtr, 0xa0), vk_s3_com_x) + mstore(add(mPtr, 0xc0), vk_s3_com_y) + mstore(add(mPtr, 0xe0), vk_ql_com_x) + mstore(add(mPtr, 0x100), vk_ql_com_y) + mstore(add(mPtr, 0x120), vk_qr_com_x) + mstore(add(mPtr, 0x140), vk_qr_com_y) + mstore(add(mPtr, 0x160), vk_qm_com_x) + mstore(add(mPtr, 0x180), vk_qm_com_y) + mstore(add(mPtr, 0x1a0), vk_qo_com_x) + mstore(add(mPtr, 0x1c0), vk_qo_com_y) + mstore(add(mPtr, 0x1e0), vk_qk_com_x) + mstore(add(mPtr, 0x200), vk_qk_com_y) + + let pi := add(ins, 0x20) + let _mPtr := add(mPtr, 0x220) + for {let i:=0} lt(i, mload(ins)) {i:=add(i,1)} + { + mstore(_mPtr, mload(pi)) + pi := add(pi, 0x20) + _mPtr := add(_mPtr, 0x20) + } + + let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) + _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + mstore(_mPtr, mload(_proof)) + mstore(add(_mPtr, 0x20), mload(add(_proof, 0x20))) + _mPtr := add(_mPtr, 0x40) + _proof := add(_proof, 0x40) + } + + mstore(_mPtr, mload(add(aproof, proof_l_com_x))) + mstore(add(_mPtr, 0x20), mload(add(aproof, proof_l_com_y))) + mstore(add(_mPtr, 0x40), mload(add(aproof, proof_r_com_x))) + mstore(add(_mPtr, 0x60), mload(add(aproof, proof_r_com_y))) + mstore(add(_mPtr, 0x80), mload(add(aproof, proof_o_com_x))) + mstore(add(_mPtr, 0xa0), mload(add(aproof, proof_o_com_y))) + + let size := add(0x2c5, mul(mload(ins), 0x20)) // 0x2c5 = 22*32+5 + size := add(size, mul(vk_nb_commitments_commit_api, 0x40)) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() + } + gamma_not_reduced := mload(mPtr) + mstore(add(state, state_gamma), mod(gamma_not_reduced, r_mod)) + } + + function derive_beta(aproof, gamma_not_reduced)->beta_not_reduced{ + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // beta + mstore(mPtr, 0x62657461) // "beta" + mstore(add(mPtr, 0x20), gamma_not_reduced) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() + } + beta_not_reduced := mload(mPtr) + mstore(add(state, state_beta), mod(beta_not_reduced, r_mod)) + } + + // alpha depends on the previous challenge (beta) and on the commitment to the grand product polynomial + function derive_alpha(aproof, beta_not_reduced)->alpha_not_reduced { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // alpha + mstore(mPtr, 0x616C706861) // "alpha" + mstore(add(mPtr, 0x20), beta_not_reduced) + mstore(add(mPtr, 0x40), mload(add(aproof, proof_grand_product_commitment_x))) + mstore(add(mPtr, 0x60), mload(add(aproof, proof_grand_product_commitment_y))) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() + } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) + } + + // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial + function derive_zeta(aproof, alpha_not_reduced) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + + // zeta + mstore(mPtr, 0x7a657461) // "zeta" + mstore(add(mPtr, 0x20), alpha_not_reduced) + mstore(add(mPtr, 0x40), mload(add(aproof, proof_h_0_x))) + mstore(add(mPtr, 0x60), mload(add(aproof, proof_h_0_y))) + mstore(add(mPtr, 0x80), mload(add(aproof, proof_h_1_x))) + mstore(add(mPtr, 0xa0), mload(add(aproof, proof_h_1_y))) + mstore(add(mPtr, 0xc0), mload(add(aproof, proof_h_2_x))) + mstore(add(mPtr, 0xe0), mload(add(aproof, proof_h_2_y))) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) + if iszero(l_success) { + error_verify() + } + let zeta_not_reduced := mload(mPtr) + mstore(add(state, state_zeta), mod(zeta_not_reduced, r_mod)) + } + // END challenges ------------------------------------------------- + + // BEGINNING compute_pi ------------------------------------------------- function compute_pi(ins, aproof) { let state := mload(0x40) @@ -695,7 +831,7 @@ contract PlonkVerifier { ins := sub(ins, 0x20) } } - // END ------------------------------------------------- + // END compute_pi ------------------------------------------------- // compute α² * 1/n * (ζ{n}-1)/(ζ - 1) where // * α = challenge derived in derive_gamma_beta_alpha_zeta From 1b89624232cf900dc69db9e748d41c8d966c9cb9 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 21:28:20 +0200 Subject: [PATCH 08/13] feat: check_input_size in main block --- .../bn254/solidity/contracts/Verifier.sol | 205 +++--------------- 1 file changed, 31 insertions(+), 174 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index 3d6087bcd2..e93925b2b9 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -173,172 +173,7 @@ contract PlonkVerifier { uint8 private constant one = 1; uint8 private constant two = 2; - function derive_gamma_beta_alpha_zeta(bytes memory proof, uint256[] memory public_inputs) - internal view returns(uint256 gamma, uint256 beta, uint256 alpha, uint256 zeta) { - assembly { - - let mem := mload(0x40) - - derive_gamma(proof, public_inputs) - gamma := mload(mem) - - derive_beta(proof, gamma) - beta := mload(mem) - - derive_alpha(proof, beta) - alpha := mload(mem) - - derive_zeta(proof, alpha) - zeta := mload(mem) - - gamma := mod(gamma, r_mod) - beta := mod(beta, r_mod) - alpha := mod(alpha, r_mod) - zeta := mod(zeta, r_mod) - - function error_sha2_256() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x19) - mstore(add(ptError, 0x44), "error staticcall sha2-256") - revert(ptError, 0x64) - } - - // Derive gamma as Sha256() - // where transcript is the concatenation (in this order) of: - // * the word "gamma" in ascii, equal to [0x67,0x61,0x6d, 0x6d, 0x61] and encoded as a uint256. - // * the commitments to the permutation polynomials S1, S2, S3, where we concatenate the coordinates of those points - // * the commitments of Ql, Qr, Qm, Qo, Qk - // * the public inputs - // * the commitments of the wires related to the custom gates (commitments_wires_commit_api) - // * commitments to L, R, O (proof__com_) - // The data described above is written starting at mPtr. "gamma" lies on 5 bytes, - // and is encoded as a uint256 number n. In basis b = 256, the number looks like this - // [0 0 0 .. 0x67 0x61 0x6d, 0x6d, 0x61]. The first non zero entry is at position 27=0x1b - function derive_gamma(aproof, pub_inputs) { - - let mPtr := mload(0x40) - - // gamma - // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61] - // (same for alpha, beta, zeta) - mstore(mPtr, 0x67616d6d61) // "gamma" - - mstore(add(mPtr, 0x20), vk_s1_com_x) - mstore(add(mPtr, 0x40), vk_s1_com_y) - mstore(add(mPtr, 0x60), vk_s2_com_x) - mstore(add(mPtr, 0x80), vk_s2_com_y) - mstore(add(mPtr, 0xa0), vk_s3_com_x) - mstore(add(mPtr, 0xc0), vk_s3_com_y) - mstore(add(mPtr, 0xe0), vk_ql_com_x) - mstore(add(mPtr, 0x100), vk_ql_com_y) - mstore(add(mPtr, 0x120), vk_qr_com_x) - mstore(add(mPtr, 0x140), vk_qr_com_y) - mstore(add(mPtr, 0x160), vk_qm_com_x) - mstore(add(mPtr, 0x180), vk_qm_com_y) - mstore(add(mPtr, 0x1a0), vk_qo_com_x) - mstore(add(mPtr, 0x1c0), vk_qo_com_y) - mstore(add(mPtr, 0x1e0), vk_qk_com_x) - mstore(add(mPtr, 0x200), vk_qk_com_y) - - let pi := add(pub_inputs, 0x20) - let _mPtr := add(mPtr, 0x220) - for {let i:=0} lt(i, mload(pub_inputs)) {i:=add(i,1)} - { - mstore(_mPtr, mload(pi)) - pi := add(pi, 0x20) - _mPtr := add(_mPtr, 0x20) - } - - let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - mstore(_mPtr, mload(_proof)) - mstore(add(_mPtr, 0x20), mload(add(_proof, 0x20))) - _mPtr := add(_mPtr, 0x40) - _proof := add(_proof, 0x40) - } - - mstore(_mPtr, mload(add(aproof, proof_l_com_x))) - mstore(add(_mPtr, 0x20), mload(add(aproof, proof_l_com_y))) - mstore(add(_mPtr, 0x40), mload(add(aproof, proof_r_com_x))) - mstore(add(_mPtr, 0x60), mload(add(aproof, proof_r_com_y))) - mstore(add(_mPtr, 0x80), mload(add(aproof, proof_o_com_x))) - mstore(add(_mPtr, 0xa0), mload(add(aproof, proof_o_com_y))) - - let size := add(0x2c5, mul(mload(pub_inputs), 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 0x40)) - let success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(success) { - error_sha2_256() - } - } - - function derive_beta(aproof, prev_challenge){ - let mPtr := mload(0x40) - // beta - mstore(mPtr, 0x62657461) // "beta" - mstore(add(mPtr, 0x20), prev_challenge) - let success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(success) { - error_sha2_256() - } - } - - // alpha depends on the previous challenge (beta) and on the commitment to the grand product polynomial - function derive_alpha(aproof, prev_challenge){ - let mPtr := mload(0x40) - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), prev_challenge) - mstore(add(mPtr, 0x40), mload(add(aproof, proof_grand_product_commitment_x))) - mstore(add(mPtr, 0x60), mload(add(aproof, proof_grand_product_commitment_y))) - let success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(success) { - error_sha2_256() - } - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, prev_challenge) { - let mPtr := mload(0x40) - // zeta - mstore(mPtr, 0x7a657461) // "zeta" - mstore(add(mPtr, 0x20), prev_challenge) - mstore(add(mPtr, 0x40), mload(add(aproof, proof_h_0_x))) - mstore(add(mPtr, 0x60), mload(add(aproof, proof_h_0_y))) - mstore(add(mPtr, 0x80), mload(add(aproof, proof_h_1_x))) - mstore(add(mPtr, 0xa0), mload(add(aproof, proof_h_1_y))) - mstore(add(mPtr, 0xc0), mload(add(aproof, proof_h_2_x))) - mstore(add(mPtr, 0xe0), mload(add(aproof, proof_h_2_y))) - let success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) - if iszero(success) { - error_sha2_256() - } - } - } - } - event PrintUint256(uint256 a); - - function check_inputs_size(uint256[] memory public_inputs) - internal pure { - - bool input_checks = true; - assembly { - let s := mload(public_inputs) - let p := add(public_inputs, 0x20) - for {let i} lt(i, s) {i:=add(i,1)} - { - input_checks := and(input_checks,lt(mload(p), r_mod)) - p := add(p, 0x20) - } - } - require(input_checks, "some inputs are bigger than r"); - - } function check_proof_size(bytes memory proof) internal pure { @@ -406,25 +241,24 @@ contract PlonkVerifier { function Verify(bytes memory proof, uint256[] memory public_inputs) public view returns(bool success) { - check_inputs_size(public_inputs); check_proof_size(proof); check_proof_openings_size(proof); // (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta, proof); // emit PrintUint256(pi); - uint256 check; - assembly { let mem := mload(0x40) - + + // sanity checks + check_inputs_size(public_inputs) + // compute the challenges let gamma_nr := derive_gamma(proof, public_inputs) let beta_nr := derive_beta(proof, gamma_nr) let alpha_nr := derive_alpha(proof, beta_nr) derive_zeta(proof, alpha_nr) - // check := mload(add(mem, state_zeta)) // public inputs contribution compute_pi(public_inputs, proof) @@ -436,12 +270,9 @@ contract PlonkVerifier { compute_gamma_kzg(proof) fold_state(proof) batch_verify_multi_points(proof) - // check := mload(add(mem, state_pi)) success := mload(add(mem, state_success)) - // check := mload(add(mem, state_check_var)) - function error_verify() { let ptError := mload(0x40) mstore(ptError, error_string_id) // selector for function Error(string) @@ -451,7 +282,33 @@ contract PlonkVerifier { revert(ptError, 0x64) } - // END challenges ------------------------------------------------- + // Beginning checks ------------------------------------------------- + function error_inputs_size() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x18) + mstore(add(ptError, 0x44), "inputs are bigger than r") + revert(ptError, 0x64) + } + + function check_inputs_size(ins) { + let s := mload(ins) + let p := add(ins, 0x20) + let input_checks := 1 + for {let i} lt(i, s) {i:=add(i,1)} + { + input_checks := and(input_checks,lt(mload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { + error_inputs_size() + } + } + + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- // Derive gamma as Sha256() // where transcript is the concatenation (in this order) of: From aceb2d8c6c66844c4158ad8d4143ff7ad0b975ce Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 21:44:53 +0200 Subject: [PATCH 09/13] feat: one single assembly block ok --- .../bn254/solidity/contracts/Verifier.sol | 147 ++++++++++-------- 1 file changed, 78 insertions(+), 69 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index e93925b2b9..eb789c4a6c 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -175,84 +175,17 @@ contract PlonkVerifier { event PrintUint256(uint256 a); - function check_proof_size(bytes memory proof) - internal pure { - unchecked { - uint256 expected_proof_size = 0x340+vk_nb_commitments_commit_api*0x60; - uint256 actual_proof_size; - assembly { - actual_proof_size := mload(proof) - } - require(actual_proof_size==expected_proof_size, "wrong proof size"); - } - } - - function check_proof_openings_size(bytes memory proof) - internal pure { - bool openings_check = true; - assembly { - - // linearised polynomial at zeta - let p := add(proof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // quotient polynomial at zeta - p := add(proof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_l_at_zeta - p := add(proof, proof_l_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_r_at_zeta - p := add(proof, proof_r_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_o_at_zeta - p := add(proof, proof_o_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_s1_at_zeta - p := add(proof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_s2_at_zeta - p := add(proof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_grand_product_at_zeta_omega - p := add(proof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_openings_selector_commit_api_at_zeta - - p := add(proof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - openings_check := and(openings_check, lt(mload(p), r_mod)) - p := add(p, 0x20) - } - - - } - require(openings_check, "some openings are bigger than r"); - } - function Verify(bytes memory proof, uint256[] memory public_inputs) public view returns(bool success) { - check_proof_size(proof); - check_proof_openings_size(proof); - - // (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta, proof); - // emit PrintUint256(pi); - assembly { let mem := mload(0x40) // sanity checks check_inputs_size(public_inputs) + check_proof_size(proof) + check_proof_openings_size(proof) // compute the challenges let gamma_nr := derive_gamma(proof, public_inputs) @@ -305,7 +238,83 @@ contract PlonkVerifier { error_inputs_size() } } + + function error_proof_size() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x10) + mstore(add(ptError, 0x44), "wrong proof size") + revert(ptError, 0x64) + } + + function check_proof_size(aproof) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) + let actual_proof_size := mload(aproof) + if iszero(eq(actual_proof_size,expected_proof_size)) { + error_proof_size() + } + } + + function error_proof_openings_size() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x16) + mstore(add(ptError, 0x44), "openings bigger than r") + revert(ptError, 0x64) + } + function check_proof_openings_size(aproof) { + + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + openings_check := and(openings_check, lt(mload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { + error_proof_openings_size() + } + + } // end checks ------------------------------------------------- // Beginning challenges ------------------------------------------------- From f0e12e73710db269cefebec1b3ae84be3927c587 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 22:03:43 +0200 Subject: [PATCH 10/13] feat: zeta to the n minus 1 extracted from compute_pi --- backend/plonk/bn254/solidity/contracts/Verifier.sol | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol index eb789c4a6c..664d8f3047 100644 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ b/backend/plonk/bn254/solidity/contracts/Verifier.sol @@ -181,6 +181,7 @@ contract PlonkVerifier { assembly { let mem := mload(0x40) + let freeMem := add(mem, state_last_mem) // sanity checks check_inputs_size(public_inputs) @@ -193,6 +194,11 @@ contract PlonkVerifier { let alpha_nr := derive_alpha(proof, beta_nr) derive_zeta(proof, alpha_nr) + // evaluation of Z=Xⁿ-1 at ζ, we save this value + let zeta := mload(add(mem, state_zeta)) + let zeta_power_n_minus_one := addmod(pow(zeta, vk_domain_size, freeMem), sub(r_mod, 1), r_mod) + mstore(add(mem, state_zeta_power_n_minus_one), zeta_power_n_minus_one) + // public inputs contribution compute_pi(public_inputs, proof) @@ -457,15 +463,12 @@ contract PlonkVerifier { let state := mload(0x40) let mPtr := add(mload(0x40), state_last_mem) - - // evaluation of Z=Xⁿ-1 at ζ, we save this value - let z := mload(add(state, state_zeta)) - let zpnmo := addmod(pow(z, vk_domain_size, mPtr), sub(r_mod, 1), r_mod) - mstore(add(state, state_zeta_power_n_minus_one), zpnmo) let l_pi := sum_pi_wo_api_commit(add(ins,0x20), mload(ins), mPtr) + let l_wocommit := sum_pi_commit(aproof, mload(ins), mPtr) l_pi := addmod(l_wocommit, l_pi, r_mod) + mstore(add(state, state_pi), l_pi) } From cbb7d55a35093d84ffb98da21375679c5af3f246 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 22:28:59 +0200 Subject: [PATCH 11/13] feat: verifier in one assembly block --- backend/plonk/bn254/solidity.go | 738 +++++------ .../bn254/solidity/contracts/Verifier.sol | 1159 ----------------- 2 files changed, 310 insertions(+), 1587 deletions(-) delete mode 100644 backend/plonk/bn254/solidity/contracts/Verifier.sol diff --git a/backend/plonk/bn254/solidity.go b/backend/plonk/bn254/solidity.go index 739e8068f2..dafb512820 100644 --- a/backend/plonk/bn254/solidity.go +++ b/backend/plonk/bn254/solidity.go @@ -55,18 +55,10 @@ contract PlonkVerifier { uint256 private constant vk_selector_commitments_commit_api_{{ $index }}_y = {{ (fpstr $element.Y) }}; {{ end }} - {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} - function load_vk_commitments_indices_commit_api(uint256[] memory v) - internal pure { - assembly { - let _v := add(v, 0x20) - {{ range .CommitmentConstraintIndexes }} - mstore(_v, {{ . }}) - _v := add(_v, 0x20) - {{ end }} - } - } + {{ range $index, $element := .CommitmentConstraintIndexes -}} + uint256 private constant vk_index_commit_api_{{ $index }} = {{ $element }}; {{ end }} + uint256 private constant vk_nb_commitments_commit_api = {{ len .CommitmentConstraintIndexes }}; // ------------------------------------------------ @@ -167,63 +159,142 @@ contract PlonkVerifier { uint8 private constant one = 1; uint8 private constant two = 2; {{ end }} - - {{ if (gt (len .CommitmentConstraintIndexes) 0 ) -}} - // read the commitments to the wires related to the commit api and store them in wire_commitments. - // The commitments are points on Bn254(Fp) so they are stored on 2 uint256. - function load_wire_commitments_commit_api(uint256[] memory wire_commitments, bytes memory proof) - internal pure { - assembly { - let w := add(wire_commitments, 0x20) - let p := add(proof, proof_openings_selector_commit_api_at_zeta) - p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - // x coordinate - mstore(w, mload(p)) - w := add(w,0x20) - p := add(p,0x20) - - // y coordinate - mstore(w, mload(p)) - w := add(w,0x20) - p := add(p,0x20) - } - } - } - {{ end }} - function derive_gamma_beta_alpha_zeta(bytes memory proof, uint256[] memory public_inputs) - internal view returns(uint256 gamma, uint256 beta, uint256 alpha, uint256 zeta) { + function Verify(bytes memory proof, uint256[] memory public_inputs) + public view returns(bool success) { + assembly { let mem := mload(0x40) + let freeMem := add(mem, state_last_mem) + + // sanity checks + check_inputs_size(public_inputs) + check_proof_size(proof) + check_proof_openings_size(proof) + + // compute the challenges + let gamma_nr := derive_gamma(proof, public_inputs) + let beta_nr := derive_beta(proof, gamma_nr) + let alpha_nr := derive_alpha(proof, beta_nr) + derive_zeta(proof, alpha_nr) + + // evaluation of Z=Xⁿ-1 at ζ, we save this value + let zeta := mload(add(mem, state_zeta)) + let zeta_power_n_minus_one := addmod(pow(zeta, vk_domain_size, freeMem), sub(r_mod, 1), r_mod) + mstore(add(mem, state_zeta_power_n_minus_one), zeta_power_n_minus_one) - derive_gamma(proof, public_inputs) - gamma := mload(mem) + // public inputs contribution + let l_pi := sum_pi_wo_api_commit(add(public_inputs,0x20), mload(public_inputs), freeMem) + {{ if (gt (len .CommitmentConstraintIndexes) 0 ) -}} + let l_wocommit := sum_pi_commit(proof, mload(public_inputs), freeMem) + l_pi := addmod(l_wocommit, l_pi, r_mod) + {{ end -}} + mstore(add(mem, state_pi), l_pi) - derive_beta(proof, gamma) - beta := mload(mem) + // Beginning checks ------------------------------------------------- + function error_inputs_size() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x18) + mstore(add(ptError, 0x44), "inputs are bigger than r") + revert(ptError, 0x64) + } - derive_alpha(proof, beta) - alpha := mload(mem) + function check_inputs_size(ins) { + let s := mload(ins) + let p := add(ins, 0x20) + let input_checks := 1 + for {let i} lt(i, s) {i:=add(i,1)} + { + input_checks := and(input_checks,lt(mload(p), r_mod)) + p := add(p, 0x20) + } + if iszero(input_checks) { + error_inputs_size() + } + } - derive_zeta(proof, alpha) - zeta := mload(mem) + function error_proof_size() { + let ptError := mload(0x40) + mstore(ptError, error_string_id) // selector for function Error(string) + mstore(add(ptError, 0x4), 0x20) + mstore(add(ptError, 0x24), 0x10) + mstore(add(ptError, 0x44), "wrong proof size") + revert(ptError, 0x64) + } - gamma := mod(gamma, r_mod) - beta := mod(beta, r_mod) - alpha := mod(alpha, r_mod) - zeta := mod(zeta, r_mod) + function check_proof_size(aproof) { + let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) + let actual_proof_size := mload(aproof) + if iszero(eq(actual_proof_size,expected_proof_size)) { + error_proof_size() + } + } - function error_sha2_256() { + function error_proof_openings_size() { let ptError := mload(0x40) mstore(ptError, error_string_id) // selector for function Error(string) mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x19) - mstore(add(ptError, 0x44), "error staticcall sha2-256") + mstore(add(ptError, 0x24), 0x16) + mstore(add(ptError, 0x44), "openings bigger than r") revert(ptError, 0x64) } + + function check_proof_openings_size(aproof) { + + let openings_check := 1 + + // linearised polynomial at zeta + let p := add(aproof, proof_linearised_polynomial_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // quotient polynomial at zeta + p := add(aproof, proof_quotient_polynomial_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_l_at_zeta + p := add(aproof, proof_l_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_r_at_zeta + p := add(aproof, proof_r_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_o_at_zeta + p := add(aproof, proof_o_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_s1_at_zeta + p := add(aproof, proof_s1_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_s2_at_zeta + p := add(aproof, proof_s2_at_zeta) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_grand_product_at_zeta_omega + p := add(aproof, proof_grand_product_at_zeta_omega) + openings_check := and(openings_check, lt(mload(p), r_mod)) + + // proof_openings_selector_commit_api_at_zeta + + p := add(aproof, proof_openings_selector_commit_api_at_zeta) + for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} + { + openings_check := and(openings_check, lt(mload(p), r_mod)) + p := add(p, 0x20) + } + + if iszero(openings_check) { + error_proof_openings_size() + } + + } + // end checks ------------------------------------------------- + + // Beginning challenges ------------------------------------------------- // Derive gamma as Sha256() // where transcript is the concatenation (in this order) of: @@ -236,9 +307,10 @@ contract PlonkVerifier { // The data described above is written starting at mPtr. "gamma" lies on 5 bytes, // and is encoded as a uint256 number n. In basis b = 256, the number looks like this // [0 0 0 .. 0x67 0x61 0x6d, 0x6d, 0x61]. The first non zero entry is at position 27=0x1b - function derive_gamma(aproof, pub_inputs) { + function derive_gamma(aproof, ins)->gamma_not_reduced { - let mPtr := mload(0x40) + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) // gamma // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61] @@ -262,9 +334,9 @@ contract PlonkVerifier { mstore(add(mPtr, 0x1e0), vk_qk_com_x) mstore(add(mPtr, 0x200), vk_qk_com_y) - let pi := add(pub_inputs, 0x20) + let pi := add(ins, 0x20) let _mPtr := add(mPtr, 0x220) - for {let i:=0} lt(i, mload(pub_inputs)) {i:=add(i,1)} + for {let i:=0} lt(i, mload(ins)) {i:=add(i,1)} { mstore(_mPtr, mload(pi)) pi := add(pi, 0x20) @@ -288,275 +360,94 @@ contract PlonkVerifier { mstore(add(_mPtr, 0x80), mload(add(aproof, proof_o_com_x))) mstore(add(_mPtr, 0xa0), mload(add(aproof, proof_o_com_y))) - let size := add(0x2c5, mul(mload(pub_inputs), 0x20)) // 0x2c5 = 22*32+5 + let size := add(0x2c5, mul(mload(ins), 0x20)) // 0x2c5 = 22*32+5 size := add(size, mul(vk_nb_commitments_commit_api, 0x40)) - let success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(success) { - error_sha2_256() + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() } + gamma_not_reduced := mload(mPtr) + mstore(add(state, state_gamma), mod(gamma_not_reduced, r_mod)) } - function derive_beta(aproof, prev_challenge){ - let mPtr := mload(0x40) + function derive_beta(aproof, gamma_not_reduced)->beta_not_reduced{ + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + // beta mstore(mPtr, 0x62657461) // "beta" - mstore(add(mPtr, 0x20), prev_challenge) - let success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(success) { - error_sha2_256() + mstore(add(mPtr, 0x20), gamma_not_reduced) + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() } + beta_not_reduced := mload(mPtr) + mstore(add(state, state_beta), mod(beta_not_reduced, r_mod)) } // alpha depends on the previous challenge (beta) and on the commitment to the grand product polynomial - function derive_alpha(aproof, prev_challenge){ - let mPtr := mload(0x40) + function derive_alpha(aproof, beta_not_reduced)->alpha_not_reduced { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + // alpha mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), prev_challenge) + mstore(add(mPtr, 0x20), beta_not_reduced) mstore(add(mPtr, 0x40), mload(add(aproof, proof_grand_product_commitment_x))) mstore(add(mPtr, 0x60), mload(add(aproof, proof_grand_product_commitment_y))) - let success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(success) { - error_sha2_256() + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" + if iszero(l_success) { + error_verify() } + alpha_not_reduced := mload(mPtr) + mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) } // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, prev_challenge) { - let mPtr := mload(0x40) + function derive_zeta(aproof, alpha_not_reduced) { + + let state := mload(0x40) + let mPtr := add(mload(0x40), state_last_mem) + // zeta mstore(mPtr, 0x7a657461) // "zeta" - mstore(add(mPtr, 0x20), prev_challenge) + mstore(add(mPtr, 0x20), alpha_not_reduced) mstore(add(mPtr, 0x40), mload(add(aproof, proof_h_0_x))) mstore(add(mPtr, 0x60), mload(add(aproof, proof_h_0_y))) mstore(add(mPtr, 0x80), mload(add(aproof, proof_h_1_x))) mstore(add(mPtr, 0xa0), mload(add(aproof, proof_h_1_y))) mstore(add(mPtr, 0xc0), mload(add(aproof, proof_h_2_x))) mstore(add(mPtr, 0xe0), mload(add(aproof, proof_h_2_y))) - let success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) - if iszero(success) { - error_sha2_256() + let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) + if iszero(l_success) { + error_verify() } + let zeta_not_reduced := mload(mPtr) + mstore(add(state, state_zeta), mod(zeta_not_reduced, r_mod)) } - } - } - -{{ if (gt (len .CommitmentConstraintIndexes) 0 )}} - // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = zeta (challenge derived with Fiat Shamir) - // * zpnmo = ζⁿ-1 - function compute_ith_lagrange_at_z(uint256 zeta, uint256 zpnmo, uint256 i) - internal view returns (uint256 res) { - assembly { - - function error_pow_local() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x17) - mstore(add(ptError, 0x44), "error staticcall modexp") - revert(ptError, 0x64) - } - - // _n^_i [r] - function pow_local(x, e)->result { - let mPtr := mload(0x40) - mstore(mPtr, 0x20) - mstore(add(mPtr, 0x20), 0x20) - mstore(add(mPtr, 0x40), 0x20) - mstore(add(mPtr, 0x60), x) - mstore(add(mPtr, 0x80), e) - mstore(add(mPtr, 0xa0), r_mod) - let success := staticcall(gas(),0x05,mPtr,0xc0,0x00,0x20) - if iszero(success) { - error_pow_local() - } - result := mload(0x00) - } - - let w := pow_local(vk_omega,i) // w**i - i := addmod(zeta, sub(r_mod, w), r_mod) // z-w**i - w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n - i := pow_local(i, sub(r_mod,2)) // (z-w**i)**-1 - w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1 - res := mulmod(w, zpnmo, r_mod) - } - } - {{ end }} - - {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} - // corresponds to https://github.com/ConsenSys/gnark-crypto/blob/develop/ecc/bn254/fr/element.go - // function used to hash points resulting from calls to Commit() in the circuit. - function hash_fr(uint256 x, uint256 y) internal view returns (uint256 res) { - - assembly { - - function error_sha2_256() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x19) - mstore(add(ptError, 0x44), "error staticcall sha2-256") - revert(ptError, 0x64) - } - - // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] - // <- 64 bytes -> <-64b -> <- 1 bytes each -> - let mPtr := mload(0x40) - - // [0x00, .., 0x00] 64 bytes of zero - mstore(mPtr, zero_uint256) - mstore(add(mPtr, 0x20), zero_uint256) - - // msg = x || y , both on 32 bytes - mstore(add(mPtr, 0x40), x) - mstore(add(mPtr, 0x60), y) - - // 0 || 48 || 0 all on 1 byte - mstore8(add(mPtr, 0x80), 0) - mstore8(add(mPtr, 0x81), lenInBytes) - mstore8(add(mPtr, 0x82), 0) - - // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] - mstore8(add(mPtr, 0x83), 0x42) - mstore8(add(mPtr, 0x84), 0x53) - mstore8(add(mPtr, 0x85), 0x42) - mstore8(add(mPtr, 0x86), 0x32) - mstore8(add(mPtr, 0x87), 0x32) - mstore8(add(mPtr, 0x88), 0x2d) - mstore8(add(mPtr, 0x89), 0x50) - mstore8(add(mPtr, 0x8a), 0x6c) - mstore8(add(mPtr, 0x8b), 0x6f) - mstore8(add(mPtr, 0x8c), 0x6e) - mstore8(add(mPtr, 0x8d), 0x6b) - - // size domain - mstore8(add(mPtr, 0x8e), sizeDomain) - - let success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) - if iszero(success) { - error_sha2_256() - } - - let b0 := mload(mPtr) - - // [b0 || one || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore8(add(mPtr, 0x20), one) // 1 - - mstore8(add(mPtr, 0x21), 0x42) // dst - mstore8(add(mPtr, 0x22), 0x53) - mstore8(add(mPtr, 0x23), 0x42) - mstore8(add(mPtr, 0x24), 0x32) - mstore8(add(mPtr, 0x25), 0x32) - mstore8(add(mPtr, 0x26), 0x2d) - mstore8(add(mPtr, 0x27), 0x50) - mstore8(add(mPtr, 0x28), 0x6c) - mstore8(add(mPtr, 0x29), 0x6f) - mstore8(add(mPtr, 0x2a), 0x6e) - mstore8(add(mPtr, 0x2b), 0x6b) - - mstore8(add(mPtr, 0x2c), sizeDomain) // size domain - success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) - if iszero(success) { - error_sha2_256() - } - - // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) - - // [b0^b1 || two || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) - mstore8(add(mPtr, 0x40), two) - - mstore8(add(mPtr, 0x41), 0x42) // dst - mstore8(add(mPtr, 0x42), 0x53) - mstore8(add(mPtr, 0x43), 0x42) - mstore8(add(mPtr, 0x44), 0x32) - mstore8(add(mPtr, 0x45), 0x32) - mstore8(add(mPtr, 0x46), 0x2d) - mstore8(add(mPtr, 0x47), 0x50) - mstore8(add(mPtr, 0x48), 0x6c) - mstore8(add(mPtr, 0x49), 0x6f) - mstore8(add(mPtr, 0x4a), 0x6e) - mstore8(add(mPtr, 0x4b), 0x6b) - - mstore8(add(mPtr, 0x4c), sizeDomain) // size domain - - let offset := add(mPtr, 0x20) - success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) - if iszero(success) { - error_sha2_256() - } - - // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. - // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) - // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] - res := mulmod(mload(mPtr), bb, r_mod) // <- res = 2**128 * mPtr[:32] - offset := add(mPtr, 0x10) - for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] - { - mstore8(offset, 0x00) - offset := add(offset, 0x1) - } - let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] - res := addmod(res, b1, r_mod) - - } - - } - {{ end }} - - // returns the contribution of the public inputs + ζⁿ-1 which will - // be reused several times - {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} - function compute_pi( - uint256[] memory public_inputs, - uint256 zeta, - bytes memory proof - ) internal view returns (uint256 pi, uint256 zeta_power_n_minus_one) { - {{ end -}} - {{ if (eq (len .CommitmentConstraintIndexes) 0 )}} - function compute_pi( - uint256[] memory public_inputs, - uint256 zeta - ) internal view returns (uint256 pi, uint256 zeta_power_n_minus_one) { - {{ end }} - - assembly { - function error_pow() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x17) - mstore(add(ptError, 0x44), "error staticcall modexp") - revert(ptError, 0x64) - } - - // evaluation of Z=Xⁿ-1 at ζ, we save this value - zeta_power_n_minus_one := addmod(pow(zeta, vk_domain_size, mload(0x40)), sub(r_mod, 1), r_mod) - sum_pi_wo_api_commit(add(public_inputs,0x20), mload(public_inputs), zeta, zeta_power_n_minus_one) - pi := mload(mload(0x40)) + // END challenges ------------------------------------------------- - function sum_pi_wo_api_commit(ins, n, z, zpnmo) { + // BEGINNING compute_pi ------------------------------------------------- + function sum_pi_wo_api_commit(ins, n, mPtr)->pi_wo_commit { - let li := mload(0x40) - batch_compute_lagranges_at_z(z, zpnmo, n, li) + let state := mload(0x40) + let z := mload(add(state, state_zeta)) + let zpnmo := mload(add(state, state_zeta_power_n_minus_one)) - // at this stage zeta_power_n_minus_one is computed + let li := mPtr + batch_compute_lagranges_at_z(z, zpnmo, n, li) - let res := 0 let tmp := 0 for {let i:=0} lt(i,n) {i:=add(i,1)} { tmp := mulmod(mload(li), mload(ins), r_mod) - res := addmod(res, tmp, r_mod) + pi_wo_commit := addmod(pi_wo_commit, tmp, r_mod) li := add(li, 0x20) ins := add(ins, 0x20) } - mstore(mload(0x40), res) + } // mPtr <- [L_0(z), .., L_{n-1}(z)] @@ -615,159 +506,152 @@ contract PlonkVerifier { } } - // res <- x^e mod r - function pow(x, e, mPtr)->res { - mstore(mPtr, 0x20) - mstore(add(mPtr, 0x20), 0x20) - mstore(add(mPtr, 0x40), 0x20) - mstore(add(mPtr, 0x60), x) - mstore(add(mPtr, 0x80), e) - mstore(add(mPtr, 0xa0), r_mod) - let success := staticcall(gas(),0x05,mPtr,0xc0,mPtr,0x20) - if iszero(success) { - error_pow() - } - res := mload(mPtr) - } - } + {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} + // mPtr free memory. Computes the public input contribution related to the commit + function sum_pi_commit(aproof, nb_public_inputs, mPtr)->pi_commit { - {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} - // compute the contribution of the public inputs whose indices are in commitment_indices, - // and whose value is hash_fr of the corresponding commitme - uint256[] memory commitment_indices = new uint256[](vk_nb_commitments_commit_api); - load_vk_commitments_indices_commit_api(commitment_indices); - - unchecked { - uint256[] memory wire_committed_commitments = new uint256[](2 * vk_nb_commitments_commit_api); - - load_wire_commitments_commit_api(wire_committed_commitments, proof); - uint256 hash_res; - uint256 ith_lagrange_at_z; - for (uint256 i; i < vk_nb_commitments_commit_api; ) { - hash_res = hash_fr(wire_committed_commitments[2 * i], wire_committed_commitments[2 * i + 1]); - ith_lagrange_at_z = compute_ith_lagrange_at_z( - zeta, - zeta_power_n_minus_one, - commitment_indices[i] + public_inputs.length - ); - assembly { - ith_lagrange_at_z := mulmod(hash_res, ith_lagrange_at_z, r_mod) - pi := addmod(pi, ith_lagrange_at_z, r_mod) - } - ++i; - } - } - {{ end }} - } - - function check_inputs_size(uint256[] memory public_inputs) - internal pure { + let state := mload(0x40) + let z := mload(add(state, state_zeta)) + let zpnmo := mload(add(state, state_zeta_power_n_minus_one)) + + let p := add(aproof, proof_openings_selector_commit_api_at_zeta) + p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire commitments + + let h_fr, ith_lagrange + + {{ range $index, $element := .CommitmentConstraintIndexes}} + h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) + ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_{{ $index }}), mPtr) + pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) + p := add(p, 0x40) + {{ end }} - bool input_checks = true; - assembly { - let s := mload(public_inputs) - let p := add(public_inputs, 0x20) - for {let i} lt(i, s) {i:=add(i,1)} - { - input_checks := and(input_checks,lt(mload(p), r_mod)) - p := add(p, 0x20) } - } - require(input_checks, "some inputs are bigger than r"); - } - - function check_proof_size(bytes memory proof) - internal pure { - unchecked { - uint256 expected_proof_size = 0x340+vk_nb_commitments_commit_api*0x60; - uint256 actual_proof_size; - assembly { - actual_proof_size := mload(proof) + // z zeta + // zpmno ζⁿ-1 + // i i-th lagrange + // mPtr free memory + // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: + function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr)->res { + + let w := pow(vk_omega, i, mPtr) // w**i + i := addmod(z, sub(r_mod, w), r_mod) // z-w**i + w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n + i := pow(i, sub(r_mod,2), mPtr) // (z-w**i)**-1 + w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1 + res := mulmod(w, zpnmo, r_mod) + } - require(actual_proof_size==expected_proof_size, "wrong proof size"); - } - } - function check_proof_openings_size(bytes memory proof) - internal pure { - bool openings_check = true; - assembly { - - // linearised polynomial at zeta - let p := add(proof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) + // (x, y) point on bn254, both on 32bytes + // mPtr free memory + function hash_fr(x, y, mPtr)->res { - // quotient polynomial at zeta - p := add(proof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_l_at_zeta - p := add(proof, proof_l_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) + // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] + // <- 64 bytes -> <-64b -> <- 1 bytes each -> - // proof_r_at_zeta - p := add(proof, proof_r_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) + // [0x00, .., 0x00] 64 bytes of zero + mstore(mPtr, zero_uint256) + mstore(add(mPtr, 0x20), zero_uint256) + + // msg = x || y , both on 32 bytes + mstore(add(mPtr, 0x40), x) + mstore(add(mPtr, 0x60), y) + + // 0 || 48 || 0 all on 1 byte + mstore8(add(mPtr, 0x80), 0) + mstore8(add(mPtr, 0x81), lenInBytes) + mstore8(add(mPtr, 0x82), 0) + + // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] + mstore8(add(mPtr, 0x83), 0x42) + mstore8(add(mPtr, 0x84), 0x53) + mstore8(add(mPtr, 0x85), 0x42) + mstore8(add(mPtr, 0x86), 0x32) + mstore8(add(mPtr, 0x87), 0x32) + mstore8(add(mPtr, 0x88), 0x2d) + mstore8(add(mPtr, 0x89), 0x50) + mstore8(add(mPtr, 0x8a), 0x6c) + mstore8(add(mPtr, 0x8b), 0x6f) + mstore8(add(mPtr, 0x8c), 0x6e) + mstore8(add(mPtr, 0x8d), 0x6b) + + // size domain + mstore8(add(mPtr, 0x8e), sizeDomain) + + let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) + if iszero(l_success) { + error_verify() + } - // proof_o_at_zeta - p := add(proof, proof_o_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) + let b0 := mload(mPtr) - // proof_s1_at_zeta - p := add(proof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_s2_at_zeta - p := add(proof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) + // [b0 || one || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore8(add(mPtr, 0x20), one) // 1 + + mstore8(add(mPtr, 0x21), 0x42) // dst + mstore8(add(mPtr, 0x22), 0x53) + mstore8(add(mPtr, 0x23), 0x42) + mstore8(add(mPtr, 0x24), 0x32) + mstore8(add(mPtr, 0x25), 0x32) + mstore8(add(mPtr, 0x26), 0x2d) + mstore8(add(mPtr, 0x27), 0x50) + mstore8(add(mPtr, 0x28), 0x6c) + mstore8(add(mPtr, 0x29), 0x6f) + mstore8(add(mPtr, 0x2a), 0x6e) + mstore8(add(mPtr, 0x2b), 0x6b) + + mstore8(add(mPtr, 0x2c), sizeDomain) // size domain + l_success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) + if iszero(l_success) { + error_verify() + } - // proof_grand_product_at_zeta_omega - p := add(proof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(mload(p), r_mod)) + // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) + + // [b0^b1 || two || dst || sizeDomain] + // <-64bytes -> <- 1 byte each -> + mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) + mstore8(add(mPtr, 0x40), two) + + mstore8(add(mPtr, 0x41), 0x42) // dst + mstore8(add(mPtr, 0x42), 0x53) + mstore8(add(mPtr, 0x43), 0x42) + mstore8(add(mPtr, 0x44), 0x32) + mstore8(add(mPtr, 0x45), 0x32) + mstore8(add(mPtr, 0x46), 0x2d) + mstore8(add(mPtr, 0x47), 0x50) + mstore8(add(mPtr, 0x48), 0x6c) + mstore8(add(mPtr, 0x49), 0x6f) + mstore8(add(mPtr, 0x4a), 0x6e) + mstore8(add(mPtr, 0x4b), 0x6b) + + mstore8(add(mPtr, 0x4c), sizeDomain) // size domain + + let offset := add(mPtr, 0x20) + l_success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) + if iszero(l_success) { + error_verify() + } + + // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. + // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) + // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] + res := mulmod(mload(mPtr), bb, r_mod) // <- res = 2**128 * mPtr[:32] + offset := add(mPtr, 0x10) + for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] + { + mstore8(offset, 0x00) + offset := add(offset, 0x1) + } + let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] + res := addmod(res, b1, r_mod) - // proof_openings_selector_commit_api_at_zeta - {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} - p := add(proof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - openings_check := and(openings_check, lt(mload(p), r_mod)) - p := add(p, 0x20) } {{ end }} - - } - require(openings_check, "some openings are bigger than r"); - } - - function Verify(bytes memory proof, uint256[] memory public_inputs) - public view returns(bool success) { - check_inputs_size(public_inputs); - check_proof_size(proof); - check_proof_openings_size(proof); - - (uint256 gamma, uint256 beta, uint256 alpha, uint256 zeta) = derive_gamma_beta_alpha_zeta(proof, public_inputs); - - {{ if (gt (len .CommitmentConstraintIndexes) 0 )}} - (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta, proof); - {{ end }} - {{ if (eq (len .CommitmentConstraintIndexes) 0 )}} - (uint256 pi, uint256 zeta_power_n_minus_one) = compute_pi(public_inputs, zeta); - {{ end }} - - uint256 check; - - assembly { - - let mem := mload(0x40) - mstore(add(mem, state_alpha), alpha) - mstore(add(mem, state_gamma), gamma) - mstore(add(mem, state_zeta), zeta) - mstore(add(mem, state_beta), beta) - - mstore(add(mem, state_pi), pi) - - mstore(add(mem, state_zeta_power_n_minus_one), zeta_power_n_minus_one) + // END compute_pi ------------------------------------------------- compute_alpha_square_lagrange_0() verify_quotient_poly_eval_at_zeta(proof) @@ -779,8 +663,6 @@ contract PlonkVerifier { success := mload(add(mem, state_success)) - check := mload(add(mem, state_check_var)) - function error_verify() { let ptError := mload(0x40) mstore(ptError, error_string_id) // selector for function Error(string) diff --git a/backend/plonk/bn254/solidity/contracts/Verifier.sol b/backend/plonk/bn254/solidity/contracts/Verifier.sol deleted file mode 100644 index 664d8f3047..0000000000 --- a/backend/plonk/bn254/solidity/contracts/Verifier.sol +++ /dev/null @@ -1,1159 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -// Copyright 2023 Consensys Software Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Code generated by gnark DO NOT EDIT - -pragma solidity ^0.8.19; - -contract PlonkVerifier { - - uint256 private constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - uint256 private constant p_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - - uint256 private constant g2_srs_0_x_0 = 11559732032986387107991004021392285783925812861821192530917403151452391805634; - uint256 private constant g2_srs_0_x_1 = 10857046999023057135944570762232829481370756359578518086990519993285655852781; - uint256 private constant g2_srs_0_y_0 = 4082367875863433681332203403145435568316851327593401208105741076214120093531; - uint256 private constant g2_srs_0_y_1 = 8495653923123431417604973247489272438418190587263600148770280649306958101930; - - uint256 private constant g2_srs_1_x_0 = 47713196425749429575943217444118312942213353489131971952158393057073394709; - uint256 private constant g2_srs_1_x_1 = 14082566465881351121005025145697241888648303397746826167017579314219467944174; - uint256 private constant g2_srs_1_y_0 = 2701615586724206752938529831863939849831470165250987450603179980450952505250; - uint256 private constant g2_srs_1_y_1 = 4826080465536228920237187461721988550508671189557677442630659399549457339706; - - // ----------------------- vk --------------------- - uint256 private constant vk_domain_size = 64; - uint256 private constant vk_inv_domain_size = 21546239076966786546898805655487630165289796206659533807077919746160561487873; - uint256 private constant vk_omega = 9088801421649573101014283686030284801466796108869023335878462724291607593530; - uint256 private constant vk_ql_com_x = 10868848045043734703276297354523927435135720685817894190739415177156976922804; - uint256 private constant vk_ql_com_y = 20843608126201315617190154936873147978207810863848578062491214103973954353900; - uint256 private constant vk_qr_com_x = 2679235231493861237565984579160056368873174851272447016820965771639185884129; - uint256 private constant vk_qr_com_y = 5769890692747333821981369609828300566137216559866495823241488516555117850765; - uint256 private constant vk_qm_com_x = 12174728365463593868597410524635068192072309684162556252866030467398704224320; - uint256 private constant vk_qm_com_y = 2601873741960857746398047572147634701494442710834991645015027130583495490073; - uint256 private constant vk_qo_com_x = 2679235231493861237565984579160056368873174851272447016820965771639185884129; - uint256 private constant vk_qo_com_y = 16118352179091941400265036135428974522559094597431327839447549378090108357818; - uint256 private constant vk_qk_com_x = 12174728365463593868597410524635068192072309684162556252866030467398704224320; - uint256 private constant vk_qk_com_y = 19286369129878417475848358173109640387201868446462832017674010764061730718510; - - uint256 private constant vk_s1_com_x = 3732030616711959612213933484143551003153419855408787100769579029855683361349; - uint256 private constant vk_s1_com_y = 8863350200848143456467367762048138762160747177984624877332042811932646213158; - - uint256 private constant vk_s2_com_x = 14978594494098749485501410444126109880601380191003862373946945147295051518552; - uint256 private constant vk_s2_com_y = 14609132512541969816702917650350145292194838347590364588003909602629760651344; - - uint256 private constant vk_s3_com_x = 20661969894449877091914268765653852026526548709497569785432654520121872287907; - uint256 private constant vk_s3_com_y = 7147432922287329972047167657582942187695189639450447765447794162301168023303; - - uint256 private constant vk_coset_shift = 5; - - - uint256 private constant vk_selector_commitments_commit_api_0_x = 17939871339693758397907567934363234620791071268920919277161314430004366183253; - uint256 private constant vk_selector_commitments_commit_api_0_y = 17021664810853884023109619590156222347987157544435252377102168154366528134123; - - uint256 private constant vk_selector_commitments_commit_api_1_x = 10428549206826812980966537226094112662425214493145610768212486078263283139225; - uint256 private constant vk_selector_commitments_commit_api_1_y = 7981313614471682011874166235075241848872806781904931743163227627908232767976; - - uint256 private constant vk_selector_commitments_commit_api_2_x = 15547614805428943792721823383916476247217088537196428817825813781338621128228; - uint256 private constant vk_selector_commitments_commit_api_2_y = 9707207482676214718109065159968174936261689123781781812609918340958746242815; - - uint256 private constant vk_index_commit_api_0 = 3; - uint256 private constant vk_index_commit_api_1 = 7; - uint256 private constant vk_index_commit_api_2 = 11; - - uint256 private constant vk_nb_commitments_commit_api = 3; - - // ------------------------------------------------ - - // offset proof - uint256 private constant proof_l_com_x = 0x20; - uint256 private constant proof_l_com_y = 0x40; - uint256 private constant proof_r_com_x = 0x60; - uint256 private constant proof_r_com_y = 0x80; - uint256 private constant proof_o_com_x = 0xa0; - uint256 private constant proof_o_com_y = 0xc0; - - // h = h_0 + x^{n+2}h_1 + x^{2(n+2)}h_2 - uint256 private constant proof_h_0_x = 0xe0; - uint256 private constant proof_h_0_y = 0x100; - uint256 private constant proof_h_1_x = 0x120; - uint256 private constant proof_h_1_y = 0x140; - uint256 private constant proof_h_2_x = 0x160; - uint256 private constant proof_h_2_y = 0x180; - - // wire values at zeta - uint256 private constant proof_l_at_zeta = 0x1a0; - uint256 private constant proof_r_at_zeta = 0x1c0; - uint256 private constant proof_o_at_zeta = 0x1e0; - - //uint256[STATE_WIDTH-1] permutation_polynomials_at_zeta; // Sσ1(zeta),Sσ2(zeta) - uint256 private constant proof_s1_at_zeta = 0x200; // Sσ1(zeta) - uint256 private constant proof_s2_at_zeta = 0x220; // Sσ2(zeta) - - //Bn254.G1Point grand_product_commitment; // [z(x)] - uint256 private constant proof_grand_product_commitment_x = 0x240; - uint256 private constant proof_grand_product_commitment_y = 0x260; - - uint256 private constant proof_grand_product_at_zeta_omega = 0x280; // z(w*zeta) - uint256 private constant proof_quotient_polynomial_at_zeta = 0x2a0; // t(zeta) - uint256 private constant proof_linearised_polynomial_at_zeta = 0x2c0; // r(zeta) - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant proof_batch_opening_at_zeta_x = 0x2e0; // [Wzeta] - uint256 private constant proof_batch_opening_at_zeta_y = 0x300; - - //Bn254.G1Point opening_at_zeta_omega_proof; // [Wzeta*omega] - uint256 private constant proof_opening_at_zeta_omega_x = 0x320; - uint256 private constant proof_opening_at_zeta_omega_y = 0x340; - - uint256 private constant proof_openings_selector_commit_api_at_zeta = 0x360; - // -> next part of proof is - // [ openings_selector_commits || commitments_wires_commit_api] - - // -------- offset state - - // challenges to check the claimed quotient - uint256 private constant state_alpha = 0x00; - uint256 private constant state_beta = 0x20; - uint256 private constant state_gamma = 0x40; - uint256 private constant state_zeta = 0x60; - - // reusable value - uint256 private constant state_alpha_square_lagrange_0 = 0x80; - - // commitment to H - uint256 private constant state_folded_h_x = 0xa0; - uint256 private constant state_folded_h_y = 0xc0; - - // commitment to the linearised polynomial - uint256 private constant state_linearised_polynomial_x = 0xe0; - uint256 private constant state_linearised_polynomial_y = 0x100; - - // Folded proof for the opening of H, linearised poly, l, r, o, s_1, s_2, qcp - uint256 private constant state_folded_claimed_values = 0x120; - - // folded digests of H, linearised poly, l, r, o, s_1, s_2, qcp - // Bn254.G1Point folded_digests; - uint256 private constant state_folded_digests_x = 0x140; - uint256 private constant state_folded_digests_y = 0x160; - - uint256 private constant state_pi = 0x180; - - uint256 private constant state_zeta_power_n_minus_one = 0x1a0; - - uint256 private constant state_gamma_kzg = 0x1c0; - - uint256 private constant state_success = 0x1e0; - uint256 private constant state_check_var = 0x200; // /!\ this slot is used for debugging only - - uint256 private constant state_last_mem = 0x220; - - // -------- errors - uint256 private constant error_string_id = 0x08c379a000000000000000000000000000000000000000000000000000000000; // selector for function Error(string) - - - // -------- utils (for hash_fr) - uint256 private constant bb = 340282366920938463463374607431768211456; // 2**128 - uint256 private constant zero_uint256 = 0; - - uint8 private constant lenInBytes = 48; - uint8 private constant sizeDomain = 11; - uint8 private constant one = 1; - uint8 private constant two = 2; - - event PrintUint256(uint256 a); - - function Verify(bytes memory proof, uint256[] memory public_inputs) - public view returns(bool success) { - - assembly { - - let mem := mload(0x40) - let freeMem := add(mem, state_last_mem) - - // sanity checks - check_inputs_size(public_inputs) - check_proof_size(proof) - check_proof_openings_size(proof) - - // compute the challenges - let gamma_nr := derive_gamma(proof, public_inputs) - let beta_nr := derive_beta(proof, gamma_nr) - let alpha_nr := derive_alpha(proof, beta_nr) - derive_zeta(proof, alpha_nr) - - // evaluation of Z=Xⁿ-1 at ζ, we save this value - let zeta := mload(add(mem, state_zeta)) - let zeta_power_n_minus_one := addmod(pow(zeta, vk_domain_size, freeMem), sub(r_mod, 1), r_mod) - mstore(add(mem, state_zeta_power_n_minus_one), zeta_power_n_minus_one) - - // public inputs contribution - compute_pi(public_inputs, proof) - - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(proof) - fold_h(proof) - compute_commitment_linearised_polynomial(proof) - compute_gamma_kzg(proof) - fold_state(proof) - batch_verify_multi_points(proof) - - success := mload(add(mem, state_success)) - - function error_verify() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0xc) - mstore(add(ptError, 0x44), "error verify") - revert(ptError, 0x64) - } - - // Beginning checks ------------------------------------------------- - function error_inputs_size() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x18) - mstore(add(ptError, 0x44), "inputs are bigger than r") - revert(ptError, 0x64) - } - - function check_inputs_size(ins) { - let s := mload(ins) - let p := add(ins, 0x20) - let input_checks := 1 - for {let i} lt(i, s) {i:=add(i,1)} - { - input_checks := and(input_checks,lt(mload(p), r_mod)) - p := add(p, 0x20) - } - if iszero(input_checks) { - error_inputs_size() - } - } - - function error_proof_size() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x10) - mstore(add(ptError, 0x44), "wrong proof size") - revert(ptError, 0x64) - } - - function check_proof_size(aproof) { - let expected_proof_size := add(0x340, mul(vk_nb_commitments_commit_api,0x60)) - let actual_proof_size := mload(aproof) - if iszero(eq(actual_proof_size,expected_proof_size)) { - error_proof_size() - } - } - - function error_proof_openings_size() { - let ptError := mload(0x40) - mstore(ptError, error_string_id) // selector for function Error(string) - mstore(add(ptError, 0x4), 0x20) - mstore(add(ptError, 0x24), 0x16) - mstore(add(ptError, 0x44), "openings bigger than r") - revert(ptError, 0x64) - } - - function check_proof_openings_size(aproof) { - - let openings_check := 1 - - // linearised polynomial at zeta - let p := add(aproof, proof_linearised_polynomial_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // quotient polynomial at zeta - p := add(aproof, proof_quotient_polynomial_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_l_at_zeta - p := add(aproof, proof_l_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_r_at_zeta - p := add(aproof, proof_r_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_o_at_zeta - p := add(aproof, proof_o_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_s1_at_zeta - p := add(aproof, proof_s1_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_s2_at_zeta - p := add(aproof, proof_s2_at_zeta) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_grand_product_at_zeta_omega - p := add(aproof, proof_grand_product_at_zeta_omega) - openings_check := and(openings_check, lt(mload(p), r_mod)) - - // proof_openings_selector_commit_api_at_zeta - - p := add(aproof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - openings_check := and(openings_check, lt(mload(p), r_mod)) - p := add(p, 0x20) - } - - if iszero(openings_check) { - error_proof_openings_size() - } - - } - // end checks ------------------------------------------------- - - // Beginning challenges ------------------------------------------------- - - // Derive gamma as Sha256() - // where transcript is the concatenation (in this order) of: - // * the word "gamma" in ascii, equal to [0x67,0x61,0x6d, 0x6d, 0x61] and encoded as a uint256. - // * the commitments to the permutation polynomials S1, S2, S3, where we concatenate the coordinates of those points - // * the commitments of Ql, Qr, Qm, Qo, Qk - // * the public inputs - // * the commitments of the wires related to the custom gates (commitments_wires_commit_api) - // * commitments to L, R, O (proof__com_) - // The data described above is written starting at mPtr. "gamma" lies on 5 bytes, - // and is encoded as a uint256 number n. In basis b = 256, the number looks like this - // [0 0 0 .. 0x67 0x61 0x6d, 0x6d, 0x61]. The first non zero entry is at position 27=0x1b - function derive_gamma(aproof, ins)->gamma_not_reduced { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // gamma - // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61] - // (same for alpha, beta, zeta) - mstore(mPtr, 0x67616d6d61) // "gamma" - - mstore(add(mPtr, 0x20), vk_s1_com_x) - mstore(add(mPtr, 0x40), vk_s1_com_y) - mstore(add(mPtr, 0x60), vk_s2_com_x) - mstore(add(mPtr, 0x80), vk_s2_com_y) - mstore(add(mPtr, 0xa0), vk_s3_com_x) - mstore(add(mPtr, 0xc0), vk_s3_com_y) - mstore(add(mPtr, 0xe0), vk_ql_com_x) - mstore(add(mPtr, 0x100), vk_ql_com_y) - mstore(add(mPtr, 0x120), vk_qr_com_x) - mstore(add(mPtr, 0x140), vk_qr_com_y) - mstore(add(mPtr, 0x160), vk_qm_com_x) - mstore(add(mPtr, 0x180), vk_qm_com_y) - mstore(add(mPtr, 0x1a0), vk_qo_com_x) - mstore(add(mPtr, 0x1c0), vk_qo_com_y) - mstore(add(mPtr, 0x1e0), vk_qk_com_x) - mstore(add(mPtr, 0x200), vk_qk_com_y) - - let pi := add(ins, 0x20) - let _mPtr := add(mPtr, 0x220) - for {let i:=0} lt(i, mload(ins)) {i:=add(i,1)} - { - mstore(_mPtr, mload(pi)) - pi := add(pi, 0x20) - _mPtr := add(_mPtr, 0x20) - } - - let _proof := add(aproof, proof_openings_selector_commit_api_at_zeta) - _proof := add(_proof, mul(vk_nb_commitments_commit_api, 0x20)) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - mstore(_mPtr, mload(_proof)) - mstore(add(_mPtr, 0x20), mload(add(_proof, 0x20))) - _mPtr := add(_mPtr, 0x40) - _proof := add(_proof, 0x40) - } - - mstore(_mPtr, mload(add(aproof, proof_l_com_x))) - mstore(add(_mPtr, 0x20), mload(add(aproof, proof_l_com_y))) - mstore(add(_mPtr, 0x40), mload(add(aproof, proof_r_com_x))) - mstore(add(_mPtr, 0x60), mload(add(aproof, proof_r_com_y))) - mstore(add(_mPtr, 0x80), mload(add(aproof, proof_o_com_x))) - mstore(add(_mPtr, 0xa0), mload(add(aproof, proof_o_com_y))) - - let size := add(0x2c5, mul(mload(ins), 0x20)) // 0x2c5 = 22*32+5 - size := add(size, mul(vk_nb_commitments_commit_api, 0x40)) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), size, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { - error_verify() - } - gamma_not_reduced := mload(mPtr) - mstore(add(state, state_gamma), mod(gamma_not_reduced, r_mod)) - } - - function derive_beta(aproof, gamma_not_reduced)->beta_not_reduced{ - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // beta - mstore(mPtr, 0x62657461) // "beta" - mstore(add(mPtr, 0x20), gamma_not_reduced) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0x24, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { - error_verify() - } - beta_not_reduced := mload(mPtr) - mstore(add(state, state_beta), mod(beta_not_reduced, r_mod)) - } - - // alpha depends on the previous challenge (beta) and on the commitment to the grand product polynomial - function derive_alpha(aproof, beta_not_reduced)->alpha_not_reduced { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // alpha - mstore(mPtr, 0x616C706861) // "alpha" - mstore(add(mPtr, 0x20), beta_not_reduced) - mstore(add(mPtr, 0x40), mload(add(aproof, proof_grand_product_commitment_x))) - mstore(add(mPtr, 0x60), mload(add(aproof, proof_grand_product_commitment_y))) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1b), 0x65, mPtr, 0x20) //0x1b -> 000.."gamma" - if iszero(l_success) { - error_verify() - } - alpha_not_reduced := mload(mPtr) - mstore(add(state, state_alpha), mod(alpha_not_reduced, r_mod)) - } - - // zeta depends on the previous challenge (alpha) and on the commitment to the quotient polynomial - function derive_zeta(aproof, alpha_not_reduced) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - // zeta - mstore(mPtr, 0x7a657461) // "zeta" - mstore(add(mPtr, 0x20), alpha_not_reduced) - mstore(add(mPtr, 0x40), mload(add(aproof, proof_h_0_x))) - mstore(add(mPtr, 0x60), mload(add(aproof, proof_h_0_y))) - mstore(add(mPtr, 0x80), mload(add(aproof, proof_h_1_x))) - mstore(add(mPtr, 0xa0), mload(add(aproof, proof_h_1_y))) - mstore(add(mPtr, 0xc0), mload(add(aproof, proof_h_2_x))) - mstore(add(mPtr, 0xe0), mload(add(aproof, proof_h_2_y))) - let l_success := staticcall(gas(), 0x2, add(mPtr, 0x1c), 0xe4, mPtr, 0x20) - if iszero(l_success) { - error_verify() - } - let zeta_not_reduced := mload(mPtr) - mstore(add(state, state_zeta), mod(zeta_not_reduced, r_mod)) - } - // END challenges ------------------------------------------------- - - // BEGINNING compute_pi ------------------------------------------------- - function compute_pi(ins, aproof) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_pi := sum_pi_wo_api_commit(add(ins,0x20), mload(ins), mPtr) - - let l_wocommit := sum_pi_commit(aproof, mload(ins), mPtr) - l_pi := addmod(l_wocommit, l_pi, r_mod) - - mstore(add(state, state_pi), l_pi) - - } - - function sum_pi_wo_api_commit(ins, n, mPtr)->pi_wo_commit { - - let state := mload(0x40) - let z := mload(add(state, state_zeta)) - let zpnmo := mload(add(state, state_zeta_power_n_minus_one)) - - let li := mPtr - batch_compute_lagranges_at_z(z, zpnmo, n, li) - - let tmp := 0 - for {let i:=0} lt(i,n) {i:=add(i,1)} - { - tmp := mulmod(mload(li), mload(ins), r_mod) - pi_wo_commit := addmod(pi_wo_commit, tmp, r_mod) - li := add(li, 0x20) - ins := add(ins, 0x20) - } - - } - - // mPtr free memory. Computes the public input contribution related to the commit - function sum_pi_commit(aproof, nb_public_inputs, mPtr)->pi_commit { - - let state := mload(0x40) - let z := mload(add(state, state_zeta)) - let zpnmo := mload(add(state, state_zeta_power_n_minus_one)) - - let p := add(aproof, proof_openings_selector_commit_api_at_zeta) - p := add(p, mul(vk_nb_commitments_commit_api, 0x20)) // p points now to the wire commitments - - let h_fr, ith_lagrange - - h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) - ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_0), mPtr) - pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) - p := add(p, 0x40) - - h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) - ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_1), mPtr) - pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) - p := add(p, 0x40) - - h_fr := hash_fr(mload(p), mload(add(p, 0x20)), mPtr) - ith_lagrange := compute_ith_lagrange_at_z(z, zpnmo, add(nb_public_inputs, vk_index_commit_api_2), mPtr) - pi_commit := addmod(pi_commit, mulmod(h_fr, ith_lagrange, r_mod), r_mod) - p := add(p, 0x40) - - } - - // z zeta - // zpmno ζⁿ-1 - // i i-th lagrange - // mPtr free memory - // Computes L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - function compute_ith_lagrange_at_z(z, zpnmo, i, mPtr)->res { - - let w := pow(vk_omega, i, mPtr) // w**i - i := addmod(z, sub(r_mod, w), r_mod) // z-w**i - w := mulmod(w, vk_inv_domain_size, r_mod) // w**i/n - i := pow(i, sub(r_mod,2), mPtr) // (z-w**i)**-1 - w := mulmod(w, i, r_mod) // w**i/n*(z-w)**-1 - res := mulmod(w, zpnmo, r_mod) - - } - - // (x, y) point on bn254, both on 32bytes - // mPtr free memory - function hash_fr(x, y, mPtr)->res { - - // [0x00, .. , 0x00 || x, y, || 0, 48, 0, dst, sizeDomain] - // <- 64 bytes -> <-64b -> <- 1 bytes each -> - - // [0x00, .., 0x00] 64 bytes of zero - mstore(mPtr, zero_uint256) - mstore(add(mPtr, 0x20), zero_uint256) - - // msg = x || y , both on 32 bytes - mstore(add(mPtr, 0x40), x) - mstore(add(mPtr, 0x60), y) - - // 0 || 48 || 0 all on 1 byte - mstore8(add(mPtr, 0x80), 0) - mstore8(add(mPtr, 0x81), lenInBytes) - mstore8(add(mPtr, 0x82), 0) - - // "BSB22-Plonk" = [42, 53, 42, 32, 32, 2d, 50, 6c, 6f, 6e, 6b,] - mstore8(add(mPtr, 0x83), 0x42) - mstore8(add(mPtr, 0x84), 0x53) - mstore8(add(mPtr, 0x85), 0x42) - mstore8(add(mPtr, 0x86), 0x32) - mstore8(add(mPtr, 0x87), 0x32) - mstore8(add(mPtr, 0x88), 0x2d) - mstore8(add(mPtr, 0x89), 0x50) - mstore8(add(mPtr, 0x8a), 0x6c) - mstore8(add(mPtr, 0x8b), 0x6f) - mstore8(add(mPtr, 0x8c), 0x6e) - mstore8(add(mPtr, 0x8d), 0x6b) - - // size domain - mstore8(add(mPtr, 0x8e), sizeDomain) - - let l_success := staticcall(gas(), 0x2, mPtr, 0x8f, mPtr, 0x20) - if iszero(l_success) { - error_verify() - } - - let b0 := mload(mPtr) - - // [b0 || one || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore8(add(mPtr, 0x20), one) // 1 - - mstore8(add(mPtr, 0x21), 0x42) // dst - mstore8(add(mPtr, 0x22), 0x53) - mstore8(add(mPtr, 0x23), 0x42) - mstore8(add(mPtr, 0x24), 0x32) - mstore8(add(mPtr, 0x25), 0x32) - mstore8(add(mPtr, 0x26), 0x2d) - mstore8(add(mPtr, 0x27), 0x50) - mstore8(add(mPtr, 0x28), 0x6c) - mstore8(add(mPtr, 0x29), 0x6f) - mstore8(add(mPtr, 0x2a), 0x6e) - mstore8(add(mPtr, 0x2b), 0x6b) - - mstore8(add(mPtr, 0x2c), sizeDomain) // size domain - l_success := staticcall(gas(), 0x2, mPtr, 0x2d, mPtr, 0x20) - if iszero(l_success) { - error_verify() - } - - // b1 is located at mPtr. We store b2 at add(mPtr, 0x20) - - // [b0^b1 || two || dst || sizeDomain] - // <-64bytes -> <- 1 byte each -> - mstore(add(mPtr, 0x20), xor(mload(mPtr), b0)) - mstore8(add(mPtr, 0x40), two) - - mstore8(add(mPtr, 0x41), 0x42) // dst - mstore8(add(mPtr, 0x42), 0x53) - mstore8(add(mPtr, 0x43), 0x42) - mstore8(add(mPtr, 0x44), 0x32) - mstore8(add(mPtr, 0x45), 0x32) - mstore8(add(mPtr, 0x46), 0x2d) - mstore8(add(mPtr, 0x47), 0x50) - mstore8(add(mPtr, 0x48), 0x6c) - mstore8(add(mPtr, 0x49), 0x6f) - mstore8(add(mPtr, 0x4a), 0x6e) - mstore8(add(mPtr, 0x4b), 0x6b) - - mstore8(add(mPtr, 0x4c), sizeDomain) // size domain - - let offset := add(mPtr, 0x20) - l_success := staticcall(gas(), 0x2, offset, 0x2d, offset, 0x20) - if iszero(l_success) { - error_verify() - } - - // at this point we have mPtr = [ b1 || b2] where b1 is on 32byes and b2 in 16bytes. - // we interpret it as a big integer mod r in big endian (similar to regular decimal notation) - // the result is then 2**(8*16)*mPtr[32:] + mPtr[32:48] - res := mulmod(mload(mPtr), bb, r_mod) // <- res = 2**128 * mPtr[:32] - offset := add(mPtr, 0x10) - for {let i:=0} lt(i, 0x10) {i:=add(i,1)} // mPtr <- [xx, xx, .., | 0, 0, .. 0 || b2 ] - { - mstore8(offset, 0x00) - offset := add(offset, 0x1) - } - let b1 := mload(add(mPtr, 0x10)) // b1 <- [0, 0, .., 0 || b2[:16] ] - res := addmod(res, b1, r_mod) - - } - - // mPtr <- [L_0(z), .., L_{n-1}(z)] - // - // Here L_i(zeta) = ωⁱ/n * (ζⁿ-1)/(ζ-ωⁱ) where: - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = z (challenge derived with Fiat Shamir) - // * zpnmo = 'zeta power n minus one' (ζⁿ-1) which has been precomputed - function batch_compute_lagranges_at_z(z, zpnmo, n, mPtr) { - - let zn := mulmod(zpnmo, vk_inv_domain_size, r_mod) // 1/n * (ζⁿ - 1) - - let _w := 1 - let _mPtr := mPtr - for {let i:=0} lt(i,n) {i:=add(i,1)} - { - mstore(_mPtr, addmod(z,sub(r_mod, _w), r_mod)) - _w := mulmod(_w, vk_omega, r_mod) - _mPtr := add(_mPtr, 0x20) - } - batch_invert(mPtr, n, _mPtr) - _mPtr := mPtr - _w := 1 - for {let i:=0} lt(i,n) {i:=add(i,1)} - { - mstore(_mPtr, mulmod(mulmod(mload(_mPtr), zn , r_mod), _w, r_mod)) - _mPtr := add(_mPtr, 0x20) - _w := mulmod(_w, vk_omega, r_mod) - } - } - - // batch invert (modulo r) in place the nb_ins uint256 inputs starting at ins. - function batch_invert(ins, nb_ins, mPtr) { - mstore(mPtr, 1) - let offset := 0 - for {let i:=0} lt(i, nb_ins) {i:=add(i,1)} - { - let prev := mload(add(mPtr, offset)) - let cur := mload(add(ins, offset)) - cur := mulmod(prev, cur, r_mod) - offset := add(offset, 0x20) - mstore(add(mPtr, offset), cur) - } - ins := add(ins, sub(offset, 0x20)) - mPtr := add(mPtr, offset) - let inv := pow(mload(mPtr), sub(r_mod,2), add(mPtr, 0x20)) - for {let i:=0} lt(i, nb_ins) {i:=add(i,1)} - { - mPtr := sub(mPtr, 0x20) - let tmp := mload(ins) - let cur := mulmod(inv, mload(mPtr), r_mod) - mstore(ins, cur) - inv := mulmod(inv, tmp, r_mod) - ins := sub(ins, 0x20) - } - } - // END compute_pi ------------------------------------------------- - - // compute α² * 1/n * (ζ{n}-1)/(ζ - 1) where - // * α = challenge derived in derive_gamma_beta_alpha_zeta - // * n = vk_domain_size - // * ω = vk_omega (generator of the multiplicative cyclic group of order n in (ℤ/rℤ)*) - // * ζ = zeta (challenge derived with Fiat Shamir) - function compute_alpha_square_lagrange_0() { - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let res := mload(add(state, state_zeta_power_n_minus_one)) - let den := addmod(mload(add(state, state_zeta)), sub(r_mod, 1), r_mod) - den := pow(den, sub(r_mod, 2), mPtr) - den := mulmod(den, vk_inv_domain_size, r_mod) - res := mulmod(den, res, r_mod) - - let l_alpha := mload(add(state, state_alpha)) - res := mulmod(res, l_alpha, r_mod) - res := mulmod(res, l_alpha, r_mod) - mstore(add(state, state_alpha_square_lagrange_0), res) - } - - // follows alg. p.13 of https://eprint.iacr.org/2019/953.pdf - // with t₁ = t₂ = 1, and the proofs are ([digest] + [quotient] +purported evaluation): - // * [state_folded_state_digests], [proof_batch_opening_at_zeta_x], state_folded_evals - // * [proof_grand_product_commitment], [proof_opening_at_zeta_omega_x], [proof_grand_product_at_zeta_omega] - function batch_verify_multi_points(aproof) { - - let state := mload(0x40) - let mPtr := add(state, state_last_mem) - - // here the random is not a challenge, hence no need to use Fiat Shamir, we just - // need an unpredictible result. - let random := mod(keccak256(state, 0x20), r_mod) - - let folded_quotients := mPtr - mPtr := add(folded_quotients, 0x40) - mstore(folded_quotients, mload(add(aproof, proof_batch_opening_at_zeta_x))) - mstore(add(folded_quotients, 0x20), mload(add(aproof, proof_batch_opening_at_zeta_y))) - point_acc_mul(folded_quotients, add(aproof, proof_opening_at_zeta_omega_x), random, mPtr) - - let folded_digests := add(state, state_folded_digests_x) - point_acc_mul(folded_digests, add(aproof, proof_grand_product_commitment_x), random, mPtr) - - let folded_evals := add(state, state_folded_claimed_values) - fr_acc_mul(folded_evals, add(aproof, proof_grand_product_at_zeta_omega), random) - - let folded_evals_commit := mPtr - mPtr := add(folded_evals_commit, 0x40) - mstore(folded_evals_commit, 1) - mstore(add(folded_evals_commit, 0x20), 2) - mstore(add(folded_evals_commit, 0x40), mload(folded_evals)) - let check_staticcall := staticcall(gas(),7,folded_evals_commit,0x60,folded_evals_commit,0x40) - if eq(check_staticcall, 0) { - error_verify() - } - - let folded_evals_commit_y := add(folded_evals_commit, 0x20) - mstore(folded_evals_commit_y, sub(p_mod, mload(folded_evals_commit_y))) - point_add(folded_digests, folded_digests, folded_evals_commit, mPtr) - - let folded_points_quotients := mPtr - mPtr := add(mPtr, 0x40) - point_mul(folded_points_quotients, add(aproof, proof_batch_opening_at_zeta_x), mload(add(state, state_zeta)), mPtr) - let zeta_omega := mulmod(mload(add(state, state_zeta)), vk_omega, r_mod) - random := mulmod(random, zeta_omega, r_mod) - point_acc_mul(folded_points_quotients, add(aproof, proof_opening_at_zeta_omega_x), random, mPtr) - - point_add(folded_digests, folded_digests, folded_points_quotients, mPtr) - - let folded_quotients_y := add(folded_quotients, 0x20) - mstore(folded_quotients_y, sub(p_mod, mload(folded_quotients_y))) - - mstore(mPtr, mload(folded_digests)) - mstore(add(mPtr, 0x20), mload(add(folded_digests, 0x20))) - mstore(add(mPtr, 0x40), g2_srs_0_x_0) // the 4 lines are the canonical G2 point on BN254 - mstore(add(mPtr, 0x60), g2_srs_0_x_1) - mstore(add(mPtr, 0x80), g2_srs_0_y_0) - mstore(add(mPtr, 0xa0), g2_srs_0_y_1) - mstore(add(mPtr, 0xc0), mload(folded_quotients)) - mstore(add(mPtr, 0xe0), mload(add(folded_quotients, 0x20))) - mstore(add(mPtr, 0x100), g2_srs_1_x_0) - mstore(add(mPtr, 0x120), g2_srs_1_x_1) - mstore(add(mPtr, 0x140), g2_srs_1_y_0) - mstore(add(mPtr, 0x160), g2_srs_1_y_1) - check_pairing_kzg(mPtr) - } - - // check_pairing_kzg checks the result of the final pairing product of the batched - // kzg verification. The purpose of this function is too avoid exhausting the stack - // in the function batch_verify_multi_points. - // mPtr: pointer storing the tuple of pairs - function check_pairing_kzg(mPtr) { - - let state := mload(0x40) - - // TODO test the staticcall using the method from audit_4-5 - let l_success := staticcall(gas(),8,mPtr,0x180,0x00,0x20) - let res_pairing := mload(0x00) - let s_success := mload(add(state, state_success)) - res_pairing := and(and(res_pairing, l_success), s_success) - mstore(add(state, state_success), res_pairing) - } - - // Fold the opening proofs at ζ: - // * at state+state_folded_digest we store: [H] + γ[Linearised_polynomial]+γ²[L] + γ³[R] + γ⁴[O] + γ⁵[S₁] +γ⁶[S₂] + ∑ᵢγ⁶⁺ⁱ[Pi_{i}] - // * at state+state_folded_claimed_values we store: H(ζ) + γLinearised_polynomial(ζ)+γ²L(ζ) + γ³R(ζ)+ γ⁴O(ζ) + γ⁵S₁(ζ) +γ⁶S₂(ζ) + ∑ᵢγ⁶⁺ⁱPi_{i}(ζ) - // acc_gamma stores the γⁱ - function fold_state(aproof) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - let l_gamma_kzg := mload(add(state, state_gamma_kzg)) - let acc_gamma := l_gamma_kzg - - let offset := add(0x200, mul(vk_nb_commitments_commit_api, 0x40)) // 0x40 = 2*0x20 - let mPtrOffset := add(mPtr, offset) - - mstore(add(state, state_folded_digests_x), mload(add(mPtr,0x40))) - mstore(add(state, state_folded_digests_y), mload(add(mPtr,0x60))) - mstore(add(state, state_folded_claimed_values), mload(add(aproof, proof_quotient_polynomial_at_zeta))) - - point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x80), acc_gamma, mPtrOffset) - fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_linearised_polynomial_at_zeta), acc_gamma) - mstore(add(state, state_check_var), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0xc0), acc_gamma, mPtrOffset) - fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_l_at_zeta), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x100), acc_gamma, add(mPtr, offset)) - fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_r_at_zeta), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x140), acc_gamma, add(mPtr, offset)) - fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_o_at_zeta), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x180), acc_gamma, add(mPtr, offset)) - fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_s1_at_zeta), acc_gamma) - - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), add(mPtr,0x1c0), acc_gamma, add(mPtr, offset)) - fr_acc_mul(add(state, state_folded_claimed_values), add(aproof, proof_s2_at_zeta), acc_gamma) - - let poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - let opca := add(mPtr, 0x200) // offset_proof_commits_api - for {let i := 0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - acc_gamma := mulmod(acc_gamma, l_gamma_kzg, r_mod) - point_acc_mul(add(state, state_folded_digests_x), opca, acc_gamma, add(mPtr, offset)) - fr_acc_mul(add(state, state_folded_claimed_values), poscaz, acc_gamma) - poscaz := add(poscaz, 0x20) - opca := add(opca, 0x40) - } - - } - - // generate the challenge (using Fiat Shamir) to fold the opening proofs - // at ζ. - // The process for deriving γ is the same as in derive_gamma but this time the inputs are - // in this order (the [] means it's a commitment): - // * ζ - // * [H] ( = H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ ) - // * [Linearised polynomial] - // * [L], [R], [O] - // * [S₁] [S₂] - // * [Pi_{i}] (wires associated to custom gates) - // Then there are the purported evaluations of the previous committed polynomials: - // * H(ζ) - // * Linearised_polynomial(ζ) - // * L(ζ), R(ζ), O(ζ), S₁(ζ), S₂(ζ) - // * Pi_{i}(ζ) - function compute_gamma_kzg(aproof) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - mstore(mPtr, 0x67616d6d61) // "gamma" - mstore(add(mPtr, 0x20), mload(add(state, state_zeta))) - mstore(add(mPtr,0x40), mload(add(state, state_folded_h_x))) - mstore(add(mPtr,0x60), mload(add(state, state_folded_h_y))) - mstore(add(mPtr,0x80), mload(add(state, state_linearised_polynomial_x))) - mstore(add(mPtr,0xa0), mload(add(state, state_linearised_polynomial_y))) - mstore(add(mPtr,0xc0), mload(add(aproof, proof_l_com_x))) - mstore(add(mPtr,0xe0), mload(add(aproof, proof_l_com_y))) - mstore(add(mPtr,0x100), mload(add(aproof, proof_r_com_x))) - mstore(add(mPtr,0x120), mload(add(aproof, proof_r_com_y))) - mstore(add(mPtr,0x140), mload(add(aproof, proof_o_com_x))) - mstore(add(mPtr,0x160), mload(add(aproof, proof_o_com_y))) - mstore(add(mPtr,0x180), vk_s1_com_x) - mstore(add(mPtr,0x1a0), vk_s1_com_y) - mstore(add(mPtr,0x1c0), vk_s2_com_x) - mstore(add(mPtr,0x1e0), vk_s2_com_y) - - let offset := 0x200 - - mstore(add(mPtr,offset), vk_selector_commitments_commit_api_0_x) - mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_0_y) - offset := add(offset, 0x40) - - mstore(add(mPtr,offset), vk_selector_commitments_commit_api_1_x) - mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_1_y) - offset := add(offset, 0x40) - - mstore(add(mPtr,offset), vk_selector_commitments_commit_api_2_x) - mstore(add(mPtr,add(offset, 0x20)), vk_selector_commitments_commit_api_2_y) - offset := add(offset, 0x40) - - - mstore(add(mPtr, offset), mload(add(aproof, proof_quotient_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x20)), mload(add(aproof, proof_linearised_polynomial_at_zeta))) - mstore(add(mPtr, add(offset, 0x40)), mload(add(aproof, proof_l_at_zeta))) - mstore(add(mPtr, add(offset, 0x60)), mload(add(aproof, proof_r_at_zeta))) - mstore(add(mPtr, add(offset, 0x80)), mload(add(aproof, proof_o_at_zeta))) - mstore(add(mPtr, add(offset, 0xa0)), mload(add(aproof, proof_s1_at_zeta))) - mstore(add(mPtr, add(offset, 0xc0)), mload(add(aproof, proof_s2_at_zeta))) - - - let _mPtr := add(mPtr, add(offset, 0xe0)) - let _poscaz := add(aproof, proof_openings_selector_commit_api_at_zeta) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - mstore(_mPtr, mload(_poscaz)) - _poscaz := add(_poscaz, 0x20) - _mPtr := add(_mPtr, 0x20) - } - - - let start_input := 0x1b // 00.."gamma" - let size_input := add(0x16, mul(vk_nb_commitments_commit_api,3)) // number of 32bytes elmts = 0x16 (zeta+2*7+7 for the digests+openings) + 2*vk_nb_commitments_commit_api (for the commitments of the selectors) + vk_nb_commitments_commit_api (for the openings of the selectors) - size_input := add(0x5, mul(size_input, 0x20)) // size in bytes: 15*32 bytes + 5 bytes for gamma - let check_staticcall := staticcall(gas(), 0x2, add(mPtr,start_input), size_input, add(state, state_gamma_kzg), 0x20) - if eq(check_staticcall, 0) { - error_verify() - } - mstore(add(state, state_gamma_kzg), mod(mload(add(state, state_gamma_kzg)), r_mod)) - } - - function compute_commitment_linearised_polynomial_ec(aproof, s1, s2) { - - let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) - - mstore(mPtr, vk_ql_com_x) - mstore(add(mPtr,0x20), vk_ql_com_y) - point_mul(add(state, state_linearised_polynomial_x), mPtr, mload(add(aproof, proof_l_at_zeta)), add(mPtr,0x40)) - - mstore(mPtr, vk_qr_com_x) - mstore(add(mPtr,0x20), vk_qr_com_y) - point_acc_mul(add(state, state_linearised_polynomial_x),mPtr,mload(add(aproof, proof_r_at_zeta)),add(mPtr,0x40)) - - let rl := mulmod(mload(add(aproof, proof_l_at_zeta)), mload(add(aproof, proof_r_at_zeta)), r_mod) - mstore(mPtr, vk_qm_com_x) - mstore(add(mPtr,0x20), vk_qm_com_y) - point_acc_mul(add(state, state_linearised_polynomial_x),mPtr,rl,add(mPtr,0x40)) - - mstore(mPtr, vk_qo_com_x) - mstore(add(mPtr,0x20), vk_qo_com_y) - point_acc_mul(add(state, state_linearised_polynomial_x),mPtr,mload(add(aproof, proof_o_at_zeta)),add(mPtr,0x40)) - - mstore(mPtr, vk_qk_com_x) - mstore(add(mPtr, 0x20), vk_qk_com_y) - point_add(add(state, state_linearised_polynomial_x),add(state, state_linearised_polynomial_x),mPtr,add(mPtr, 0x40)) - - let commits_api_at_zeta := add(aproof, proof_openings_selector_commit_api_at_zeta) - let commits_api := add(aproof, add(proof_openings_selector_commit_api_at_zeta, mul(vk_nb_commitments_commit_api, 0x20))) - for {let i:=0} lt(i, vk_nb_commitments_commit_api) {i:=add(i,1)} - { - mstore(mPtr, mload(commits_api)) - mstore(add(mPtr, 0x20), mload(add(commits_api, 0x20))) - point_acc_mul(add(state, state_linearised_polynomial_x),mPtr,mload(commits_api_at_zeta),add(mPtr,0x40)) - commits_api_at_zeta := add(commits_api_at_zeta, 0x20) - commits_api := add(commits_api, 0x40) - } - - mstore(mPtr, vk_s3_com_x) - mstore(add(mPtr, 0x20), vk_s3_com_y) - point_acc_mul(add(state, state_linearised_polynomial_x), mPtr, s1, add(mPtr, 0x40)) - - mstore(mPtr, mload(add(aproof, proof_grand_product_commitment_x))) - mstore(add(mPtr, 0x20), mload(add(aproof, proof_grand_product_commitment_y))) - point_acc_mul(add(state, state_linearised_polynomial_x), mPtr, s2, add(mPtr, 0x40)) - - } - - // Compute the commitment to the linearized polynomial equal to - // L(ζ)[Qₗ]+r(ζ)[Qᵣ]+R(ζ)L(ζ)[Qₘ]+O(ζ)[Qₒ]+[Qₖ]+Σᵢqc'ᵢ(ζ)[BsbCommitmentᵢ] + - // α*( Z(μζ)(L(ζ)+β*S₁(ζ)+γ)*(R(ζ)+β*S₂(ζ)+γ)[S₃]-[Z](L(ζ)+β*id_{1}(ζ)+γ)*(R(ζ)+β*id_{2(ζ)+γ)*(O(ζ)+β*id_{3}(ζ)+γ) ) + - // α²*L₁(ζ)[Z] - // where - // * id_1 = id, id_2 = vk_coset_shift*id, id_3 = vk_coset_shift^{2}*id - // * the [] means that it's a commitment (i.e. a point on Bn254(F_p)) - function compute_commitment_linearised_polynomial(aproof) { - - let state := mload(0x40) - let l_beta := mload(add(state, state_beta)) - let l_gamma := mload(add(state, state_gamma)) - let l_zeta := mload(add(state, state_zeta)) - let l_alpha := mload(add(state, state_alpha)) - - let u := mulmod(mload(add(aproof,proof_grand_product_at_zeta_omega)), l_beta, r_mod) - let v := mulmod(l_beta, mload(add(aproof, proof_s1_at_zeta)), r_mod) - v := addmod(v, mload(add(aproof, proof_l_at_zeta)), r_mod) - v := addmod(v, l_gamma, r_mod) - - let w := mulmod(l_beta, mload(add(aproof, proof_s2_at_zeta)), r_mod) - w := addmod(w, mload(add(aproof, proof_r_at_zeta)), r_mod) - w := addmod(w, l_gamma, r_mod) - - let s1 := mulmod(u, v, r_mod) - s1 := mulmod(s1, w, r_mod) - s1 := mulmod(s1, l_alpha, r_mod) - - let coset_square := mulmod(vk_coset_shift, vk_coset_shift, r_mod) - let betazeta := mulmod(l_beta, l_zeta, r_mod) - u := addmod(betazeta, mload(add(aproof, proof_l_at_zeta)), r_mod) - u := addmod(u, l_gamma, r_mod) - - v := mulmod(betazeta, vk_coset_shift, r_mod) - v := addmod(v, mload(add(aproof, proof_r_at_zeta)), r_mod) - v := addmod(v, l_gamma, r_mod) - - w := mulmod(betazeta, coset_square, r_mod) - w := addmod(w, mload(add(aproof, proof_o_at_zeta)), r_mod) - w := addmod(w, l_gamma, r_mod) - - let s2 := mulmod(u, v, r_mod) - s2 := mulmod(s2, w, r_mod) - s2 := sub(r_mod, s2) - s2 := mulmod(s2, l_alpha, r_mod) - s2 := addmod(s2, mload(add(state, state_alpha_square_lagrange_0)), r_mod) - - // at this stage: - // * s₁ = α*Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β - // * s₂ = -α*(l(ζ)+β*ζ+γ)*(r(ζ)+β*u*ζ+γ)*(o(ζ)+β*u²*ζ+γ) + α²*L₁(ζ) - - compute_commitment_linearised_polynomial_ec(aproof, s1, s2) - } - - // compute H₁ + ζᵐ⁺²*H₂ + ζ²⁽ᵐ⁺²⁾*H₃ and store the result at - // state + state_folded_h - function fold_h(aproof) { - let state := mload(0x40) - let n_plus_two := add(vk_domain_size, 2) - let mPtr := add(mload(0x40), state_last_mem) - let zeta_power_n_plus_two := pow(mload(add(state, state_zeta)), n_plus_two, mPtr) - point_mul(add(state, state_folded_h_x), add(aproof, proof_h_2_x), zeta_power_n_plus_two, mPtr) - point_add(add(state, state_folded_h_x), add(state, state_folded_h_x), add(aproof, proof_h_1_x), mPtr) - point_mul(add(state, state_folded_h_x), add(state, state_folded_h_x), zeta_power_n_plus_two, mPtr) - point_add(add(state, state_folded_h_x), add(state, state_folded_h_x), add(aproof, proof_h_0_x), mPtr) - } - - // check that - // L(ζ)Qₗ(ζ)+r(ζ)Qᵣ(ζ)+R(ζ)L(ζ)Qₘ(ζ)+O(ζ)Qₒ(ζ)+Qₖ(ζ)+Σᵢqc'ᵢ(ζ)BsbCommitmentᵢ(ζ) + - // α*( Z(μζ)(l(ζ)+β*s₁(ζ)+γ)*(r(ζ)+β*s₂(ζ)+γ)*β*s₃(X)-Z(X)(l(ζ)+β*id_1(ζ)+γ)*(r(ζ)+β*id_2(ζ)+γ)*(o(ζ)+β*id_3(ζ)+γ) ) ) - // + α²*L₁(ζ) = - // (ζⁿ-1)H(ζ) - function verify_quotient_poly_eval_at_zeta(aproof) { - - let state := mload(0x40) - - // (l(ζ)+β*s1(ζ)+γ) - let s1 := add(mload(0x40), state_last_mem) - mstore(s1, mulmod(mload(add(aproof,proof_s1_at_zeta)),mload(add(state, state_beta)), r_mod)) - mstore(s1, addmod(mload(s1), mload(add(state, state_gamma)), r_mod)) - mstore(s1, addmod(mload(s1), mload(add(aproof, proof_l_at_zeta)), r_mod)) - - // (r(ζ)+β*s2(ζ)+γ) - let s2 := add(s1,0x20) - mstore(s2, mulmod(mload(add(aproof,proof_s2_at_zeta)),mload(add(state, state_beta)), r_mod)) - mstore(s2, addmod(mload(s2), mload(add(state, state_gamma)), r_mod)) - mstore(s2, addmod(mload(s2), mload(add(aproof, proof_r_at_zeta)), r_mod)) - // _s2 := mload(s2) - - // (o(ζ)+γ) - let o := add(s1,0x40) - mstore(o, addmod(mload(add(aproof,proof_o_at_zeta)), mload(add(state, state_gamma)), r_mod)) - - // α*(Z(μζ))*(l(ζ)+β*s1(ζ)+γ)*(r(ζ)+β*s2(ζ)+γ)*(o(ζ)+γ) - mstore(s1, mulmod(mload(s1), mload(s2), r_mod)) - mstore(s1, mulmod(mload(s1), mload(o), r_mod)) - mstore(s1, mulmod(mload(s1), mload(add(state, state_alpha)), r_mod)) - mstore(s1, mulmod(mload(s1), mload(add(aproof, proof_grand_product_at_zeta_omega)), r_mod)) - - let computed_quotient := add(s1,0x60) - - // linearizedpolynomial + pi(zeta) - mstore(computed_quotient, addmod(mload(add(aproof, proof_linearised_polynomial_at_zeta)), mload(add(state, state_pi)), r_mod)) - mstore(computed_quotient, addmod(mload(computed_quotient), mload(s1), r_mod)) - mstore(computed_quotient, addmod(mload(computed_quotient), sub(r_mod,mload(add(state, state_alpha_square_lagrange_0))), r_mod)) - mstore(s2, mulmod(mload(add(aproof,proof_quotient_polynomial_at_zeta)), mload(add(state, state_zeta_power_n_minus_one)), r_mod)) - - mstore(add(state, state_success),eq(mload(computed_quotient), mload(s2))) - } - - function point_add(dst, p, q, mPtr) { - // let mPtr := add(mload(0x40), state_last_mem) - let state := mload(0x40) - mstore(mPtr, mload(p)) - mstore(add(mPtr, 0x20), mload(add(p, 0x20))) - mstore(add(mPtr, 0x40), mload(q)) - mstore(add(mPtr, 0x60), mload(add(q, 0x20))) - let l_success := staticcall(gas(),6,mPtr,0x80,dst,0x40) - mstore(add(state, state_success), and(l_success,mload(add(state, state_success)))) - } - - // dst <- [s]src - function point_mul(dst,src,s, mPtr) { - // let mPtr := add(mload(0x40), state_last_mem) - let state := mload(0x40) - mstore(mPtr,mload(src)) - mstore(add(mPtr,0x20),mload(add(src,0x20))) - mstore(add(mPtr,0x40),s) - let l_success := staticcall(gas(),7,mPtr,0x60,dst,0x40) - mstore(add(state, state_success), and(l_success,mload(add(state, state_success)))) - } - - // dst <- dst + [s]src (Elliptic curve) - function point_acc_mul(dst,src,s, mPtr) { - let state := mload(0x40) - mstore(mPtr,mload(src)) - mstore(add(mPtr,0x20),mload(add(src,0x20))) - mstore(add(mPtr,0x40),s) - let l_success := staticcall(gas(),7,mPtr,0x60,mPtr,0x40) - mstore(add(mPtr,0x40),mload(dst)) - mstore(add(mPtr,0x60),mload(add(dst,0x20))) - l_success := and(l_success, staticcall(gas(),6,mPtr,0x80,dst, 0x40)) - mstore(add(state, state_success), and(l_success,mload(add(state, state_success)))) - } - - // dst <- dst + src (Fr) dst,src are addresses, s is a value - function fr_acc_mul(dst, src, s) { - let tmp := mulmod(mload(src), s, r_mod) - mstore(dst, addmod(mload(dst), tmp, r_mod)) - } - - // dst <- x ** e mod r (x, e are values, not pointers) - function pow(x, e, mPtr)->res { - mstore(mPtr, 0x20) - mstore(add(mPtr, 0x20), 0x20) - mstore(add(mPtr, 0x40), 0x20) - mstore(add(mPtr, 0x60), x) - mstore(add(mPtr, 0x80), e) - mstore(add(mPtr, 0xa0), r_mod) - let check_staticcall := staticcall(gas(),0x05,mPtr,0xc0,mPtr,0x20) - if eq(check_staticcall, 0) { - error_verify() - } - res := mload(mPtr) - } - } - // emit PrintUint256(check); - } -} From 7aebeeb5f46bc00077972d1179990f5ecfc82b39 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Thu, 27 Jul 2023 22:46:29 +0200 Subject: [PATCH 12/13] feat: put function calls at the beginning of Verify --- backend/plonk/bn254/solidity.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/plonk/bn254/solidity.go b/backend/plonk/bn254/solidity.go index dafb512820..b6886a25a3 100644 --- a/backend/plonk/bn254/solidity.go +++ b/backend/plonk/bn254/solidity.go @@ -192,6 +192,16 @@ contract PlonkVerifier { {{ end -}} mstore(add(mem, state_pi), l_pi) + compute_alpha_square_lagrange_0() + verify_quotient_poly_eval_at_zeta(proof) + fold_h(proof) + compute_commitment_linearised_polynomial(proof) + compute_gamma_kzg(proof) + fold_state(proof) + batch_verify_multi_points(proof) + + success := mload(add(mem, state_success)) + // Beginning checks ------------------------------------------------- function error_inputs_size() { let ptError := mload(0x40) @@ -653,16 +663,6 @@ contract PlonkVerifier { {{ end }} // END compute_pi ------------------------------------------------- - compute_alpha_square_lagrange_0() - verify_quotient_poly_eval_at_zeta(proof) - fold_h(proof) - compute_commitment_linearised_polynomial(proof) - compute_gamma_kzg(proof) - fold_state(proof) - batch_verify_multi_points(proof) - - success := mload(add(mem, state_success)) - function error_verify() { let ptError := mload(0x40) mstore(ptError, error_string_id) // selector for function Error(string) From 2f734decb18629e639b983dd19c09e0c521ddbf5 Mon Sep 17 00:00:00 2001 From: Thomas Piellard Date: Fri, 28 Jul 2023 14:32:15 +0200 Subject: [PATCH 13/13] feat: use state instead of mload(0x40) --- backend/plonk/bn254/solidity.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/plonk/bn254/solidity.go b/backend/plonk/bn254/solidity.go index b6886a25a3..697efdcb0d 100644 --- a/backend/plonk/bn254/solidity.go +++ b/backend/plonk/bn254/solidity.go @@ -320,7 +320,7 @@ contract PlonkVerifier { function derive_gamma(aproof, ins)->gamma_not_reduced { let state := mload(0x40) - let mPtr := add(mload(0x40), state_last_mem) + let mPtr := add(state, state_last_mem) // gamma // gamma in ascii is [0x67,0x61,0x6d, 0x6d, 0x61]