diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..79fa3b033 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +tools/target +tools/data/Verifier.sol \ No newline at end of file diff --git a/ethereum/contracts/dev-contracts/test/VerifierTest.sol b/ethereum/contracts/dev-contracts/test/VerifierTest.sol new file mode 100644 index 000000000..230c706a6 --- /dev/null +++ b/ethereum/contracts/dev-contracts/test/VerifierTest.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import "../../zksync/Verifier.sol"; + +/// @author Matter Labs +contract VerifierTest is Verifier { + function _loadVerificationKey() internal pure override { + assembly { + mstore(VK_GATE_SETUP_0_X_SLOT, 0x08fa9d6f0dd6ac1cbeb94ae20fe7a23df05cb1095df66fb561190e615a4037ef) + mstore(VK_GATE_SETUP_0_Y_SLOT, 0x196dcc8692fe322d21375920559944c12ba7b1ba8b732344cf4ba2e3aa0fc8b4) + mstore(VK_GATE_SETUP_1_X_SLOT, 0x0074aaf5d97bd57551311a8b3e4aa7840bc55896502020b2f43ad6a98d81a443) + mstore(VK_GATE_SETUP_1_Y_SLOT, 0x2d275a3ad153dc9d89ebb9c9b6a0afd2dde82470554e9738d905c328fbb4c8bc) + mstore(VK_GATE_SETUP_2_X_SLOT, 0x287f1975a9aeaef5d2bb0767b5ef538f76e82f7da01c0cb6db8c6f920818ec4f) + mstore(VK_GATE_SETUP_2_Y_SLOT, 0x2fff6f53594129f794a7731d963d27e72f385c5c6d8e08829e6f66a9d29a12ea) + mstore(VK_GATE_SETUP_3_X_SLOT, 0x038809fa3d4b7320d43e023454194f0a7878baa7e73a295d2d105260f1c34cbc) + mstore(VK_GATE_SETUP_3_Y_SLOT, 0x25418b1105cf45b2a3da6c349bab1d9caaf145eaf24d1e8fb92c11654c000781) + mstore(VK_GATE_SETUP_4_X_SLOT, 0x0561cafd527ac3f0bc550db77d87cd1c63938f7ec051e62ebf84a5bbe07f9840) + mstore(VK_GATE_SETUP_4_Y_SLOT, 0x28f87201b4cbe19f1517a1c29ca6d6cb074502ccfed4c31c8931c6992c3eea43) + mstore(VK_GATE_SETUP_5_X_SLOT, 0x27e0af572bac6e36d31c33808cb44c0ef8ceee5e2850e916fb01f3747db72491) + mstore(VK_GATE_SETUP_5_Y_SLOT, 0x1da20087ba61c59366b21e31e4ac6889d357cf11bf16b94d875f94f41525c427) + mstore(VK_GATE_SETUP_6_X_SLOT, 0x2c2bcafea8f93d07f96874f470985a8d272c09c8ed49373f36497ee80bd8da17) + mstore(VK_GATE_SETUP_6_Y_SLOT, 0x299276cf6dca1a7e3780f6276c5d067403f6e024e83e0cc1ab4c5f7252b7f653) + mstore(VK_GATE_SETUP_7_X_SLOT, 0x0ba9d4a53e050da25b8410045b634f1ca065ff74acd35bab1a72bf1f20047ef3) + mstore(VK_GATE_SETUP_7_Y_SLOT, 0x1f1eefc8b0507a08f852f554bd7abcbd506e52de390ca127477a678d212abfe5) + + mstore(VK_GATE_SELECTORS_0_X_SLOT, 0x1c6b68d9920620012d85a4850dad9bd6d03ae8bbc7a08b827199e85dba1ef2b1) + mstore(VK_GATE_SELECTORS_0_Y_SLOT, 0x0f6380560d1b585628ed259289cec19d3a7c70c60e66bbfebfcb70c8c312d91e) + mstore(VK_GATE_SELECTORS_1_X_SLOT, 0x0dfead780e5067181aae631ff734a33fca302773472997daca58ba49dbd20dcc) + mstore(VK_GATE_SELECTORS_1_Y_SLOT, 0x00f13fa6e356f525d2fd1c533acf2858c0d2b9f0a9b3180f94e1543929c75073) + + mstore(VK_PERMUTATION_0_X_SLOT, 0x1df0747c787934650d99c5696f9273088ad07ec3e0825c9d39685a9b9978ebed) + mstore(VK_PERMUTATION_0_Y_SLOT, 0x2ace2a277becbc69af4e89518eb50960a733d9d71354845ea43d2e65c8e0e4cb) + mstore(VK_PERMUTATION_1_X_SLOT, 0x06598c8236a5f5045cd7444dc87f3e1f66f99bf01251e13be4dc0ab1f7f1af4b) + mstore(VK_PERMUTATION_1_Y_SLOT, 0x14ca234fe9b3bb1e5517fc60d6b90f8ad44b0899a2d4f71a64c9640b3142ce8b) + mstore(VK_PERMUTATION_2_X_SLOT, 0x01889e2c684caefde60471748f4259196ecf4209a735ccdf7b1816f05bafa50a) + mstore(VK_PERMUTATION_2_Y_SLOT, 0x092d287a080bfe2fd40ad392ff290e462cd0e347b8fd9d05b90af234ce77a11b) + mstore(VK_PERMUTATION_3_X_SLOT, 0x0dd98eeb5bc12c221da969398b67750a8774dbdd37a78da52367f9fc0e566d5c) + mstore(VK_PERMUTATION_3_Y_SLOT, 0x06750ceb40c9fb87fc424df9599340938b7552b759914a90cb0e41d3915c945b) + + mstore(VK_LOOKUP_SELECTOR_X_SLOT, 0x2f491c662ae53ceb358f57a868dc00b89befa853bd9a449127ea2d46820995bd) + mstore(VK_LOOKUP_SELECTOR_Y_SLOT, 0x231fe6538634ff8b6fa21ca248fb15e7f43d82eb0bfa705490d24ddb3e3cad77) + + mstore(VK_LOOKUP_TABLE_0_X_SLOT, 0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c) + mstore(VK_LOOKUP_TABLE_0_Y_SLOT, 0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b) + mstore(VK_LOOKUP_TABLE_1_X_SLOT, 0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9) + mstore(VK_LOOKUP_TABLE_1_Y_SLOT, 0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca) + mstore(VK_LOOKUP_TABLE_2_X_SLOT, 0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44) + mstore(VK_LOOKUP_TABLE_2_Y_SLOT, 0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765) + mstore(VK_LOOKUP_TABLE_3_X_SLOT, 0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e) + mstore(VK_LOOKUP_TABLE_3_Y_SLOT, 0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e) + + mstore(VK_LOOKUP_TABLE_TYPE_X_SLOT, 0x12cd873a6f18a4a590a846d9ebf61565197edf457efd26bc408eb61b72f37b59) + mstore(VK_LOOKUP_TABLE_TYPE_Y_SLOT, 0x19890cbdac892682e7a5910ca6c238c082130e1c71f33d0c9c901153377770d1) + } + } +} diff --git a/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol b/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol index 7f3b5a3a4..ab6f8b715 100644 --- a/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol +++ b/ethereum/contracts/upgrades/BaseZkSyncUpgrade.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.13; import "../zksync/facets/Base.sol"; import "../zksync/interfaces/IMailbox.sol"; -import "../zksync/Verifier.sol"; +import "../zksync/interfaces/IVerifier.sol"; import "../common/libraries/L2ContractHelper.sol"; import "../zksync/libraries/TransactionValidator.sol"; import {SYSTEM_UPGRADE_L2_TX_TYPE, MAX_NEW_FACTORY_DEPS} from "../zksync/Config.sol"; @@ -103,16 +103,16 @@ abstract contract BaseZkSyncUpgrade is Base { /// @notice Change the address of the verifier smart contract /// @param _newVerifier Verifier smart contract address - function _setVerifier(Verifier _newVerifier) private { + function _setVerifier(IVerifier _newVerifier) private { // An upgrade to the verifier must be done carefully to ensure there aren't blocks in the committed state // during the transition. If verifier is upgraded, it will immediately be used to prove all committed blocks. // Blocks committed expecting the old verifier will fail. Ensure all commited blocks are finalized before the // verifier is upgraded. - if (_newVerifier == Verifier(address(0))) { + if (_newVerifier == IVerifier(address(0))) { return; } - Verifier oldVerifier = s.verifier; + IVerifier oldVerifier = s.verifier; s.verifier = _newVerifier; emit NewVerifier(address(oldVerifier), address(_newVerifier)); } @@ -137,7 +137,7 @@ abstract contract BaseZkSyncUpgrade is Base { /// @param _newVerifier The address of the new verifier. If 0, the verifier will not be updated. /// @param _verifierParams The new verifier params. If either of the fields is 0, the params will not be updated. function _upgradeVerifier(address _newVerifier, VerifierParams calldata _verifierParams) internal { - _setVerifier(Verifier(_newVerifier)); + _setVerifier(IVerifier(_newVerifier)); _setVerifierParams(_verifierParams); } diff --git a/ethereum/contracts/zksync/DiamondInit.sol b/ethereum/contracts/zksync/DiamondInit.sol index d9fb9021d..e5dbeb866 100644 --- a/ethereum/contracts/zksync/DiamondInit.sol +++ b/ethereum/contracts/zksync/DiamondInit.sol @@ -29,7 +29,7 @@ contract DiamondInit is Base { /// @param _priorityTxMaxGasLimit maximum number of the L2 gas that a user can request for L1 -> L2 transactions /// @return Magic 32 bytes, which indicates that the contract logic is expected to be used as a diamond proxy initializer function initialize( - Verifier _verifier, + IVerifier _verifier, address _governor, bytes32 _genesisBlockHash, uint64 _genesisIndexRepeatedStorageChanges, diff --git a/ethereum/contracts/zksync/Plonk4VerifierWithAccessToDNext.sol b/ethereum/contracts/zksync/Plonk4VerifierWithAccessToDNext.sol deleted file mode 100644 index 31f594491..000000000 --- a/ethereum/contracts/zksync/Plonk4VerifierWithAccessToDNext.sol +++ /dev/null @@ -1,702 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import "./libraries/PairingsBn254.sol"; -import "./libraries/TranscriptLib.sol"; -import "../common/libraries/UncheckedMath.sol"; - -uint256 constant STATE_WIDTH = 4; -uint256 constant NUM_G2_ELS = 2; - -struct VerificationKey { - uint256 domain_size; - uint256 num_inputs; - PairingsBn254.Fr omega; - PairingsBn254.G1Point[2] gate_selectors_commitments; - PairingsBn254.G1Point[8] gate_setup_commitments; - PairingsBn254.G1Point[STATE_WIDTH] permutation_commitments; - PairingsBn254.G1Point lookup_selector_commitment; - PairingsBn254.G1Point[4] lookup_tables_commitments; - PairingsBn254.G1Point lookup_table_type_commitment; - PairingsBn254.Fr[STATE_WIDTH - 1] non_residues; - PairingsBn254.G2Point[NUM_G2_ELS] g2_elements; -} - -contract Plonk4VerifierWithAccessToDNext { - using PairingsBn254 for PairingsBn254.G1Point; - using PairingsBn254 for PairingsBn254.G2Point; - using PairingsBn254 for PairingsBn254.Fr; - - using TranscriptLib for TranscriptLib.Transcript; - - using UncheckedMath for uint256; - - struct Proof { - uint256[] input_values; - // commitments - PairingsBn254.G1Point[STATE_WIDTH] state_polys_commitments; - PairingsBn254.G1Point copy_permutation_grand_product_commitment; - PairingsBn254.G1Point[STATE_WIDTH] quotient_poly_parts_commitments; - // openings - PairingsBn254.Fr[STATE_WIDTH] state_polys_openings_at_z; - PairingsBn254.Fr[1] state_polys_openings_at_z_omega; - PairingsBn254.Fr[1] gate_selectors_openings_at_z; - PairingsBn254.Fr[STATE_WIDTH - 1] copy_permutation_polys_openings_at_z; - PairingsBn254.Fr copy_permutation_grand_product_opening_at_z_omega; - PairingsBn254.Fr quotient_poly_opening_at_z; - PairingsBn254.Fr linearization_poly_opening_at_z; - // lookup commitments - PairingsBn254.G1Point lookup_s_poly_commitment; - PairingsBn254.G1Point lookup_grand_product_commitment; - // lookup openings - PairingsBn254.Fr lookup_s_poly_opening_at_z_omega; - PairingsBn254.Fr lookup_grand_product_opening_at_z_omega; - PairingsBn254.Fr lookup_t_poly_opening_at_z; - PairingsBn254.Fr lookup_t_poly_opening_at_z_omega; - PairingsBn254.Fr lookup_selector_poly_opening_at_z; - PairingsBn254.Fr lookup_table_type_poly_opening_at_z; - PairingsBn254.G1Point opening_proof_at_z; - PairingsBn254.G1Point opening_proof_at_z_omega; - } - - struct PartialVerifierState { - PairingsBn254.Fr zero; - PairingsBn254.Fr alpha; - PairingsBn254.Fr beta; - PairingsBn254.Fr gamma; - PairingsBn254.Fr[9] alpha_values; - PairingsBn254.Fr eta; - PairingsBn254.Fr beta_lookup; - PairingsBn254.Fr gamma_lookup; - PairingsBn254.Fr beta_plus_one; - PairingsBn254.Fr beta_gamma; - PairingsBn254.Fr v; - PairingsBn254.Fr u; - PairingsBn254.Fr z; - PairingsBn254.Fr z_omega; - PairingsBn254.Fr z_minus_last_omega; - PairingsBn254.Fr l_0_at_z; - PairingsBn254.Fr l_n_minus_one_at_z; - PairingsBn254.Fr t; - PairingsBn254.G1Point tp; - } - - function evaluate_l0_at_point(uint256 domain_size, PairingsBn254.Fr memory at) - internal - view - returns (PairingsBn254.Fr memory num) - { - PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); - - PairingsBn254.Fr memory size_fe = PairingsBn254.new_fr(domain_size); - PairingsBn254.Fr memory den = at.copy(); - den.sub_assign(one); - den.mul_assign(size_fe); - - den = den.inverse(); - - num = at.pow(domain_size); - num.sub_assign(one); - num.mul_assign(den); - } - - function evaluate_lagrange_poly_out_of_domain( - uint256 poly_num, - uint256 domain_size, - PairingsBn254.Fr memory omega, - PairingsBn254.Fr memory at - ) internal view returns (PairingsBn254.Fr memory res) { - // (omega^i / N) / (X - omega^i) * (X^N - 1) - require(poly_num < domain_size); - PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); - PairingsBn254.Fr memory omega_power = omega.pow(poly_num); - res = at.pow(domain_size); - res.sub_assign(one); - require(res.value != 0); // Vanishing polynomial cannot be zero at point `at` - res.mul_assign(omega_power); - - PairingsBn254.Fr memory den = PairingsBn254.copy(at); - den.sub_assign(omega_power); - den.mul_assign(PairingsBn254.new_fr(domain_size)); - - den = den.inverse(); - - res.mul_assign(den); - } - - function evaluate_vanishing(uint256 domain_size, PairingsBn254.Fr memory at) - internal - view - returns (PairingsBn254.Fr memory res) - { - res = at.pow(domain_size); - res.sub_assign(PairingsBn254.new_fr(1)); - } - - function initialize_transcript(Proof memory proof, VerificationKey memory vk) - internal - pure - returns (PartialVerifierState memory state) - { - TranscriptLib.Transcript memory transcript = TranscriptLib.new_transcript(); - - for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) { - transcript.update_with_u256(proof.input_values[i]); - } - - for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { - transcript.update_with_g1(proof.state_polys_commitments[i]); - } - - state.eta = transcript.get_challenge(); - transcript.update_with_g1(proof.lookup_s_poly_commitment); - - state.beta = transcript.get_challenge(); - state.gamma = transcript.get_challenge(); - - transcript.update_with_g1(proof.copy_permutation_grand_product_commitment); - state.beta_lookup = transcript.get_challenge(); - state.gamma_lookup = transcript.get_challenge(); - transcript.update_with_g1(proof.lookup_grand_product_commitment); - state.alpha = transcript.get_challenge(); - - for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { - transcript.update_with_g1(proof.quotient_poly_parts_commitments[i]); - } - state.z = transcript.get_challenge(); - - transcript.update_with_fr(proof.quotient_poly_opening_at_z); - - for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) { - transcript.update_with_fr(proof.state_polys_openings_at_z[i]); - } - - for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) { - transcript.update_with_fr(proof.state_polys_openings_at_z_omega[i]); - } - for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) { - transcript.update_with_fr(proof.gate_selectors_openings_at_z[i]); - } - for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { - transcript.update_with_fr(proof.copy_permutation_polys_openings_at_z[i]); - } - - state.z_omega = state.z.copy(); - state.z_omega.mul_assign(vk.omega); - - transcript.update_with_fr(proof.copy_permutation_grand_product_opening_at_z_omega); - - transcript.update_with_fr(proof.lookup_t_poly_opening_at_z); - transcript.update_with_fr(proof.lookup_selector_poly_opening_at_z); - transcript.update_with_fr(proof.lookup_table_type_poly_opening_at_z); - transcript.update_with_fr(proof.lookup_s_poly_opening_at_z_omega); - transcript.update_with_fr(proof.lookup_grand_product_opening_at_z_omega); - transcript.update_with_fr(proof.lookup_t_poly_opening_at_z_omega); - transcript.update_with_fr(proof.linearization_poly_opening_at_z); - - state.v = transcript.get_challenge(); - - transcript.update_with_g1(proof.opening_proof_at_z); - transcript.update_with_g1(proof.opening_proof_at_z_omega); - - state.u = transcript.get_challenge(); - } - - // compute some powers of challenge alpha([alpha^1, .. alpha^8]) - function compute_powers_of_alpha(PartialVerifierState memory state) public pure { - require(state.alpha.value != 0); - state.alpha_values[0] = PairingsBn254.new_fr(1); - state.alpha_values[1] = state.alpha.copy(); - PairingsBn254.Fr memory current_alpha = state.alpha.copy(); - for (uint256 i = 2; i < state.alpha_values.length; i = i.uncheckedInc()) { - current_alpha.mul_assign(state.alpha); - state.alpha_values[i] = current_alpha.copy(); - } - } - - function verify(Proof memory proof, VerificationKey memory vk) internal view returns (bool) { - // we initialize all challenges beforehand, we can draw each challenge in its own place - PartialVerifierState memory state = initialize_transcript(proof, vk); - if (verify_quotient_evaluation(vk, proof, state) == false) { - return false; - } - require(proof.state_polys_openings_at_z_omega.length == 1); - - PairingsBn254.G1Point memory quotient_result = proof.quotient_poly_parts_commitments[0].copy_g1(); - { - // block scope - PairingsBn254.Fr memory z_in_domain_size = state.z.pow(vk.domain_size); - PairingsBn254.Fr memory current_z = z_in_domain_size.copy(); - PairingsBn254.G1Point memory tp; - // start from i =1 - for (uint256 i = 1; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { - tp = proof.quotient_poly_parts_commitments[i].copy_g1(); - tp.point_mul_assign(current_z); - quotient_result.point_add_assign(tp); - - current_z.mul_assign(z_in_domain_size); - } - } - - Queries memory queries = prepare_queries(vk, proof, state); - queries.commitments_at_z[0] = quotient_result; - queries.values_at_z[0] = proof.quotient_poly_opening_at_z; - queries.commitments_at_z[1] = aggregated_linearization_commitment(vk, proof, state); - queries.values_at_z[1] = proof.linearization_poly_opening_at_z; - - require(queries.commitments_at_z.length == queries.values_at_z.length); - - PairingsBn254.G1Point memory aggregated_commitment_at_z = queries.commitments_at_z[0]; - - PairingsBn254.Fr memory aggregated_opening_at_z = queries.values_at_z[0]; - PairingsBn254.Fr memory aggregation_challenge = PairingsBn254.new_fr(1); - PairingsBn254.G1Point memory scaled; - for (uint256 i = 1; i < queries.commitments_at_z.length; i = i.uncheckedInc()) { - aggregation_challenge.mul_assign(state.v); - scaled = queries.commitments_at_z[i].point_mul(aggregation_challenge); - aggregated_commitment_at_z.point_add_assign(scaled); - - state.t = queries.values_at_z[i]; - state.t.mul_assign(aggregation_challenge); - aggregated_opening_at_z.add_assign(state.t); - } - - aggregation_challenge.mul_assign(state.v); - - PairingsBn254.G1Point memory aggregated_commitment_at_z_omega = queries.commitments_at_z_omega[0].point_mul( - aggregation_challenge - ); - PairingsBn254.Fr memory aggregated_opening_at_z_omega = queries.values_at_z_omega[0]; - aggregated_opening_at_z_omega.mul_assign(aggregation_challenge); - for (uint256 i = 1; i < queries.commitments_at_z_omega.length; i = i.uncheckedInc()) { - aggregation_challenge.mul_assign(state.v); - - scaled = queries.commitments_at_z_omega[i].point_mul(aggregation_challenge); - aggregated_commitment_at_z_omega.point_add_assign(scaled); - - state.t = queries.values_at_z_omega[i]; - state.t.mul_assign(aggregation_challenge); - aggregated_opening_at_z_omega.add_assign(state.t); - } - - return - final_pairing( - vk.g2_elements, - proof, - state, - aggregated_commitment_at_z, - aggregated_commitment_at_z_omega, - aggregated_opening_at_z, - aggregated_opening_at_z_omega - ); - } - - function verify_quotient_evaluation( - VerificationKey memory vk, - Proof memory proof, - PartialVerifierState memory state - ) internal view returns (bool) { - uint256[] memory lagrange_poly_numbers = new uint256[](vk.num_inputs); - for (uint256 i = 0; i < lagrange_poly_numbers.length; i = i.uncheckedInc()) { - lagrange_poly_numbers[i] = i; - } - require(vk.num_inputs > 0); - - PairingsBn254.Fr memory inputs_term = PairingsBn254.new_fr(0); - for (uint256 i = 0; i < vk.num_inputs; i = i.uncheckedInc()) { - state.t = evaluate_lagrange_poly_out_of_domain(i, vk.domain_size, vk.omega, state.z); - state.t.mul_assign(PairingsBn254.new_fr(proof.input_values[i])); - inputs_term.add_assign(state.t); - } - inputs_term.mul_assign(proof.gate_selectors_openings_at_z[0]); - PairingsBn254.Fr memory result = proof.linearization_poly_opening_at_z.copy(); - result.add_assign(inputs_term); - - // compute powers of alpha - compute_powers_of_alpha(state); - PairingsBn254.Fr memory factor = state.alpha_values[4].copy(); - factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega); - - // - alpha_0 * (a + perm(z) * beta + gamma)*()*(d + gamma) * z(z*omega) - require(proof.copy_permutation_polys_openings_at_z.length == STATE_WIDTH - 1); - PairingsBn254.Fr memory t; // TMP; - for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { - t = proof.copy_permutation_polys_openings_at_z[i].copy(); - t.mul_assign(state.beta); - t.add_assign(proof.state_polys_openings_at_z[i]); - t.add_assign(state.gamma); - - factor.mul_assign(t); - } - - t = proof.state_polys_openings_at_z[3].copy(); - t.add_assign(state.gamma); - factor.mul_assign(t); - result.sub_assign(factor); - - // - L_0(z) * alpha_1 - PairingsBn254.Fr memory l_0_at_z = evaluate_l0_at_point(vk.domain_size, state.z); - l_0_at_z.mul_assign(state.alpha_values[4 + 1]); - result.sub_assign(l_0_at_z); - - PairingsBn254.Fr memory lookup_quotient_contrib = lookup_quotient_contribution(vk, proof, state); - result.add_assign(lookup_quotient_contrib); - - PairingsBn254.Fr memory lhs = proof.quotient_poly_opening_at_z.copy(); - lhs.mul_assign(evaluate_vanishing(vk.domain_size, state.z)); - return lhs.value == result.value; - } - - function lookup_quotient_contribution( - VerificationKey memory vk, - Proof memory proof, - PartialVerifierState memory state - ) internal view returns (PairingsBn254.Fr memory result) { - PairingsBn254.Fr memory t; - - PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); - state.beta_plus_one = state.beta_lookup.copy(); - state.beta_plus_one.add_assign(one); - state.beta_gamma = state.beta_plus_one.copy(); - state.beta_gamma.mul_assign(state.gamma_lookup); - - // (s'*beta + gamma)*(zw')*alpha - t = proof.lookup_s_poly_opening_at_z_omega.copy(); - t.mul_assign(state.beta_lookup); - t.add_assign(state.beta_gamma); - t.mul_assign(proof.lookup_grand_product_opening_at_z_omega); - t.mul_assign(state.alpha_values[6]); - - // (z - omega^{n-1}) for this part - PairingsBn254.Fr memory last_omega = vk.omega.pow(vk.domain_size - 1); - state.z_minus_last_omega = state.z.copy(); - state.z_minus_last_omega.sub_assign(last_omega); - t.mul_assign(state.z_minus_last_omega); - result.add_assign(t); - - // - alpha_1 * L_{0}(z) - state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z); - t = state.l_0_at_z.copy(); - t.mul_assign(state.alpha_values[6 + 1]); - result.sub_assign(t); - - // - alpha_2 * beta_gamma_powered L_{n-1}(z) - PairingsBn254.Fr memory beta_gamma_powered = state.beta_gamma.pow(vk.domain_size - 1); - state.l_n_minus_one_at_z = evaluate_lagrange_poly_out_of_domain( - vk.domain_size - 1, - vk.domain_size, - vk.omega, - state.z - ); - t = state.l_n_minus_one_at_z.copy(); - t.mul_assign(beta_gamma_powered); - t.mul_assign(state.alpha_values[6 + 2]); - - result.sub_assign(t); - } - - function aggregated_linearization_commitment( - VerificationKey memory vk, - Proof memory proof, - PartialVerifierState memory state - ) internal view returns (PairingsBn254.G1Point memory result) { - // qMain*(Q_a * A + Q_b * B + Q_c * C + Q_d * D + Q_m * A*B + Q_const + Q_dNext * D_next) - result = PairingsBn254.new_g1(0, 0); - // Q_a * A - PairingsBn254.G1Point memory scaled = vk.gate_setup_commitments[0].point_mul( - proof.state_polys_openings_at_z[0] - ); - result.point_add_assign(scaled); - // Q_b * B - scaled = vk.gate_setup_commitments[1].point_mul(proof.state_polys_openings_at_z[1]); - result.point_add_assign(scaled); - // Q_c * C - scaled = vk.gate_setup_commitments[2].point_mul(proof.state_polys_openings_at_z[2]); - result.point_add_assign(scaled); - // Q_d * D - scaled = vk.gate_setup_commitments[3].point_mul(proof.state_polys_openings_at_z[3]); - result.point_add_assign(scaled); - // Q_m* A*B or Q_ab*A*B - PairingsBn254.Fr memory t = proof.state_polys_openings_at_z[0].copy(); - t.mul_assign(proof.state_polys_openings_at_z[1]); - scaled = vk.gate_setup_commitments[4].point_mul(t); - result.point_add_assign(scaled); - // Q_AC* A*C - t = proof.state_polys_openings_at_z[0].copy(); - t.mul_assign(proof.state_polys_openings_at_z[2]); - scaled = vk.gate_setup_commitments[5].point_mul(t); - result.point_add_assign(scaled); - // Q_const - result.point_add_assign(vk.gate_setup_commitments[6]); - // Q_dNext * D_next - scaled = vk.gate_setup_commitments[7].point_mul(proof.state_polys_openings_at_z_omega[0]); - result.point_add_assign(scaled); - result.point_mul_assign(proof.gate_selectors_openings_at_z[0]); - - PairingsBn254.G1Point - memory rescue_custom_gate_linearization_contrib = rescue_custom_gate_linearization_contribution( - vk, - proof, - state - ); - result.point_add_assign(rescue_custom_gate_linearization_contrib); - require(vk.non_residues.length == STATE_WIDTH - 1); - - PairingsBn254.Fr memory one = PairingsBn254.new_fr(1); - PairingsBn254.Fr memory factor = state.alpha_values[4].copy(); - for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; ) { - t = state.z.copy(); - if (i == 0) { - t.mul_assign(one); - } else { - t.mul_assign(vk.non_residues[i - 1]); - } - t.mul_assign(state.beta); - t.add_assign(state.gamma); - t.add_assign(proof.state_polys_openings_at_z[i]); - - factor.mul_assign(t); - unchecked { - ++i; - } - } - - scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor); - result.point_add_assign(scaled); - - // - (a(z) + beta*perm_a + gamma)*()*()*z(z*omega) * beta * perm_d(X) - factor = state.alpha_values[4].copy(); - factor.mul_assign(state.beta); - factor.mul_assign(proof.copy_permutation_grand_product_opening_at_z_omega); - for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { - t = proof.copy_permutation_polys_openings_at_z[i].copy(); - t.mul_assign(state.beta); - t.add_assign(state.gamma); - t.add_assign(proof.state_polys_openings_at_z[i]); - - factor.mul_assign(t); - } - scaled = vk.permutation_commitments[3].point_mul(factor); - result.point_sub_assign(scaled); - - // + L_0(z) * Z(x) - state.l_0_at_z = evaluate_lagrange_poly_out_of_domain(0, vk.domain_size, vk.omega, state.z); - require(state.l_0_at_z.value != 0); - factor = state.l_0_at_z.copy(); - factor.mul_assign(state.alpha_values[4 + 1]); - scaled = proof.copy_permutation_grand_product_commitment.point_mul(factor); - result.point_add_assign(scaled); - - PairingsBn254.G1Point memory lookup_linearization_contrib = lookup_linearization_contribution(proof, state); - result.point_add_assign(lookup_linearization_contrib); - } - - function rescue_custom_gate_linearization_contribution( - VerificationKey memory vk, - Proof memory proof, - PartialVerifierState memory state - ) public view returns (PairingsBn254.G1Point memory result) { - PairingsBn254.Fr memory t; - PairingsBn254.Fr memory intermediate_result; - - // a^2 - b = 0 - t = proof.state_polys_openings_at_z[0].copy(); - t.mul_assign(t); - t.sub_assign(proof.state_polys_openings_at_z[1]); - // t.mul_assign(challenge1); - t.mul_assign(state.alpha_values[1]); - intermediate_result.add_assign(t); - - // b^2 - c = 0 - t = proof.state_polys_openings_at_z[1].copy(); - t.mul_assign(t); - t.sub_assign(proof.state_polys_openings_at_z[2]); - t.mul_assign(state.alpha_values[1 + 1]); - intermediate_result.add_assign(t); - - // c*a - d = 0; - t = proof.state_polys_openings_at_z[2].copy(); - t.mul_assign(proof.state_polys_openings_at_z[0]); - t.sub_assign(proof.state_polys_openings_at_z[3]); - t.mul_assign(state.alpha_values[1 + 2]); - intermediate_result.add_assign(t); - - result = vk.gate_selectors_commitments[1].point_mul(intermediate_result); - } - - function lookup_linearization_contribution(Proof memory proof, PartialVerifierState memory state) - internal - view - returns (PairingsBn254.G1Point memory result) - { - PairingsBn254.Fr memory zero = PairingsBn254.new_fr(0); - - PairingsBn254.Fr memory t; - PairingsBn254.Fr memory factor; - // s(x) from the Z(x*omega)*(\gamma*(1 + \beta) + s(x) + \beta * s(x*omega))) - factor = proof.lookup_grand_product_opening_at_z_omega.copy(); - factor.mul_assign(state.alpha_values[6]); - factor.mul_assign(state.z_minus_last_omega); - - PairingsBn254.G1Point memory scaled = proof.lookup_s_poly_commitment.point_mul(factor); - result.point_add_assign(scaled); - - // Z(x) from - alpha_0 * Z(x) * (\beta + 1) * (\gamma + f(x)) * (\gamma(1 + \beta) + t(x) + \beta * t(x*omega)) - // + alpha_1 * Z(x) * L_{0}(z) + alpha_2 * Z(x) * L_{n-1}(z) - - // accumulate coefficient - factor = proof.lookup_t_poly_opening_at_z_omega.copy(); - factor.mul_assign(state.beta_lookup); - factor.add_assign(proof.lookup_t_poly_opening_at_z); - factor.add_assign(state.beta_gamma); - - // (\gamma + f(x)) - PairingsBn254.Fr memory f_reconstructed; - PairingsBn254.Fr memory current = PairingsBn254.new_fr(1); - PairingsBn254.Fr memory tmp0; - for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { - tmp0 = proof.state_polys_openings_at_z[i].copy(); - tmp0.mul_assign(current); - f_reconstructed.add_assign(tmp0); - - current.mul_assign(state.eta); - } - - // add type of table - t = proof.lookup_table_type_poly_opening_at_z.copy(); - t.mul_assign(current); - f_reconstructed.add_assign(t); - - f_reconstructed.mul_assign(proof.lookup_selector_poly_opening_at_z); - f_reconstructed.add_assign(state.gamma_lookup); - - // end of (\gamma + f(x)) part - factor.mul_assign(f_reconstructed); - factor.mul_assign(state.beta_plus_one); - t = zero.copy(); - t.sub_assign(factor); - factor = t; - factor.mul_assign(state.alpha_values[6]); - - // Multiply by (z - omega^{n-1}) - factor.mul_assign(state.z_minus_last_omega); - - // L_{0}(z) in front of Z(x) - t = state.l_0_at_z.copy(); - t.mul_assign(state.alpha_values[6 + 1]); - factor.add_assign(t); - - // L_{n-1}(z) in front of Z(x) - t = state.l_n_minus_one_at_z.copy(); - t.mul_assign(state.alpha_values[6 + 2]); - factor.add_assign(t); - - scaled = proof.lookup_grand_product_commitment.point_mul(factor); - result.point_add_assign(scaled); - } - - struct Queries { - PairingsBn254.G1Point[13] commitments_at_z; - PairingsBn254.Fr[13] values_at_z; - PairingsBn254.G1Point[6] commitments_at_z_omega; - PairingsBn254.Fr[6] values_at_z_omega; - } - - function prepare_queries( - VerificationKey memory vk, - Proof memory proof, - PartialVerifierState memory state - ) public view returns (Queries memory queries) { - // we set first two items in calee side so start idx from 2 - uint256 idx = 2; - for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { - queries.commitments_at_z[idx] = proof.state_polys_commitments[i]; - queries.values_at_z[idx] = proof.state_polys_openings_at_z[i]; - idx = idx.uncheckedInc(); - } - require(proof.gate_selectors_openings_at_z.length == 1); - queries.commitments_at_z[idx] = vk.gate_selectors_commitments[0]; - queries.values_at_z[idx] = proof.gate_selectors_openings_at_z[0]; - idx = idx.uncheckedInc(); - for (uint256 i = 0; i < STATE_WIDTH - 1; i = i.uncheckedInc()) { - queries.commitments_at_z[idx] = vk.permutation_commitments[i]; - queries.values_at_z[idx] = proof.copy_permutation_polys_openings_at_z[i]; - idx = idx.uncheckedInc(); - } - - queries.commitments_at_z_omega[0] = proof.copy_permutation_grand_product_commitment; - queries.commitments_at_z_omega[1] = proof.state_polys_commitments[STATE_WIDTH - 1]; - - queries.values_at_z_omega[0] = proof.copy_permutation_grand_product_opening_at_z_omega; - queries.values_at_z_omega[1] = proof.state_polys_openings_at_z_omega[0]; - - PairingsBn254.G1Point memory lookup_t_poly_commitment_aggregated = vk.lookup_tables_commitments[0]; - PairingsBn254.Fr memory current_eta = state.eta.copy(); - for (uint256 i = 1; i < vk.lookup_tables_commitments.length; i = i.uncheckedInc()) { - state.tp = vk.lookup_tables_commitments[i].point_mul(current_eta); - lookup_t_poly_commitment_aggregated.point_add_assign(state.tp); - - current_eta.mul_assign(state.eta); - } - queries.commitments_at_z[idx] = lookup_t_poly_commitment_aggregated; - queries.values_at_z[idx] = proof.lookup_t_poly_opening_at_z; - idx = idx.uncheckedInc(); - queries.commitments_at_z[idx] = vk.lookup_selector_commitment; - queries.values_at_z[idx] = proof.lookup_selector_poly_opening_at_z; - idx = idx.uncheckedInc(); - queries.commitments_at_z[idx] = vk.lookup_table_type_commitment; - queries.values_at_z[idx] = proof.lookup_table_type_poly_opening_at_z; - queries.commitments_at_z_omega[2] = proof.lookup_s_poly_commitment; - queries.values_at_z_omega[2] = proof.lookup_s_poly_opening_at_z_omega; - queries.commitments_at_z_omega[3] = proof.lookup_grand_product_commitment; - queries.values_at_z_omega[3] = proof.lookup_grand_product_opening_at_z_omega; - queries.commitments_at_z_omega[4] = lookup_t_poly_commitment_aggregated; - queries.values_at_z_omega[4] = proof.lookup_t_poly_opening_at_z_omega; - } - - function final_pairing( - // VerificationKey memory vk, - PairingsBn254.G2Point[NUM_G2_ELS] memory g2_elements, - Proof memory proof, - PartialVerifierState memory state, - PairingsBn254.G1Point memory aggregated_commitment_at_z, - PairingsBn254.G1Point memory aggregated_commitment_at_z_omega, - PairingsBn254.Fr memory aggregated_opening_at_z, - PairingsBn254.Fr memory aggregated_opening_at_z_omega - ) internal view returns (bool) { - // q(x) = f(x) - f(z) / (x - z) - // q(x) * (x-z) = f(x) - f(z) - - // f(x) - PairingsBn254.G1Point memory pair_with_generator = aggregated_commitment_at_z.copy_g1(); - aggregated_commitment_at_z_omega.point_mul_assign(state.u); - pair_with_generator.point_add_assign(aggregated_commitment_at_z_omega); - - // - f(z)*g - PairingsBn254.Fr memory aggregated_value = aggregated_opening_at_z_omega.copy(); - aggregated_value.mul_assign(state.u); - aggregated_value.add_assign(aggregated_opening_at_z); - PairingsBn254.G1Point memory tp = PairingsBn254.P1().point_mul(aggregated_value); - pair_with_generator.point_sub_assign(tp); - - // +z * q(x) - tp = proof.opening_proof_at_z.point_mul(state.z); - PairingsBn254.Fr memory t = state.z_omega.copy(); - t.mul_assign(state.u); - PairingsBn254.G1Point memory t1 = proof.opening_proof_at_z_omega.point_mul(t); - tp.point_add_assign(t1); - pair_with_generator.point_add_assign(tp); - - // rhs - PairingsBn254.G1Point memory pair_with_x = proof.opening_proof_at_z_omega.point_mul(state.u); - pair_with_x.point_add_assign(proof.opening_proof_at_z); - pair_with_x.negate(); - // Pairing precompile expects points to be in a `i*x[1] + x[0]` form instead of `x[0] + i*x[1]` - // so we handle it in code generation step - PairingsBn254.G2Point memory first_g2 = g2_elements[0]; - PairingsBn254.G2Point memory second_g2 = g2_elements[1]; - - return PairingsBn254.pairingProd2(pair_with_generator, first_g2, pair_with_x, second_g2); - } -} diff --git a/ethereum/contracts/zksync/Storage.sol b/ethereum/contracts/zksync/Storage.sol index 27e13906b..01160d53e 100644 --- a/ethereum/contracts/zksync/Storage.sol +++ b/ethereum/contracts/zksync/Storage.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; -import "./Verifier.sol"; +import "./../zksync/interfaces/IVerifier.sol"; import "../common/interfaces/IAllowList.sol"; import "./libraries/PriorityQueue.sol"; @@ -83,7 +83,7 @@ struct AppStorage { /// @notice List of permitted validators mapping(address => bool) validators; /// @dev Verifier contract. Used to verify aggregated proof for blocks - Verifier verifier; + IVerifier verifier; /// @notice Total number of executed blocks i.e. blocks[totalBlocksExecuted] points at the latest executed block (block 0 is genesis) uint256 totalBlocksExecuted; /// @notice Total number of proved blocks i.e. blocks[totalBlocksProved] points at the latest proved block diff --git a/ethereum/contracts/zksync/Verifier.sol b/ethereum/contracts/zksync/Verifier.sol index 710782ccf..32549d012 100644 --- a/ethereum/contracts/zksync/Verifier.sol +++ b/ethereum/contracts/zksync/Verifier.sol @@ -2,236 +2,1678 @@ pragma solidity ^0.8.13; -import "./Plonk4VerifierWithAccessToDNext.sol"; -import "../common/libraries/UncheckedMath.sol"; - -contract Verifier is Plonk4VerifierWithAccessToDNext { - using UncheckedMath for uint256; - - function get_verification_key() public pure returns (VerificationKey memory vk) { - vk.num_inputs = 1; - vk.domain_size = 67108864; - vk.omega = PairingsBn254.new_fr(0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97); - // coefficients - vk.gate_setup_commitments[0] = PairingsBn254.new_g1( - 0x14c289d746e37aa82ec428491881c4732766492a8bc2e8e3cca2000a40c0ea27, - 0x2f617a7eb9808ad9843d1e080b7cfbf99d61bb1b02076c905f31adb12731bc41 - ); - vk.gate_setup_commitments[1] = PairingsBn254.new_g1( - 0x210b5cc8e6a85d63b65b701b8fb5ad24ff9c41f923432de17fe4ebae04526a8c, - 0x05c10ab17ea731b2b87fb890fa5b10bd3d6832917a616b807a9b640888ebc731 - ); - vk.gate_setup_commitments[2] = PairingsBn254.new_g1( - 0x29d4d14adcfe67a2ac690d6369db6b75e82d8ab3124bc4fa1dd145f41ca6949c, - 0x004f6cd229373f1c1f735ccf49aef6a5c32025bc36c3328596dd0db7d87bef67 - ); - vk.gate_setup_commitments[3] = PairingsBn254.new_g1( - 0x06d15382e8cabae9f98374a9fbdadd424f48e24da7e4c65bf710fd7d7d59a05a, - 0x22e438ad5c51673879ce17073a3d2d29327a97dc3ce61c4f88540e00087695f6 - ); - vk.gate_setup_commitments[4] = PairingsBn254.new_g1( - 0x274a668dfc485cf192d0086f214146d9e02b3040a5a586df344c53c16a87882b, - 0x15f5bb7ad01f162b70fc77c8ea456d67d15a6ce98acbbfd521222810f8ec0a66 - ); - vk.gate_setup_commitments[5] = PairingsBn254.new_g1( - 0x0ba53bf4fb0446927857e33978d02abf45948fc68f4091394ae0827a22cf1e47, - 0x0720d818751ce5b3f11c716e925f60df4679ea90bed516499bdec066f5ff108f - ); - vk.gate_setup_commitments[6] = PairingsBn254.new_g1( - 0x2e986ba2ea495e5ec6af532980b1dc567f1430bfa82f8de07c12fc097c0e0483, - 0x1555d189f6164e82d78de1b8313c2e923e616b3c8ed0e350c3b61c94516d0b58 - ); - vk.gate_setup_commitments[7] = PairingsBn254.new_g1( - 0x0925959592604ca73c917f9b2e029aa2563c318ddcc5ca29c11badb7b880127b, - 0x2b4a430fcb2fa7d6d67d6c358e01cf0524c7df7e1e56442f65b39bc1a1052367 - ); - // gate selectors - vk.gate_selectors_commitments[0] = PairingsBn254.new_g1( - 0x28f2a0a95af79ba67e9dd1986bd3190199f661b710a693fc82fb395c126edcbd, - 0x0db75db5de5192d1ba1c24710fc00da16fa8029ac7fe82d855674dcd6d090e05 - ); - vk.gate_selectors_commitments[1] = PairingsBn254.new_g1( - 0x143471a174dfcb2d9cb5ae621e519387bcc93c9dcfc011160b2f5c5f88e32cbe, - 0x2a0194c0224c3d964223a96c4c99e015719bc879125aa0df3f0715d154e71a31 - ); - // permutation - vk.permutation_commitments[0] = PairingsBn254.new_g1( - 0x1423fa82e00ba22c280181afb12c56eea541933eeb5ec39119b0365b6beab4b9, - 0x0efdcd3423a38f5e2ecf8c7e4fd46f13189f8fed392ad9d8d393e8ba568b06e4 - ); - vk.permutation_commitments[1] = PairingsBn254.new_g1( - 0x0e9b5b12c1090d62224e64aa1696c009aa59a9c3eec458e781fae773e1f4eca5, - 0x1fe3df508c7e9750eb37d9cae5e7437ad11a21fa36530ff821b407b165a79a55 - ); - vk.permutation_commitments[2] = PairingsBn254.new_g1( - 0x25d1a714bd1e258f196e38d6b2826153382c2d04b870d0b7ec250296005129ae, - 0x0883a121b41ca7beaa9de97ecf4417e62aa2eeb9434f24ddacbfed57cbf016a8 - ); - vk.permutation_commitments[3] = PairingsBn254.new_g1( - 0x2f3ede68e854a6b3b14589851cf077a606e2aeb3205c43cc579b7abae39d8f58, - 0x178ccd4b1f78fd79ee248e376b6fc8297d5450900d1e15e8c03e3ed2c171ac8c - ); - // lookup table commitments - vk.lookup_selector_commitment = PairingsBn254.new_g1( - 0x1f814e2d87c332e964eeef94ec695eef9d2caaac58b682a43da5107693b06f30, - 0x196d56fb01907e66af9303886fd95328d398e5b2b72906882a9d12c1718e2ee2 - ); - vk.lookup_tables_commitments[0] = PairingsBn254.new_g1( - 0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c, - 0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b - ); - vk.lookup_tables_commitments[1] = PairingsBn254.new_g1( - 0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9, - 0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca - ); - vk.lookup_tables_commitments[2] = PairingsBn254.new_g1( - 0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44, - 0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765 - ); - vk.lookup_tables_commitments[3] = PairingsBn254.new_g1( - 0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e, - 0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e - ); - vk.lookup_table_type_commitment = PairingsBn254.new_g1( - 0x2f85df2d6249ccbcc11b91727333cc800459de6ee274f29c657c8d56f6f01563, - 0x088e1df178c47116a69c3c8f6d0c5feb530e2a72493694a623b1cceb7d44a76c - ); - // non residues - vk.non_residues[0] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000005); - vk.non_residues[1] = PairingsBn254.new_fr(0x0000000000000000000000000000000000000000000000000000000000000007); - vk.non_residues[2] = PairingsBn254.new_fr(0x000000000000000000000000000000000000000000000000000000000000000a); - - // g2 elements - vk.g2_elements[0] = PairingsBn254.new_g2( - [ - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ], - [ - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ] - ); - vk.g2_elements[1] = PairingsBn254.new_g2( - [ - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1, - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ], - [ - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4, - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ] - ); - } +import "./interfaces/IVerifier.sol"; - function deserialize_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof) - internal - pure - returns (Proof memory proof) - { - require(serialized_proof.length == 44); - proof.input_values = new uint256[](public_inputs.length); - for (uint256 i = 0; i < public_inputs.length; i = i.uncheckedInc()) { - proof.input_values[i] = public_inputs[i]; - } +/// @author Matter Labs +/// @notice Modified version of the Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of Knowledge (PLONK) verifier. +/// Modifications have been made to optimize the proof system for zkSync Era circuits. +/// @dev It uses a custom memory layout inside the inline assembly block. Each reserved memory cell is declared in the constants below. +/// @dev For a better understanding of the verifier algorithm please refer to the following papers: +/// * Original Plonk Article: https://eprint.iacr.org/2019/953.pdf +/// * Original LookUp Article: https://eprint.iacr.org/2020/315.pdf +/// * Plonk for zkSync v1.1: https://github.com/matter-labs/solidity_plonk_verifier/raw/recursive/bellman_vk_codegen_recursive/RecursivePlonkUnrolledForEthereum.pdf +/// The notation used in the code is the same as in the papers. +contract Verifier is IVerifier { + /*////////////////////////////////////////////////////////////// + Verification keys + //////////////////////////////////////////////////////////////*/ - uint256 j; - for (uint256 i = 0; i < STATE_WIDTH; i = i.uncheckedInc()) { - proof.state_polys_commitments[i] = PairingsBn254.new_g1_checked( - serialized_proof[j], - serialized_proof[j.uncheckedInc()] - ); + // Memory slots from 0x000 to 0x200 are reserved for intermediate computations and call to precompiles. - j = j.uncheckedAdd(2); - } - proof.copy_permutation_grand_product_commitment = PairingsBn254.new_g1_checked( - serialized_proof[j], - serialized_proof[j.uncheckedInc()] - ); - j = j.uncheckedAdd(2); - - proof.lookup_s_poly_commitment = PairingsBn254.new_g1_checked( - serialized_proof[j], - serialized_proof[j.uncheckedInc()] - ); - j = j.uncheckedAdd(2); - - proof.lookup_grand_product_commitment = PairingsBn254.new_g1_checked( - serialized_proof[j], - serialized_proof[j.uncheckedInc()] - ); - j = j.uncheckedAdd(2); - for (uint256 i = 0; i < proof.quotient_poly_parts_commitments.length; i = i.uncheckedInc()) { - proof.quotient_poly_parts_commitments[i] = PairingsBn254.new_g1_checked( - serialized_proof[j], - serialized_proof[j.uncheckedInc()] - ); - j = j.uncheckedAdd(2); - } + uint256 internal constant VK_GATE_SETUP_0_X_SLOT = 0x200 + 0x000; + uint256 internal constant VK_GATE_SETUP_0_Y_SLOT = 0x200 + 0x020; + uint256 internal constant VK_GATE_SETUP_1_X_SLOT = 0x200 + 0x040; + uint256 internal constant VK_GATE_SETUP_1_Y_SLOT = 0x200 + 0x060; + uint256 internal constant VK_GATE_SETUP_2_X_SLOT = 0x200 + 0x080; + uint256 internal constant VK_GATE_SETUP_2_Y_SLOT = 0x200 + 0x0a0; + uint256 internal constant VK_GATE_SETUP_3_X_SLOT = 0x200 + 0x0c0; + uint256 internal constant VK_GATE_SETUP_3_Y_SLOT = 0x200 + 0x0e0; + uint256 internal constant VK_GATE_SETUP_4_X_SLOT = 0x200 + 0x100; + uint256 internal constant VK_GATE_SETUP_4_Y_SLOT = 0x200 + 0x120; + uint256 internal constant VK_GATE_SETUP_5_X_SLOT = 0x200 + 0x140; + uint256 internal constant VK_GATE_SETUP_5_Y_SLOT = 0x200 + 0x160; + uint256 internal constant VK_GATE_SETUP_6_X_SLOT = 0x200 + 0x180; + uint256 internal constant VK_GATE_SETUP_6_Y_SLOT = 0x200 + 0x1a0; + uint256 internal constant VK_GATE_SETUP_7_X_SLOT = 0x200 + 0x1c0; + uint256 internal constant VK_GATE_SETUP_7_Y_SLOT = 0x200 + 0x1e0; - for (uint256 i = 0; i < proof.state_polys_openings_at_z.length; i = i.uncheckedInc()) { - proof.state_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); + uint256 internal constant VK_GATE_SELECTORS_0_X_SLOT = 0x200 + 0x200; + uint256 internal constant VK_GATE_SELECTORS_0_Y_SLOT = 0x200 + 0x220; + uint256 internal constant VK_GATE_SELECTORS_1_X_SLOT = 0x200 + 0x240; + uint256 internal constant VK_GATE_SELECTORS_1_Y_SLOT = 0x200 + 0x260; - j = j.uncheckedInc(); - } + uint256 internal constant VK_PERMUTATION_0_X_SLOT = 0x200 + 0x280; + uint256 internal constant VK_PERMUTATION_0_Y_SLOT = 0x200 + 0x2a0; + uint256 internal constant VK_PERMUTATION_1_X_SLOT = 0x200 + 0x2c0; + uint256 internal constant VK_PERMUTATION_1_Y_SLOT = 0x200 + 0x2e0; + uint256 internal constant VK_PERMUTATION_2_X_SLOT = 0x200 + 0x300; + uint256 internal constant VK_PERMUTATION_2_Y_SLOT = 0x200 + 0x320; + uint256 internal constant VK_PERMUTATION_3_X_SLOT = 0x200 + 0x340; + uint256 internal constant VK_PERMUTATION_3_Y_SLOT = 0x200 + 0x360; - for (uint256 i = 0; i < proof.state_polys_openings_at_z_omega.length; i = i.uncheckedInc()) { - proof.state_polys_openings_at_z_omega[i] = PairingsBn254.new_fr(serialized_proof[j]); + uint256 internal constant VK_LOOKUP_SELECTOR_X_SLOT = 0x200 + 0x380; + uint256 internal constant VK_LOOKUP_SELECTOR_Y_SLOT = 0x200 + 0x3a0; - j = j.uncheckedInc(); - } - for (uint256 i = 0; i < proof.gate_selectors_openings_at_z.length; i = i.uncheckedInc()) { - proof.gate_selectors_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); + uint256 internal constant VK_LOOKUP_TABLE_0_X_SLOT = 0x200 + 0x3c0; + uint256 internal constant VK_LOOKUP_TABLE_0_Y_SLOT = 0x200 + 0x3e0; + uint256 internal constant VK_LOOKUP_TABLE_1_X_SLOT = 0x200 + 0x400; + uint256 internal constant VK_LOOKUP_TABLE_1_Y_SLOT = 0x200 + 0x420; + uint256 internal constant VK_LOOKUP_TABLE_2_X_SLOT = 0x200 + 0x440; + uint256 internal constant VK_LOOKUP_TABLE_2_Y_SLOT = 0x200 + 0x460; + uint256 internal constant VK_LOOKUP_TABLE_3_X_SLOT = 0x200 + 0x480; + uint256 internal constant VK_LOOKUP_TABLE_3_Y_SLOT = 0x200 + 0x4a0; + + uint256 internal constant VK_LOOKUP_TABLE_TYPE_X_SLOT = 0x200 + 0x4c0; + uint256 internal constant VK_LOOKUP_TABLE_TYPE_Y_SLOT = 0x200 + 0x4e0; + + /*////////////////////////////////////////////////////////////// + Proof + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant PROOF_PUBLIC_INPUT = 0x200 + 0x600 + 0x000; + + uint256 internal constant PROOF_STATE_POLYS_0_X_SLOT = 0x200 + 0x600 + 0x020; + uint256 internal constant PROOF_STATE_POLYS_0_Y_SLOT = 0x200 + 0x600 + 0x040; + uint256 internal constant PROOF_STATE_POLYS_1_X_SLOT = 0x200 + 0x600 + 0x060; + uint256 internal constant PROOF_STATE_POLYS_1_Y_SLOT = 0x200 + 0x600 + 0x080; + uint256 internal constant PROOF_STATE_POLYS_2_X_SLOT = 0x200 + 0x600 + 0x0a0; + uint256 internal constant PROOF_STATE_POLYS_2_Y_SLOT = 0x200 + 0x600 + 0x0c0; + uint256 internal constant PROOF_STATE_POLYS_3_X_SLOT = 0x200 + 0x600 + 0x0e0; + uint256 internal constant PROOF_STATE_POLYS_3_Y_SLOT = 0x200 + 0x600 + 0x100; + + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT = 0x200 + 0x600 + 0x120; + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x600 + 0x140; + + uint256 internal constant PROOF_LOOKUP_S_POLY_X_SLOT = 0x200 + 0x600 + 0x160; + uint256 internal constant PROOF_LOOKUP_S_POLY_Y_SLOT = 0x200 + 0x600 + 0x180; + + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT = 0x200 + 0x600 + 0x1a0; + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x600 + 0x1c0; + + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT = 0x200 + 0x600 + 0x1e0; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT = 0x200 + 0x600 + 0x200; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT = 0x200 + 0x600 + 0x220; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT = 0x200 + 0x600 + 0x240; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT = 0x200 + 0x600 + 0x260; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT = 0x200 + 0x600 + 0x280; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT = 0x200 + 0x600 + 0x2a0; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT = 0x200 + 0x600 + 0x2c0; + + uint256 internal constant PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x2e0; + uint256 internal constant PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x300; + uint256 internal constant PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x320; + uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x340; + + uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x360; + uint256 internal constant PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x380; + + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x3a0; + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x3c0; + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x3e0; + + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x400; + uint256 internal constant PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x420; + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x440; + uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x460; + uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x480; + uint256 internal constant PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x4a0; + uint256 internal constant PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x4c0; + uint256 internal constant PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x4e0; + uint256 internal constant PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x500; + + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_X_SLOT = 0x200 + 0x600 + 0x520; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_Y_SLOT = 0x200 + 0x600 + 0x540; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT = 0x200 + 0x600 + 0x560; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x600 + 0x580; + + uint256 internal constant PROOF_RECURSIVE_PART_P1_X_SLOT = 0x200 + 0x600 + 0x5a0; + uint256 internal constant PROOF_RECURSIVE_PART_P1_Y_SLOT = 0x200 + 0x600 + 0x5c0; + + uint256 internal constant PROOF_RECURSIVE_PART_P2_X_SLOT = 0x200 + 0x600 + 0x5e0; + uint256 internal constant PROOF_RECURSIVE_PART_P2_Y_SLOT = 0x200 + 0x600 + 0x600; + + /*////////////////////////////////////////////////////////////// + Transcript slot + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant TRANSCRIPT_BEGIN_SLOT = 0x200 + 0x600 + 0x620 + 0x00; + uint256 internal constant TRANSCRIPT_DST_BYTE_SLOT = 0x200 + 0x600 + 0x620 + 0x03; + uint256 internal constant TRANSCRIPT_STATE_0_SLOT = 0x200 + 0x600 + 0x620 + 0x04; + uint256 internal constant TRANSCRIPT_STATE_1_SLOT = 0x200 + 0x600 + 0x620 + 0x24; + uint256 internal constant TRANSCRIPT_CHALLENGE_SLOT = 0x200 + 0x600 + 0x620 + 0x44; + + /*////////////////////////////////////////////////////////////// + Partial verifier state + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant STATE_ALPHA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x000; + uint256 internal constant STATE_BETA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x020; + uint256 internal constant STATE_GAMMA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x040; + uint256 internal constant STATE_POWER_OF_ALPHA_2_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x060; + uint256 internal constant STATE_POWER_OF_ALPHA_3_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x080; + uint256 internal constant STATE_POWER_OF_ALPHA_4_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x0a0; + uint256 internal constant STATE_POWER_OF_ALPHA_5_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x0c0; + uint256 internal constant STATE_POWER_OF_ALPHA_6_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x0e0; + uint256 internal constant STATE_POWER_OF_ALPHA_7_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x100; + uint256 internal constant STATE_POWER_OF_ALPHA_8_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x120; + uint256 internal constant STATE_ETA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x140; + uint256 internal constant STATE_BETA_LOOKUP_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x160; + uint256 internal constant STATE_GAMMA_LOOKUP_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x180; + uint256 internal constant STATE_BETA_PLUS_ONE_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x1a0; + uint256 internal constant STATE_BETA_GAMMA_PLUS_GAMMA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x1c0; + uint256 internal constant STATE_V_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x1e0; + uint256 internal constant STATE_U_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x200; + uint256 internal constant STATE_Z_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x220; + uint256 internal constant STATE_Z_MINUS_LAST_OMEGA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x240; + uint256 internal constant STATE_L_0_AT_Z_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x260; + uint256 internal constant STATE_L_N_MINUS_ONE_AT_Z_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x280; + uint256 internal constant STATE_Z_IN_DOMAIN_SIZE = 0x200 + 0x600 + 0x620 + 0x80 + 0x2a0; - j = j.uncheckedInc(); + /*////////////////////////////////////////////////////////////// + Queries + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant QUERIES_BUFFER_POINT_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x00; + + uint256 internal constant QUERIES_AT_Z_0_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x40; + uint256 internal constant QUERIES_AT_Z_0_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x60; + uint256 internal constant QUERIES_AT_Z_1_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x80; + uint256 internal constant QUERIES_AT_Z_1_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0xa0; + + uint256 internal constant QUERIES_T_POLY_AGGREGATED_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0xc0; + uint256 internal constant QUERIES_T_POLY_AGGREGATED_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0xe0; + + /*////////////////////////////////////////////////////////////// + Aggregated commitment + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant AGGREGATED_AT_Z_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x00; + uint256 internal constant AGGREGATED_AT_Z_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x20; + + uint256 internal constant AGGREGATED_AT_Z_OMEGA_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x40; + uint256 internal constant AGGREGATED_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x60; + + uint256 internal constant AGGREGATED_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x80; + uint256 internal constant AGGREGATED_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xa0; + + /*////////////////////////////////////////////////////////////// + Pairing data + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant PAIRING_BUFFER_POINT_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x00; + uint256 internal constant PAIRING_BUFFER_POINT_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x20; + + uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_X_SLOT = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x40; + uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_Y_SLOT = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x60; + + uint256 internal constant PAIRING_PAIR_WITH_X_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x80; + uint256 internal constant PAIRING_PAIR_WITH_X_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xa0; + + /*////////////////////////////////////////////////////////////// + Slots for scalar multiplication optimizations + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xc0; + uint256 internal constant LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xe0; + uint256 internal constant LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x100; + + /*////////////////////////////////////////////////////////////// + Constants + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant OMEGA = 0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97; + uint256 internal constant DOMAIN_SIZE = 0x4000000; // 2^26 + uint256 internal constant Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 internal constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + /// @dev flip of 0xe000000000000000000000000000000000000000000000000000000000000000; + uint256 internal constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + + // non residues + uint256 internal constant NON_RESIDUES_0 = 0x5; + uint256 internal constant NON_RESIDUES_1 = 0x7; + uint256 internal constant NON_RESIDUES_2 = 0xa; + + // g2 elements + uint256 internal constant G2_ELEMENTS_0_X1 = 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2; + uint256 internal constant G2_ELEMENTS_0_X2 = 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed; + uint256 internal constant G2_ELEMENTS_0_Y1 = 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b; + uint256 internal constant G2_ELEMENTS_0_Y2 = 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa; + uint256 internal constant G2_ELEMENTS_1_X1 = 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1; + uint256 internal constant G2_ELEMENTS_1_X2 = 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0; + uint256 internal constant G2_ELEMENTS_1_Y1 = 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4; + uint256 internal constant G2_ELEMENTS_1_Y2 = 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55; + + /// @notice Calculates a keccak256 hash of the runtime loaded verification keys. + /// @return vkHash The keccak256 hash of the loaded verification keys. + function verificationKeyHash() external pure returns (bytes32 vkHash) { + _loadVerificationKey(); + + assembly { + let start := VK_GATE_SETUP_0_X_SLOT + let end := VK_LOOKUP_TABLE_TYPE_Y_SLOT + let length := add(sub(end, start), 0x20) + + vkHash := keccak256(start, length) } - for (uint256 i = 0; i < proof.copy_permutation_polys_openings_at_z.length; i = i.uncheckedInc()) { - proof.copy_permutation_polys_openings_at_z[i] = PairingsBn254.new_fr(serialized_proof[j]); + } + + /// @notice Load verification keys to memory in runtime. + /// @dev The constants are loaded into memory in a specific layout declared in the constants starting from `VK_` prefix. + /// NOTE: Function may corrupt the memory state if some memory was used before this function was called. + /// The VK consists of commitments to setup polynomials: + /// [q_a], [q_b], [q_c], [q_d], - main gate setup commitments + /// [q_{d_next}], [q_ab], [q_ac], [q_const] / + /// [main_gate_selector], [custom_gate_selector] - gate selectors commitments + /// [sigma_0], [sigma_1], [sigma_2], [sigma_3] - permutation polynomials commitments + /// [lookup_selector] - lookup selector commitment + /// [col_0], [col_1], [col_2], [col_3] - lookup columns commitments + /// [table_type] - lookup table type commitment + function _loadVerificationKey() internal pure virtual { + assembly { + // gate setup commitments + mstore(VK_GATE_SETUP_0_X_SLOT, 0x14c289d746e37aa82ec428491881c4732766492a8bc2e8e3cca2000a40c0ea27) + mstore(VK_GATE_SETUP_0_Y_SLOT, 0x2f617a7eb9808ad9843d1e080b7cfbf99d61bb1b02076c905f31adb12731bc41) + mstore(VK_GATE_SETUP_1_X_SLOT, 0x210b5cc8e6a85d63b65b701b8fb5ad24ff9c41f923432de17fe4ebae04526a8c) + mstore(VK_GATE_SETUP_1_Y_SLOT, 0x05c10ab17ea731b2b87fb890fa5b10bd3d6832917a616b807a9b640888ebc731) + mstore(VK_GATE_SETUP_2_X_SLOT, 0x29d4d14adcfe67a2ac690d6369db6b75e82d8ab3124bc4fa1dd145f41ca6949c) + mstore(VK_GATE_SETUP_2_Y_SLOT, 0x004f6cd229373f1c1f735ccf49aef6a5c32025bc36c3328596dd0db7d87bef67) + mstore(VK_GATE_SETUP_3_X_SLOT, 0x06d15382e8cabae9f98374a9fbdadd424f48e24da7e4c65bf710fd7d7d59a05a) + mstore(VK_GATE_SETUP_3_Y_SLOT, 0x22e438ad5c51673879ce17073a3d2d29327a97dc3ce61c4f88540e00087695f6) + mstore(VK_GATE_SETUP_4_X_SLOT, 0x274a668dfc485cf192d0086f214146d9e02b3040a5a586df344c53c16a87882b) + mstore(VK_GATE_SETUP_4_Y_SLOT, 0x15f5bb7ad01f162b70fc77c8ea456d67d15a6ce98acbbfd521222810f8ec0a66) + mstore(VK_GATE_SETUP_5_X_SLOT, 0x0ba53bf4fb0446927857e33978d02abf45948fc68f4091394ae0827a22cf1e47) + mstore(VK_GATE_SETUP_5_Y_SLOT, 0x0720d818751ce5b3f11c716e925f60df4679ea90bed516499bdec066f5ff108f) + mstore(VK_GATE_SETUP_6_X_SLOT, 0x2e986ba2ea495e5ec6af532980b1dc567f1430bfa82f8de07c12fc097c0e0483) + mstore(VK_GATE_SETUP_6_Y_SLOT, 0x1555d189f6164e82d78de1b8313c2e923e616b3c8ed0e350c3b61c94516d0b58) + mstore(VK_GATE_SETUP_7_X_SLOT, 0x0925959592604ca73c917f9b2e029aa2563c318ddcc5ca29c11badb7b880127b) + mstore(VK_GATE_SETUP_7_Y_SLOT, 0x2b4a430fcb2fa7d6d67d6c358e01cf0524c7df7e1e56442f65b39bc1a1052367) + + // gate selectors commitments + mstore(VK_GATE_SELECTORS_0_X_SLOT, 0x28f2a0a95af79ba67e9dd1986bd3190199f661b710a693fc82fb395c126edcbd) + mstore(VK_GATE_SELECTORS_0_Y_SLOT, 0x0db75db5de5192d1ba1c24710fc00da16fa8029ac7fe82d855674dcd6d090e05) + mstore(VK_GATE_SELECTORS_1_X_SLOT, 0x143471a174dfcb2d9cb5ae621e519387bcc93c9dcfc011160b2f5c5f88e32cbe) + mstore(VK_GATE_SELECTORS_1_Y_SLOT, 0x2a0194c0224c3d964223a96c4c99e015719bc879125aa0df3f0715d154e71a31) + + // permutation commitments + mstore(VK_PERMUTATION_0_X_SLOT, 0x1423fa82e00ba22c280181afb12c56eea541933eeb5ec39119b0365b6beab4b9) + mstore(VK_PERMUTATION_0_Y_SLOT, 0x0efdcd3423a38f5e2ecf8c7e4fd46f13189f8fed392ad9d8d393e8ba568b06e4) + mstore(VK_PERMUTATION_1_X_SLOT, 0x0e9b5b12c1090d62224e64aa1696c009aa59a9c3eec458e781fae773e1f4eca5) + mstore(VK_PERMUTATION_1_Y_SLOT, 0x1fe3df508c7e9750eb37d9cae5e7437ad11a21fa36530ff821b407b165a79a55) + mstore(VK_PERMUTATION_2_X_SLOT, 0x25d1a714bd1e258f196e38d6b2826153382c2d04b870d0b7ec250296005129ae) + mstore(VK_PERMUTATION_2_Y_SLOT, 0x0883a121b41ca7beaa9de97ecf4417e62aa2eeb9434f24ddacbfed57cbf016a8) + mstore(VK_PERMUTATION_3_X_SLOT, 0x2f3ede68e854a6b3b14589851cf077a606e2aeb3205c43cc579b7abae39d8f58) + mstore(VK_PERMUTATION_3_Y_SLOT, 0x178ccd4b1f78fd79ee248e376b6fc8297d5450900d1e15e8c03e3ed2c171ac8c) - j = j.uncheckedInc(); + // lookup tables commitments + mstore(VK_LOOKUP_TABLE_0_X_SLOT, 0x0ebe0de4a2f39df3b903da484c1641ffdffb77ff87ce4f9508c548659eb22d3c) + mstore(VK_LOOKUP_TABLE_0_Y_SLOT, 0x12a3209440242d5662729558f1017ed9dcc08fe49a99554dd45f5f15da5e4e0b) + mstore(VK_LOOKUP_TABLE_1_X_SLOT, 0x1b7d54f8065ca63bed0bfbb9280a1011b886d07e0c0a26a66ecc96af68c53bf9) + mstore(VK_LOOKUP_TABLE_1_Y_SLOT, 0x2c51121fff5b8f58c302f03c74e0cb176ae5a1d1730dec4696eb9cce3fe284ca) + mstore(VK_LOOKUP_TABLE_2_X_SLOT, 0x0138733c5faa9db6d4b8df9748081e38405999e511fb22d40f77cf3aef293c44) + mstore(VK_LOOKUP_TABLE_2_Y_SLOT, 0x269bee1c1ac28053238f7fe789f1ea2e481742d6d16ae78ed81e87c254af0765) + mstore(VK_LOOKUP_TABLE_3_X_SLOT, 0x1b1be7279d59445065a95f01f16686adfa798ec4f1e6845ffcec9b837e88372e) + mstore(VK_LOOKUP_TABLE_3_Y_SLOT, 0x057c90cb96d8259238ed86b05f629efd55f472a721efeeb56926e979433e6c0e) + + // lookup selector commitment + mstore(VK_LOOKUP_SELECTOR_X_SLOT, 0x1f814e2d87c332e964eeef94ec695eef9d2caaac58b682a43da5107693b06f30) + mstore(VK_LOOKUP_SELECTOR_Y_SLOT, 0x196d56fb01907e66af9303886fd95328d398e5b2b72906882a9d12c1718e2ee2) + + // table type commitment + mstore(VK_LOOKUP_TABLE_TYPE_X_SLOT, 0x2f85df2d6249ccbcc11b91727333cc800459de6ee274f29c657c8d56f6f01563) + mstore(VK_LOOKUP_TABLE_TYPE_Y_SLOT, 0x088e1df178c47116a69c3c8f6d0c5feb530e2a72493694a623b1cceb7d44a76c) } - proof.copy_permutation_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); - - j = j.uncheckedInc(); - proof.lookup_s_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); - j = j.uncheckedInc(); - proof.lookup_grand_product_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); - - j = j.uncheckedInc(); - proof.lookup_t_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); - - j = j.uncheckedInc(); - proof.lookup_t_poly_opening_at_z_omega = PairingsBn254.new_fr(serialized_proof[j]); - j = j.uncheckedInc(); - proof.lookup_selector_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); - j = j.uncheckedInc(); - proof.lookup_table_type_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); - j = j.uncheckedInc(); - proof.quotient_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); - j = j.uncheckedInc(); - proof.linearization_poly_opening_at_z = PairingsBn254.new_fr(serialized_proof[j]); - j = j.uncheckedInc(); - proof.opening_proof_at_z = PairingsBn254.new_g1_checked( - serialized_proof[j], - serialized_proof[j.uncheckedInc()] - ); - j = j.uncheckedAdd(2); - proof.opening_proof_at_z_omega = PairingsBn254.new_g1_checked( - serialized_proof[j], - serialized_proof[j.uncheckedInc()] - ); } - function verify_serialized_proof(uint256[] calldata public_inputs, uint256[] calldata serialized_proof) - public - view - returns (bool) - { - VerificationKey memory vk = get_verification_key(); - require(vk.num_inputs == public_inputs.length); + /// @dev Verifies a zk-SNARK proof. + /// @return A boolean value indicating whether the zk-SNARK proof is valid. + /// Note: The function may revert execution instead of returning false in some cases. + function verify( + uint256[] calldata, // _publicInputs + uint256[] calldata, // _proof + uint256[] calldata // _recursiveAggregationInput + ) external view returns (bool) { + // No memory was accessed yet, so keys can be loaded into the right place and not corrupt any other memory. + _loadVerificationKey(); + + // Begining of the big inline assembly block that makes all the verification work. + // Note: We use the custom memory layout, so the return value should be returned from the assembly, not Solidity code. + assembly { + /*////////////////////////////////////////////////////////////// + Utils + //////////////////////////////////////////////////////////////*/ + + /// @dev Reverts execution with a provided revert reason. + /// @param len The byte length of the error message string, which is expected to be no more than 32. + /// @param reason The 1-word revert reason string, encoded in ASCII. + function revertWithMessage(len, reason) { + // "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // Data offset + mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) + // Length of revert string + mstore(0x24, len) + // Revert reason + mstore(0x44, reason) + // Revert + revert(0x00, 0x64) + } + + /// @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD. + function modexp(value, power) -> res { + mstore(0x00, 0x20) + mstore(0x20, 0x20) + mstore(0x40, 0x20) + mstore(0x60, value) + mstore(0x80, power) + mstore(0xa0, R_MOD) + if iszero(staticcall(gas(), 5, 0, 0xc0, 0x00, 0x20)) { + revertWithMessage(24, "modexp precompile failed") + } + res := mload(0x00) + } + + /// @dev Performs a point multiplication operation and stores the result in a given memory destination. + function pointMulIntoDest(point, s, dest) { + mstore(0x00, mload(point)) + mstore(0x20, mload(add(point, 0x20))) + mstore(0x40, s) + if iszero(staticcall(gas(), 7, 0, 0x60, dest, 0x40)) { + revertWithMessage(30, "pointMulIntoDest: ecMul failed") + } + } + + /// @dev Performs a point addition operation and stores the result in a given memory destination. + function pointAddIntoDest(p1, p2, dest) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, mload(add(p2, 0x20))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) { + revertWithMessage(30, "pointAddIntoDest: ecAdd failed") + } + } + + /// @dev Performs a point subtraction operation and updates the first point with the result. + function pointSubAssign(p1, p2) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, sub(Q_MOD, mload(add(p2, 0x20)))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) { + revertWithMessage(28, "pointSubAssign: ecAdd failed") + } + } + + /// @dev Performs a point addition operation and updates the first point with the result. + function pointAddAssign(p1, p2) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, mload(add(p2, 0x20))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) { + revertWithMessage(28, "pointAddAssign: ecAdd failed") + } + } + + /// @dev Performs a point multiplication operation and then adds the result to the destination point. + function pointMulAndAddIntoDest(point, s, dest) { + mstore(0x00, mload(point)) + mstore(0x20, mload(add(point, 0x20))) + mstore(0x40, s) + let success := staticcall(gas(), 7, 0, 0x60, 0, 0x40) + + mstore(0x40, mload(dest)) + mstore(0x60, mload(add(dest, 0x20))) + success := and(success, staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) + + if iszero(success) { + revertWithMessage(22, "pointMulAndAddIntoDest") + } + } + + /// @dev Negates an elliptic curve point by changing the sign of the y-coordinate. + function pointNegate(point) { + let pY := mload(add(point, 0x20)) + switch pY + case 0 { + if mload(point) { + revertWithMessage(26, "pointNegate: invalid point") + } + } + default { + mstore(add(point, 0x20), sub(Q_MOD, pY)) + } + } + + /*////////////////////////////////////////////////////////////// + Transcript helpers + //////////////////////////////////////////////////////////////*/ + + /// @dev Updates the transcript state with a new challenge value. + function updateTranscript(value) { + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x00) + mstore(TRANSCRIPT_CHALLENGE_SLOT, value) + let newState0 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64) + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x01) + let newState1 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64) + mstore(TRANSCRIPT_STATE_1_SLOT, newState1) + mstore(TRANSCRIPT_STATE_0_SLOT, newState0) + } + + /// @dev Retrieves a transcript challenge. + function getTranscriptChallenge(numberOfChallenge) -> challenge { + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x02) + mstore(TRANSCRIPT_CHALLENGE_SLOT, shl(224, numberOfChallenge)) + challenge := and(keccak256(TRANSCRIPT_BEGIN_SLOT, 0x48), FR_MASK) + } + + /*////////////////////////////////////////////////////////////// + 1. Load Proof + //////////////////////////////////////////////////////////////*/ + + /// @dev This function loads a zk-SNARK proof, ensures it's properly formatted, and stores it in memory. + /// It ensures the number of inputs and the elliptic curve point's validity. + /// Note: It does NOT reject inputs that exceed these module sizes, but rather wraps them within the module bounds. + /// The proof consists of: + /// 1. Public input: (1 field element from F_r) + /// + /// 2. Polynomial commitments (elliptic curve points over F_q): + /// [a], [b], [c], [d] - state polynomials commitments + /// [z_perm] - copy-permutation grand product commitment + /// [s] - polynomial for lookup argument commitment + /// [z_lookup] - lookup grand product commitment + /// [t_0], [t_1], [t_2], [t_3] - quotient polynomial parts commitments + /// [W], [W'] - proof openings commitments + /// + /// 3. Polynomial evaluations at z and z*omega (field elements from F_r): + /// t(z) - quotient polynomial opening + /// a(z), b(z), c(z), d(z), d(z*omega) - state polynomials openings + /// main_gate_selector(z) - main gate selector opening + /// sigma_0(z), sigma_1(z), sigma_2(z) - permutation polynomials openings + /// z_perm(z*omega) - copy-permutation grand product opening + /// z_lookup(z*omega) - lookup grand product opening + /// lookup_selector(z) - lookup selector opening + /// s(x*omega), t(z*omega), table_type(z) - lookup argument polynomial openings + /// r(z) - linearisation polynomial opening + /// + /// 4. Recursive proof (2 elliptic curve points over F_q) + function loadProof() { + // 1. Load public input + let offset := calldataload(0x04) + let publicInputLengthInWords := calldataload(add(offset, 0x04)) + let isValid := eq(publicInputLengthInWords, 1) // We expect only one public input + mstore(PROOF_PUBLIC_INPUT, and(calldataload(add(offset, 0x24)), FR_MASK)) + + // 2. Load the proof (except for the recursive part) + offset := calldataload(0x24) + let proofLengthInWords := calldataload(add(offset, 0x04)) + isValid := and(eq(proofLengthInWords, 44), isValid) + + // PROOF_STATE_POLYS_0 + { + let x := mod(calldataload(add(offset, 0x024)), Q_MOD) + let y := mod(calldataload(add(offset, 0x044)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_0_X_SLOT, x) + mstore(PROOF_STATE_POLYS_0_Y_SLOT, y) + } + // PROOF_STATE_POLYS_1 + { + let x := mod(calldataload(add(offset, 0x064)), Q_MOD) + let y := mod(calldataload(add(offset, 0x084)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_1_X_SLOT, x) + mstore(PROOF_STATE_POLYS_1_Y_SLOT, y) + } + // PROOF_STATE_POLYS_2 + { + let x := mod(calldataload(add(offset, 0x0a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x0c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_2_X_SLOT, x) + mstore(PROOF_STATE_POLYS_2_Y_SLOT, y) + } + // PROOF_STATE_POLYS_3 + { + let x := mod(calldataload(add(offset, 0x0e4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x104)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_3_X_SLOT, x) + mstore(PROOF_STATE_POLYS_3_Y_SLOT, y) + } + // PROOF_COPY_PERMUTATION_GRAND_PRODUCT + { + let x := mod(calldataload(add(offset, 0x124)), Q_MOD) + let y := mod(calldataload(add(offset, 0x144)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, x) + mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT, y) + } + // PROOF_LOOKUP_S_POLY + { + let x := mod(calldataload(add(offset, 0x164)), Q_MOD) + let y := mod(calldataload(add(offset, 0x184)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_LOOKUP_S_POLY_X_SLOT, x) + mstore(PROOF_LOOKUP_S_POLY_Y_SLOT, y) + } + // PROOF_LOOKUP_GRAND_PRODUCT + { + let x := mod(calldataload(add(offset, 0x1a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x1c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, x) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_0 + { + let x := mod(calldataload(add(offset, 0x1e4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x204)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_1 + { + let x := mod(calldataload(add(offset, 0x224)), Q_MOD) + let y := mod(calldataload(add(offset, 0x244)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_2 + { + let x := mod(calldataload(add(offset, 0x264)), Q_MOD) + let y := mod(calldataload(add(offset, 0x284)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_3 + { + let x := mod(calldataload(add(offset, 0x2a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x2c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT, y) + } + + mstore(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x2e4)), R_MOD)) + mstore(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x304)), R_MOD)) + mstore(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x324)), R_MOD)) + mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x344)), R_MOD)) + + mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x364)), R_MOD)) + mstore(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x384)), R_MOD)) + + mstore(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3a4)), R_MOD)) + mstore(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3c4)), R_MOD)) + mstore(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3e4)), R_MOD)) + + mstore( + PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, + mod(calldataload(add(offset, 0x404)), R_MOD) + ) + mstore(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x424)), R_MOD)) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x444)), R_MOD)) + mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x464)), R_MOD)) + mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x484)), R_MOD)) + mstore(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4a4)), R_MOD)) + mstore(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4c4)), R_MOD)) + mstore(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4e4)), R_MOD)) + mstore(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x504)), R_MOD)) + + // PROOF_OPENING_PROOF_AT_Z + { + let x := mod(calldataload(add(offset, 0x524)), Q_MOD) + let y := mod(calldataload(add(offset, 0x544)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_OPENING_PROOF_AT_Z_X_SLOT, x) + mstore(PROOF_OPENING_PROOF_AT_Z_Y_SLOT, y) + } + // PROOF_OPENING_PROOF_AT_Z_OMEGA + { + let x := mod(calldataload(add(offset, 0x564)), Q_MOD) + let y := mod(calldataload(add(offset, 0x584)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, x) + mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT, y) + } + + // 3. Load the recursive part of the proof + offset := calldataload(0x44) + let recursiveProofLengthInWords := calldataload(add(offset, 0x04)) + isValid := and(eq(recursiveProofLengthInWords, 4), isValid) + // PROOF_RECURSIVE_PART_P1 + { + let x := mod(calldataload(add(offset, 0x024)), Q_MOD) + let y := mod(calldataload(add(offset, 0x044)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_RECURSIVE_PART_P1_X_SLOT, x) + mstore(PROOF_RECURSIVE_PART_P1_Y_SLOT, y) + } + // PROOF_RECURSIVE_PART_P2 + { + let x := mod(calldataload(add(offset, 0x064)), Q_MOD) + let y := mod(calldataload(add(offset, 0x084)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_RECURSIVE_PART_P2_X_SLOT, x) + mstore(PROOF_RECURSIVE_PART_P2_Y_SLOT, y) + } + + // Revert if a proof is not valid + if iszero(isValid) { + revertWithMessage(27, "loadProof: Proof is invalid") + } + } + + /*////////////////////////////////////////////////////////////// + 2. Transcript initialization + //////////////////////////////////////////////////////////////*/ + + /// @notice Recomputes all challenges + /// @dev The process is the following: + /// Commit: PI, [a], [b], [c], [d] + /// Get: eta + /// Commit: [s] + /// Get: beta, gamma + /// Commit: [z_perm] + /// Get: beta', gamma' + /// Commit: [z_lookup] + /// Get: alpha + /// Commit: [t_0], [t_1], [t_2], [t_3] + /// Get: z + /// Commit: t(z), a(z), b(z), c(z), d(z), d(z*omega), + /// main_gate_selector(z), + /// sigma_0(z), sigma_1(z), sigma_2(z), + /// z_perm(z*omega), + /// t(z), lookup_selector(z), table_type(z), + /// s(x*omega), z_lookup(z*omega), t(z*omega), + /// r(z) + /// Get: v + /// Commit: [W], [W'] + /// Get: u + function initializeTranscript() { + // Round 1 + updateTranscript(mload(PROOF_PUBLIC_INPUT)) + updateTranscript(mload(PROOF_STATE_POLYS_0_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_0_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_Y_SLOT)) + + mstore(STATE_ETA_SLOT, getTranscriptChallenge(0)) + + // Round 1.5 + updateTranscript(mload(PROOF_LOOKUP_S_POLY_X_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_S_POLY_Y_SLOT)) + + mstore(STATE_BETA_SLOT, getTranscriptChallenge(1)) + mstore(STATE_GAMMA_SLOT, getTranscriptChallenge(2)) + + // Round 2 + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT)) + + mstore(STATE_BETA_LOOKUP_SLOT, getTranscriptChallenge(3)) + mstore(STATE_GAMMA_LOOKUP_SLOT, getTranscriptChallenge(4)) + + // Round 2.5 + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT)) + + mstore(STATE_ALPHA_SLOT, getTranscriptChallenge(5)) + + // Round 3 + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT)) + + { + let z := getTranscriptChallenge(6) + + mstore(STATE_Z_SLOT, z) + mstore(STATE_Z_IN_DOMAIN_SIZE, modexp(z, DOMAIN_SIZE)) + } + + // Round 4 + updateTranscript(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT)) + + mstore(STATE_V_SLOT, getTranscriptChallenge(7)) + + // Round 5 + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT)) + + mstore(STATE_U_SLOT, getTranscriptChallenge(8)) + } + + /*////////////////////////////////////////////////////////////// + 3. Verifying quotient evaluation + //////////////////////////////////////////////////////////////*/ + + /// @notice Compute linearisation polynomial's constant term: r_0 + /// @dev To save a verifier scalar multiplication, we split linearisation polynomial + /// into its constant and non-constant terms. The constant term is computed with the formula: + /// + /// r_0 = alpha^0 * L_0(z) * PI * q_{main selector}(z) + r(z) -- main gate contribution + /// + /// - alpha^4 * z_perm(z*omega)(sigma_0(z) * beta + gamma + a(z)) \ + /// (sigma_1(z) * beta + gamma + b(z)) | + /// (sigma_2(z) * beta + gamma + c(z)) | - permutation contribution + /// (sigma_3(z) + gamma) | + /// - alpha^5 * L_0(z) / + /// + /// + alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) \ + /// * (z - omega^{n-1}) * z_lookup(z*omega) | - lookup contribution + /// - alpha^7 * L_0(z) | + /// - alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1} / + /// + /// In the end we should check that t(z)*Z_H(z) = r(z) + r_0! + function verifyQuotientEvaluation() { + // Compute power of alpha + { + let alpha := mload(STATE_ALPHA_SLOT) + let currentAlpha := mulmod(alpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_2_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_3_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_4_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_5_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_6_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_7_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_8_SLOT, currentAlpha) + } + + let stateZ := mload(STATE_Z_SLOT) // z + mstore(STATE_L_0_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(0, stateZ)) // L_0(z) + mstore(STATE_L_N_MINUS_ONE_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(sub(DOMAIN_SIZE, 1), stateZ)) // L_{n-1}(z) + let stateT := mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(PROOF_PUBLIC_INPUT), R_MOD) // L_0(z) * PI - Proof memory proof = deserialize_proof(public_inputs, serialized_proof); + // Compute main gate contribution + let result := mulmod(stateT, mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), R_MOD) - return verify(proof, vk); + // Compute permutation contribution + result := addmod(result, permutationQuotientContribution(), R_MOD) + + // Compute lookup contribution + result := addmod(result, lookupQuotientContribution(), R_MOD) + + // Check that r(z) + r_0 = t(z) * Z_H(z) + result := addmod(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), result, R_MOD) + + let vanishing := addmod(mload(STATE_Z_IN_DOMAIN_SIZE), sub(R_MOD, 1), R_MOD) + let lhs := mulmod(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT), vanishing, R_MOD) + if iszero(eq(lhs, result)) { + revertWithMessage(27, "invalid quotient evaluation") + } + } + + /// @notice Evaluating L_{polyNum}(at) out of domain + /// @dev L_i is a Lagrange polynomial for our domain such that: + /// L_i(omega^i) = 1 and L_i(omega^j) = 0 for all j != i + function evaluateLagrangePolyOutOfDomain(polyNum, at) -> res { + let omegaPower := 1 + if polyNum { + omegaPower := modexp(OMEGA, polyNum) + } + + res := addmod(modexp(at, DOMAIN_SIZE), sub(R_MOD, 1), R_MOD) + + // Vanishing polynomial can not be zero at point `at` + if iszero(res) { + revertWithMessage(28, "invalid vanishing polynomial") + } + res := mulmod(res, omegaPower, R_MOD) + let denominator := addmod(at, sub(R_MOD, omegaPower), R_MOD) + denominator := mulmod(denominator, DOMAIN_SIZE, R_MOD) + denominator := modexp(denominator, sub(R_MOD, 2)) + res := mulmod(res, denominator, R_MOD) + } + + /// @notice Compute permutation contribution to linearisation polynomial's constant term + function permutationQuotientContribution() -> res { + // res = alpha^4 * z_perm(z*omega) + res := mulmod( + mload(STATE_POWER_OF_ALPHA_4_SLOT), + mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), + R_MOD + ) + + { + let gamma := mload(STATE_GAMMA_SLOT) + let beta := mload(STATE_BETA_SLOT) + + let factorMultiplier + { + // res *= sigma_0(z) * beta + gamma + a(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + { + // res *= sigma_1(z) * beta + gamma + b(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + { + // res *= sigma_2(z) * beta + gamma + c(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + + // res *= sigma_3(z) + gamma + res := mulmod(res, addmod(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), gamma, R_MOD), R_MOD) + } + + // res = -res + res := sub(R_MOD, res) + + // -= L_0(z) * alpha^5 + let l0AtZ := mload(STATE_L_0_AT_Z_SLOT) + l0AtZ := mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD) + res := addmod(res, sub(R_MOD, l0AtZ), R_MOD) + } + + /// @notice Compute lookup contribution to linearisation polynomial's constant term + function lookupQuotientContribution() -> res { + let betaLookup := mload(STATE_BETA_LOOKUP_SLOT) + let gammaLookup := mload(STATE_GAMMA_LOOKUP_SLOT) + let betaPlusOne := addmod(betaLookup, 1, R_MOD) + let betaGamma := mulmod(betaPlusOne, gammaLookup, R_MOD) + + mstore(STATE_BETA_PLUS_ONE_SLOT, betaPlusOne) + mstore(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT, betaGamma) + + // res = alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) * z_lookup(z*omega) + res := mulmod(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT), betaLookup, R_MOD) + res := addmod(res, betaGamma, R_MOD) + res := mulmod(res, mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD) + res := mulmod(res, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + + // res *= z - omega^{n-1} + { + let lastOmega := modexp(OMEGA, sub(DOMAIN_SIZE, 1)) + let zMinusLastOmega := addmod(mload(STATE_Z_SLOT), sub(R_MOD, lastOmega), R_MOD) + mstore(STATE_Z_MINUS_LAST_OMEGA_SLOT, zMinusLastOmega) + res := mulmod(res, zMinusLastOmega, R_MOD) + } + + // res -= alpha^7 * L_{0}(z) + { + let intermediateValue := mulmod( + mload(STATE_L_0_AT_Z_SLOT), + mload(STATE_POWER_OF_ALPHA_7_SLOT), + R_MOD + ) + res := addmod(res, sub(R_MOD, intermediateValue), R_MOD) + } + + // res -= alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1} + { + let lnMinusOneAtZ := mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT) + let betaGammaPowered := modexp(betaGamma, sub(DOMAIN_SIZE, 1)) + let alphaPower8 := mload(STATE_POWER_OF_ALPHA_8_SLOT) + + let subtrahend := mulmod(mulmod(lnMinusOneAtZ, betaGammaPowered, R_MOD), alphaPower8, R_MOD) + res := addmod(res, sub(R_MOD, subtrahend), R_MOD) + } + } + + /// @notice Compute main gate contribution to linearisation polynomial commitment multiplied by v + function mainGateLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + // += a(z) * [q_a] + pointMulIntoDest(VK_GATE_SETUP_0_X_SLOT, stateOpening0AtZ, dest) + // += b(z) * [q_b] + pointMulAndAddIntoDest(VK_GATE_SETUP_1_X_SLOT, stateOpening1AtZ, dest) + // += c(z) * [q_c] + pointMulAndAddIntoDest(VK_GATE_SETUP_2_X_SLOT, stateOpening2AtZ, dest) + // += d(z) * [q_d] + pointMulAndAddIntoDest(VK_GATE_SETUP_3_X_SLOT, stateOpening3AtZ, dest) + // += a(z) * b(z) * [q_ab] + pointMulAndAddIntoDest(VK_GATE_SETUP_4_X_SLOT, mulmod(stateOpening0AtZ, stateOpening1AtZ, R_MOD), dest) + // += a(z) * c(z) * [q_ac] + pointMulAndAddIntoDest(VK_GATE_SETUP_5_X_SLOT, mulmod(stateOpening0AtZ, stateOpening2AtZ, R_MOD), dest) + // += [q_const] + pointAddAssign(dest, VK_GATE_SETUP_6_X_SLOT) + // += d(z*omega) * [q_{d_next}] + pointMulAndAddIntoDest(VK_GATE_SETUP_7_X_SLOT, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT), dest) + + // *= v * main_gate_selector(z) + let coeff := mulmod(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), mload(STATE_V_SLOT), R_MOD) + pointMulIntoDest(dest, coeff, dest) + } + + /// @notice Compute custom gate contribution to linearisation polynomial commitment multiplied by v + function addAssignRescueCustomGateLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + let accumulator + let intermediateValue + // = alpha * (a(z)^2 - b(z)) + accumulator := mulmod(stateOpening0AtZ, stateOpening0AtZ, R_MOD) + accumulator := addmod(accumulator, sub(R_MOD, stateOpening1AtZ), R_MOD) + accumulator := mulmod(accumulator, mload(STATE_ALPHA_SLOT), R_MOD) + // += alpha^2 * (b(z)^2 - c(z)) + intermediateValue := mulmod(stateOpening1AtZ, stateOpening1AtZ, R_MOD) + intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening2AtZ), R_MOD) + intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_2_SLOT), R_MOD) + accumulator := addmod(accumulator, intermediateValue, R_MOD) + // += alpha^3 * (c(z) * a(z) - d(z)) + intermediateValue := mulmod(stateOpening2AtZ, stateOpening0AtZ, R_MOD) + intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening3AtZ), R_MOD) + intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_3_SLOT), R_MOD) + accumulator := addmod(accumulator, intermediateValue, R_MOD) + + // *= v * [custom_gate_selector] + accumulator := mulmod(accumulator, mload(STATE_V_SLOT), R_MOD) + pointMulAndAddIntoDest(VK_GATE_SELECTORS_1_X_SLOT, accumulator, dest) + } + + /// @notice Compute copy-permutation contribution to linearisation polynomial commitment multiplied by v + function addAssignPermutationLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + // alpha^4 + let factor := mload(STATE_POWER_OF_ALPHA_4_SLOT) + // Calculate the factor + { + // *= (a(z) + beta * z + gamma) + let zMulBeta := mulmod(mload(STATE_Z_SLOT), mload(STATE_BETA_SLOT), R_MOD) + let gamma := mload(STATE_GAMMA_SLOT) + + let intermediateValue := addmod(addmod(zMulBeta, gamma, R_MOD), stateOpening0AtZ, R_MOD) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (b(z) + beta * z * k0 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_0, R_MOD), gamma, R_MOD), + stateOpening1AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (c(z) + beta * z * k1 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_1, R_MOD), gamma, R_MOD), + stateOpening2AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (d(z) + beta * z * k2 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_2, R_MOD), gamma, R_MOD), + stateOpening3AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + } + + // += alpha^5 * L_0(z) + let l0AtZ := mload(STATE_L_0_AT_Z_SLOT) + factor := addmod(factor, mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD), R_MOD) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [z_perm] during computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_perm] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + mstore(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + + // alpha^4 * beta * z_perm(z*omega) + factor := mulmod(mload(STATE_POWER_OF_ALPHA_4_SLOT), mload(STATE_BETA_SLOT), R_MOD) + factor := mulmod(factor, mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD) + { + // *= (a(z) + beta * sigma_0(z) + gamma) + let beta := mload(STATE_BETA_SLOT) + let gamma := mload(STATE_GAMMA_SLOT) + + let intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening0AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // *= (b(z) + beta * sigma_1(z) + gamma) + intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening1AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // *= (c(z) + beta * sigma_2(z) + gamma) + intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening2AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + } + + // *= v * [sigma_3] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + pointMulIntoDest(VK_PERMUTATION_3_X_SLOT, factor, QUERIES_BUFFER_POINT_SLOT) + + pointSubAssign(dest, QUERIES_BUFFER_POINT_SLOT) + } + + /// @notice Compute lookup contribution to linearisation polynomial commitment multiplied by v + function addAssignLookupLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ + ) { + // alpha^6 * v * z_lookup(z*omega) * (z - omega^{n-1}) * [s] + let factor := mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT) + factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD) + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [s] during computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [s] + mstore(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + + // gamma(1 + beta) + t(x) + beta * t(x*omega) + factor := mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT) + factor := mulmod(factor, mload(STATE_BETA_LOOKUP_SLOT), R_MOD) + factor := addmod(factor, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD) + factor := addmod(factor, mload(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT), R_MOD) + + // *= (gamma + f(z)) + // We should use fact that f(x) = + // lookup_selector(x) * (a(x) + eta * b(x) + eta^2 * c(x) + eta^3 * table_type(x)) + // to restore f(z) + let fReconstructed + { + fReconstructed := stateOpening0AtZ + let eta := mload(STATE_ETA_SLOT) + let currentEta := eta + + fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening1AtZ, R_MOD), R_MOD) + currentEta := mulmod(currentEta, eta, R_MOD) + fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening2AtZ, R_MOD), R_MOD) + currentEta := mulmod(currentEta, eta, R_MOD) + + // add type of table + fReconstructed := addmod( + fReconstructed, + mulmod(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT), currentEta, R_MOD), + R_MOD + ) + fReconstructed := mulmod(fReconstructed, mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT), R_MOD) + fReconstructed := addmod(fReconstructed, mload(STATE_GAMMA_LOOKUP_SLOT), R_MOD) + } + // *= -alpha^6 * (beta + 1) * (z - omega^{n-1}) + factor := mulmod(factor, fReconstructed, R_MOD) + factor := mulmod(factor, mload(STATE_BETA_PLUS_ONE_SLOT), R_MOD) + factor := sub(R_MOD, factor) + factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + + factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD) + + // += alpha^7 * L_0(z) + factor := addmod( + factor, + mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_7_SLOT), R_MOD), + R_MOD + ) + + // += alpha^8 * L_{n-1}(z) + factor := addmod( + factor, + mulmod(mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_8_SLOT), R_MOD), + R_MOD + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [z_lookup] during computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_lookup] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + mstore(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + } + + /*////////////////////////////////////////////////////////////// + 4. Prepare queries + //////////////////////////////////////////////////////////////*/ + + /// @dev Here we compute the first and second parts of batched polynomial commitment + /// We use the formula: + /// [D0] = [t_0] + z^n * [t_1] + z^{2n} * [t_2] + z^{3n} * [t_3] + /// and + /// [D1] = main_gate_selector(z) * ( \ + /// a(z) * [q_a] + b(z) * [q_b] + c(z) * [q_c] + d(z) * [q_d] + | - main gate contribution + /// a(z) * b(z) * [q_ab] + a(z) * c(z) * [q_ac] + | + /// [q_const] + d(z*omega) * [q_{d_next}]) / + /// + /// + alpha * [custom_gate_selector] * ( \ + /// (a(z)^2 - b(z)) + | - custom gate contribution + /// (b(z)^2 - c(z)) * alpha + | + /// (a(z)*c(z) - d(z)) * alpha^2 ) / + /// + /// + alpha^4 * [z_perm] * \ + /// (a(z) + beta * z + gamma) * | + /// (b(z) + beta * z * k0 + gamma) * | + /// (c(z) + beta * z * k1 + gamma) * | + /// (d(z) + beta * z * k2 + gamma) | - permutation contribution + /// - alpha^4 * z_perm(z*omega) * beta * [sigma_3] * | + /// (a(z) + beta * sigma_0(z) + gamma) * | + /// (b(z) + beta * sigma_1(z) + gamma) * | + /// (c(z) + beta * sigma_2(z) + gamma) * | + /// + alpha^5 * L_0(z) * [z_perm] / + /// + /// - alpha^6 * (1 + beta') * (gamma' + f(z)) * (z - omega^{n-1}) * \ + /// (gamma'(1 + beta') + t(z) + beta' * t(z*omega)) * [z_lookup] | + /// + alpha^6 * z_lookup(z*omega) * (z - omega^{n-1}) * [s] | - lookup contribution + /// + alpha^7 * L_0(z) * [z_lookup] | + /// + alpha^8 * L_{n-1}(z) * [z_lookup] / + function prepareQueries() { + // Calculate [D0] + { + let zInDomainSize := mload(STATE_Z_IN_DOMAIN_SIZE) + let currentZ := zInDomainSize + + mstore(QUERIES_AT_Z_0_X_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT)) + mstore(QUERIES_AT_Z_0_Y_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT)) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + currentZ := mulmod(currentZ, zInDomainSize, R_MOD) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + currentZ := mulmod(currentZ, zInDomainSize, R_MOD) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + } + + // Calculate v * [D1] + // We are going to multiply all the points in the sum by v to save + // one scalar multiplication during [F] computation + { + let stateOpening0AtZ := mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT) + let stateOpening1AtZ := mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT) + let stateOpening2AtZ := mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT) + let stateOpening3AtZ := mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT) + + mainGateLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignRescueCustomGateLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignPermutationLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignLookupLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ + ) + } + + // Also we should restore [t] for future computations + // [t] = [col_0] + eta*[col_1] + eta^2*[col_2] + eta^3*[col_3] + { + mstore(QUERIES_T_POLY_AGGREGATED_X_SLOT, mload(VK_LOOKUP_TABLE_0_X_SLOT)) + mstore(QUERIES_T_POLY_AGGREGATED_Y_SLOT, mload(VK_LOOKUP_TABLE_0_Y_SLOT)) + + let eta := mload(STATE_ETA_SLOT) + let currentEta := eta + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_1_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + currentEta := mulmod(currentEta, eta, R_MOD) + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_2_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + currentEta := mulmod(currentEta, eta, R_MOD) + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_3_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + } + } + + /*////////////////////////////////////////////////////////////// + 5. Prepare aggregated commitment + //////////////////////////////////////////////////////////////*/ + + /// @dev Here we compute aggregated commitment for the final pairing + /// We use the formula: + /// [E] = ( t(z) + v * r(z) + /// + v^2*a(z) + v^3*b(z) + v^4*c(z) + v^5*d(z) + /// + v^6*main_gate_selector(z) + /// + v^7*sigma_0(z) + v^8*sigma_1(z) + v^9*sigma_2(z) + /// + v^10*t(z) + v^11*lookup_selector(z) + v^12*table_type(z) + /// + u * (v^13*z_perm(z*omega) + v^14*d(z*omega) + /// + v^15*s(z*omega) + v^16*z_lookup(z*omega) + v^17*t(z*omega) + /// ) + /// ) * [1] + /// and + /// [F] = [D0] + v * [D1] + /// + v^2*[a] + v^3*[b] + v^4*[c] + v^5*[d] + /// + v^6*[main_gate_selector] + /// + v^7*[sigma_0] + v^8*[sigma_1] + v^9*[sigma_2] + /// + v^10*[t] + v^11*[lookup_selector] + v^12*[table_type] + /// + u * ( v^13*[z_perm] + v^14*[d] + /// + v^15*[s] + v^16*[z_lookup] + v^17*[t] + /// ) + function prepareAggregatedCommitment() { + // Here we compute parts of [E] and [F] without u multiplier + let aggregationChallenge := 1 + let firstDCoeff + let firstTCoeff + + mstore(AGGREGATED_AT_Z_X_SLOT, mload(QUERIES_AT_Z_0_X_SLOT)) + mstore(AGGREGATED_AT_Z_Y_SLOT, mload(QUERIES_AT_Z_0_Y_SLOT)) + let aggregatedOpeningAtZ := mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT) + { + function updateAggregationChallenge( + queriesCommitmentPoint, + valueAtZ, + curAggregationChallenge, + curAggregatedOpeningAtZ + ) -> newAggregationChallenge, newAggregatedOpeningAtZ { + newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD) + pointMulAndAddIntoDest(queriesCommitmentPoint, newAggregationChallenge, AGGREGATED_AT_Z_X_SLOT) + newAggregatedOpeningAtZ := addmod( + curAggregatedOpeningAtZ, + mulmod(newAggregationChallenge, mload(valueAtZ), R_MOD), + R_MOD + ) + } + + // We don't need to multiply by v, because we have already computed v * [D1] + pointAddIntoDest(AGGREGATED_AT_Z_X_SLOT, QUERIES_AT_Z_1_X_SLOT, AGGREGATED_AT_Z_X_SLOT) + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_0_X_SLOT, + PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_1_X_SLOT, + PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_2_X_SLOT, + PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [d] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [d] + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + firstDCoeff := aggregationChallenge + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_GATE_SELECTORS_0_X_SLOT, + PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_0_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_1_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_2_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [t] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [t] + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + firstTCoeff := aggregationChallenge + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_LOOKUP_SELECTOR_X_SLOT, + PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_LOOKUP_TABLE_TYPE_X_SLOT, + PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + } + mstore(AGGREGATED_OPENING_AT_Z_SLOT, aggregatedOpeningAtZ) + + // Here we compute parts of [E] and [F] with u multiplier + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + + let copyPermutationCoeff := addmod( + mload(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF), + mulmod(aggregationChallenge, mload(STATE_U_SLOT), R_MOD), + R_MOD + ) + + pointMulIntoDest( + PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, + copyPermutationCoeff, + AGGREGATED_AT_Z_OMEGA_X_SLOT + ) + let aggregatedOpeningAtZOmega := mulmod( + mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), + aggregationChallenge, + R_MOD + ) + + { + function updateAggregationChallenge( + queriesCommitmentPoint, + valueAtZ_Omega, + previousCoeff, + curAggregationChallenge, + curAggregatedOpeningAtZ_Omega + ) -> newAggregationChallenge, newAggregatedOpeningAtZ_Omega { + newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD) + let finalCoeff := addmod( + previousCoeff, + mulmod(newAggregationChallenge, mload(STATE_U_SLOT), R_MOD), + R_MOD + ) + pointMulAndAddIntoDest(queriesCommitmentPoint, finalCoeff, AGGREGATED_AT_Z_OMEGA_X_SLOT) + newAggregatedOpeningAtZ_Omega := addmod( + curAggregatedOpeningAtZ_Omega, + mulmod(newAggregationChallenge, mload(valueAtZ_Omega), R_MOD), + R_MOD + ) + } + + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_STATE_POLYS_3_X_SLOT, + PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, + firstDCoeff, + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_LOOKUP_S_POLY_X_SLOT, + PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, + mload(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF), + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, + PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, + mload(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF), + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + QUERIES_T_POLY_AGGREGATED_X_SLOT, + PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, + firstTCoeff, + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + } + mstore(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT, aggregatedOpeningAtZOmega) + + // Now we can merge both parts and get [E] and [F] + let u := mload(STATE_U_SLOT) + + // [F] + pointAddIntoDest( + AGGREGATED_AT_Z_X_SLOT, + AGGREGATED_AT_Z_OMEGA_X_SLOT, + PAIRING_PAIR_WITH_GENERATOR_X_SLOT + ) + + // [E] = (aggregatedOpeningAtZ + u * aggregatedOpeningAtZOmega) * [1] + let aggregatedValue := addmod( + mulmod(mload(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT), u, R_MOD), + mload(AGGREGATED_OPENING_AT_Z_SLOT), + R_MOD + ) + + mstore(PAIRING_BUFFER_POINT_X_SLOT, 1) + mstore(PAIRING_BUFFER_POINT_Y_SLOT, 2) + pointMulIntoDest(PAIRING_BUFFER_POINT_X_SLOT, aggregatedValue, PAIRING_BUFFER_POINT_X_SLOT) + } + + /*////////////////////////////////////////////////////////////// + 5. Pairing + //////////////////////////////////////////////////////////////*/ + + /// @notice Checks the final pairing + /// @dev We should check the equation: + /// e([W] + u * [W'], [x]_2) = e(z * [W] + u * z * omega * [W'] + [F] - [E], [1]_2), + /// where [F] and [E] were computed previously + /// + /// Also we need to check that e([P1], [x]_2) = e([P2], [1]_2) + /// where [P1] and [P2] are parts of the recursive proof + /// + /// We can aggregate both pairings into one for gas optimization: + /// e([W] + u * [W'] + u^2 * [P1], [x]_2) = + /// e(z * [W] + u * z * omega * [W'] + [F] - [E] + u^2 * [P2], [1]_2) + /// + /// u is a valid challenge for such aggregation, + /// because [P1] and [P2] are used in PI + function finalPairing() { + let u := mload(STATE_U_SLOT) + let z := mload(STATE_Z_SLOT) + let zOmega := mulmod(mload(STATE_Z_SLOT), OMEGA, R_MOD) + + // [F] - [E] + pointSubAssign(PAIRING_PAIR_WITH_GENERATOR_X_SLOT, PAIRING_BUFFER_POINT_X_SLOT) + + // +z * [W] + u * z * omega * [W'] + pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_X_SLOT, z, PAIRING_PAIR_WITH_GENERATOR_X_SLOT) + pointMulAndAddIntoDest( + PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, + mulmod(zOmega, u, R_MOD), + PAIRING_PAIR_WITH_GENERATOR_X_SLOT + ) + + // [W] + u * [W'] + mstore(PAIRING_PAIR_WITH_X_X_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT)) + mstore(PAIRING_PAIR_WITH_X_Y_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT)) + pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, u, PAIRING_PAIR_WITH_X_X_SLOT) + pointNegate(PAIRING_PAIR_WITH_X_X_SLOT) + + // Add recursive proof part + let uu := mulmod(u, u, R_MOD) + pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P1_X_SLOT, uu, PAIRING_PAIR_WITH_GENERATOR_X_SLOT) + pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P2_X_SLOT, uu, PAIRING_PAIR_WITH_X_X_SLOT) + + // Calculate pairing + { + mstore(0x000, mload(PAIRING_PAIR_WITH_GENERATOR_X_SLOT)) + mstore(0x020, mload(PAIRING_PAIR_WITH_GENERATOR_Y_SLOT)) + + mstore(0x040, G2_ELEMENTS_0_X1) + mstore(0x060, G2_ELEMENTS_0_X2) + mstore(0x080, G2_ELEMENTS_0_Y1) + mstore(0x0a0, G2_ELEMENTS_0_Y2) + + mstore(0x0c0, mload(PAIRING_PAIR_WITH_X_X_SLOT)) + mstore(0x0e0, mload(PAIRING_PAIR_WITH_X_Y_SLOT)) + + mstore(0x100, G2_ELEMENTS_1_X1) + mstore(0x120, G2_ELEMENTS_1_X2) + mstore(0x140, G2_ELEMENTS_1_Y1) + mstore(0x160, G2_ELEMENTS_1_Y2) + + let success := staticcall(gas(), 8, 0, 0x180, 0x00, 0x20) + if iszero(success) { + revertWithMessage(32, "finalPairing: precompile failure") + } + if iszero(mload(0)) { + revertWithMessage(29, "finalPairing: pairing failure") + } + } + } + + /*////////////////////////////////////////////////////////////// + Verification + //////////////////////////////////////////////////////////////*/ + + // Step 1: Load the proof and check the correctness of its parts + loadProof() + + // Step 2: Recompute all the challenges with the transcript + initializeTranscript() + + // Step 3: Check the quotient equality + verifyQuotientEvaluation() + + // Step 4: Compute queries [D0] and v * [D1] + prepareQueries() + + // Step 5: Compute [E] and [F] + prepareAggregatedCommitment() + + // Step 6: Check the final pairing with aggregated recursive proof + finalPairing() + + mstore(0, true) + return(0, 32) + } } } diff --git a/ethereum/contracts/zksync/facets/Executor.sol b/ethereum/contracts/zksync/facets/Executor.sol index 729c3d53b..31acc985b 100644 --- a/ethereum/contracts/zksync/facets/Executor.sol +++ b/ethereum/contracts/zksync/facets/Executor.sol @@ -5,7 +5,6 @@ pragma solidity ^0.8.13; import {Base} from "./Base.sol"; import {COMMIT_TIMESTAMP_NOT_OLDER, COMMIT_TIMESTAMP_APPROXIMATION_DELTA, EMPTY_STRING_KECCAK, L2_TO_L1_LOG_SERIALIZE_SIZE, INPUT_MASK, MAX_INITIAL_STORAGE_CHANGES_COMMITMENT_BYTES, MAX_REPEATED_STORAGE_CHANGES_COMMITMENT_BYTES, MAX_L2_TO_L1_LOGS_COMMITMENT_BYTES, PACKED_L2_BLOCK_TIMESTAMP_MASK} from "../Config.sol"; import {IExecutor} from "../interfaces/IExecutor.sol"; -import {PairingsBn254} from "../libraries/PairingsBn254.sol"; import {PriorityQueue, PriorityOperation} from "../libraries/PriorityQueue.sol"; import {UncheckedMath} from "../../common/libraries/UncheckedMath.sol"; import {UnsafeBytes} from "../../common/libraries/UnsafeBytes.sol"; @@ -353,21 +352,20 @@ contract ExecutorFacet is Base, IExecutor { // We allow skipping the zkp verification for the test(net) environment // If the proof is not empty, verify it, otherwise, skip the verification if (_proof.serializedProof.length > 0) { - // TODO: We keep the code duplication here to NOT to invalidate the audit, refactor it before the next audit. (SMA-1631) - bool successVerifyProof = s.verifier.verify_serialized_proof(proofPublicInput, _proof.serializedProof); + bool successVerifyProof = s.verifier.verify( + proofPublicInput, + _proof.serializedProof, + _proof.recursiveAggregationInput + ); require(successVerifyProof, "p"); // Proof verification fail - - // Verify the recursive part that was given to us through the public input - bool successProofAggregation = _verifyRecursivePartOfProof(_proof.recursiveAggregationInput); - require(successProofAggregation, "hh"); // Proof aggregation must be valid } // #else - bool successVerifyProof = s.verifier.verify_serialized_proof(proofPublicInput, _proof.serializedProof); + bool successVerifyProof = s.verifier.verify( + proofPublicInput, + _proof.serializedProof, + _proof.recursiveAggregationInput + ); require(successVerifyProof, "p"); // Proof verification fail - - // Verify the recursive part that was given to us through the public input - bool successProofAggregation = _verifyRecursivePartOfProof(_proof.recursiveAggregationInput); - require(successProofAggregation, "hh"); // Proof aggregation must be valid // #endif emit BlocksVerification(s.totalBlocksVerified, currentTotalBlocksVerified); @@ -396,43 +394,6 @@ contract ExecutorFacet is Base, IExecutor { ) & INPUT_MASK; } - /// @dev Verify a part of the zkp, that is responsible for the aggregation - function _verifyRecursivePartOfProof(uint256[] calldata _recursiveAggregationInput) internal view returns (bool) { - require(_recursiveAggregationInput.length == 4, "vr"); - - PairingsBn254.G1Point memory pairWithGen = PairingsBn254.new_g1_checked( - _recursiveAggregationInput[0], - _recursiveAggregationInput[1] - ); - PairingsBn254.G1Point memory pairWithX = PairingsBn254.new_g1_checked( - _recursiveAggregationInput[2], - _recursiveAggregationInput[3] - ); - - PairingsBn254.G2Point memory g2Gen = PairingsBn254.new_g2( - [ - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ], - [ - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ] - ); - PairingsBn254.G2Point memory g2X = PairingsBn254.new_g2( - [ - 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1, - 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0 - ], - [ - 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4, - 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55 - ] - ); - - return PairingsBn254.pairingProd2(pairWithGen, g2Gen, pairWithX, g2X); - } - /// @notice Reverts unexecuted blocks /// @param _newLastBlock block number after which blocks should be reverted /// NOTE: Doesn't delete the stored data about blocks, but only decreases diff --git a/ethereum/contracts/zksync/interfaces/IVerifier.sol b/ethereum/contracts/zksync/interfaces/IVerifier.sol new file mode 100644 index 000000000..83fb06615 --- /dev/null +++ b/ethereum/contracts/zksync/interfaces/IVerifier.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +interface IVerifier { + function verify( + uint256[] calldata _publicInputs, + uint256[] calldata _proof, + uint256[] calldata _recursiveAggregationInput + ) external view returns (bool); + + function verificationKeyHash() external pure returns (bytes32); +} diff --git a/ethereum/contracts/zksync/libraries/PairingsBn254.sol b/ethereum/contracts/zksync/libraries/PairingsBn254.sol deleted file mode 100644 index 02533c3ba..000000000 --- a/ethereum/contracts/zksync/libraries/PairingsBn254.sol +++ /dev/null @@ -1,275 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -library PairingsBn254 { - uint256 constant q_mod = 21888242871839275222246405745257275088696311157297823662689037894645226208583; - uint256 constant r_mod = 21888242871839275222246405745257275088548364400416034343698204186575808495617; - uint256 constant bn254_b_coeff = 3; - - struct G1Point { - uint256 X; - uint256 Y; - } - - struct Fr { - uint256 value; - } - - function new_fr(uint256 fr) internal pure returns (Fr memory) { - require(fr < r_mod); - return Fr({value: fr}); - } - - function copy(Fr memory self) internal pure returns (Fr memory n) { - n.value = self.value; - } - - function assign(Fr memory self, Fr memory other) internal pure { - self.value = other.value; - } - - function inverse(Fr memory fr) internal view returns (Fr memory) { - require(fr.value != 0); - return pow(fr, r_mod - 2); - } - - function add_assign(Fr memory self, Fr memory other) internal pure { - self.value = addmod(self.value, other.value, r_mod); - } - - function sub_assign(Fr memory self, Fr memory other) internal pure { - self.value = addmod(self.value, r_mod - other.value, r_mod); - } - - function mul_assign(Fr memory self, Fr memory other) internal pure { - self.value = mulmod(self.value, other.value, r_mod); - } - - function pow(Fr memory self, uint256 power) internal view returns (Fr memory) { - uint256[6] memory input = [32, 32, 32, self.value, power, r_mod]; - uint256[1] memory result; - bool success; - assembly { - success := staticcall(gas(), 0x05, input, 0xc0, result, 0x20) - } - require(success); - return Fr({value: result[0]}); - } - - // Encoding of field elements is: X[0] * z + X[1] - struct G2Point { - uint256[2] X; - uint256[2] Y; - } - - function P1() internal pure returns (G1Point memory) { - return G1Point(1, 2); - } - - function new_g1(uint256 x, uint256 y) internal pure returns (G1Point memory) { - return G1Point(x, y); - } - - // function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) { - function new_g1_checked(uint256 x, uint256 y) internal pure returns (G1Point memory) { - if (x == 0 && y == 0) { - // point of infinity is (0,0) - return G1Point(x, y); - } - - // check encoding - require(x < q_mod, "x axis isn't valid"); - require(y < q_mod, "y axis isn't valid"); - // check on curve - uint256 lhs = mulmod(y, y, q_mod); // y^2 - - uint256 rhs = mulmod(x, x, q_mod); // x^2 - rhs = mulmod(rhs, x, q_mod); // x^3 - rhs = addmod(rhs, bn254_b_coeff, q_mod); // x^3 + b - require(lhs == rhs, "is not on curve"); - - return G1Point(x, y); - } - - function new_g2(uint256[2] memory x, uint256[2] memory y) internal pure returns (G2Point memory) { - return G2Point(x, y); - } - - function copy_g1(G1Point memory self) internal pure returns (G1Point memory result) { - result.X = self.X; - result.Y = self.Y; - } - - function P2() internal pure returns (G2Point memory) { - // for some reason ethereum expects to have c1*v + c0 form - - return - G2Point( - [ - 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2, - 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed - ], - [ - 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b, - 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa - ] - ); - } - - function negate(G1Point memory self) internal pure { - // The prime q in the base field F_q for G1 - if (self.Y == 0) { - require(self.X == 0); - return; - } - - self.Y = q_mod - self.Y; - } - - function point_add(G1Point memory p1, G1Point memory p2) internal view returns (G1Point memory r) { - point_add_into_dest(p1, p2, r); - return r; - } - - function point_add_assign(G1Point memory p1, G1Point memory p2) internal view { - point_add_into_dest(p1, p2, p1); - } - - function point_add_into_dest( - G1Point memory p1, - G1Point memory p2, - G1Point memory dest - ) internal view { - if (p2.X == 0 && p2.Y == 0) { - // we add zero, nothing happens - dest.X = p1.X; - dest.Y = p1.Y; - return; - } else if (p1.X == 0 && p1.Y == 0) { - // we add into zero, and we add non-zero point - dest.X = p2.X; - dest.Y = p2.Y; - return; - } else { - uint256[4] memory input; - - input[0] = p1.X; - input[1] = p1.Y; - input[2] = p2.X; - input[3] = p2.Y; - - bool success; - assembly { - success := staticcall(gas(), 6, input, 0x80, dest, 0x40) - } - require(success); - } - } - - function point_sub_assign(G1Point memory p1, G1Point memory p2) internal view { - point_sub_into_dest(p1, p2, p1); - } - - function point_sub_into_dest( - G1Point memory p1, - G1Point memory p2, - G1Point memory dest - ) internal view { - if (p2.X == 0 && p2.Y == 0) { - // we subtracted zero, nothing happens - dest.X = p1.X; - dest.Y = p1.Y; - return; - } else if (p1.X == 0 && p1.Y == 0) { - // we subtract from zero, and we subtract non-zero point - dest.X = p2.X; - dest.Y = q_mod - p2.Y; - return; - } else { - uint256[4] memory input; - - input[0] = p1.X; - input[1] = p1.Y; - input[2] = p2.X; - input[3] = q_mod - p2.Y; - - bool success = false; - assembly { - success := staticcall(gas(), 6, input, 0x80, dest, 0x40) - } - require(success); - } - } - - function point_mul(G1Point memory p, Fr memory s) internal view returns (G1Point memory r) { - // https://eips.ethereum.org/EIPS/eip-197 - // Elliptic curve points are encoded as a Jacobian pair (X, Y) where the point at infinity is encoded as (0, 0) - if (p.X == 0 && p.Y == 1) { - p.Y = 0; - } - point_mul_into_dest(p, s, r); - return r; - } - - function point_mul_assign(G1Point memory p, Fr memory s) internal view { - point_mul_into_dest(p, s, p); - } - - function point_mul_into_dest( - G1Point memory p, - Fr memory s, - G1Point memory dest - ) internal view { - uint256[3] memory input; - input[0] = p.X; - input[1] = p.Y; - input[2] = s.value; - bool success; - assembly { - success := staticcall(gas(), 7, input, 0x60, dest, 0x40) - } - require(success); - } - - function pairing(G1Point[] memory p1, G2Point[] memory p2) internal view returns (bool) { - require(p1.length == p2.length); - uint256 elements = p1.length; - uint256 inputSize = elements * 6; - uint256[] memory input = new uint256[](inputSize); - for (uint256 i = 0; i < elements; ) { - input[i * 6 + 0] = p1[i].X; - input[i * 6 + 1] = p1[i].Y; - input[i * 6 + 2] = p2[i].X[0]; - input[i * 6 + 3] = p2[i].X[1]; - input[i * 6 + 4] = p2[i].Y[0]; - input[i * 6 + 5] = p2[i].Y[1]; - unchecked { - ++i; - } - } - uint256[1] memory out; - bool success; - assembly { - success := staticcall(gas(), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) - } - require(success); - return out[0] != 0; - } - - /// Convenience method for a pairing check for two pairs. - function pairingProd2( - G1Point memory a1, - G2Point memory a2, - G1Point memory b1, - G2Point memory b2 - ) internal view returns (bool) { - G1Point[] memory p1 = new G1Point[](2); - G2Point[] memory p2 = new G2Point[](2); - p1[0] = a1; - p1[1] = b1; - p2[0] = a2; - p2[1] = b2; - return pairing(p1, p2); - } -} diff --git a/ethereum/contracts/zksync/libraries/TranscriptLib.sol b/ethereum/contracts/zksync/libraries/TranscriptLib.sol deleted file mode 100644 index e08f74044..000000000 --- a/ethereum/contracts/zksync/libraries/TranscriptLib.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.8.13; - -import "./PairingsBn254.sol"; - -library TranscriptLib { - // flip 0xe000000000000000000000000000000000000000000000000000000000000000; - uint256 constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; - - uint32 constant DST_0 = 0; - uint32 constant DST_1 = 1; - uint32 constant DST_CHALLENGE = 2; - - struct Transcript { - bytes32 state_0; - bytes32 state_1; - uint32 challenge_counter; - } - - function new_transcript() internal pure returns (Transcript memory t) { - t.state_0 = bytes32(0); - t.state_1 = bytes32(0); - t.challenge_counter = 0; - } - - function update_with_u256(Transcript memory self, uint256 value) internal pure { - bytes32 old_state_0 = self.state_0; - self.state_0 = keccak256(abi.encodePacked(DST_0, old_state_0, self.state_1, value)); - self.state_1 = keccak256(abi.encodePacked(DST_1, old_state_0, self.state_1, value)); - } - - function update_with_fr(Transcript memory self, PairingsBn254.Fr memory value) internal pure { - update_with_u256(self, value.value); - } - - function update_with_g1(Transcript memory self, PairingsBn254.G1Point memory p) internal pure { - update_with_u256(self, p.X); - update_with_u256(self, p.Y); - } - - function get_challenge(Transcript memory self) internal pure returns (PairingsBn254.Fr memory challenge) { - bytes32 query = keccak256(abi.encodePacked(DST_CHALLENGE, self.state_0, self.state_1, self.challenge_counter)); - self.challenge_counter += 1; - challenge = PairingsBn254.Fr({value: uint256(query) & FR_MASK}); - } -} diff --git a/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit3.sol b/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit3.sol index c4705b789..3f1b7445d 100644 --- a/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit3.sol +++ b/ethereum/contracts/zksync/upgrade-initializers/DiamondUpgradeInit3.sol @@ -43,7 +43,7 @@ contract DiamondUpgradeInit3 is Base { function upgrade( uint256 _priorityTxMaxGasLimit, IAllowList _allowList, - Verifier _verifier + IVerifier _verifier ) external payable returns (bytes32) { // Zero out the deprecated storage slots delete s.__DEPRECATED_diamondCutStorage; diff --git a/ethereum/test/unit_tests/verifier.spec.ts b/ethereum/test/unit_tests/verifier.spec.ts index 549d57d36..71859681e 100644 --- a/ethereum/test/unit_tests/verifier.spec.ts +++ b/ethereum/test/unit_tests/verifier.spec.ts @@ -1,75 +1,227 @@ -// import * as hardhat from 'hardhat'; -// import { expect } from 'chai'; -// import { Verifier, VerifierFactory } from '../../typechain'; -// -// describe('Verifier tests', function () { -// const PROOF = { -// public_inputs: ['0x143e5a9ae11ce7c212304dd3f434ac8dff25332ad8adfc4203d95c7b4aea2f'], -// serialized_proof: [ -// '0x010f11270a0ddb00a666e1c6532065905092112e5fad0d1c9b615f9f3065c8dd', -// '0x014f93fc29fed9372193fc9535150c8d4ebe2b49fad387fc230344d16149a9bb', -// '0x23a4dc9d7f679cd15923c6dfc0e8e457edc0a3862a824e5aa4702ceabda0a047', -// '0x948cb016bb21c4dd9c8cf107ddd4ddbc308eff7743c019d680fb4d8587d05a', -// '0x133b7e7e63f70910acce3feaf44d89cf893d410b0880f87ff308ef44c5aee773', -// '0x24878e90e224cb4246492115e94e79809dce757ca7a4decbcff6092ea5ce510a', -// '0x2e172ed1fb272478ca968965488da0b5c18d7fce9241f4a8ad295f01ba8478bd', -// '0x108bbbc2153a50515d9d8b2a5757a4b4d62efd3fd425da9d8b9a1b2f795421ea', -// '0x21ebacd434b555e1a82af0e0535f6aa47c9b38006a10f79f0fe8a52df474c51c', -// '0x1c2c8ca9750ed300dbc06ade43e69acbb614592a305c8e7ef4bf8aee50865fc8', -// '0x9e1b717a35bb31ab11d7ecd5802f7e598ca7918f79d575f17921d6681f7e79', -// '0x038209dfd674204198de3d291ca47b4212dfd4d6a20a2b360b1a6138623094ac', -// '0x1036e836c58c78400fd7b30e337e26d765b19d51d3d3447a01c0a003306b7efc', -// '0x1e7f110f8601fe84c79bb7a6c75cbd8cb92658ef563245280b9457c2a69139', -// '0x01cc91f946efb91a4e14f2d0790f73d6ba647aa414bc2e6fddd800d925371d82', -// '0x04e93b5790dd468d77df0b9b8bea80dc2ebe525412db5e724510e2feb7cad955', -// '0x06ebd8b3c96266a6fb9ff923431147bef97d701d1a2333bbf91cbfdb276080f2', -// '0x2d5dab85db469066124fb7706b9c710773c05d8c360efc1c31992e336762c7d7', -// '0x114f8bf9a2050f2763affa8d7213c22adbcf01e42e785d3441ffea118888243d', -// '0x2692422d6933cdf24274fd29cef859856cc169e5d2e72b31deb620001cf3b940', -// '0x2ca0abed52054af163f83d0d6fabf082579d20bbb734eb9bf4e33a28cd8b4bdd', -// '0x0a44d037425f05a738e6ebc2e44127c05d6a6564a9091bab3407c223dbd2900f', -// '0x2e74025e8f7b63473b2639e8e876c04ce28907892ce6f56d976f16a9d5d3d314', -// '0x0caa73604a585da384d06686f4cc7b01f4cdc9627ff9b32b954048da29538f08', -// '0x0a79a1acfe5cc1064672fe805b8efa3b3730dfa6558a17f323ad94bc47ab6d4a', -// '0x23922b78fa043a80c9cfdae2907a050804e7821e71d8fdcabeaece0d47eba0ad', -// '0x27394d6a7c23dff26d46eb9a0a4ded65f663359db6dfaf5d020bbdf4defd4a', -// '0x21be59cd244bd991ddf098d632c68a61a1f5834567906aa6129aa1e38d269f6c', -// '0x0fb1ba84318af85e01302d8610a3810aea901f783e4f38cf01130f4774569079', -// '0x26fdabdf196e08cbb15d242b74604fdf5466e34ac457f7776b166f9e74d4d09b', -// '0x15aae20ac54fb73ac5f19f5e8d8234bc619c2f93e14894e53e3a39059e9904d4', -// '0x23cee98e838bc6f77a12ceb104f87f6a72974827e8a64698e18c3485a4e09544', -// '0x1a0a8d84b5833271ad69ee59116ba97d1dd86efa77f75486a049195b47c499bd', -// '0x1fe6778aa9d3d435ad39d30564ecf51751db67f12669fa1bdae3f84c163f75c7', -// '0x0feb723ac8ddd1a21c6adaedc36c86d1f6d5177183afa8d288ba699bac17b1c5', -// '0x02bdd5679a965f57d8e1f71306c0adb67ccf46163b4538af86aa49d8d917590f', -// '0x1ce9caaed894da866c8a904161f4490c4381e073dbc1959cff9fbdc5ad8395a2', -// '0x303af8ffd4f4c8accf6bec9c91bb29ab90a334291134aaa3fcc392f156fc8bc2', -// '0x0902d7167e48f2a66924d91098ce44aaf35c6c45df42ab66fea90bb4935e26e8', -// '0x15fb71a86f46171b9fb5e77f2789b663643450e0ecc4a321089518f21f0838f7', -// '0x0b1a21e343b967eed4b93b0764708d8ec57597848658bf8777dff4541faf0bf2', -// '0x20117d30650de2bb1812f77f853ae99e5de5ff48587f5e4277061ad19bfcbd30', -// '0x0e4d544ce4205b02bf74dd6e2dd6d132a3dd678a09fef6f98f3917b04bf1583e', -// '0x2673b5373e44ec861370732e2d2a8eeb2b719e12f4d7e085c2ee7bfdc4e9475f' -// ] -// }; -// let verifier: Verifier; -// -// before(async function () { -// const verifierFactory = await hardhat.ethers.getContractFactory('Verifier'); -// const verifierContract = await verifierFactory.deploy(); -// verifier = VerifierFactory.connect(verifierContract.address, verifierContract.signer); -// }); -// -// it('Should verify proof', async () => { -// // Call the verifier directly (though the call, not static call) to add the save the consumed gas into the statistic. -// const calldata = verifier.interface.encodeFunctionData('verify_serialized_proof', [ -// PROOF.public_inputs, -// PROOF.serialized_proof -// ]); -// await verifier.fallback({ data: calldata }); -// -// // Check that proof is verified -// let result = await verifier.verify_serialized_proof(PROOF.public_inputs, PROOF.serialized_proof); -// expect(result, 'proof verification failed').true; -// }); -// }); +import * as hardhat from 'hardhat'; +import { expect } from 'chai'; +import { VerifierTest, VerifierTestFactory } from '../../typechain'; +import { getCallRevertReason } from './utils'; +import { ethers } from 'hardhat'; + +describe('Verifier test', function () { + const Q_MOD = '21888242871839275222246405745257275088696311157297823662689037894645226208583'; + const R_MOD = '21888242871839275222246405745257275088548364400416034343698204186575808495617'; + + const PROOF = { + publicInputs: ['0x00461afd95c6bd5a38a01a995f5c292d19a816a139bbc78fc23321c3b8da6243'], + serializedProof: [ + '0x2b80ef6480b0c1a4ab9ccac1b1f5549d8d0e875e45f445599de5e1a88c3ccf25', + '0x173e23b955ea8f1972358bbeae3539d96e60494032faf3ada36fb3660f45d752', + '0x0579422893e75ebcf9ebfefd6bf80513bee55e16f0971779d774cca3227c11a3', + '0x257c35d228de381fa897042758ef80e4f29c84e8851878d12bae17d7700059e5', + '0x11cb7bc2927e1ffd32b7c0bf9b75e7f3f2915c33ca525bbb91a39d5ba9d050d1', + '0x0b396e2027a7e5cbffb8ef303560420c2ec2c25df1325b037208f61679596021', + '0x1d6feb9bfaf92d370a8041b1669fc901ac083c6f09d815df8e57e3bc0af529c6', + '0x1dd56a14ac384b74aab66e11dfeb36242a3d3c83c7fc11beed1ebb2d4b921aa3', + '0x07158e6a51b6354ab3355f298d5cc24948bddd48b0715eff52e0f135936536fc', + '0x18969b22583c701ef304d793e22d11a56ca9e5b08c20cd877b4fb142dfab852f', + '0x0c49d474877b03b231cb8aeb592728c93f6b5b62e357a4a77c7dd2818181fc43', + '0x186e08d590ce9937d193189a0c74890237df96ebc6593dc55b988eae74b9ea44', + '0x180772b6ef5bd078663a3ba77c3c997b0f9d6a62664a9aa35be4acfe5fd52acb', + '0x01e19ccd1fa25da95ce7799c6946a64eb12b04bb59fb31b0f48346e844ee06bb', + '0x0a991aee2dfdea382dd4ed65083c15004d812dcc6017aed812360c1a750f6994', + '0x2eba4d12e899bd433bc277127d3bb98997ea4953aa092705e185971c5bf95057', + '0x16ebb143325b1da3c88baf9f69a6911962c89cc34f364cb62f0db35e645baaa3', + '0x10a1806face2c2906455ac9060155bd648eb18f30a73f0d8214ef75683a2f015', + '0x2f153ebf44a9ebe05033a085c9c5a20ef002437420badd9723b59d9d9fed7666', + '0x054da7edbb7dd64940f64d5a46e6d2b70f8d16496657acf01d1bff905e70fe34', + '0x11a54b951c5f0120c00d6c0ad6b188f21c3d2b955ebea2578926eaf7b0607a34', + '0x2b5266f06d505e753e8ca5b9a4718f060ed1386313ef9c78b79f7f0474b3ecfc', + '0x202b9746f651068481021d43598dafcd8aa5e1c662de5baf24507cf8483e517f', + '0x0e4c150798976c5dbf261b2f50d43e2ae145eec6d63d361b79abdf5a875c7312', + '0x0d78beaef934700a7a3f63cc94f8ff11f056b770fc7f2e72f6cf2b7b29fb2298', + '0x26d892a58479bb3a147a7bfd8488ab1e6d97a89b647c886ace6d072134be3474', + '0x22ee472ea71eb002d8e3b35f93825ef831ab6d321eccc62ae4a1230449f05316', + '0x18b8f397a1a1db84ce0985252007c532c7d6f0454ef88a446180d6ab3b348321', + '0x0cbecff5b91f1da7dd1d440f7dd8c48726d7edd5cd119c8f2603fbfba03acd59', + '0x1f73e67e371a989ef56adc605ce4be99fb1a1200cdc9f15e1cbd9c825a400ed7', + '0x028667567deeadd469936a07962ba1c7215df0b9d27836cb1160088fc9e44b4c', + '0x17d4f2ed4b820a8222d2b839035ef0c26ee5ec8e8d2d1a7c16486e54240455cd', + '0x07a3089dc75c8035530c84d5067f481d42d2a095e9a8bb839c20909b5c978fcc', + '0x091c2be5555c05bb87116b667992af159e4ad0616c0ec7335570e26c6e627531', + '0x03c5e763840a185dbc363ed770645d8a0fef39736741848f12d90c3027d3fbfd', + '0x1f6e675ad9dd1cb9f92086111c47511f510e27c3632527d56c48be1c7b8a03e2', + '0x23aa0ab9bfb0e38ff029ba5a4cc6f4b8a1dde5b54b1db7435e22c9048ffa7029', + '0x19a6d569cc94a65fa3685ea1144db7415ceb1cabb11e267c35097dea637536d9', + '0x04dc0a7c7669340261725af51e4c32eb7f8968b163e70f0beccdf20bd7f771c1', + '0x1bf9dd4999e0e82da492c292fbb8287bcccd0cb3cd2f1de14f8b4a1592786715', + '0x257c2aa02452019ea981bc722f0777552be886772eea9a3bdf3257a1e3b75954', + '0x01b4dc62f39bdb3596ff653b6035e5fb17d278466ba4621a632962a7299523f1', + '0x0df615b627d9dd8e0d4d7f96c7e30f34d0cbda04c761c191d81cac19de41ccbd', + '0x1c22d1d281177a86617454edf488d6bb18c6a60222be2121091f4b18d4f5be92' + ], + recursiveAggregationInput: [ + '0x04fdf01a2faedb9e3a620bc1cd8ceb4b0adac04631bdfa9e7e9fc15e35693cc0', + '0x1419728b438cc9afa63ab4861753e0798e29e08aac0da17b2c7617b994626ca2', + '0x23ca418458f6bdc30dfdbc13b80c604f8864619582eb247d09c8e4703232897b', + '0x0713c1371914ac18d7dced467a8a60eeca0f3d80a2cbd5dcc75abb6cbab39f39' + ] + }; + let verifier: VerifierTest; + + before(async function () { + const verifierFactory = await hardhat.ethers.getContractFactory('VerifierTest'); + const verifierContract = await verifierFactory.deploy(); + verifier = VerifierTestFactory.connect(verifierContract.address, verifierContract.signer); + }); + + it('Should verify proof', async () => { + // Call the verifier directly (though the call, not static call) to add the save the consumed gas into the statistic. + const calldata = verifier.interface.encodeFunctionData('verify', [ + PROOF.publicInputs, + PROOF.serializedProof, + PROOF.recursiveAggregationInput + ]); + await verifier.fallback({ data: calldata }); + + // Check that proof is verified + let result = await verifier.verify(PROOF.publicInputs, PROOF.serializedProof, PROOF.recursiveAggregationInput); + expect(result, 'proof verification failed').true; + }); + + describe('Should verify valid proof with fields values in non standard format', function () { + it('Public input with dirty bits over Fr mask', async () => { + let validProof = JSON.parse(JSON.stringify(PROOF)); + // Fill dirty bits + validProof.publicInputs[0] = ethers.BigNumber.from(validProof.publicInputs[0]) + .add('0xe000000000000000000000000000000000000000000000000000000000000000') + .toHexString(); + const result = await verifier.verify( + validProof.publicInputs, + validProof.serializedProof, + validProof.recursiveAggregationInput + ); + expect(result, 'proof verification failed').true; + }); + + it('Elliptic curve points over modulo', async () => { + let validProof = JSON.parse(JSON.stringify(PROOF)); + // Add modulo to points + validProof.serializedProof[0] = ethers.BigNumber.from(validProof.serializedProof[0]).add(Q_MOD); + validProof.serializedProof[1] = ethers.BigNumber.from(validProof.serializedProof[1]).add(Q_MOD).add(Q_MOD); + const result = await verifier.verify( + validProof.publicInputs, + validProof.serializedProof, + validProof.recursiveAggregationInput + ); + expect(result, 'proof verification failed').true; + }); + + it('Fr over modulo', async () => { + let validProof = JSON.parse(JSON.stringify(PROOF)); + // Add modulo to number + validProof.serializedProof[22] = ethers.BigNumber.from(validProof.serializedProof[22]).add(R_MOD); + const result = await verifier.verify( + validProof.publicInputs, + validProof.serializedProof, + validProof.recursiveAggregationInput + ); + expect(result, 'proof verification failed').true; + }); + }); + + describe('Should revert on invalid input', function () { + it('More than 1 public inputs', async () => { + let invalidProof = JSON.parse(JSON.stringify(PROOF)); + // Add one more public input to proof + invalidProof.publicInputs.push(invalidProof.publicInputs[0]); + const revertReason = await getCallRevertReason( + verifier.verify( + invalidProof.publicInputs, + invalidProof.serializedProof, + invalidProof.recursiveAggregationInput + ) + ); + expect(revertReason).equal('loadProof: Proof is invalid'); + }); + + it('Empty public inputs', async () => { + const revertReason = await getCallRevertReason( + verifier.verify([], PROOF.serializedProof, PROOF.recursiveAggregationInput) + ); + expect(revertReason).equal('loadProof: Proof is invalid'); + }); + + it('More than 44 words for proof', async () => { + let invalidProof = JSON.parse(JSON.stringify(PROOF)); + // Add one more "serialized proof" input + invalidProof.serializedProof.push(invalidProof.serializedProof[0]); + const revertReason = await getCallRevertReason( + verifier.verify( + invalidProof.publicInputs, + invalidProof.serializedProof, + invalidProof.recursiveAggregationInput + ) + ); + expect(revertReason).equal('loadProof: Proof is invalid'); + }); + + it('Empty serialized proof', async () => { + const revertReason = await getCallRevertReason( + verifier.verify(PROOF.publicInputs, [], PROOF.recursiveAggregationInput) + ); + expect(revertReason).equal('loadProof: Proof is invalid'); + }); + + it('More than 4 words for recursive aggregation input', async () => { + let invalidProof = JSON.parse(JSON.stringify(PROOF)); + // Add one more "recursive aggregation input" value + invalidProof.recursiveAggregationInput.push(invalidProof.recursiveAggregationInput[0]); + const revertReason = await getCallRevertReason( + verifier.verify( + invalidProof.publicInputs, + invalidProof.serializedProof, + invalidProof.recursiveAggregationInput + ) + ); + expect(revertReason).equal('loadProof: Proof is invalid'); + }); + + it('Empty recursive aggregation input', async () => { + const revertReason = await getCallRevertReason( + verifier.verify(PROOF.publicInputs, PROOF.serializedProof, []) + ); + expect(revertReason).equal('loadProof: Proof is invalid'); + }); + + it('Elliptic curve point at infinity', async () => { + let invalidProof = JSON.parse(JSON.stringify(PROOF)); + // Change first point to point at infinity (encode as (0, 0) on EVM) + invalidProof.serializedProof[0] = ethers.constants.HashZero; + invalidProof.serializedProof[1] = ethers.constants.HashZero; + const revertReason = await getCallRevertReason( + verifier.verify( + invalidProof.publicInputs, + invalidProof.serializedProof, + invalidProof.recursiveAggregationInput + ) + ); + expect(revertReason).equal('loadProof: Proof is invalid'); + }); + }); + + it('Should failed with invalid public input', async () => { + const revertReason = await getCallRevertReason( + verifier.verify([ethers.constants.HashZero], PROOF.serializedProof, PROOF.recursiveAggregationInput) + ); + expect(revertReason).equal('invalid quotient evaluation'); + }); + + it('Should failed with invalid recursive aggregative input', async () => { + const revertReason = await getCallRevertReason( + verifier.verify(PROOF.publicInputs, PROOF.serializedProof, [1, 2, 1, 2]) + ); + expect(revertReason).equal('finalPairing: pairing failure'); + }); + + it('Should return correct Verification key hash', async () => { + const vksHash = await verifier.verificationKeyHash(); + expect(vksHash).equal('0x8a50c24dacf7e4d0e8cccb618261cbc775f7b8a2a1e5e794510b10ff42a49323'); + }); +}); diff --git a/tools/Cargo.lock b/tools/Cargo.lock new file mode 100644 index 000000000..90d7c01b3 --- /dev/null +++ b/tools/Cargo.lock @@ -0,0 +1,457 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "handlebars" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39b3bc2a8f715298032cf5087e58573809374b08160aa7d750582bdb82d2683" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pest" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a4d085fd991ac8d5b05a147b437791b4260b76326baf0fc60cf7c9c27ecd33" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bee7be22ce7918f641a33f08e3f43388c7656772244e2bbb2477f44cc9021a" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1511785c5e98d79a05e8a6bc34b4ac2168a0e3e92161862030ad84daa223141" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "pest_meta" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42f0394d3123e33353ca5e1e89092e533d2cc490389f2bd6131c43c634ebc5f" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "serde_json" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "structopt" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" +dependencies = [ + "clap", + "lazy_static", + "structopt-derive", +] + +[[package]] +name = "structopt-derive" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb5ae327f9cc13b68763b5749770cb9e048a99bd9dfdfa58d0cf05d5f64afe0" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.33", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zksync_verifier_contract_generator" +version = "0.1.0" +dependencies = [ + "handlebars", + "lazy_static", + "serde_derive", + "serde_json", + "structopt", +] diff --git a/tools/Cargo.toml b/tools/Cargo.toml new file mode 100644 index 000000000..480d91cb3 --- /dev/null +++ b/tools/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "zksync_verifier_contract_generator" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde_derive = "1.0" +serde_json = "1.0" +lazy_static = "1.4" +structopt = "0.3.26" +handlebars = "4.4.0" diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 000000000..7937224af --- /dev/null +++ b/tools/README.md @@ -0,0 +1,3 @@ +# Tool for generating `Verifier.sol` using json Verification key + +`cargo run --bin zksync_verifier_contract_generator --release -- --input_path /path/to/scheduler_verification_key.json --output_path /path/to/Verifier.sol` diff --git a/tools/data/scheduler_key.json b/tools/data/scheduler_key.json new file mode 100644 index 000000000..c3262193a --- /dev/null +++ b/tools/data/scheduler_key.json @@ -0,0 +1,399 @@ +{ + "n": 67108863, + "num_inputs": 1, + "state_width": 4, + "num_witness_polys": 0, + "gate_setup_commitments": [ + { + "x": [ + 14745348174000482855, + 2839037062185937123, + 3369862715588854899, + 1495909583940713128 + ], + "y": [ + 6859454683840363585, + 11340551061368171664, + 9528805406487149561, + 3414144677220223705 + ], + "infinity": false + }, + { + "x": [ + 9215749870136224396, + 18418669114332753377, + 13140219601461030180, + 2381098845928447331 + ], + "y": [ + 8834765081837029169, + 4424842234296363904, + 13294547557836067005, + 414624398145171890 + ], + "infinity": false + }, + { + "x": [ + 2148575411987453084, + 16730180692461995258, + 12423475767707134837, + 3014264170083149730 + ], + "y": [ + 10870860158804422503, + 14060279526953529989, + 2266257082861680293, + 22356173050560284 + ], + "infinity": false + }, + { + "x": [ + 17803008042411335770, + 5713064950476621403, + 17979342410816871746, + 491265656076548841 + ], + "y": [ + 9823492080506672630, + 3637386621225409615, + 8776978043600973097, + 2514196809208915768 + ], + "infinity": false + }, + { + "x": [ + 3768479078383323179, + 16153057542709544671, + 10578964798085613273, + 2831188075764800753 + ], + "y": [ + 2387514805820590694, + 15085489652142686165, + 8141513931186597223, + 1582376980242699819 + ], + "infinity": false + }, + { + "x": [ + 5395455814671474247, + 5013790368139874617, + 8671649443504728767, + 839142828943885970 + ], + "y": [ + 11231626069154926735, + 5078347962234771017, + 17373886182204596447, + 513647957075879347 + ], + "infinity": false + }, + { + "x": [ + 8940485327950054531, + 9156997542069636576, + 14316753178152000598, + 3357551869664255582 + ], + "y": [ + 14102490706504125272, + 4494991810930729808, + 15532318871086968466, + 1537365238286274178 + ], + "infinity": false + }, + { + "x": [ + 13914906478277300859, + 6213896071228541481, + 4364409818367302306, + 659097390118096039 + ], + "y": [ + 7328372274594390887, + 2650332638498669615, + 15455628473476960005, + 3119379427019958230 + ], + "infinity": false + } + ], + "gate_selectors_commitments": [ + { + "x": [ + 9438200511694036157, + 11094162170960057340, + 9123678872696723713, + 2950597355117190054 + ], + "y": [ + 6153972960518016517, + 8045683598100955864, + 13410633858416643489, + 988361678931464913 + ], + "infinity": false + }, + { + "x": [ + 805964423710846142, + 13603470797942296854, + 11292123377140077447, + 1455913517812009773 + ], + "y": [ + 4541622738043214385, + 8186357170000535775, + 4765839113294831637, + 3026863977499737494 + ], + "infinity": false + } + ], + "permutation_commitments": [ + { + "x": [ + 1851039213129741497, + 11907960788190413713, + 2882727828085561070, + 1451278944954982956 + ], + "y": [ + 15245785050592773860, + 1774295027236395480, + 3373069120056880915, + 1080245109458702174 + ], + "infinity": false + }, + { + "x": [ + 9366052859968548005, + 12275028918364559591, + 2472023927159177225, + 1052535074027277666 + ], + "y": [ + 2428574557555628629, + 15067392861858369528, + 16949255188095910778, + 2297925771936569168 + ], + "infinity": false + }, + { + "x": [ + 17016009610362956206, + 4047659663396753591, + 1832464593155416403, + 2725142957049914767 + ], + "y": [ + 12447928856414787240, + 3072280375285720285, + 12294239288643819494, + 613511140380288958 + ], + "infinity": false + }, + { + "x": [ + 6312774245791141720, + 496150993329472460, + 12773767122915456934, + 3404402910494500531 + ], + "y": [ + 13852578578747731084, + 9030931732410275304, + 17159996848865265705, + 1696956882146098553 + ], + "infinity": false + } + ], + "total_lookup_entries_length": 1073530, + "lookup_selector_commitment": { + "x": [ + 4441974708940861232, + 11325614820129407652, + 7273013871150456559, + 2270181644629652201 + ], + "y": [ + 3070631142979677922, + 15247189094202672776, + 12651459662740804392, + 1832216259472686694 + ], + "infinity": false + }, + "lookup_tables_commitments": [ + { + "x": [ + 631990924006796604, + 16139625628991115157, + 13331739325995827711, + 1062301837743594995 + ], + "y": [ + 15303054606290800139, + 15906872095881647437, + 7093896572295020249, + 1342952934989901142 + ], + "infinity": false + }, + { + "x": [ + 7983921919542246393, + 13296544189644416678, + 17081022784392007697, + 1980832835348244027 + ], + "y": [ + 10874958134865200330, + 7702740658637630534, + 14052057929798961943, + 3193353539419869016 + ], + "infinity": false + }, + { + "x": [ + 1114587284824996932, + 4636906500482867924, + 15328247172597030456, + 87946895873973686 + ], + "y": [ + 15573033830207915877, + 5194694185599035278, + 2562407345425607214, + 2782078999306862675 + ], + "infinity": false + }, + { + "x": [ + 18225112781127431982, + 18048613958187123807, + 7325490730844456621, + 1953409020724855888 + ], + "y": [ + 7577000130125917198, + 6193701449695751861, + 4102082927677054717, + 395350071385269650 + ], + "infinity": false + } + ], + "lookup_table_type_commitment": { + "x": [ + 7312875299592476003, + 313526216906044060, + 13914875394436353152, + 3424388477700656316 + ], + "y": [ + 2572062173996296044, + 5984767625164919974, + 12005537293370417131, + 616463121946800406 + ], + "infinity": false + }, + "non_residues": [ + [ + 5, + 0, + 0, + 0 + ], + [ + 7, + 0, + 0, + 0 + ], + [ + 10, + 0, + 0, + 0 + ] + ], + "g2_elements": [ + { + "x": { + "c0": [ + 5106727233969649389, + 7440829307424791261, + 4785637993704342649, + 1729627375292849782 + ], + "c1": [ + 10945020018377822914, + 17413811393473931026, + 8241798111626485029, + 1841571559660931130 + ] + }, + "y": { + "c0": [ + 5541340697920699818, + 16416156555105522555, + 5380518976772849807, + 1353435754470862315 + ], + "c1": [ + 6173549831154472795, + 13567992399387660019, + 17050234209342075797, + 650358724130500725 + ] + }, + "infinity": false + }, + { + "x": { + "c0": [ + 9089143573911733168, + 11482283522806384523, + 13585589533905622862, + 79029415676722370 + ], + "c1": [ + 5692040832573735873, + 16884514497384809355, + 16717166481813659368, + 2742131088506155463 + ] + }, + "y": { + "c0": [ + 9604638503594647125, + 1289961608472612514, + 6217038149984805214, + 2521661352385209130 + ], + "c1": [ + 17168069778630926308, + 11309277837895768996, + 15154989611154567813, + 359271377050603491 + ] + }, + "infinity": false + } + ] +} \ No newline at end of file diff --git a/tools/data/verifier_contract_template.txt b/tools/data/verifier_contract_template.txt new file mode 100644 index 000000000..049766e42 --- /dev/null +++ b/tools/data/verifier_contract_template.txt @@ -0,0 +1,1616 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.13; + +import "./interfaces/IVerifier.sol"; + +/// @author Matter Labs +/// @notice Modified version of the Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of Knowledge (PLONK) verifier. +/// Modifications have been made to optimize the proof system for zkSync Era circuits. +/// @dev It uses a custom memory layout inside the inline assembly block. Each reserved memory cell is declared in the constants below. +/// @dev For a better understanding of the verifier algorithm please refer to the following papers: +/// * Original Plonk Article: https://eprint.iacr.org/2019/953.pdf +/// * Original LookUp Article: https://eprint.iacr.org/2020/315.pdf +/// * Plonk for zkSync v1.1: https://github.com/matter-labs/solidity_plonk_verifier/raw/recursive/bellman_vk_codegen_recursive/RecursivePlonkUnrolledForEthereum.pdf +/// The notation used in the code is the same as in the papers. +contract Verifier is IVerifier { + /*////////////////////////////////////////////////////////////// + Verification keys + //////////////////////////////////////////////////////////////*/ + + // Memory slots from 0x000 to 0x200 are reserved for intermediate computations and call to precompiles. + + uint256 internal constant VK_GATE_SETUP_0_X_SLOT = 0x200 + 0x000; + uint256 internal constant VK_GATE_SETUP_0_Y_SLOT = 0x200 + 0x020; + uint256 internal constant VK_GATE_SETUP_1_X_SLOT = 0x200 + 0x040; + uint256 internal constant VK_GATE_SETUP_1_Y_SLOT = 0x200 + 0x060; + uint256 internal constant VK_GATE_SETUP_2_X_SLOT = 0x200 + 0x080; + uint256 internal constant VK_GATE_SETUP_2_Y_SLOT = 0x200 + 0x0a0; + uint256 internal constant VK_GATE_SETUP_3_X_SLOT = 0x200 + 0x0c0; + uint256 internal constant VK_GATE_SETUP_3_Y_SLOT = 0x200 + 0x0e0; + uint256 internal constant VK_GATE_SETUP_4_X_SLOT = 0x200 + 0x100; + uint256 internal constant VK_GATE_SETUP_4_Y_SLOT = 0x200 + 0x120; + uint256 internal constant VK_GATE_SETUP_5_X_SLOT = 0x200 + 0x140; + uint256 internal constant VK_GATE_SETUP_5_Y_SLOT = 0x200 + 0x160; + uint256 internal constant VK_GATE_SETUP_6_X_SLOT = 0x200 + 0x180; + uint256 internal constant VK_GATE_SETUP_6_Y_SLOT = 0x200 + 0x1a0; + uint256 internal constant VK_GATE_SETUP_7_X_SLOT = 0x200 + 0x1c0; + uint256 internal constant VK_GATE_SETUP_7_Y_SLOT = 0x200 + 0x1e0; + + uint256 internal constant VK_GATE_SELECTORS_0_X_SLOT = 0x200 + 0x200; + uint256 internal constant VK_GATE_SELECTORS_0_Y_SLOT = 0x200 + 0x220; + uint256 internal constant VK_GATE_SELECTORS_1_X_SLOT = 0x200 + 0x240; + uint256 internal constant VK_GATE_SELECTORS_1_Y_SLOT = 0x200 + 0x260; + + uint256 internal constant VK_PERMUTATION_0_X_SLOT = 0x200 + 0x280; + uint256 internal constant VK_PERMUTATION_0_Y_SLOT = 0x200 + 0x2a0; + uint256 internal constant VK_PERMUTATION_1_X_SLOT = 0x200 + 0x2c0; + uint256 internal constant VK_PERMUTATION_1_Y_SLOT = 0x200 + 0x2e0; + uint256 internal constant VK_PERMUTATION_2_X_SLOT = 0x200 + 0x300; + uint256 internal constant VK_PERMUTATION_2_Y_SLOT = 0x200 + 0x320; + uint256 internal constant VK_PERMUTATION_3_X_SLOT = 0x200 + 0x340; + uint256 internal constant VK_PERMUTATION_3_Y_SLOT = 0x200 + 0x360; + + uint256 internal constant VK_LOOKUP_SELECTOR_X_SLOT = 0x200 + 0x380; + uint256 internal constant VK_LOOKUP_SELECTOR_Y_SLOT = 0x200 + 0x3a0; + + uint256 internal constant VK_LOOKUP_TABLE_0_X_SLOT = 0x200 + 0x3c0; + uint256 internal constant VK_LOOKUP_TABLE_0_Y_SLOT = 0x200 + 0x3e0; + uint256 internal constant VK_LOOKUP_TABLE_1_X_SLOT = 0x200 + 0x400; + uint256 internal constant VK_LOOKUP_TABLE_1_Y_SLOT = 0x200 + 0x420; + uint256 internal constant VK_LOOKUP_TABLE_2_X_SLOT = 0x200 + 0x440; + uint256 internal constant VK_LOOKUP_TABLE_2_Y_SLOT = 0x200 + 0x460; + uint256 internal constant VK_LOOKUP_TABLE_3_X_SLOT = 0x200 + 0x480; + uint256 internal constant VK_LOOKUP_TABLE_3_Y_SLOT = 0x200 + 0x4a0; + + uint256 internal constant VK_LOOKUP_TABLE_TYPE_X_SLOT = 0x200 + 0x4c0; + uint256 internal constant VK_LOOKUP_TABLE_TYPE_Y_SLOT = 0x200 + 0x4e0; + + /*////////////////////////////////////////////////////////////// + Proof + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant PROOF_PUBLIC_INPUT = 0x200 + 0x600 + 0x000; + + uint256 internal constant PROOF_STATE_POLYS_0_X_SLOT = 0x200 + 0x600 + 0x020; + uint256 internal constant PROOF_STATE_POLYS_0_Y_SLOT = 0x200 + 0x600 + 0x040; + uint256 internal constant PROOF_STATE_POLYS_1_X_SLOT = 0x200 + 0x600 + 0x060; + uint256 internal constant PROOF_STATE_POLYS_1_Y_SLOT = 0x200 + 0x600 + 0x080; + uint256 internal constant PROOF_STATE_POLYS_2_X_SLOT = 0x200 + 0x600 + 0x0a0; + uint256 internal constant PROOF_STATE_POLYS_2_Y_SLOT = 0x200 + 0x600 + 0x0c0; + uint256 internal constant PROOF_STATE_POLYS_3_X_SLOT = 0x200 + 0x600 + 0x0e0; + uint256 internal constant PROOF_STATE_POLYS_3_Y_SLOT = 0x200 + 0x600 + 0x100; + + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT = 0x200 + 0x600 + 0x120; + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x600 + 0x140; + + uint256 internal constant PROOF_LOOKUP_S_POLY_X_SLOT = 0x200 + 0x600 + 0x160; + uint256 internal constant PROOF_LOOKUP_S_POLY_Y_SLOT = 0x200 + 0x600 + 0x180; + + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT = 0x200 + 0x600 + 0x1a0; + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x600 + 0x1c0; + + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT = 0x200 + 0x600 + 0x1e0; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT = 0x200 + 0x600 + 0x200; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT = 0x200 + 0x600 + 0x220; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT = 0x200 + 0x600 + 0x240; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT = 0x200 + 0x600 + 0x260; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT = 0x200 + 0x600 + 0x280; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT = 0x200 + 0x600 + 0x2a0; + uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT = 0x200 + 0x600 + 0x2c0; + + uint256 internal constant PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x2e0; + uint256 internal constant PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x300; + uint256 internal constant PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x320; + uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x340; + + uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x360; + uint256 internal constant PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x380; + + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x3a0; + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x3c0; + uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x3e0; + + uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x400; + uint256 internal constant PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x420; + uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x440; + uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x460; + uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x480; + uint256 internal constant PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x4a0; + uint256 internal constant PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x4c0; + uint256 internal constant PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x4e0; + uint256 internal constant PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x500; + + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_X_SLOT = 0x200 + 0x600 + 0x520; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_Y_SLOT = 0x200 + 0x600 + 0x540; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT = 0x200 + 0x600 + 0x560; + uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x600 + 0x580; + + uint256 internal constant PROOF_RECURSIVE_PART_P1_X_SLOT = 0x200 + 0x600 + 0x5a0; + uint256 internal constant PROOF_RECURSIVE_PART_P1_Y_SLOT = 0x200 + 0x600 + 0x5c0; + + uint256 internal constant PROOF_RECURSIVE_PART_P2_X_SLOT = 0x200 + 0x600 + 0x5e0; + uint256 internal constant PROOF_RECURSIVE_PART_P2_Y_SLOT = 0x200 + 0x600 + 0x600; + + /*////////////////////////////////////////////////////////////// + Transcript slot + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant TRANSCRIPT_BEGIN_SLOT = 0x200 + 0x600 + 0x620 + 0x00; + uint256 internal constant TRANSCRIPT_DST_BYTE_SLOT = 0x200 + 0x600 + 0x620 + 0x03; + uint256 internal constant TRANSCRIPT_STATE_0_SLOT = 0x200 + 0x600 + 0x620 + 0x04; + uint256 internal constant TRANSCRIPT_STATE_1_SLOT = 0x200 + 0x600 + 0x620 + 0x24; + uint256 internal constant TRANSCRIPT_CHALLENGE_SLOT = 0x200 + 0x600 + 0x620 + 0x44; + + /*////////////////////////////////////////////////////////////// + Partial verifier state + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant STATE_ALPHA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x000; + uint256 internal constant STATE_BETA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x020; + uint256 internal constant STATE_GAMMA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x040; + uint256 internal constant STATE_POWER_OF_ALPHA_2_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x060; + uint256 internal constant STATE_POWER_OF_ALPHA_3_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x080; + uint256 internal constant STATE_POWER_OF_ALPHA_4_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x0a0; + uint256 internal constant STATE_POWER_OF_ALPHA_5_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x0c0; + uint256 internal constant STATE_POWER_OF_ALPHA_6_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x0e0; + uint256 internal constant STATE_POWER_OF_ALPHA_7_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x100; + uint256 internal constant STATE_POWER_OF_ALPHA_8_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x120; + uint256 internal constant STATE_ETA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x140; + uint256 internal constant STATE_BETA_LOOKUP_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x160; + uint256 internal constant STATE_GAMMA_LOOKUP_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x180; + uint256 internal constant STATE_BETA_PLUS_ONE_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x1a0; + uint256 internal constant STATE_BETA_GAMMA_PLUS_GAMMA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x1c0; + uint256 internal constant STATE_V_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x1e0; + uint256 internal constant STATE_U_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x200; + uint256 internal constant STATE_Z_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x220; + uint256 internal constant STATE_Z_MINUS_LAST_OMEGA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x240; + uint256 internal constant STATE_L_0_AT_Z_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x260; + uint256 internal constant STATE_L_N_MINUS_ONE_AT_Z_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x280; + uint256 internal constant STATE_Z_IN_DOMAIN_SIZE = 0x200 + 0x600 + 0x620 + 0x80 + 0x2a0; + + /*////////////////////////////////////////////////////////////// + Queries + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant QUERIES_BUFFER_POINT_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x00; + + uint256 internal constant QUERIES_AT_Z_0_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x40; + uint256 internal constant QUERIES_AT_Z_0_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x60; + uint256 internal constant QUERIES_AT_Z_1_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x80; + uint256 internal constant QUERIES_AT_Z_1_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0xa0; + + uint256 internal constant QUERIES_T_POLY_AGGREGATED_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0xc0; + uint256 internal constant QUERIES_T_POLY_AGGREGATED_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0xe0; + + /*////////////////////////////////////////////////////////////// + Aggregated commitment + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant AGGREGATED_AT_Z_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x00; + uint256 internal constant AGGREGATED_AT_Z_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x20; + + uint256 internal constant AGGREGATED_AT_Z_OMEGA_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x40; + uint256 internal constant AGGREGATED_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x60; + + uint256 internal constant AGGREGATED_OPENING_AT_Z_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x80; + uint256 internal constant AGGREGATED_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xa0; + + /*////////////////////////////////////////////////////////////// + Pairing data + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant PAIRING_BUFFER_POINT_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x00; + uint256 internal constant PAIRING_BUFFER_POINT_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x20; + + uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_X_SLOT = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x40; + uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_Y_SLOT = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x60; + + uint256 internal constant PAIRING_PAIR_WITH_X_X_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x80; + uint256 internal constant PAIRING_PAIR_WITH_X_Y_SLOT = 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xa0; + + /*////////////////////////////////////////////////////////////// + Slots for scalar multiplication optimizations + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xc0; + uint256 internal constant LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xe0; + uint256 internal constant LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF = + 0x200 + 0x600 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x100; + + /*////////////////////////////////////////////////////////////// + Constants + //////////////////////////////////////////////////////////////*/ + + uint256 internal constant OMEGA = 0x1dba8b5bdd64ef6ce29a9039aca3c0e524395c43b9227b96c75090cc6cc7ec97; + uint256 internal constant DOMAIN_SIZE = 0x4000000; // 2^26 + uint256 internal constant Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 internal constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + + /// @dev flip of 0xe000000000000000000000000000000000000000000000000000000000000000; + uint256 internal constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + + {{residue_g2_elements}} + + /// @notice Calculates a keccak256 hash of the runtime loaded verification keys. + /// @return vkHash The keccak256 hash of the loaded verification keys. + function verificationKeyHash() external pure returns (bytes32 vkHash) { + _loadVerificationKey(); + + assembly { + let start := VK_GATE_SETUP_0_X_SLOT + let end := VK_LOOKUP_TABLE_TYPE_Y_SLOT + let length := add(sub(end, start), 0x20) + + vkHash := keccak256(start, length) + } + } + + /// @notice Load verification keys to memory in runtime. + /// @dev The constants are loaded into memory in a specific layout declared in the constants starting from `VK_` prefix. + /// NOTE: Function may corrupt the memory state if some memory was used before this function was called. + /// The VK consists of commitments to setup polynomials: + /// [q_a], [q_b], [q_c], [q_d], - main gate setup commitments + /// [q_{d_next}], [q_ab], [q_ac], [q_const] / + /// [main_gate_selector], [custom_gate_selector] - gate selectors commitments + /// [sigma_0], [sigma_1], [sigma_2], [sigma_3] - permutation polynomials commitments + /// [lookup_selector] - lookup selector commitment + /// [col_0], [col_1], [col_2], [col_3] - lookup columns commitments + /// [table_type] - lookup table type commitment + function _loadVerificationKey() internal pure virtual { + assembly { + {{commitments}} + } + } + + /// @dev Verifies a zk-SNARK proof. + /// @return A boolean value indicating whether the zk-SNARK proof is valid. + /// Note: The function may revert execution instead of returning false in some cases. + function verify( + uint256[] calldata, // _publicInputs + uint256[] calldata, // _proof + uint256[] calldata // _recursiveAggregationInput + ) external view returns (bool) { + // No memory was accessed yet, so keys can be loaded into the right place and not corrupt any other memory. + _loadVerificationKey(); + + // Begining of the big inline assembly block that makes all the verification work. + // Note: We use the custom memory layout, so the return value should be returned from the assembly, not Solidity code. + assembly { + /*////////////////////////////////////////////////////////////// + Utils + //////////////////////////////////////////////////////////////*/ + + /// @dev Reverts execution with a provided revert reason. + /// @param len The byte length of the error message string, which is expected to be no more than 32. + /// @param reason The 1-word revert reason string, encoded in ASCII. + function revertWithMessage(len, reason) { + // "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // Data offset + mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) + // Length of revert string + mstore(0x24, len) + // Revert reason + mstore(0x44, reason) + // Revert + revert(0x00, 0x64) + } + + /// @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD. + function modexp(value, power) -> res { + mstore(0x00, 0x20) + mstore(0x20, 0x20) + mstore(0x40, 0x20) + mstore(0x60, value) + mstore(0x80, power) + mstore(0xa0, R_MOD) + if iszero(staticcall(gas(), 5, 0, 0xc0, 0x00, 0x20)) { + revertWithMessage(24, "modexp precompile failed") + } + res := mload(0x00) + } + + /// @dev Performs a point multiplication operation and stores the result in a given memory destination. + function pointMulIntoDest(point, s, dest) { + mstore(0x00, mload(point)) + mstore(0x20, mload(add(point, 0x20))) + mstore(0x40, s) + if iszero(staticcall(gas(), 7, 0, 0x60, dest, 0x40)) { + revertWithMessage(30, "pointMulIntoDest: ecMul failed") + } + } + + /// @dev Performs a point addition operation and stores the result in a given memory destination. + function pointAddIntoDest(p1, p2, dest) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, mload(add(p2, 0x20))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) { + revertWithMessage(30, "pointAddIntoDest: ecAdd failed") + } + } + + /// @dev Performs a point subtraction operation and updates the first point with the result. + function pointSubAssign(p1, p2) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, sub(Q_MOD, mload(add(p2, 0x20)))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) { + revertWithMessage(28, "pointSubAssign: ecAdd failed") + } + } + + /// @dev Performs a point addition operation and updates the first point with the result. + function pointAddAssign(p1, p2) { + mstore(0x00, mload(p1)) + mstore(0x20, mload(add(p1, 0x20))) + mstore(0x40, mload(p2)) + mstore(0x60, mload(add(p2, 0x20))) + if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) { + revertWithMessage(28, "pointAddAssign: ecAdd failed") + } + } + + /// @dev Performs a point multiplication operation and then adds the result to the destination point. + function pointMulAndAddIntoDest(point, s, dest) { + mstore(0x00, mload(point)) + mstore(0x20, mload(add(point, 0x20))) + mstore(0x40, s) + let success := staticcall(gas(), 7, 0, 0x60, 0, 0x40) + + mstore(0x40, mload(dest)) + mstore(0x60, mload(add(dest, 0x20))) + success := and(success, staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) + + if iszero(success) { + revertWithMessage(22, "pointMulAndAddIntoDest") + } + } + + /// @dev Negates an elliptic curve point by changing the sign of the y-coordinate. + function pointNegate(point) { + let pY := mload(add(point, 0x20)) + switch pY + case 0 { + if mload(point) { + revertWithMessage(26, "pointNegate: invalid point") + } + } + default { + mstore(add(point, 0x20), sub(Q_MOD, pY)) + } + } + + /*////////////////////////////////////////////////////////////// + Transcript helpers + //////////////////////////////////////////////////////////////*/ + + /// @dev Updates the transcript state with a new challenge value. + function updateTranscript(value) { + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x00) + mstore(TRANSCRIPT_CHALLENGE_SLOT, value) + let newState0 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64) + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x01) + let newState1 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64) + mstore(TRANSCRIPT_STATE_1_SLOT, newState1) + mstore(TRANSCRIPT_STATE_0_SLOT, newState0) + } + + /// @dev Retrieves a transcript challenge. + function getTranscriptChallenge(numberOfChallenge) -> challenge { + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x02) + mstore(TRANSCRIPT_CHALLENGE_SLOT, shl(224, numberOfChallenge)) + challenge := and(keccak256(TRANSCRIPT_BEGIN_SLOT, 0x48), FR_MASK) + } + + /*////////////////////////////////////////////////////////////// + 1. Load Proof + //////////////////////////////////////////////////////////////*/ + + /// @dev This function loads a zk-SNARK proof, ensures it's properly formatted, and stores it in memory. + /// It ensures the number of inputs and the elliptic curve point's validity. + /// Note: It does NOT reject inputs that exceed these module sizes, but rather wraps them within the module bounds. + /// The proof consists of: + /// 1. Public input: (1 field element from F_r) + /// + /// 2. Polynomial commitments (elliptic curve points over F_q): + /// [a], [b], [c], [d] - state polynomials commitments + /// [z_perm] - copy-permutation grand product commitment + /// [s] - polynomial for lookup argument commitment + /// [z_lookup] - lookup grand product commitment + /// [t_0], [t_1], [t_2], [t_3] - quotient polynomial parts commitments + /// [W], [W'] - proof openings commitments + /// + /// 3. Polynomial evaluations at z and z*omega (field elements from F_r): + /// t(z) - quotient polynomial opening + /// a(z), b(z), c(z), d(z), d(z*omega) - state polynomials openings + /// main_gate_selector(z) - main gate selector opening + /// sigma_0(z), sigma_1(z), sigma_2(z) - permutation polynomials openings + /// z_perm(z*omega) - copy-permutation grand product opening + /// z_lookup(z*omega) - lookup grand product opening + /// lookup_selector(z) - lookup selector opening + /// s(x*omega), t(z*omega), table_type(z) - lookup argument polynomial openings + /// r(z) - linearisation polynomial opening + /// + /// 4. Recursive proof (2 elliptic curve points over F_q) + function loadProof() { + // 1. Load public input + let offset := calldataload(0x04) + let publicInputLengthInWords := calldataload(add(offset, 0x04)) + let isValid := eq(publicInputLengthInWords, 1) // We expect only one public input + mstore(PROOF_PUBLIC_INPUT, and(calldataload(add(offset, 0x24)), FR_MASK)) + + // 2. Load the proof (except for the recursive part) + offset := calldataload(0x24) + let proofLengthInWords := calldataload(add(offset, 0x04)) + isValid := and(eq(proofLengthInWords, 44), isValid) + + // PROOF_STATE_POLYS_0 + { + let x := mod(calldataload(add(offset, 0x024)), Q_MOD) + let y := mod(calldataload(add(offset, 0x044)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_0_X_SLOT, x) + mstore(PROOF_STATE_POLYS_0_Y_SLOT, y) + } + // PROOF_STATE_POLYS_1 + { + let x := mod(calldataload(add(offset, 0x064)), Q_MOD) + let y := mod(calldataload(add(offset, 0x084)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_1_X_SLOT, x) + mstore(PROOF_STATE_POLYS_1_Y_SLOT, y) + } + // PROOF_STATE_POLYS_2 + { + let x := mod(calldataload(add(offset, 0x0a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x0c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_2_X_SLOT, x) + mstore(PROOF_STATE_POLYS_2_Y_SLOT, y) + } + // PROOF_STATE_POLYS_3 + { + let x := mod(calldataload(add(offset, 0x0e4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x104)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_STATE_POLYS_3_X_SLOT, x) + mstore(PROOF_STATE_POLYS_3_Y_SLOT, y) + } + // PROOF_COPY_PERMUTATION_GRAND_PRODUCT + { + let x := mod(calldataload(add(offset, 0x124)), Q_MOD) + let y := mod(calldataload(add(offset, 0x144)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, x) + mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT, y) + } + // PROOF_LOOKUP_S_POLY + { + let x := mod(calldataload(add(offset, 0x164)), Q_MOD) + let y := mod(calldataload(add(offset, 0x184)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_LOOKUP_S_POLY_X_SLOT, x) + mstore(PROOF_LOOKUP_S_POLY_Y_SLOT, y) + } + // PROOF_LOOKUP_GRAND_PRODUCT + { + let x := mod(calldataload(add(offset, 0x1a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x1c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, x) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_0 + { + let x := mod(calldataload(add(offset, 0x1e4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x204)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_1 + { + let x := mod(calldataload(add(offset, 0x224)), Q_MOD) + let y := mod(calldataload(add(offset, 0x244)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_2 + { + let x := mod(calldataload(add(offset, 0x264)), Q_MOD) + let y := mod(calldataload(add(offset, 0x284)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT, y) + } + // PROOF_QUOTIENT_POLY_PARTS_3 + { + let x := mod(calldataload(add(offset, 0x2a4)), Q_MOD) + let y := mod(calldataload(add(offset, 0x2c4)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, x) + mstore(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT, y) + } + + mstore(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x2e4)), R_MOD)) + mstore(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x304)), R_MOD)) + mstore(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x324)), R_MOD)) + mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x344)), R_MOD)) + + mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x364)), R_MOD)) + mstore(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x384)), R_MOD)) + + mstore(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3a4)), R_MOD)) + mstore(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3c4)), R_MOD)) + mstore(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3e4)), R_MOD)) + + mstore( + PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, + mod(calldataload(add(offset, 0x404)), R_MOD) + ) + mstore(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x424)), R_MOD)) + mstore(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x444)), R_MOD)) + mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x464)), R_MOD)) + mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x484)), R_MOD)) + mstore(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4a4)), R_MOD)) + mstore(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4c4)), R_MOD)) + mstore(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4e4)), R_MOD)) + mstore(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x504)), R_MOD)) + + // PROOF_OPENING_PROOF_AT_Z + { + let x := mod(calldataload(add(offset, 0x524)), Q_MOD) + let y := mod(calldataload(add(offset, 0x544)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_OPENING_PROOF_AT_Z_X_SLOT, x) + mstore(PROOF_OPENING_PROOF_AT_Z_Y_SLOT, y) + } + // PROOF_OPENING_PROOF_AT_Z_OMEGA + { + let x := mod(calldataload(add(offset, 0x564)), Q_MOD) + let y := mod(calldataload(add(offset, 0x584)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, x) + mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT, y) + } + + // 3. Load the recursive part of the proof + offset := calldataload(0x44) + let recursiveProofLengthInWords := calldataload(add(offset, 0x04)) + isValid := and(eq(recursiveProofLengthInWords, 4), isValid) + // PROOF_RECURSIVE_PART_P1 + { + let x := mod(calldataload(add(offset, 0x024)), Q_MOD) + let y := mod(calldataload(add(offset, 0x044)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_RECURSIVE_PART_P1_X_SLOT, x) + mstore(PROOF_RECURSIVE_PART_P1_Y_SLOT, y) + } + // PROOF_RECURSIVE_PART_P2 + { + let x := mod(calldataload(add(offset, 0x064)), Q_MOD) + let y := mod(calldataload(add(offset, 0x084)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) + mstore(PROOF_RECURSIVE_PART_P2_X_SLOT, x) + mstore(PROOF_RECURSIVE_PART_P2_Y_SLOT, y) + } + + // Revert if a proof is not valid + if iszero(isValid) { + revertWithMessage(27, "loadProof: Proof is invalid") + } + } + + /*////////////////////////////////////////////////////////////// + 2. Transcript initialization + //////////////////////////////////////////////////////////////*/ + + /// @notice Recomputes all challenges + /// @dev The process is the following: + /// Commit: PI, [a], [b], [c], [d] + /// Get: eta + /// Commit: [s] + /// Get: beta, gamma + /// Commit: [z_perm] + /// Get: beta', gamma' + /// Commit: [z_lookup] + /// Get: alpha + /// Commit: [t_0], [t_1], [t_2], [t_3] + /// Get: z + /// Commit: t(z), a(z), b(z), c(z), d(z), d(z*omega), + /// main_gate_selector(z), + /// sigma_0(z), sigma_1(z), sigma_2(z), + /// z_perm(z*omega), + /// t(z), lookup_selector(z), table_type(z), + /// s(x*omega), z_lookup(z*omega), t(z*omega), + /// r(z) + /// Get: v + /// Commit: [W], [W'] + /// Get: u + function initializeTranscript() { + // Round 1 + updateTranscript(mload(PROOF_PUBLIC_INPUT)) + updateTranscript(mload(PROOF_STATE_POLYS_0_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_0_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_Y_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_X_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_Y_SLOT)) + + mstore(STATE_ETA_SLOT, getTranscriptChallenge(0)) + + // Round 1.5 + updateTranscript(mload(PROOF_LOOKUP_S_POLY_X_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_S_POLY_Y_SLOT)) + + mstore(STATE_BETA_SLOT, getTranscriptChallenge(1)) + mstore(STATE_GAMMA_SLOT, getTranscriptChallenge(2)) + + // Round 2 + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT)) + + mstore(STATE_BETA_LOOKUP_SLOT, getTranscriptChallenge(3)) + mstore(STATE_GAMMA_LOOKUP_SLOT, getTranscriptChallenge(4)) + + // Round 2.5 + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT)) + + mstore(STATE_ALPHA_SLOT, getTranscriptChallenge(5)) + + // Round 3 + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT)) + updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT)) + + { + let z := getTranscriptChallenge(6) + + mstore(STATE_Z_SLOT, z) + mstore(STATE_Z_IN_DOMAIN_SIZE, modexp(z, DOMAIN_SIZE)) + } + + // Round 4 + updateTranscript(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT)) + + updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT)) + updateTranscript(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT)) + + mstore(STATE_V_SLOT, getTranscriptChallenge(7)) + + // Round 5 + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT)) + updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT)) + + mstore(STATE_U_SLOT, getTranscriptChallenge(8)) + } + + /*////////////////////////////////////////////////////////////// + 3. Verifying quotient evaluation + //////////////////////////////////////////////////////////////*/ + + /// @notice Compute linearisation polynomial's constant term: r_0 + /// @dev To save a verifier scalar multiplication, we split linearisation polynomial + /// into its constant and non-constant terms. The constant term is computed with the formula: + /// + /// r_0 = alpha^0 * L_0(z) * PI * q_{main selector}(z) + r(z) -- main gate contribution + /// + /// - alpha^4 * z_perm(z*omega)(sigma_0(z) * beta + gamma + a(z)) \ + /// (sigma_1(z) * beta + gamma + b(z)) | + /// (sigma_2(z) * beta + gamma + c(z)) | - permutation contribution + /// (sigma_3(z) + gamma) | + /// - alpha^5 * L_0(z) / + /// + /// + alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) \ + /// * (z - omega^{n-1}) * z_lookup(z*omega) | - lookup contribution + /// - alpha^7 * L_0(z) | + /// - alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1} / + /// + /// In the end we should check that t(z)*Z_H(z) = r(z) + r_0! + function verifyQuotientEvaluation() { + // Compute power of alpha + { + let alpha := mload(STATE_ALPHA_SLOT) + let currentAlpha := mulmod(alpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_2_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_3_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_4_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_5_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_6_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_7_SLOT, currentAlpha) + currentAlpha := mulmod(currentAlpha, alpha, R_MOD) + mstore(STATE_POWER_OF_ALPHA_8_SLOT, currentAlpha) + } + + let stateZ := mload(STATE_Z_SLOT) // z + mstore(STATE_L_0_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(0, stateZ)) // L_0(z) + mstore(STATE_L_N_MINUS_ONE_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(sub(DOMAIN_SIZE, 1), stateZ)) // L_{n-1}(z) + let stateT := mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(PROOF_PUBLIC_INPUT), R_MOD) // L_0(z) * PI + + // Compute main gate contribution + let result := mulmod(stateT, mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), R_MOD) + + // Compute permutation contribution + result := addmod(result, permutationQuotientContribution(), R_MOD) + + // Compute lookup contribution + result := addmod(result, lookupQuotientContribution(), R_MOD) + + // Check that r(z) + r_0 = t(z) * Z_H(z) + result := addmod(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), result, R_MOD) + + let vanishing := addmod(mload(STATE_Z_IN_DOMAIN_SIZE), sub(R_MOD, 1), R_MOD) + let lhs := mulmod(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT), vanishing, R_MOD) + if iszero(eq(lhs, result)) { + revertWithMessage(27, "invalid quotient evaluation") + } + } + + /// @notice Evaluating L_{polyNum}(at) out of domain + /// @dev L_i is a Lagrange polynomial for our domain such that: + /// L_i(omega^i) = 1 and L_i(omega^j) = 0 for all j != i + function evaluateLagrangePolyOutOfDomain(polyNum, at) -> res { + let omegaPower := 1 + if polyNum { + omegaPower := modexp(OMEGA, polyNum) + } + + res := addmod(modexp(at, DOMAIN_SIZE), sub(R_MOD, 1), R_MOD) + + // Vanishing polynomial can not be zero at point `at` + if iszero(res) { + revertWithMessage(28, "invalid vanishing polynomial") + } + res := mulmod(res, omegaPower, R_MOD) + let denominator := addmod(at, sub(R_MOD, omegaPower), R_MOD) + denominator := mulmod(denominator, DOMAIN_SIZE, R_MOD) + denominator := modexp(denominator, sub(R_MOD, 2)) + res := mulmod(res, denominator, R_MOD) + } + + /// @notice Compute permutation contribution to linearisation polynomial's constant term + function permutationQuotientContribution() -> res { + // res = alpha^4 * z_perm(z*omega) + res := mulmod( + mload(STATE_POWER_OF_ALPHA_4_SLOT), + mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), + R_MOD + ) + + { + let gamma := mload(STATE_GAMMA_SLOT) + let beta := mload(STATE_BETA_SLOT) + + let factorMultiplier + { + // res *= sigma_0(z) * beta + gamma + a(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + { + // res *= sigma_1(z) * beta + gamma + b(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + { + // res *= sigma_2(z) * beta + gamma + c(z) + factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD) + factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) + factorMultiplier := addmod( + factorMultiplier, + mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT), + R_MOD + ) + res := mulmod(res, factorMultiplier, R_MOD) + } + + // res *= sigma_3(z) + gamma + res := mulmod(res, addmod(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), gamma, R_MOD), R_MOD) + } + + // res = -res + res := sub(R_MOD, res) + + // -= L_0(z) * alpha^5 + let l0AtZ := mload(STATE_L_0_AT_Z_SLOT) + l0AtZ := mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD) + res := addmod(res, sub(R_MOD, l0AtZ), R_MOD) + } + + /// @notice Compute lookup contribution to linearisation polynomial's constant term + function lookupQuotientContribution() -> res { + let betaLookup := mload(STATE_BETA_LOOKUP_SLOT) + let gammaLookup := mload(STATE_GAMMA_LOOKUP_SLOT) + let betaPlusOne := addmod(betaLookup, 1, R_MOD) + let betaGamma := mulmod(betaPlusOne, gammaLookup, R_MOD) + + mstore(STATE_BETA_PLUS_ONE_SLOT, betaPlusOne) + mstore(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT, betaGamma) + + // res = alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) * z_lookup(z*omega) + res := mulmod(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT), betaLookup, R_MOD) + res := addmod(res, betaGamma, R_MOD) + res := mulmod(res, mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD) + res := mulmod(res, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + + // res *= z - omega^{n-1} + { + let lastOmega := modexp(OMEGA, sub(DOMAIN_SIZE, 1)) + let zMinusLastOmega := addmod(mload(STATE_Z_SLOT), sub(R_MOD, lastOmega), R_MOD) + mstore(STATE_Z_MINUS_LAST_OMEGA_SLOT, zMinusLastOmega) + res := mulmod(res, zMinusLastOmega, R_MOD) + } + + // res -= alpha^7 * L_{0}(z) + { + let intermediateValue := mulmod( + mload(STATE_L_0_AT_Z_SLOT), + mload(STATE_POWER_OF_ALPHA_7_SLOT), + R_MOD + ) + res := addmod(res, sub(R_MOD, intermediateValue), R_MOD) + } + + // res -= alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1} + { + let lnMinusOneAtZ := mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT) + let betaGammaPowered := modexp(betaGamma, sub(DOMAIN_SIZE, 1)) + let alphaPower8 := mload(STATE_POWER_OF_ALPHA_8_SLOT) + + let subtrahend := mulmod(mulmod(lnMinusOneAtZ, betaGammaPowered, R_MOD), alphaPower8, R_MOD) + res := addmod(res, sub(R_MOD, subtrahend), R_MOD) + } + } + + /// @notice Compute main gate contribution to linearisation polynomial commitment multiplied by v + function mainGateLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + // += a(z) * [q_a] + pointMulIntoDest(VK_GATE_SETUP_0_X_SLOT, stateOpening0AtZ, dest) + // += b(z) * [q_b] + pointMulAndAddIntoDest(VK_GATE_SETUP_1_X_SLOT, stateOpening1AtZ, dest) + // += c(z) * [q_c] + pointMulAndAddIntoDest(VK_GATE_SETUP_2_X_SLOT, stateOpening2AtZ, dest) + // += d(z) * [q_d] + pointMulAndAddIntoDest(VK_GATE_SETUP_3_X_SLOT, stateOpening3AtZ, dest) + // += a(z) * b(z) * [q_ab] + pointMulAndAddIntoDest(VK_GATE_SETUP_4_X_SLOT, mulmod(stateOpening0AtZ, stateOpening1AtZ, R_MOD), dest) + // += a(z) * c(z) * [q_ac] + pointMulAndAddIntoDest(VK_GATE_SETUP_5_X_SLOT, mulmod(stateOpening0AtZ, stateOpening2AtZ, R_MOD), dest) + // += [q_const] + pointAddAssign(dest, VK_GATE_SETUP_6_X_SLOT) + // += d(z*omega) * [q_{d_next}] + pointMulAndAddIntoDest(VK_GATE_SETUP_7_X_SLOT, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT), dest) + + // *= v * main_gate_selector(z) + let coeff := mulmod(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), mload(STATE_V_SLOT), R_MOD) + pointMulIntoDest(dest, coeff, dest) + } + + /// @notice Compute custom gate contribution to linearisation polynomial commitment multiplied by v + function addAssignRescueCustomGateLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + let accumulator + let intermediateValue + // = alpha * (a(z)^2 - b(z)) + accumulator := mulmod(stateOpening0AtZ, stateOpening0AtZ, R_MOD) + accumulator := addmod(accumulator, sub(R_MOD, stateOpening1AtZ), R_MOD) + accumulator := mulmod(accumulator, mload(STATE_ALPHA_SLOT), R_MOD) + // += alpha^2 * (b(z)^2 - c(z)) + intermediateValue := mulmod(stateOpening1AtZ, stateOpening1AtZ, R_MOD) + intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening2AtZ), R_MOD) + intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_2_SLOT), R_MOD) + accumulator := addmod(accumulator, intermediateValue, R_MOD) + // += alpha^3 * (c(z) * a(z) - d(z)) + intermediateValue := mulmod(stateOpening2AtZ, stateOpening0AtZ, R_MOD) + intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening3AtZ), R_MOD) + intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_3_SLOT), R_MOD) + accumulator := addmod(accumulator, intermediateValue, R_MOD) + + // *= v * [custom_gate_selector] + accumulator := mulmod(accumulator, mload(STATE_V_SLOT), R_MOD) + pointMulAndAddIntoDest(VK_GATE_SELECTORS_1_X_SLOT, accumulator, dest) + } + + /// @notice Compute copy-permutation contribution to linearisation polynomial commitment multiplied by v + function addAssignPermutationLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) { + // alpha^4 + let factor := mload(STATE_POWER_OF_ALPHA_4_SLOT) + // Calculate the factor + { + // *= (a(z) + beta * z + gamma) + let zMulBeta := mulmod(mload(STATE_Z_SLOT), mload(STATE_BETA_SLOT), R_MOD) + let gamma := mload(STATE_GAMMA_SLOT) + + let intermediateValue := addmod(addmod(zMulBeta, gamma, R_MOD), stateOpening0AtZ, R_MOD) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (b(z) + beta * z * k0 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_0, R_MOD), gamma, R_MOD), + stateOpening1AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (c(z) + beta * z * k1 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_1, R_MOD), gamma, R_MOD), + stateOpening2AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // (d(z) + beta * z * k2 + gamma) + intermediateValue := addmod( + addmod(mulmod(zMulBeta, NON_RESIDUES_2, R_MOD), gamma, R_MOD), + stateOpening3AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + } + + // += alpha^5 * L_0(z) + let l0AtZ := mload(STATE_L_0_AT_Z_SLOT) + factor := addmod(factor, mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD), R_MOD) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [z_perm] during computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_perm] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + mstore(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + + // alpha^4 * beta * z_perm(z*omega) + factor := mulmod(mload(STATE_POWER_OF_ALPHA_4_SLOT), mload(STATE_BETA_SLOT), R_MOD) + factor := mulmod(factor, mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD) + { + // *= (a(z) + beta * sigma_0(z) + gamma) + let beta := mload(STATE_BETA_SLOT) + let gamma := mload(STATE_GAMMA_SLOT) + + let intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening0AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // *= (b(z) + beta * sigma_1(z) + gamma) + intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening1AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + + // *= (c(z) + beta * sigma_2(z) + gamma) + intermediateValue := addmod( + addmod( + mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD), + gamma, + R_MOD + ), + stateOpening2AtZ, + R_MOD + ) + factor := mulmod(factor, intermediateValue, R_MOD) + } + + // *= v * [sigma_3] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + pointMulIntoDest(VK_PERMUTATION_3_X_SLOT, factor, QUERIES_BUFFER_POINT_SLOT) + + pointSubAssign(dest, QUERIES_BUFFER_POINT_SLOT) + } + + /// @notice Compute lookup contribution to linearisation polynomial commitment multiplied by v + function addAssignLookupLinearisationContributionWithV( + dest, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ + ) { + // alpha^6 * v * z_lookup(z*omega) * (z - omega^{n-1}) * [s] + let factor := mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT) + factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD) + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [s] during computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [s] + mstore(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + + // gamma(1 + beta) + t(x) + beta * t(x*omega) + factor := mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT) + factor := mulmod(factor, mload(STATE_BETA_LOOKUP_SLOT), R_MOD) + factor := addmod(factor, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD) + factor := addmod(factor, mload(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT), R_MOD) + + // *= (gamma + f(z)) + // We should use fact that f(x) = + // lookup_selector(x) * (a(x) + eta * b(x) + eta^2 * c(x) + eta^3 * table_type(x)) + // to restore f(z) + let fReconstructed + { + fReconstructed := stateOpening0AtZ + let eta := mload(STATE_ETA_SLOT) + let currentEta := eta + + fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening1AtZ, R_MOD), R_MOD) + currentEta := mulmod(currentEta, eta, R_MOD) + fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening2AtZ, R_MOD), R_MOD) + currentEta := mulmod(currentEta, eta, R_MOD) + + // add type of table + fReconstructed := addmod( + fReconstructed, + mulmod(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT), currentEta, R_MOD), + R_MOD + ) + fReconstructed := mulmod(fReconstructed, mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT), R_MOD) + fReconstructed := addmod(fReconstructed, mload(STATE_GAMMA_LOOKUP_SLOT), R_MOD) + } + // *= -alpha^6 * (beta + 1) * (z - omega^{n-1}) + factor := mulmod(factor, fReconstructed, R_MOD) + factor := mulmod(factor, mload(STATE_BETA_PLUS_ONE_SLOT), R_MOD) + factor := sub(R_MOD, factor) + factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) + + factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD) + + // += alpha^7 * L_0(z) + factor := addmod( + factor, + mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_7_SLOT), R_MOD), + R_MOD + ) + + // += alpha^8 * L_{n-1}(z) + factor := addmod( + factor, + mulmod(mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_8_SLOT), R_MOD), + R_MOD + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [z_lookup] during computing [F] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_lookup] + factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) + mstore(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) + } + + /*////////////////////////////////////////////////////////////// + 4. Prepare queries + //////////////////////////////////////////////////////////////*/ + + /// @dev Here we compute the first and second parts of batched polynomial commitment + /// We use the formula: + /// [D0] = [t_0] + z^n * [t_1] + z^{2n} * [t_2] + z^{3n} * [t_3] + /// and + /// [D1] = main_gate_selector(z) * ( \ + /// a(z) * [q_a] + b(z) * [q_b] + c(z) * [q_c] + d(z) * [q_d] + | - main gate contribution + /// a(z) * b(z) * [q_ab] + a(z) * c(z) * [q_ac] + | + /// [q_const] + d(z*omega) * [q_{d_next}]) / + /// + /// + alpha * [custom_gate_selector] * ( \ + /// (a(z)^2 - b(z)) + | - custom gate contribution + /// (b(z)^2 - c(z)) * alpha + | + /// (a(z)*c(z) - d(z)) * alpha^2 ) / + /// + /// + alpha^4 * [z_perm] * \ + /// (a(z) + beta * z + gamma) * | + /// (b(z) + beta * z * k0 + gamma) * | + /// (c(z) + beta * z * k1 + gamma) * | + /// (d(z) + beta * z * k2 + gamma) | - permutation contribution + /// - alpha^4 * z_perm(z*omega) * beta * [sigma_3] * | + /// (a(z) + beta * sigma_0(z) + gamma) * | + /// (b(z) + beta * sigma_1(z) + gamma) * | + /// (c(z) + beta * sigma_2(z) + gamma) * | + /// + alpha^5 * L_0(z) * [z_perm] / + /// + /// - alpha^6 * (1 + beta') * (gamma' + f(z)) * (z - omega^{n-1}) * \ + /// (gamma'(1 + beta') + t(z) + beta' * t(z*omega)) * [z_lookup] | + /// + alpha^6 * z_lookup(z*omega) * (z - omega^{n-1}) * [s] | - lookup contribution + /// + alpha^7 * L_0(z) * [z_lookup] | + /// + alpha^8 * L_{n-1}(z) * [z_lookup] / + function prepareQueries() { + // Calculate [D0] + { + let zInDomainSize := mload(STATE_Z_IN_DOMAIN_SIZE) + let currentZ := zInDomainSize + + mstore(QUERIES_AT_Z_0_X_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT)) + mstore(QUERIES_AT_Z_0_Y_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT)) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + currentZ := mulmod(currentZ, zInDomainSize, R_MOD) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + currentZ := mulmod(currentZ, zInDomainSize, R_MOD) + + pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) + } + + // Calculate v * [D1] + // We are going to multiply all the points in the sum by v to save + // one scalar multiplication during [F] computation + { + let stateOpening0AtZ := mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT) + let stateOpening1AtZ := mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT) + let stateOpening2AtZ := mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT) + let stateOpening3AtZ := mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT) + + mainGateLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignRescueCustomGateLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignPermutationLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ, + stateOpening3AtZ + ) + + addAssignLookupLinearisationContributionWithV( + QUERIES_AT_Z_1_X_SLOT, + stateOpening0AtZ, + stateOpening1AtZ, + stateOpening2AtZ + ) + } + + // Also we should restore [t] for future computations + // [t] = [col_0] + eta*[col_1] + eta^2*[col_2] + eta^3*[col_3] + { + mstore(QUERIES_T_POLY_AGGREGATED_X_SLOT, mload(VK_LOOKUP_TABLE_0_X_SLOT)) + mstore(QUERIES_T_POLY_AGGREGATED_Y_SLOT, mload(VK_LOOKUP_TABLE_0_Y_SLOT)) + + let eta := mload(STATE_ETA_SLOT) + let currentEta := eta + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_1_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + currentEta := mulmod(currentEta, eta, R_MOD) + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_2_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + currentEta := mulmod(currentEta, eta, R_MOD) + + pointMulAndAddIntoDest(VK_LOOKUP_TABLE_3_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) + } + } + + /*////////////////////////////////////////////////////////////// + 5. Prepare aggregated commitment + //////////////////////////////////////////////////////////////*/ + + /// @dev Here we compute aggregated commitment for the final pairing + /// We use the formula: + /// [E] = ( t(z) + v * r(z) + /// + v^2*a(z) + v^3*b(z) + v^4*c(z) + v^5*d(z) + /// + v^6*main_gate_selector(z) + /// + v^7*sigma_0(z) + v^8*sigma_1(z) + v^9*sigma_2(z) + /// + v^10*t(z) + v^11*lookup_selector(z) + v^12*table_type(z) + /// + u * (v^13*z_perm(z*omega) + v^14*d(z*omega) + /// + v^15*s(z*omega) + v^16*z_lookup(z*omega) + v^17*t(z*omega) + /// ) + /// ) * [1] + /// and + /// [F] = [D0] + v * [D1] + /// + v^2*[a] + v^3*[b] + v^4*[c] + v^5*[d] + /// + v^6*[main_gate_selector] + /// + v^7*[sigma_0] + v^8*[sigma_1] + v^9*[sigma_2] + /// + v^10*[t] + v^11*[lookup_selector] + v^12*[table_type] + /// + u * ( v^13*[z_perm] + v^14*[d] + /// + v^15*[s] + v^16*[z_lookup] + v^17*[t] + /// ) + function prepareAggregatedCommitment() { + // Here we compute parts of [E] and [F] without u multiplier + let aggregationChallenge := 1 + let firstDCoeff + let firstTCoeff + + mstore(AGGREGATED_AT_Z_X_SLOT, mload(QUERIES_AT_Z_0_X_SLOT)) + mstore(AGGREGATED_AT_Z_Y_SLOT, mload(QUERIES_AT_Z_0_Y_SLOT)) + let aggregatedOpeningAtZ := mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT) + { + function updateAggregationChallenge( + queriesCommitmentPoint, + valueAtZ, + curAggregationChallenge, + curAggregatedOpeningAtZ + ) -> newAggregationChallenge, newAggregatedOpeningAtZ { + newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD) + pointMulAndAddIntoDest(queriesCommitmentPoint, newAggregationChallenge, AGGREGATED_AT_Z_X_SLOT) + newAggregatedOpeningAtZ := addmod( + curAggregatedOpeningAtZ, + mulmod(newAggregationChallenge, mload(valueAtZ), R_MOD), + R_MOD + ) + } + + // We don't need to multiply by v, because we have already computed v * [D1] + pointAddIntoDest(AGGREGATED_AT_Z_X_SLOT, QUERIES_AT_Z_1_X_SLOT, AGGREGATED_AT_Z_X_SLOT) + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_0_X_SLOT, + PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_1_X_SLOT, + PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + PROOF_STATE_POLYS_2_X_SLOT, + PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [d] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [d] + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + firstDCoeff := aggregationChallenge + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_GATE_SELECTORS_0_X_SLOT, + PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_0_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_1_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_PERMUTATION_2_X_SLOT, + PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + + // Here we can optimize one scalar multiplication by aggregating coefficients near [t] + // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [t] + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + firstTCoeff := aggregationChallenge + aggregatedOpeningAtZ := addmod( + aggregatedOpeningAtZ, + mulmod(aggregationChallenge, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD), + R_MOD + ) + + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_LOOKUP_SELECTOR_X_SLOT, + PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( + VK_LOOKUP_TABLE_TYPE_X_SLOT, + PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, + aggregationChallenge, + aggregatedOpeningAtZ + ) + } + mstore(AGGREGATED_OPENING_AT_Z_SLOT, aggregatedOpeningAtZ) + + // Here we compute parts of [E] and [F] with u multiplier + aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) + + let copyPermutationCoeff := addmod( + mload(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF), + mulmod(aggregationChallenge, mload(STATE_U_SLOT), R_MOD), + R_MOD + ) + + pointMulIntoDest( + PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, + copyPermutationCoeff, + AGGREGATED_AT_Z_OMEGA_X_SLOT + ) + let aggregatedOpeningAtZOmega := mulmod( + mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), + aggregationChallenge, + R_MOD + ) + + { + function updateAggregationChallenge( + queriesCommitmentPoint, + valueAtZ_Omega, + previousCoeff, + curAggregationChallenge, + curAggregatedOpeningAtZ_Omega + ) -> newAggregationChallenge, newAggregatedOpeningAtZ_Omega { + newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD) + let finalCoeff := addmod( + previousCoeff, + mulmod(newAggregationChallenge, mload(STATE_U_SLOT), R_MOD), + R_MOD + ) + pointMulAndAddIntoDest(queriesCommitmentPoint, finalCoeff, AGGREGATED_AT_Z_OMEGA_X_SLOT) + newAggregatedOpeningAtZ_Omega := addmod( + curAggregatedOpeningAtZ_Omega, + mulmod(newAggregationChallenge, mload(valueAtZ_Omega), R_MOD), + R_MOD + ) + } + + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_STATE_POLYS_3_X_SLOT, + PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, + firstDCoeff, + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_LOOKUP_S_POLY_X_SLOT, + PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, + mload(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF), + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, + PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, + mload(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF), + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( + QUERIES_T_POLY_AGGREGATED_X_SLOT, + PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, + firstTCoeff, + aggregationChallenge, + aggregatedOpeningAtZOmega + ) + } + mstore(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT, aggregatedOpeningAtZOmega) + + // Now we can merge both parts and get [E] and [F] + let u := mload(STATE_U_SLOT) + + // [F] + pointAddIntoDest( + AGGREGATED_AT_Z_X_SLOT, + AGGREGATED_AT_Z_OMEGA_X_SLOT, + PAIRING_PAIR_WITH_GENERATOR_X_SLOT + ) + + // [E] = (aggregatedOpeningAtZ + u * aggregatedOpeningAtZOmega) * [1] + let aggregatedValue := addmod( + mulmod(mload(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT), u, R_MOD), + mload(AGGREGATED_OPENING_AT_Z_SLOT), + R_MOD + ) + + mstore(PAIRING_BUFFER_POINT_X_SLOT, 1) + mstore(PAIRING_BUFFER_POINT_Y_SLOT, 2) + pointMulIntoDest(PAIRING_BUFFER_POINT_X_SLOT, aggregatedValue, PAIRING_BUFFER_POINT_X_SLOT) + } + + /*////////////////////////////////////////////////////////////// + 5. Pairing + //////////////////////////////////////////////////////////////*/ + + /// @notice Checks the final pairing + /// @dev We should check the equation: + /// e([W] + u * [W'], [x]_2) = e(z * [W] + u * z * omega * [W'] + [F] - [E], [1]_2), + /// where [F] and [E] were computed previously + /// + /// Also we need to check that e([P1], [x]_2) = e([P2], [1]_2) + /// where [P1] and [P2] are parts of the recursive proof + /// + /// We can aggregate both pairings into one for gas optimization: + /// e([W] + u * [W'] + u^2 * [P1], [x]_2) = + /// e(z * [W] + u * z * omega * [W'] + [F] - [E] + u^2 * [P2], [1]_2) + /// + /// u is a valid challenge for such aggregation, + /// because [P1] and [P2] are used in PI + function finalPairing() { + let u := mload(STATE_U_SLOT) + let z := mload(STATE_Z_SLOT) + let zOmega := mulmod(mload(STATE_Z_SLOT), OMEGA, R_MOD) + + // [F] - [E] + pointSubAssign(PAIRING_PAIR_WITH_GENERATOR_X_SLOT, PAIRING_BUFFER_POINT_X_SLOT) + + // +z * [W] + u * z * omega * [W'] + pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_X_SLOT, z, PAIRING_PAIR_WITH_GENERATOR_X_SLOT) + pointMulAndAddIntoDest( + PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, + mulmod(zOmega, u, R_MOD), + PAIRING_PAIR_WITH_GENERATOR_X_SLOT + ) + + // [W] + u * [W'] + mstore(PAIRING_PAIR_WITH_X_X_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT)) + mstore(PAIRING_PAIR_WITH_X_Y_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT)) + pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, u, PAIRING_PAIR_WITH_X_X_SLOT) + pointNegate(PAIRING_PAIR_WITH_X_X_SLOT) + + // Add recursive proof part + let uu := mulmod(u, u, R_MOD) + pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P1_X_SLOT, uu, PAIRING_PAIR_WITH_GENERATOR_X_SLOT) + pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P2_X_SLOT, uu, PAIRING_PAIR_WITH_X_X_SLOT) + + // Calculate pairing + { + mstore(0x000, mload(PAIRING_PAIR_WITH_GENERATOR_X_SLOT)) + mstore(0x020, mload(PAIRING_PAIR_WITH_GENERATOR_Y_SLOT)) + + mstore(0x040, G2_ELEMENTS_0_X1) + mstore(0x060, G2_ELEMENTS_0_X2) + mstore(0x080, G2_ELEMENTS_0_Y1) + mstore(0x0a0, G2_ELEMENTS_0_Y2) + + mstore(0x0c0, mload(PAIRING_PAIR_WITH_X_X_SLOT)) + mstore(0x0e0, mload(PAIRING_PAIR_WITH_X_Y_SLOT)) + + mstore(0x100, G2_ELEMENTS_1_X1) + mstore(0x120, G2_ELEMENTS_1_X2) + mstore(0x140, G2_ELEMENTS_1_Y1) + mstore(0x160, G2_ELEMENTS_1_Y2) + + let success := staticcall(gas(), 8, 0, 0x180, 0x00, 0x20) + if iszero(success) { + revertWithMessage(32, "finalPairing: precompile failure") + } + if iszero(mload(0)) { + revertWithMessage(29, "finalPairing: pairing failure") + } + } + } + + /*////////////////////////////////////////////////////////////// + Verification + //////////////////////////////////////////////////////////////*/ + + // Step 1: Load the proof and check the correctness of its parts + loadProof() + + // Step 2: Recompute all the challenges with the transcript + initializeTranscript() + + // Step 3: Check the quotient equality + verifyQuotientEvaluation() + + // Step 4: Compute queries [D0] and v * [D1] + prepareQueries() + + // Step 5: Compute [E] and [F] + prepareAggregatedCommitment() + + // Step 6: Check the final pairing with aggregated recursive proof + finalPairing() + + mstore(0, true) + return(0, 32) + } + } +} diff --git a/tools/rust-toolchain b/tools/rust-toolchain new file mode 100644 index 000000000..0834888f5 --- /dev/null +++ b/tools/rust-toolchain @@ -0,0 +1 @@ +1.72.0 diff --git a/tools/src/main.rs b/tools/src/main.rs new file mode 100644 index 000000000..39681bda2 --- /dev/null +++ b/tools/src/main.rs @@ -0,0 +1,322 @@ +use handlebars::Handlebars; +use serde_json::json; +use std::collections::HashMap; +use std::error::Error; +use std::fs; +use std::fs::File; +use std::io::{BufReader, Write}; + +use lazy_static::lazy_static; +use serde_json::{from_reader, Value}; +use structopt::StructOpt; + +#[derive(Debug, Clone, Copy)] +struct CommitmentSlot { + x: &'static str, + y: &'static str, +} + +#[derive(Debug, Clone, Copy)] +struct G2Elements { + x1: &'static str, + x2: &'static str, + y1: &'static str, + y2: &'static str, +} + +fn create_hash_map( + key_value_pairs: &[(&'static str, Type)], +) -> HashMap<&'static str, Type> { + let mut hash_map = HashMap::new(); + for &(key, value) in key_value_pairs { + hash_map.insert(key, value); + } + hash_map +} + +lazy_static! { + static ref COMMITMENTS_SLOTS: HashMap<&'static str, CommitmentSlot> = create_hash_map(&[ + ( + "gate_setup_commitments", + CommitmentSlot { + x: "VK_GATE_SETUP_{}_X_SLOT", + y: "VK_GATE_SETUP_{}_Y_SLOT" + } + ), + ( + "gate_selectors_commitments", + CommitmentSlot { + x: "VK_GATE_SELECTORS_{}_X_SLOT", + y: "VK_GATE_SELECTORS_{}_Y_SLOT" + } + ), + ( + "permutation_commitments", + CommitmentSlot { + x: "VK_PERMUTATION_{}_X_SLOT", + y: "VK_PERMUTATION_{}_Y_SLOT" + } + ), + ( + "lookup_tables_commitments", + CommitmentSlot { + x: "VK_LOOKUP_TABLE_{}_X_SLOT", + y: "VK_LOOKUP_TABLE_{}_Y_SLOT" + } + ), + ]); + static ref INDIVIDUAL_COMMITMENTS: HashMap<&'static str, CommitmentSlot> = create_hash_map(&[ + ( + "lookup_selector_commitment", + CommitmentSlot { + x: "VK_LOOKUP_SELECTOR_X_SLOT", + y: "VK_LOOKUP_SELECTOR_Y_SLOT" + } + ), + ( + "lookup_table_type_commitment", + CommitmentSlot { + x: "VK_LOOKUP_TABLE_TYPE_X_SLOT", + y: "VK_LOOKUP_TABLE_TYPE_Y_SLOT" + } + ), + ]); + static ref G2_ELEMENTS: HashMap<&'static str, G2Elements> = create_hash_map(&[( + "g2_elements", + G2Elements { + x1: "G2_ELEMENTS_{}_X1", + x2: "G2_ELEMENTS_{}_X2", + y1: "G2_ELEMENTS_{}_Y1", + y2: "G2_ELEMENTS_{}_Y2", + } + ),]); + static ref NON_RESIDUES: HashMap<&'static str, &'static str> = + create_hash_map(&[("non_residues", "NON_RESIDUES_{}"),]); +} + +#[derive(Debug, StructOpt)] +#[structopt( + name = "zksync_verifier_contract_generator", + about = "Tool for generating verifier contract using scheduler json key" +)] +struct Opt { + /// Input path to scheduler verification key file. + #[structopt( + short = "i", + long = "input_path", + default_value = "data/scheduler_key.json" + )] + input_path: String, + + /// Output path to verifier contract file. + #[structopt(short = "o", long = "output_path", default_value = "data/Verifier.sol")] + output_path: String, +} + +fn main() -> Result<(), Box> { + let opt = Opt::from_args(); + + let reader = BufReader::new(File::open(&opt.input_path)?); + + let vk: HashMap = from_reader(reader)?; + + let verifier_contract_template = fs::read_to_string("data/verifier_contract_template.txt")?; + + let verifier_contract_template = + insert_residue_elements_and_commitments(&verifier_contract_template, &vk)?; + + let mut file = File::create(opt.output_path)?; + + file.write_all(verifier_contract_template.as_bytes())?; + Ok(()) +} + +fn insert_residue_elements_and_commitments( + template: &str, + vk: &HashMap, +) -> Result> { + let reg = Handlebars::new(); + let residue_g2_elements = generate_residue_g2_elements(&vk); + let commitments = generate_commitments(&vk); + + let verifier_contract_template = + template.replace("{{residue_g2_elements}}", &residue_g2_elements); + + Ok(reg.render_template( + &verifier_contract_template, + &json!({"residue_g2_elements": residue_g2_elements, + "commitments": commitments}), + )?) +} + +fn format_mstore(hex_value: &str, slot: &str) -> String { + format!(" mstore({}, 0x{})\n", slot, hex_value) +} + +fn format_const(hex_value: &str, slot_name: &str) -> String { + let hex_value = hex_value.trim_start_matches('0'); + let formatted_hex_value = if hex_value.len() < 64 && hex_value.len() >= 1 { + format!("0{}", hex_value) + } else { + String::from(hex_value) + }; + format!( + " uint256 internal constant {} = 0x{};\n", + slot_name, formatted_hex_value + ) +} + +fn convert_list_to_hexadecimal(numbers: &Vec) -> String { + numbers + .iter() + .map(|v| format!("{:01$x}", v.as_u64().expect("Failed to parse as u64"), 16)) + .rev() + .collect::() +} + +fn extract_commitment_slots(items: &[Value], slot_tuple: CommitmentSlot) -> String { + items + .iter() + .enumerate() + .filter_map(|(idx, item)| { + let map = item.as_object().unwrap(); + let x = map.get("x").unwrap().as_array().unwrap(); + let y = map.get("y").unwrap().as_array().unwrap(); + let x = convert_list_to_hexadecimal(x); + let mstore_x = format_mstore(&x, &slot_tuple.x.replace("{}", &idx.to_string())); + let y = convert_list_to_hexadecimal(y); + let mstore_y = format_mstore(&y, &slot_tuple.y.replace("{}", &idx.to_string())); + Some(format!("{}{}", mstore_x, mstore_y)) + }) + .collect::>() + .join("") +} + +fn extract_non_residues(items: &[Value], slot_name: &str) -> String { + items + .iter() + .enumerate() + .map(|(idx, item)| { + let hex_value = convert_list_to_hexadecimal( + item.as_array().expect("Failed to parse item as array"), + ); + format_const(&hex_value, &slot_name.replace("{}", &idx.to_string())) + }) + .collect::>() + .join("") +} + +fn extract_individual_commitments(item: &Value, commitment_slot: CommitmentSlot) -> String { + let x = convert_list_to_hexadecimal( + item.get("x") + .expect("x value not found") + .as_array() + .expect("x value not an array"), + ); + let mut output = format_mstore(&x, commitment_slot.x); + + let y = convert_list_to_hexadecimal( + item.get("y") + .expect("y value not found") + .as_array() + .expect("y value not an array"), + ); + output.push_str(&format_mstore(&y, commitment_slot.y)); + + output +} + +fn extract_g2_elements(elements: &[Value], g2_elements: G2Elements) -> String { + let slots: [&str; 4] = [ + g2_elements.x1, + g2_elements.x2, + g2_elements.y1, + g2_elements.y2, + ]; + let xy_pairs = [("x", "c1"), ("x", "c0"), ("y", "c1"), ("y", "c0")]; + + elements + .iter() + .enumerate() + .flat_map(|(idx, element)| { + xy_pairs.iter().enumerate().map(move |(i, &(xy, c))| { + let field = element + .get(xy) + .expect(&format!("{} value not found", xy)) + .as_object() + .expect(&format!("{} value not an object", xy)); + + let c_value = convert_list_to_hexadecimal( + field + .get(c) + .expect(&format!("{} value not found", c)) + .as_array() + .expect(&format!("{} value not an array", c)), + ); + format_const(&c_value, &slots[i].replace("{}", &idx.to_string())) + }) + }) + .collect::>() + .join("") +} + +fn generate_commitments(vk: &HashMap) -> String { + let commitments_data = [ + ("gate_setup_commitments", "gate setup commitments"), + ("gate_selectors_commitments", "gate selectors commitments"), + ("permutation_commitments", "permutation commitments"), + ("lookup_tables_commitments", "lookup tables commitments"), + ]; + + let individual_commitments_data = [ + ("lookup_selector_commitment", "lookup selector commitment"), + ("lookup_table_type_commitment", "table type commitment"), + ]; + + let commitments = commitments_data + .iter() + .map(|(key, comment)| { + let data = vk.get(*key).unwrap().as_array().unwrap(); + format!( + "\n // {}\n{}", + comment, + extract_commitment_slots(data, COMMITMENTS_SLOTS[*key]) + ) + }) + .collect::>() + .join(""); + + let individual_commitments = individual_commitments_data + .iter() + .map(|(key, comment)| { + format!( + "\n // {}\n{}", + comment, + extract_individual_commitments(vk.get(*key).unwrap(), INDIVIDUAL_COMMITMENTS[*key]) + ) + }) + .collect::>() + .join(""); + + format!("{}{}", commitments, individual_commitments) +} + +fn generate_residue_g2_elements(vk: &HashMap) -> String { + let mut residue_g2_elements = String::new(); + + let vk_non_residues = vk.get("non_residues").unwrap().as_array().unwrap(); + residue_g2_elements.push_str("\n // non residues\n"); + residue_g2_elements.push_str(&extract_non_residues( + vk_non_residues, + NON_RESIDUES["non_residues"], + )); + + let vk_g2_elements = vk.get("g2_elements").unwrap().as_array().unwrap(); + residue_g2_elements.push_str("\n // g2 elements\n"); + residue_g2_elements.push_str(&extract_g2_elements( + vk_g2_elements, + G2_ELEMENTS["g2_elements"], + )); + + residue_g2_elements +}