diff --git a/.github/workflows/l1-contracts-ci.yaml b/.github/workflows/l1-contracts-ci.yaml index 5a27c65f8..deb4ab444 100644 --- a/.github/workflows/l1-contracts-ci.yaml +++ b/.github/workflows/l1-contracts-ci.yaml @@ -157,12 +157,15 @@ jobs: with: toolchain: 1.72.0 - - name: Generate Verifier.sol + - name: Generate verifiers working-directory: tools run: cargo run - - name: Compare - run: diff tools/data/Verifier.sol l1-contracts/contracts/state-transition/Verifier.sol + - name: Compare VerifierPlonk.sol + run: diff tools/data/VerifierPlonk.sol l1-contracts/contracts/state-transition/verifiers/VerifierPlonk.sol + + - name: Compare VerifierFflonk.sol + run: diff tools/data/VerifierFflonk.sol l1-contracts/contracts/state-transition/verifiers/VerifierFflonk.sol coverage: defaults: diff --git a/.github/workflows/slither.yaml b/.github/workflows/slither.yaml index 50c9194dc..25e5b60fe 100644 --- a/.github/workflows/slither.yaml +++ b/.github/workflows/slither.yaml @@ -42,10 +42,12 @@ jobs: - name: Remove non-compiled files run: | rm -rf ./l1-contracts/contracts/state-transition/utils/ - rm -rf ./l1-contracts/contracts/state-transition/Verifier.sol - rm -rf ./l1-contracts/contracts/state-transition/TestnetVerifier.sol - rm -rf ./l1-contracts/contracts/dev-contracts/test/VerifierTest.sol - rm -rf ./l1-contracts/contracts/dev-contracts/test/VerifierRecursiveTest.sol + rm -rf ./l1-contracts/contracts/state-transition/verifiers/DualVerifier.sol + rm -rf ./l1-contracts/contracts/state-transition/verifiers/VerifierPlonk.sol + rm -rf ./l1-contracts/contracts/state-transition/verifiers/VerifierFflonk.sol + rm -rf ./l1-contracts/contracts/state-transition/verifiers/TestnetVerifier.sol + rm -rf ./l1-contracts/contracts/dev-contracts/test/PlonkVerifierTest.sol + rm -rf ./l1-contracts/contracts/dev-contracts/test/PlonkVerifierRecursiveTest.sol - name: Run Slither working-directory: l1-contracts diff --git a/l1-contracts/contracts/common/L1ContractErrors.sol b/l1-contracts/contracts/common/L1ContractErrors.sol index 73ff72cc9..5f3e95614 100644 --- a/l1-contracts/contracts/common/L1ContractErrors.sol +++ b/l1-contracts/contracts/common/L1ContractErrors.sol @@ -305,6 +305,10 @@ error ZeroAddress(); error ZeroBalance(); // 0xc84885d4 error ZeroChainId(); +// 0xc352bb73 +error UnknownVerifierType(); +// 0xdf320f0a +error EmptyRecursiveAggregationInputLength(); enum SharedBridgeKey { PostUpgradeFirstBatch, diff --git a/l1-contracts/contracts/dev-contracts/test/VerifierRecursiveTest.sol b/l1-contracts/contracts/dev-contracts/test/PlonkVerifierRecursiveTest.sol similarity index 97% rename from l1-contracts/contracts/dev-contracts/test/VerifierRecursiveTest.sol rename to l1-contracts/contracts/dev-contracts/test/PlonkVerifierRecursiveTest.sol index e2ea7cbbc..951967378 100644 --- a/l1-contracts/contracts/dev-contracts/test/VerifierRecursiveTest.sol +++ b/l1-contracts/contracts/dev-contracts/test/PlonkVerifierRecursiveTest.sol @@ -2,10 +2,10 @@ pragma solidity 0.8.24; -import {Verifier} from "../../state-transition/Verifier.sol"; +import {VerifierPlonk} from "../../state-transition/verifiers/VerifierPlonk.sol"; /// @author Matter Labs -contract VerifierRecursiveTest is Verifier { +contract PlonkVerifierRecursiveTest is VerifierPlonk { // add this to be excluded from coverage report function test() internal virtual {} diff --git a/l1-contracts/contracts/dev-contracts/test/VerifierTest.sol b/l1-contracts/contracts/dev-contracts/test/PlonkVerifierTest.sol similarity index 97% rename from l1-contracts/contracts/dev-contracts/test/VerifierTest.sol rename to l1-contracts/contracts/dev-contracts/test/PlonkVerifierTest.sol index ef60794a0..2a7888328 100644 --- a/l1-contracts/contracts/dev-contracts/test/VerifierTest.sol +++ b/l1-contracts/contracts/dev-contracts/test/PlonkVerifierTest.sol @@ -2,10 +2,10 @@ pragma solidity 0.8.24; -import {Verifier} from "../../state-transition/Verifier.sol"; +import {VerifierPlonk} from "../../state-transition/verifiers/VerifierPlonk.sol"; /// @author Matter Labs -contract VerifierTest is Verifier { +contract PlonkVerifierTest is VerifierPlonk { // add this to be excluded from coverage report function test() internal virtual {} diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IVerifier.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IVerifier.sol index 97872c370..fac8333a4 100644 --- a/l1-contracts/contracts/state-transition/chain-interfaces/IVerifier.sol +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IVerifier.sol @@ -24,5 +24,5 @@ interface IVerifier { /// @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); + function verificationKeyHash() external view returns (bytes32); } diff --git a/l1-contracts/contracts/state-transition/chain-interfaces/IVerifierV2.sol b/l1-contracts/contracts/state-transition/chain-interfaces/IVerifierV2.sol new file mode 100644 index 000000000..ac0acff25 --- /dev/null +++ b/l1-contracts/contracts/state-transition/chain-interfaces/IVerifierV2.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +// We use a floating point pragma here so it can be used within other projects that interact with the ZKsync ecosystem without using our exact pragma version. +pragma solidity ^0.8.21; + +/// @title The interface of the Verifier contract, responsible for the zero knowledge proof verification. +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +interface IVerifierV2 { + /// @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) external view returns (bool); + + /// @notice Calculates a keccak256 hash of the runtime loaded verification keys. + /// @return vkHash The keccak256 hash of the loaded verification keys. + function verificationKeyHash() external view returns (bytes32); +} diff --git a/l1-contracts/contracts/state-transition/verifiers/DualVerifier.sol b/l1-contracts/contracts/state-transition/verifiers/DualVerifier.sol new file mode 100644 index 000000000..f1b669381 --- /dev/null +++ b/l1-contracts/contracts/state-transition/verifiers/DualVerifier.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {IVerifierV2} from "../chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "../chain-interfaces/IVerifier.sol"; +import {UnknownVerifierType, EmptyRecursiveAggregationInputLength} from "../../common/L1ContractErrors.sol"; + +/// @title Dual Verifier +/// @author Matter Labs +/// @custom:security-contact security@matterlabs.dev +/// @notice This contract wraps two different verifiers (FFLONK and PLONK) and routes zk-SNARK proof verification +/// to the correct verifier based on the provided proof type. It reuses the same interface as on the original `Verifier` +/// contract, while abusing on of the fields (`_recursiveAggregationInput`) for proof verification type. The contract is +/// needed for the smooth transition from PLONK based verifier to the FFLONK verifier. +contract DualVerifier is IVerifier { + /// @notice The latest FFLONK verifier contract. + IVerifierV2 public immutable FFLONK_VERIFIER; + + /// @notice PLONK verifier contract. + IVerifier public immutable PLONK_VERIFIER; + + /// @notice Type of verification for FFLONK verifier. + uint256 internal constant FFLONK_VERIFICATION_TYPE = 0; + + /// @notice Type of verification for PLONK verifier. + uint256 internal constant PLONK_VERIFICATION_TYPE = 1; + + /// @param _fflonkVerifier The address of the FFLONK verifier contract. + /// @param _plonkVerifier The address of the PLONK verifier contract. + constructor(IVerifierV2 _fflonkVerifier, IVerifier _plonkVerifier) { + FFLONK_VERIFIER = _fflonkVerifier; + PLONK_VERIFIER = _plonkVerifier; + } + + /// @notice Routes zk-SNARK proof verification to the appropriate verifier (FFLONK or PLONK) based on the proof type. + /// @param _publicInputs The public inputs to the proof. + /// @param _proof The zk-SNARK proof itself. + /// @param _recursiveAggregationInput The recursive aggregation input, used to determine which verifier to use. + /// The first element determines the verifier type. + /// - 0 indicates the FFLONK verifier should be used. + /// - 1 indicates the PLONK verifier should be used. + /// @return Returns `true` if the proof verification succeeds, otherwise throws an error. + function verify( + uint256[] calldata _publicInputs, + uint256[] calldata _proof, + uint256[] calldata _recursiveAggregationInput + ) external view virtual returns (bool) { + // Ensure the recursive aggregation input has a valid length (at least one element + // for the proof system differentiator). + if (_recursiveAggregationInput.length == 0) { + revert EmptyRecursiveAggregationInputLength(); + } + + // The first element of `_recursiveAggregationInput` determines the verifier type (either FFLONK or PLONK). + uint256 verifierType = _recursiveAggregationInput[0]; + if (verifierType == FFLONK_VERIFICATION_TYPE) { + return FFLONK_VERIFIER.verify(_publicInputs, _proof); + } else if (verifierType == PLONK_VERIFICATION_TYPE) { + return + PLONK_VERIFIER.verify( + _publicInputs, + _proof, + _extractRecursiveAggregationInput(_recursiveAggregationInput) + ); + } + // If the verifier type is unknown, revert with an error. + else { + revert UnknownVerifierType(); + } + } + + /// @inheritdoc IVerifier + function verificationKeyHash() external view returns (bytes32) { + return PLONK_VERIFIER.verificationKeyHash(); + } + + /// @notice Calculates a keccak256 hash of the runtime loaded verification keys from the selected verifier. + /// @return The keccak256 hash of the loaded verification keys based on the verifier. + function verificationKeyHash(uint256 _verifierType) external view returns (bytes32) { + if (_verifierType == FFLONK_VERIFICATION_TYPE) { + return FFLONK_VERIFIER.verificationKeyHash(); + } else if (_verifierType == PLONK_VERIFICATION_TYPE) { + return PLONK_VERIFIER.verificationKeyHash(); + } + // If the verifier type is unknown, revert with an error. + else { + revert UnknownVerifierType(); + } + } + + /// @notice Extract the recursive aggregation input by removing the first element (proof type differentiator). + /// @param _recursiveAggregationInput The original recursive aggregation input array. + /// @return result A new array with the first element removed. The first element was used as a hack for + /// differentiator between FFLONK and PLONK proofs. + function _extractRecursiveAggregationInput( + uint256[] calldata _recursiveAggregationInput + ) internal pure returns (uint256[] memory result) { + uint256 length = _recursiveAggregationInput.length; + // Allocate memory for the new array (length - 1) since the first element is omitted. + result = new uint256[](length - 1); + + // Copy elements starting from index 1 (the second element) of the original array. + for (uint256 i = 1; i < length; ++i) { + result[i - 1] = _recursiveAggregationInput[i]; + } + } +} diff --git a/l1-contracts/contracts/state-transition/TestnetVerifier.sol b/l1-contracts/contracts/state-transition/verifiers/TestnetVerifier.sol similarity index 76% rename from l1-contracts/contracts/state-transition/TestnetVerifier.sol rename to l1-contracts/contracts/state-transition/verifiers/TestnetVerifier.sol index 6e97fed05..a778344fb 100644 --- a/l1-contracts/contracts/state-transition/TestnetVerifier.sol +++ b/l1-contracts/contracts/state-transition/verifiers/TestnetVerifier.sol @@ -2,8 +2,9 @@ pragma solidity 0.8.24; -import {Verifier} from "./Verifier.sol"; -import {IVerifier} from "./chain-interfaces/IVerifier.sol"; +import {DualVerifier} from "./DualVerifier.sol"; +import {IVerifierV2} from "../chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "../chain-interfaces/IVerifier.sol"; /// @author Matter Labs /// @custom:security-contact security@matterlabs.dev @@ -11,8 +12,8 @@ import {IVerifier} from "./chain-interfaces/IVerifier.sol"; /// @dev This contract is used to skip the zkp verification for the testnet environment. /// If the proof is not empty, it will verify it using the main verifier contract, /// otherwise, it will skip the verification. -contract TestnetVerifier is Verifier { - constructor() { +contract TestnetVerifier is DualVerifier { + constructor(IVerifierV2 _fflonkVerifier, IVerifier _plonkVerifier) DualVerifier(_fflonkVerifier, _plonkVerifier) { assert(block.chainid != 1); } diff --git a/l1-contracts/contracts/state-transition/verifiers/VerifierFflonk.sol b/l1-contracts/contracts/state-transition/verifiers/VerifierFflonk.sol new file mode 100644 index 000000000..3e0f5a573 --- /dev/null +++ b/l1-contracts/contracts/state-transition/verifiers/VerifierFflonk.sol @@ -0,0 +1,1600 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {IVerifierV2} from "../chain-interfaces/IVerifierV2.sol"; + +/// @title Fflonk Verifier Implementation +/// @author Matter Labs +/// @notice FFT inspired version of PlonK to optimize on-chain gas cost +/// @dev For better understanding of the protocol follow the below papers: +/// * Fflonk Paper: https://eprint.iacr.org/2021/1167 +/// @custom:security-contact security@matterlabs.dev +contract VerifierFflonk is IVerifierV2 { + // ================Constants================ + uint32 internal constant DST_0 = 0; + uint32 internal constant DST_1 = 1; + uint32 internal constant DST_CHALLENGE = 2; + uint256 internal constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint256 internal constant Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 internal constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 internal constant BN254_B_COEFF = 3; + + // ================Verification Key================ + uint256 internal constant VK_NUM_INPUTS = 1; + // [C0]1 = qL(X^8)+ X*qR(X^8)+ X^2*qO(X^8)+ X^3*qM(X^8)+ X^4*qC(X^8)+ X^5*Sσ1(X^8)+ X^6*Sσ2(X^8)+ X^7*Sσ3(X^8) + uint256 internal constant VK_C0_G1_X = 0x15c99dbc62b8191204ff93984b0de4fb7c79ac7a1ef2c94f4ce940319a2408b2; + uint256 internal constant VK_C0_G1_Y = 0x0521b86a104e07c8971bf2e17d7665d59df7566c08e6e0c9750f584bb24084ce; + // k1 = 5, k2 = 7 + uint256 internal constant VK_NON_RESIDUES_0 = 0x0000000000000000000000000000000000000000000000000000000000000005; + uint256 internal constant VK_NON_RESIDUES_1 = 0x0000000000000000000000000000000000000000000000000000000000000007; + // G2 Elements = [1]_2, [s]_2 + uint256 internal constant VK_G2_ELEMENT_0_X1 = 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2; + uint256 internal constant VK_G2_ELEMENT_0_X2 = 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed; + uint256 internal constant VK_G2_ELEMENT_0_Y1 = 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b; + uint256 internal constant VK_G2_ELEMENT_0_Y2 = 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa; + uint256 internal constant VK_G2_ELEMENT_1_X1 = 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1; + uint256 internal constant VK_G2_ELEMENT_1_X2 = 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0; + uint256 internal constant VK_G2_ELEMENT_1_Y1 = 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4; + uint256 internal constant VK_G2_ELEMENT_1_Y2 = 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55; + + // Memory slots from 0x000 to 0x200 are reserved for intermediate computations and call to precompiles. + + // ================Transcript================ + // ================Constants================ + uint256 internal constant ONE = 1; + uint256 internal constant DOMAIN_SIZE = 8388608; + uint256 internal constant OMEGA = 0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863; + // ========================================= + uint256 internal constant TRANSCRIPT_BEGIN_SLOT = 0x200; + uint256 internal constant TRANSCRIPT_DST_BYTE_SLOT = 0x203; + uint256 internal constant TRANSCRIPT_STATE_0_SLOT = 0x204; + uint256 internal constant TRANSCRIPT_STATE_1_SLOT = 0x224; + uint256 internal constant TRANSCRIPT_CHALLENGE_SLOT = 0x244; + + // ================PartialVerifierState================ + // copy-permutation challenges + uint256 internal constant PVS_BETA = 0x264 + 0x00; + uint256 internal constant PVS_GAMMA = 0x264 + 0x20; + // evaluation challenges + uint256 internal constant PVS_R = 0x264 + 0x40; + uint256 internal constant PVS_Z = 0x264 + 0x60; + uint256 internal constant PVS_Z_OMEGA = 0x264 + 0x80; + // aggregation challenge + uint256 internal constant PVS_ALPHA_0 = 0x264 + 0xa0; + uint256 internal constant PVS_ALPHA_1 = 0x264 + 0xc0; + // final evaluation challenge + uint256 internal constant PVS_Y = 0x264 + 0xe0; + // convenience + uint256 internal constant PVS_VANISHING_AT_Z = 0x264 + 0x100; + uint256 internal constant PVS_VANISHING_AT_Z_INV = 0x264 + 0x120; + uint256 internal constant PVS_L_0_AT_Z = 0x264 + 0x140; + uint256 internal constant MAIN_GATE_QUOTIENT_AT_Z = 0x264 + 0x160; + uint256 internal constant COPY_PERM_FIRST_QUOTIENT_AT_Z = 0x264 + 0x180; + uint256 internal constant COPY_PERM_SECOND_QUOTIENT_AT_Z = 0x264 + 0x1a0; + // ================Opening State================ + // h0, h1, h2, h2_shifted + uint256 internal constant OPS_OPENING_POINTS = 0x264 + 0x1c0 + 0x00; // 4 slots + uint256 internal constant OPS_Y_POWS = 0x264 + 0x1c0 + 0x80; // 9 SLOTS + + // ================Pairing State================ + + uint256 internal constant PS_VANISHING_AT_Y = 0x264 + 0x1c0 + 0x1a0; + uint256 internal constant PS_INV_ZTS0_AT_Y = 0x264 + 0x1c0 + 0x1c0; + uint256 internal constant PS_SET_DIFFERENCES_AT_Y = 0x264 + 0x1c0 + 0x1e0; // 3 slots + uint256 internal constant PS_MINUS_Z = 0x264 + 0x1c0 + 0x240; // 2 slots + uint256 internal constant PS_R_EVALS = 0x264 + 0x1c0 + 0x280; // 3 slots + + // ================In Memory(from Proof)================ + uint256 internal constant MEM_PROOF_PUBLIC_INPUT_SLOT = 0x264 + 0x1c0 + 0x2e0; + + uint256 internal constant MEM_PROOF_COMMITMENT_0_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0x20; + uint256 internal constant MEM_PROOF_COMMITMENT_0_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x40; + uint256 internal constant MEM_PROOF_COMMITMENT_1_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0x60; + uint256 internal constant MEM_PROOF_COMMITMENT_1_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x80; + uint256 internal constant MEM_PROOF_COMMITMENT_2_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0xa0; + uint256 internal constant MEM_PROOF_COMMITMENT_2_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0xc0; + uint256 internal constant MEM_PROOF_COMMITMENT_3_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0xe0; + uint256 internal constant MEM_PROOF_COMMITMENT_3_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x100; + + uint256 internal constant MEM_PROOF_EVALUATIONS = 0x264 + 0x1c0 + 0x2e0 + 0x120; // 15 slots + + uint256 internal constant MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x1e0; // 1 slots + + uint256 internal constant MEM_LAGRANGE_BASIS_DENOMS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x200; //18 slots + uint256 internal constant MEM_LAGRANGE_BASIS_DENOM_PRODUCTS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x440; // 18 slots + uint256 internal constant MEM_PROOF_LAGRANGE_BASIS_EVALS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x680; // 18 Slots + + // ================Constants================ + uint256 internal constant PROOF_PUBLIC_INPUTS_LENGTH = 1; + uint256 internal constant PROOF_LENGTH = 24; + uint256 internal constant PROOF_EVALUATIONS_LENGTH = 15; + uint256 internal constant TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH = 18; + + /// @inheritdoc IVerifierV2 + function verificationKeyHash() external pure returns (bytes32 vkHash) { + return + keccak256( + abi.encodePacked( + VK_NUM_INPUTS, + VK_C0_G1_X, + VK_C0_G1_Y, + VK_NON_RESIDUES_0, + VK_NON_RESIDUES_1, + VK_G2_ELEMENT_0_X1, + VK_G2_ELEMENT_0_X2, + VK_G2_ELEMENT_0_Y1, + VK_G2_ELEMENT_0_Y2, + VK_G2_ELEMENT_1_X1, + VK_G2_ELEMENT_1_X2, + VK_G2_ELEMENT_1_Y1, + VK_G2_ELEMENT_1_Y2 + ) + ); + } + + /// @inheritdoc IVerifierV2 + function verify( + uint256[] calldata, // _publicInputs + uint256[] calldata // _proof + ) external view virtual returns (bool) { + // Beginning 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 { + // load public inputs and proof from the calldata + load_inputs() + initialize_transcript() + // identities at verifier's point + compute_main_gate_quotient() + compute_copy_permutation_quotients() + // openings + initialize_opening_state() + // final pairing + let result := check_openings() + mstore(0, result) + return(0, 0x20) + + function load_inputs() { + // 1. Load public inputs + let publicInputOffset := calldataload(0x04) + let publicInputLengthInWords := calldataload(add(publicInputOffset, 0x04)) + // We expect only one public input + if iszero(eq(publicInputLengthInWords, PROOF_PUBLIC_INPUTS_LENGTH)) { + revertWithMessage(32, "public input length is incorrect") + } + mstore(MEM_PROOF_PUBLIC_INPUT_SLOT, mod(calldataload(add(publicInputOffset, 0x24)), R_MOD)) + + // 2. Load proof + let proofLengthOffset := calldataload(0x24) + let proofLengthInWords := calldataload(add(proofLengthOffset, 0x04)) + + if iszero(eq(proofLengthInWords, PROOF_LENGTH)) { + revertWithMessage(25, "proof length is incorrect") + } + let proofOffset := add(proofLengthOffset, 0x24) + // Note: We don't accept the point-at-infinity as a valid input for the commitments considering the security risks involved, + // as it may aid in proof manipulation and final pairing computation. + { + let x := mod(calldataload(proofOffset), Q_MOD) + let y := mod(calldataload(add(proofOffset, 0x20)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) { + revertWithMessage(28, "commitment 0 is not on curve") + } + mstore(MEM_PROOF_COMMITMENT_0_G1_Y, y) + mstore(MEM_PROOF_COMMITMENT_0_G1_X, x) + } + { + let x := mod(calldataload(add(proofOffset, 0x40)), Q_MOD) + let y := mod(calldataload(add(proofOffset, 0x60)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) { + revertWithMessage(28, "commitment 1 is not on curve") + } + mstore(MEM_PROOF_COMMITMENT_1_G1_Y, y) + mstore(MEM_PROOF_COMMITMENT_1_G1_X, x) + } + { + let x := mod(calldataload(add(proofOffset, 0x80)), Q_MOD) + let y := mod(calldataload(add(proofOffset, 0xa0)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) { + revertWithMessage(28, "commitment 2 is not on curve") + } + mstore(MEM_PROOF_COMMITMENT_2_G1_Y, y) + mstore(MEM_PROOF_COMMITMENT_2_G1_X, x) + } + { + let x := mod(calldataload(add(proofOffset, 0xc0)), Q_MOD) + let y := mod(calldataload(add(proofOffset, 0xe0)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) { + revertWithMessage(28, "commitment 3 is not on curve") + } + mstore(MEM_PROOF_COMMITMENT_3_G1_Y, y) + mstore(MEM_PROOF_COMMITMENT_3_G1_X, x) + } + proofOffset := add(proofOffset, 0x100) + + for { + let i := 0 + } lt(i, PROOF_EVALUATIONS_LENGTH) { + i := add(i, 1) + } { + let eval := mod(calldataload(add(proofOffset, mul(i, 0x20))), R_MOD) + let slot := add(MEM_PROOF_EVALUATIONS, mul(i, 0x20)) + mstore(slot, eval) + } + proofOffset := add(proofOffset, mul(PROOF_EVALUATIONS_LENGTH, 0x20)) + + mstore(MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE, mod(calldataload(proofOffset), R_MOD)) + } + + /** + * @dev Commits data in the transcript then gets the challenges + * @notice that at this point, the transcript only has public inputs + * But luckily prover doesn't need any randomness in the first round + * so that prover has no control over the values because quotients are + * separated(there is no quotient aggregation neither in this round nor all rounds) + * + * w = 0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863 + */ + function initialize_transcript() { + if iszero(lt(DOMAIN_SIZE, R_MOD)) { + revertWithMessage(26, "Domain size >= R_MOD [ITS]") + } + if iszero(lt(OMEGA, R_MOD)) { + revertWithMessage(20, "Omega >= R_MOD [ITS]") + } + for { + let i := 0 + } lt(i, VK_NUM_INPUTS) { + i := add(i, 1) + } { + update_transcript(mload(add(MEM_PROOF_PUBLIC_INPUT_SLOT, mul(i, 0x20)))) + } + // commit first round commitment: preprocessed polynomials + update_transcript(VK_C0_G1_X) + update_transcript(VK_C0_G1_Y) + + // commit second round commitment: witnesses and gate identities + update_transcript(mload(MEM_PROOF_COMMITMENT_0_G1_X)) + update_transcript(mload(MEM_PROOF_COMMITMENT_0_G1_Y)) + + // copy-permutation challenges + mstore(PVS_BETA, get_challenge(0)) + mstore(PVS_GAMMA, get_challenge(1)) + // commit third round commitment: copy-perm + update_transcript(mload(MEM_PROOF_COMMITMENT_1_G1_X)) + update_transcript(mload(MEM_PROOF_COMMITMENT_1_G1_Y)) + // get evaluation challenge + // all system polynomials will be evaluated at z + // then combined polynomials will be opened at h_i = r^power_i + // then it becomes e.g C_i(X) = f_0(x^2) + x*f(x^2) in case of two polynomials + mstore(PVS_R, get_challenge(2)) + // commit all evaluations + for { + let i := 0 + } lt(i, PROOF_EVALUATIONS_LENGTH) { + i := add(i, 1) + } { + update_transcript(mload(add(MEM_PROOF_EVALUATIONS, mul(i, 0x20)))) + } + // get aggregation challenge + mstore(PVS_ALPHA_0, get_challenge(3)) + mstore(PVS_ALPHA_1, mulmod(mload(PVS_ALPHA_0), mload(PVS_ALPHA_0), R_MOD)) + // commit w(X) + update_transcript(mload(MEM_PROOF_COMMITMENT_2_G1_X)) + update_transcript(mload(MEM_PROOF_COMMITMENT_2_G1_Y)) + // opening challenge + mstore(PVS_Y, get_challenge(4)) + mstore(PVS_Z, modexp(mload(PVS_R), 24)) + // grand product of copy-permutation needs to be opened at shifted position + mstore(PVS_Z_OMEGA, mulmod(mload(PVS_Z), OMEGA, R_MOD)) + // Z_h(z) = X^N - 1 + mstore(PVS_VANISHING_AT_Z, addmod(modexp(mload(PVS_Z), DOMAIN_SIZE), sub(R_MOD, ONE), R_MOD)) + // L0(z) = 1/(N*(X-1)) * (X^N - 1) + mstore( + PVS_L_0_AT_Z, + modexp(mulmod(addmod(mload(PVS_Z), sub(R_MOD, ONE), R_MOD), DOMAIN_SIZE, R_MOD), sub(R_MOD, 2)) + ) + mstore(PVS_L_0_AT_Z, mulmod(mload(PVS_L_0_AT_Z), mload(PVS_VANISHING_AT_Z), R_MOD)) + mstore(PVS_VANISHING_AT_Z_INV, modexp(mload(PVS_VANISHING_AT_Z), sub(R_MOD, 2))) + } + + /** + * @dev Computes main gate quotient T0(ζ) + * T0(ζ) = (qm(ζ)*a(ζ)*b(ζ) + qa(ζ)*a(ζ) + qb(ζ)*b(ζ) + qc(ζ)*c(ζ) + qconst(ζ) + PI*L0(ζ)) * ZH(ζ)^-1 + */ + function compute_main_gate_quotient() { + // q_const + let rhs := mload(add(MEM_PROOF_EVALUATIONS, mul(4, 0x20))) + rhs := addmod(rhs, mulmod(mload(PVS_L_0_AT_Z), mload(MEM_PROOF_PUBLIC_INPUT_SLOT), R_MOD), R_MOD) + for { + let i := 0 + } lt(i, 3) { + i := add(i, 1) + } { + rhs := addmod( + rhs, + mulmod( + mload(add(MEM_PROOF_EVALUATIONS, mul(i, 0x20))), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, i), 0x20))), + R_MOD + ), + R_MOD + ) + } + // q_m*A*B + rhs := mulmod( + addmod( + rhs, + mulmod( + mulmod( + mload(add(MEM_PROOF_EVALUATIONS, mul(3, 0x20))), + mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(9, 0x20))), + R_MOD + ), + R_MOD + ), + mload(PVS_VANISHING_AT_Z_INV), + R_MOD + ) + mstore(MAIN_GATE_QUOTIENT_AT_Z, rhs) + } + + /** + * @dev Computes copy permutation quotients T1(ζ) & T2(ζ) + * T1(ζ) = ((z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ)) + * −(z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) * ZH(ζ)^-1 + * T2(ζ) = (z(ζ)−1)*L0(ζ)*ZH(ζ)^-1 + */ + function compute_copy_permutation_quotients() { + let tmp + let tmp2 + // (c(ζ)+k2*β*ζ+γ) + let rhs := addmod( + addmod( + mulmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), VK_NON_RESIDUES_1, R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 2), 0x20))), + R_MOD + ) + // (b(ζ)+k1*β*ζ+γ) + tmp := addmod( + addmod( + mulmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), VK_NON_RESIDUES_0, R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 1), 0x20))), + R_MOD + ) + // (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ) + rhs := mulmod(rhs, tmp, R_MOD) + // (z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ) + rhs := mulmod( + mulmod( + rhs, + addmod( + addmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), mload(PVS_GAMMA), R_MOD), + mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))), + R_MOD + ), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), + R_MOD + ) + + // (z(ζω) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) + tmp2 := mulmod( + mulmod( + addmod( + addmod( + mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(add(5, 2), 0x20))), R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 2), 0x20))), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), + R_MOD + ), + addmod( + addmod( + mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(add(5, 1), 0x20))), R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 1), 0x20))), + R_MOD + ), + R_MOD + ) + // (a(ζ)+β*sσ1(ζ)+γ) + tmp := addmod( + addmod( + mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(5, 0x20))), R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))), + R_MOD + ) + // z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ) + tmp2 := mulmod(tmp2, tmp, R_MOD) + // −(z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) + tmp2 := sub(R_MOD, tmp2) + // ((z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ)) − (z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) * ZH(ζ)^-1 + rhs := mulmod(addmod(rhs, tmp2, R_MOD), mload(PVS_VANISHING_AT_Z_INV), R_MOD) + mstore(COPY_PERM_FIRST_QUOTIENT_AT_Z, rhs) + + // (z(ζ)−1)*L0(ζ)*ZH(ζ)^-1 + rhs := mulmod( + mulmod( + addmod(mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), sub(R_MOD, 1), R_MOD), + mload(PVS_L_0_AT_Z), + R_MOD + ), + mload(PVS_VANISHING_AT_Z_INV), + R_MOD + ) + mstore(COPY_PERM_SECOND_QUOTIENT_AT_Z, rhs) + } + + /** + * @dev Computes partial lagrange basis evaluations Li(y)_numerator {i = [start..(start+num_polys))} using montgomery lagrange basis inverses sent with proof. + * Li(y)_numerator = (w_i * (y^{num_polys} - h^{num_polys})) + * Li(y)_denominator = (num_polys * h^{num_polys-1} * (y - (h * w_i))) + * Li(y) = Li(y)_numerator / Li(y)_denominator = (w_i * (y^{num_polys} - h^{num_polys})) / (num_polys * h^{num_polys-1} * (y - (h * w_i))) + * + * Also calculates the products of the denominators of the lagrange basis evaluations: + * Li(y)_denominators_product = Li(y)_previous_denominators_product * (∏(Li(y)_denominator {i = [start..(start+num_polys))})) + */ + function precompute_partial_lagrange_basis_evaluations(start, num_polys, y, omega, h, product) + -> interim_product + { + if gt(add(start, num_polys), TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH) { + revertWithMessage(31, "Precompute Eval. Error [PLBEI1]") + } + let tmp := h + let loop_length := sub(num_polys, 2) + // h^{num_polys-1} + for { + let i := 0 + } lt(i, loop_length) { + i := add(i, 1) + } { + tmp := mulmod(tmp, h, R_MOD) + } + // num_polys * h^{num_polys-1} + let constant_part := mulmod(num_polys, tmp, R_MOD) + + // y^{num_polys} + let y_pow := mload(add(OPS_Y_POWS, mul(num_polys, 0x20))) + // h^{num_polys} + let num_at_y := mulmod(tmp, h, R_MOD) + // -h^{num_polys} + num_at_y := sub(R_MOD, num_at_y) + // (y^{num_polys} - h^{num_polys}) + num_at_y := addmod(num_at_y, y_pow, R_MOD) + + let current_omega := 1 + for { + let i := 0 + } lt(i, num_polys) { + i := add(i, 1) + } { + // h*w_i + tmp := mulmod(current_omega, h, R_MOD) + // -h*w_i + tmp := sub(R_MOD, tmp) + // y-(h*w_i) + tmp := addmod(tmp, y, R_MOD) + // (num_polys * h^{num_polys-1} * (y - (h * w_i))) + tmp := mulmod(tmp, constant_part, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(start, i), 0x20)), tmp) + + product := mulmod(product, tmp, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(start, i), 0x20)), product) + // Li(y) = (W_i * (y^{num_polys} - h^{num_polys})) + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(start, i), 0x20)), + mulmod(num_at_y, current_omega, R_MOD) + ) + + // w_i {i = i+1} + current_omega := mulmod(current_omega, omega, R_MOD) + } + + interim_product := product + } + + /** + * @dev Computes partial lagrange basis evaluations Li(y)_numerator = {i = [start..(start+num_polys))} & Li(y)_numerator {i = [(start+num_polys)..(start+(2*num_polys)))} using montgomery lagrange basis inverses sent with proof. + * For Li(y)_numerator{i = [start..(start+num_polys))}: + * Li(y)_numerator = w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) + * Li(y)_denominator = (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i))) + * Li(y) = Li(y)_numerator / Li(y)_denominator = (w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})))) / (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i))) + * + * For Li(y)_numerator{i = [(start+num_polys)..(start+(2*num_polys)))} + * Li(y)_numerator = w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) + * Li(y)_denominator = (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i))) + * Li(y) = Li(y)_numerator / Li(y)_denominator = (w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) ) / (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i))) + * + * Also calculates the products of the denominators of the lagrange basis evaluations: + * Li(y)_denominators_product = Li(y)_previous_denominators_product * (∏(Li(y)_denominator {i = [start..(start+num_polys))})) * (∏(Li(y)_denominator {i = [(start+num_polys)..(start+(2*num_polys)))})) + */ + + function precompute_partial_lagrange_basis_evaluations_for_union_set( + start, + num_polys, + y, + omega, + h, + h_shifted, + product + ) -> final_product { + if gt(add(start, mul(2, num_polys)), TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH) { + revertWithMessage(32, "Precompute Eval. Error [PLBEIU1]") + } + let h_pows_0 := h + let h_pows_1 := h_shifted + let loop_length := sub(num_polys, 2) + // h^{num_polys-1} & h_s^{num_polys-1} + for { + let i := 0 + } lt(i, loop_length) { + i := add(i, 1) + } { + h_pows_0 := mulmod(h_pows_0, h, R_MOD) + h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD) + } + let constant_parts_0 := h_pows_0 + let constant_parts_1 := h_pows_1 + // h^{num_polys} + h_pows_0 := mulmod(h_pows_0, h, R_MOD) + // h_s^{num_polys} + h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD) + + // h^{num_polys-1} * h_s^{num_polys} + constant_parts_0 := mulmod(constant_parts_0, h_pows_1, R_MOD) + // -h^{num_polys-1} * h_s^{num_polys} + constant_parts_0 := sub(R_MOD, constant_parts_0) + // h_s^{num_polys-1} * h^{num_polys} + constant_parts_1 := mulmod(constant_parts_1, h_pows_0, R_MOD) + // -h_s^{num_polys-1} * h^{num_polys} + constant_parts_1 := sub(R_MOD, constant_parts_1) + + // y^{num_polys} + let t_2 := mload(add(OPS_Y_POWS, mul(num_polys, 0x20))) + // h^{num_polys} * h_s^{num_polys} + let t_1 := mulmod(h_pows_0, h_pows_1, R_MOD) + // h^{num_polys} + h_s^{num_polys} + let t_0 := addmod(h_pows_0, h_pows_1, R_MOD) + // y^{num_polys} * (h^{num_polys} + h_s^{num_polys}) + t_0 := mulmod(t_0, t_2, R_MOD) + // - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})) + t_0 := sub(R_MOD, t_0) + // h^{num_polys} * h_s^{num_polys} - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})) + t_1 := addmod(t_1, t_0, R_MOD) + // y^{2*num_polys} + t_2 := mulmod(t_2, t_2, R_MOD) + // y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})) + t_1 := addmod(t_1, t_2, R_MOD) + loop_length := sub(num_polys, 1) + // h^{(2*num_polys)-1} & h_s^{(2*num_polys)-1} + for { + let i := 0 + } lt(i, loop_length) { + i := add(i, 1) + } { + h_pows_0 := mulmod(h_pows_0, h, R_MOD) + h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD) + } + // h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys}) + constant_parts_0 := addmod(constant_parts_0, h_pows_0, R_MOD) + // num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) + constant_parts_0 := mulmod(constant_parts_0, num_polys, R_MOD) + // h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys}) + constant_parts_1 := addmod(constant_parts_1, h_pows_1, R_MOD) + // num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) + constant_parts_1 := mulmod(constant_parts_1, num_polys, R_MOD) + + let current_omega := 1 + let interim_product := product + for { + let i := 0 + } lt(i, num_polys) { + i := add(i, 1) + } { + t_0 := mulmod(current_omega, h, R_MOD) + t_0 := sub(R_MOD, t_0) + t_0 := addmod(t_0, y, R_MOD) + // (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i))) + t_0 := mulmod(t_0, constant_parts_0, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(start, i), 0x20)), t_0) + + interim_product := mulmod(interim_product, t_0, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(start, i), 0x20)), interim_product) + // w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(start, i), 0x20)), + mulmod(t_1, current_omega, R_MOD) + ) + // w_i {i = i+1} + current_omega := mulmod(current_omega, omega, R_MOD) + } + + current_omega := 1 + for { + let i := 0 + } lt(i, num_polys) { + i := add(i, 1) + } { + t_0 := mulmod(current_omega, h_shifted, R_MOD) + t_0 := sub(R_MOD, t_0) + t_0 := addmod(t_0, y, R_MOD) + // (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i))) + t_0 := mulmod(t_0, constant_parts_1, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(add(start, num_polys), i), 0x20)), t_0) + + interim_product := mulmod(interim_product, t_0, R_MOD) + + mstore( + add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(add(start, num_polys), i), 0x20)), + interim_product + ) + // w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(start, num_polys), i), 0x20)), + mulmod(t_1, current_omega, R_MOD) + ) + // w_i {i = i+1} + current_omega := mulmod(current_omega, omega, R_MOD) + } + + final_product := interim_product + } + + /** + * @dev Computes lagrange basis evaluations using montgomery lagrange basis inverses sent with proof. + * @notice Check individual functions for more details + */ + function precompute_all_lagrange_basis_evaluations_from_inverses() { + let y := mload(PVS_Y) + // w8 = 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80 + // w4 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636 + // w3 = 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd + let product_0_7 := precompute_partial_lagrange_basis_evaluations( + 0, + 8, + y, + 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + 1 + ) + let product_0_11 := precompute_partial_lagrange_basis_evaluations( + 8, + 4, + y, + 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636, + mload(add(OPS_OPENING_POINTS, mul(1, 0x20))), + product_0_7 + ) + let product_0_17 := precompute_partial_lagrange_basis_evaluations_for_union_set( + add(8, 4), + 3, + y, + 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd, + mload(add(OPS_OPENING_POINTS, mul(2, 0x20))), + mload(add(OPS_OPENING_POINTS, mul(3, 0x20))), + product_0_11 + ) + + let montgomery_inverse := mload(MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE) + + if iszero(eq(mulmod(product_0_17, montgomery_inverse, R_MOD), 1)) { + revertWithMessage(30, "Precompute Eval. Error [PALBE]") + } + let temp := montgomery_inverse + let loop_length := sub(TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH, 1) + for { + let i := loop_length + } gt(i, 0) { + i := sub(i, 1) + } { + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(i, 0x20)), + mulmod( + mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(i, 0x20))), + mulmod(mload(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(sub(i, 1), 0x20))), temp, R_MOD), + R_MOD + ) + ) + temp := mulmod(temp, mload(add(MEM_LAGRANGE_BASIS_DENOMS, mul(i, 0x20))), R_MOD) + } + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20)), + mulmod(mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20))), temp, R_MOD) + ) + } + + /** + * @dev Computes opening points h0, h1, h2, h3 + */ + function compute_opening_points() { + // h = r^{power/num_polys} + let pvs_r := mload(PVS_R) + let r_2 := mulmod(pvs_r, pvs_r, R_MOD) + let r_3 := mulmod(r_2, pvs_r, R_MOD) + let r_6 := mulmod(r_3, r_3, R_MOD) + let r_8 := mulmod(r_6, r_2, R_MOD) + // h0 = pvs_r^3 + mstore(add(OPS_OPENING_POINTS, mul(0, 0x20)), r_3) + // h1 = pvs_r^6 + mstore(add(OPS_OPENING_POINTS, mul(1, 0x20)), r_6) + // h2 = pvs_r^8 + mstore(add(OPS_OPENING_POINTS, mul(2, 0x20)), r_8) + + // h3 (only round 2 needs opening at shifted point) + mstore( + add(OPS_OPENING_POINTS, mul(3, 0x20)), + mulmod(r_8, 0x0925f0bd364638ec3084b45fc27895f8f3f6f079096600fe946c8e9db9a47124, R_MOD) + ) + } + + /** + * @dev Initializes opening state OPS_Y_POWS[i] = y^i + * @notice only 9 powers are computed since the rest stay unused. + */ + function initialize_opening_state() { + compute_opening_points() + let acc := 1 + for { + let i := 0 + } lt(i, 9) { + i := add(i, 1) + } { + mstore(add(OPS_Y_POWS, mul(i, 0x20)), acc) + acc := mulmod(acc, mload(PVS_Y), R_MOD) + } + precompute_all_lagrange_basis_evaluations_from_inverses() + } + + /** + * @dev Computes r polynomial evaluations utilizing horner method + * (r*w)^{i}:{1, w*r, (w*r)^2, .. , (w*r)^{k-1}} + * horner: c0 + c1*(rw) + c2*(rw)^2 + c3*(rw)^3 -> (c0 + (rw)*(c1 + (rw)*(c2 + c3*(rw)))) + */ + function evaluate_r_polys_at_point_unrolled( + main_gate_quotient_at_z, + copy_perm_first_quotient_at_z, + copy_perm_second_quotient_at_z + ) { + let omega_h + let c + + // setup round + // r + + // w8^1 = 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80 + // w8^2 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636 + // w8^3 = 0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566 + // w8^4 = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000 + // w8^5 = 0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181 + // w8^6 = 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb + // w8^7 = 0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b + omega_h := mload(add(OPS_OPENING_POINTS, mul(0, 0x20))) + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(1, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(2, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(3, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(4, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(5, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(6, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(7, 0x20))), R_MOD), + R_MOD + ) + ) + + // first round + // r + + // w4^1 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636 + // w4^2 = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000 + // w4^3 = 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb + omega_h := mload(add(OPS_OPENING_POINTS, mul(1, 0x20))) + + c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD) + for { + let i := 1 + } lt(i, 3) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD) + + mstore( + add(PS_R_EVALS, mul(1, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(1, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 0), 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636, + mload(add(OPS_OPENING_POINTS, mul(1, 0x20))), + R_MOD + ) + + c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD) + for { + let i := 1 + } lt(i, 3) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(1, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(1, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 1), 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000, + mload(add(OPS_OPENING_POINTS, mul(1, 0x20))), + R_MOD + ) + + c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD) + for { + let i := 1 + } lt(i, 3) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(1, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(1, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 2), 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb, + mload(add(OPS_OPENING_POINTS, mul(1, 0x20))), + R_MOD + ) + + c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD) + for { + let i := 1 + } lt(i, 3) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(1, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(1, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 3), 0x20))), R_MOD), + R_MOD + ) + ) + + // second round + // c2 + // r + omega_h := mload(add(OPS_OPENING_POINTS, mul(2, 0x20))) + let omega_h_shifted := mload(add(OPS_OPENING_POINTS, mul(3, 0x20))) + c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD) + c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 0), 0x20))), R_MOD), + R_MOD + ) + ) + // c2 shifted + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD) + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD), + omega_h_shifted, + R_MOD + ) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod( + c, + mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 0), 0x20))), + R_MOD + ), + R_MOD + ) + ) + // c2 + omega_h := mulmod( + 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd, + mload(add(OPS_OPENING_POINTS, mul(2, 0x20))), + R_MOD + ) + omega_h_shifted := mulmod( + 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd, + mload(add(OPS_OPENING_POINTS, mul(3, 0x20))), + R_MOD + ) + + c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD) + c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 1), 0x20))), R_MOD), + R_MOD + ) + ) + // c2 shifted + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD) + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD), + omega_h_shifted, + R_MOD + ) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod( + c, + mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 1), 0x20))), + R_MOD + ), + R_MOD + ) + ) + // c2 + omega_h := mulmod( + 0x30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23, + mload(add(OPS_OPENING_POINTS, mul(2, 0x20))), + R_MOD + ) + omega_h_shifted := mulmod( + 0x30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23, + mload(add(OPS_OPENING_POINTS, mul(3, 0x20))), + R_MOD + ) + + c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD) + c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 2), 0x20))), R_MOD), + R_MOD + ) + ) + // c2 shifted + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD) + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD), + omega_h_shifted, + R_MOD + ) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD) + + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod( + c, + mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 2), 0x20))), + R_MOD + ), + R_MOD + ) + ) + } + + /** + * @dev Computes the openings and returns the result of pairing computation + */ + function check_openings() -> out { + // f(X) = (Z_{T\S0}(y) * (C0(X) - r0(y))) + (alpha*(Z_{T\S1}(y)*(C1(X) - r1(y)))) + (alpha^{2}*(Z_{T\S2}(y)*(C2(X) - r2(y)))) + // Note that, in our case set differences(Z_T\{S_i}) are: + // - Z_{T\S0}(y): (y^{k1}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w)) + // - Z_{T\S1}(y): (y^{k0}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w)) + // - Z_{T\S2}(y): (y^{k0}-ζ)*(y^{k1}-ζ) where + // k0=8, k1=4, and k2=3 are number of the polynomials for setup, first and second round respectively + + let tmp + evaluate_r_polys_at_point_unrolled( + mload(MAIN_GATE_QUOTIENT_AT_Z), + mload(COPY_PERM_FIRST_QUOTIENT_AT_Z), + mload(COPY_PERM_SECOND_QUOTIENT_AT_Z) + ) + + // -ζ + mstore(add(PS_MINUS_Z, mul(0, 0x20)), sub(R_MOD, mload(PVS_Z))) + // -(ζ*w) + mstore(add(PS_MINUS_Z, mul(1, 0x20)), sub(R_MOD, mload(PVS_Z_OMEGA))) + + // Z_{T\S0}(y) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)), + addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(1, 0x20))), R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), tmp, R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(4, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), tmp, R_MOD) + ) + mstore(PS_VANISHING_AT_Y, mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)))) + mstore(PS_INV_ZTS0_AT_Y, modexp(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), sub(R_MOD, 2))) + + // Z_{T\S1}(y) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)), + addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(1, 0x20))), R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), tmp, R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(8, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), tmp, R_MOD) + ) + mstore(PS_VANISHING_AT_Y, mulmod(mload(PS_VANISHING_AT_Y), tmp, R_MOD)) + + // // Z_{T\S2}(y) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20)), + addmod(mload(add(OPS_Y_POWS, mul(4, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(8, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20))), tmp, R_MOD) + ) + + // W(X) = f(X) / Z_T(y) where Z_T(y) = (y^{k0}-ζ)*(y^{k1}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w)) + // we need to check that + // f(X) - W(X) * Z_T(y) = 0 + + // W'(X) = L(X) / (Z_{T\S0}(y)*(X-y)) + // L(X)/Z_{T\S0}(y) = (C0(X) - r0(y)) + (alpha*(Z_{T\S1}(y)/Z_{T\S0}(y))*(C1(X) - r1(y))) + (alpha^{2}*(Z_{T\S2}(y)/Z_{T\S0}(y))*(C2(X) - r2(y))) - ((Z_T(y)/Z_{T\S0}(y))*W(X)) + + // the identity check is reduced into following + // L(X) - W'(X)*Z_{T\S0}(y)(X-y) == 0 + // verifier has commitments to the C_i(X) polynomials + // verifier also recomputed r_i(y) + // group constant and commitment parts + // first prepare L(X)/Z_{T\S0}(y) + // C(X) = C0(X) + ((alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*C1(X)) + ((alpha^2*Z_{T\S2}(y)/Z_{T\S0}(y))*C2(X)) + // r(y) = r0(y) + ((alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*r1(y)) + ((alpha^2*Z_{T\S2}(y)/Z_{T\S0}(y))*r2(y)) + // now construct + // L(X)/Z_{T\S0}(y) = C(X) - r(y) - ((Z_T(y)/Z_{T\S0}(y))*W(X)) + // now check following identity + // C(X) - r(y) - ((Z_t(y)/Z_{T\S0}(y))*W(X)) - (W'(X)*(X-y)) = 0 + // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\S0}(y))*[W] - [(X-y)*W'] = 0 + // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\S0}(y))*[W] - [X*W'] + [y*W]' = 0 + // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\S0}(y))*[W] + [y*W'] - [X*W'] = 0 + // points with X will be multiplied in the exponent via pairing + // so final pairing would ne + // e([C(X)] - [r(y)*G1] - [Z_T(y)/(Z_{T\S0}(y)*W)] + [y*W'], G2)*e(-W', X*G2) = 1 + + // C0 + let ps_aggregated_commitment_g1_x := VK_C0_G1_X + let ps_aggregated_commitment_g1_y := VK_C0_G1_Y + + // ((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y)) + let aggregated_r_at_y := mulmod( + mload(add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20))), + mload(PS_INV_ZTS0_AT_Y), + R_MOD + ) + aggregated_r_at_y := mulmod(aggregated_r_at_y, mload(PVS_ALPHA_1), R_MOD) + + // ((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y))*C2 + let tp_g1_x, tp_g1_y := point_mul( + mload(MEM_PROOF_COMMITMENT_1_G1_X), + mload(MEM_PROOF_COMMITMENT_1_G1_Y), + aggregated_r_at_y + ) + // c0 + (((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y))*C2) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + // ((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y))*r2 + aggregated_r_at_y := mulmod(aggregated_r_at_y, mload(add(PS_R_EVALS, mul(2, 0x20))), R_MOD) + + // (alpha*Z_{T\S1}(y)/Z_{T\S0}(y)) + tmp := mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), mload(PS_INV_ZTS0_AT_Y), R_MOD) + tmp := mulmod(tmp, mload(PVS_ALPHA_0), R_MOD) + + // (alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*C1 + tp_g1_x, tp_g1_y := point_mul( + mload(MEM_PROOF_COMMITMENT_0_G1_X), + mload(MEM_PROOF_COMMITMENT_0_G1_Y), + tmp + ) + // c0 + ((alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*C1) + (((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y))*C2) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + // (alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*r1 + tmp := mulmod(tmp, mload(add(PS_R_EVALS, mul(1, 0x20))), R_MOD) + // ((alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*r1) + ((alpha^{2}*Z_{T\S2}(y)/Z_{T\S0}(y))*r2) + aggregated_r_at_y := addmod(aggregated_r_at_y, tmp, R_MOD) + // r0 + (alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*r1 + ((alpha^{2}*Z_{T\S2}(y)/Z_{T\S0}(y))*r2) + aggregated_r_at_y := addmod(aggregated_r_at_y, mload(PS_R_EVALS), R_MOD) + tp_g1_x, tp_g1_y := point_mul(1, 2, aggregated_r_at_y) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_sub( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + // - ((Z_T(y)/Z_{T\S0}(y))*W(X)) + mstore(PS_VANISHING_AT_Y, mulmod(mload(PS_VANISHING_AT_Y), mload(PS_INV_ZTS0_AT_Y), R_MOD)) + tp_g1_x, tp_g1_y := point_mul( + mload(MEM_PROOF_COMMITMENT_2_G1_X), + mload(MEM_PROOF_COMMITMENT_2_G1_Y), + mload(PS_VANISHING_AT_Y) + ) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_sub( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + // L(X)/Z_{T\S0}(y) is aggregated + + // Now check W'(X) = L(X) / (Z_{T\S0}(y)*(x-y)) + // L(X)/Z_{T\S0}(y) + (y*W'(X)) - (x*W'(X)) = 0 + tp_g1_x, tp_g1_y := point_mul( + mload(MEM_PROOF_COMMITMENT_3_G1_X), + mload(MEM_PROOF_COMMITMENT_3_G1_Y), + mload(PVS_Y) + ) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + let is_zero_commitment + if iszero(mload(MEM_PROOF_COMMITMENT_3_G1_Y)) { + if gt(mload(MEM_PROOF_COMMITMENT_3_G1_X), 0) { + revertWithMessage(21, "non zero x value [CO]") + } + is_zero_commitment := 1 + } + + out := pairing_check(ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y, is_zero_commitment) + } + + /** + * @dev Generates the rolling hash using `val` and updates the transcript. + * The computation is done as follows: + * new_state_0 = keccak256(uint32(0) || old_state_0 || old_state_1 || value) + * new_state_1 = keccak256(uint32(1) || old_state_0 || old_state_1 || value) + * + * @notice The computation assumes that the memory slots 0x200 - 0x202 are clean and doesn't explicitly clean them + */ + function update_transcript(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 Generates a new challenge with (uint32(2) || state_0 || state_1 || uint32(challenge_counter)) + * The challenge_counter is incremented after every challenge + */ + function get_challenge(challenge_counter) -> challenge { + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x02) + mstore(TRANSCRIPT_CHALLENGE_SLOT, shl(224, challenge_counter)) + challenge := and(keccak256(TRANSCRIPT_BEGIN_SLOT, 0x48), FR_MASK) + } + + /** + * @dev Performs scalar multiplication: point * scalar -> t + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + */ + function point_mul(p_x, p_y, s) -> t_x, t_y { + mstore(0x80, p_x) + mstore(0xa0, p_y) + mstore(0xc0, s) + + let success := staticcall(gas(), 7, 0x80, 0x60, 0x80, 0x40) + if iszero(success) { + revertWithMessage(27, "point multiplication failed") + } + t_x := mload(0x80) + t_y := mload(add(0x80, 0x20)) + } + + /** + * @dev Performs point addition: point 1 + point 2 -> t + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + */ + function point_add(p1_x, p1_y, p2_x, p2_y) -> t_x, t_y { + mstore(0x80, p1_x) + mstore(0xa0, p1_y) + mstore(0xc0, p2_x) + mstore(0xe0, p2_y) + + let success := staticcall(gas(), 6, 0x80, 0x80, 0x80, 0x40) + if iszero(success) { + revertWithMessage(21, "point addition failed") + } + + t_x := mload(0x80) + t_y := mload(add(0x80, 0x20)) + } + + /** + * @dev Performs point subtraction: point 1 + point 2 -> t + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + * @notice We don't consider the highly unlikely case where p2 can be a point-at-infinity and the function would revert. + */ + function point_sub(p1_x, p1_y, p2_x, p2_y) -> t_x, t_y { + mstore(0x80, p1_x) + mstore(0xa0, p1_y) + mstore(0xc0, p2_x) + mstore(0xe0, sub(Q_MOD, p2_y)) + + let success := staticcall(gas(), 6, 0x80, 0x80, 0x80, 0x40) + if iszero(success) { + revertWithMessage(24, "point subtraction failed") + } + + t_x := mload(0x80) + t_y := mload(add(0x80, 0x20)) + } + + /** + * @dev Calculates EC Pairing result following the EIP-197: https://eips.ethereum.org/EIPS/eip-197 + * Performs point negation before pairing calculation, if the flag `is_zero_commitment` is true + * + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + * While code reformatting consider not to overwrite the first constant-defined memory location, which is currently + * TRANSCRIPT_BEGIN_SLOT = 0x200 + */ + function pairing_check(p1_x, p1_y, is_zero_commitment) -> res { + mstore(0x80, p1_x) + mstore(0xa0, p1_y) + mstore(0xc0, VK_G2_ELEMENT_0_X1) + mstore(0xe0, VK_G2_ELEMENT_0_X2) + mstore(0x100, VK_G2_ELEMENT_0_Y1) + mstore(0x120, VK_G2_ELEMENT_0_Y2) + mstore(0x140, mload(MEM_PROOF_COMMITMENT_3_G1_X)) + mstore(0x160, mload(MEM_PROOF_COMMITMENT_3_G1_Y)) + if iszero(is_zero_commitment) { + mstore(0x160, sub(Q_MOD, mload(MEM_PROOF_COMMITMENT_3_G1_Y))) + } + mstore(0x180, VK_G2_ELEMENT_1_X1) + mstore(0x1a0, VK_G2_ELEMENT_1_X2) + mstore(0x1c0, VK_G2_ELEMENT_1_Y1) + mstore(0x1e0, VK_G2_ELEMENT_1_Y2) + + let success := staticcall(gas(), 8, 0x80, mul(12, 0x20), 0x80, 0x20) + + if iszero(success) { + revertWithMessage(20, "pairing check failed") + } + res := mload(0x80) + } + + /** + * @dev Reverts with the desired custom error string. + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + */ + function revertWithMessage(len, reason) { + // "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x80, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // Data offset + mstore(0x84, 0x0000000000000000000000000000000000000000000000000000000000000020) + // Length of revert string + mstore(0xa4, len) + // Revert reason + mstore(0xc4, reason) + // Revert + revert(0x80, 0x64) + } + + /** + * @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD. + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + */ + function modexp(value, power) -> res { + mstore(0x80, 0x20) + mstore(0xa0, 0x20) + mstore(0xc0, 0x20) + mstore(0xe0, value) + mstore(0x100, power) + mstore(0x120, R_MOD) + if iszero(staticcall(gas(), 5, 0x80, 0xc0, 0x80, 0x20)) { + revertWithMessage(24, "modexp precompile failed") + } + res := mload(0x80) + } + } + } +} diff --git a/l1-contracts/contracts/state-transition/Verifier.sol b/l1-contracts/contracts/state-transition/verifiers/VerifierPlonk.sol similarity index 99% rename from l1-contracts/contracts/state-transition/Verifier.sol rename to l1-contracts/contracts/state-transition/verifiers/VerifierPlonk.sol index a74ecb12c..a47208bcb 100644 --- a/l1-contracts/contracts/state-transition/Verifier.sol +++ b/l1-contracts/contracts/state-transition/verifiers/VerifierPlonk.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.24; -import {IVerifier} from "./chain-interfaces/IVerifier.sol"; +import {IVerifier} from "../chain-interfaces/IVerifier.sol"; /* solhint-disable max-line-length */ /// @author Matter Labs @@ -18,7 +18,7 @@ import {IVerifier} from "./chain-interfaces/IVerifier.sol"; /// * 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. /* solhint-enable max-line-length */ -contract Verifier is IVerifier { +contract VerifierPlonk is IVerifier { /*////////////////////////////////////////////////////////////// Verification keys //////////////////////////////////////////////////////////////*/ diff --git a/l1-contracts/deploy-scripts/DeployL1.s.sol b/l1-contracts/deploy-scripts/DeployL1.s.sol index 416ffd6a3..2c5554684 100644 --- a/l1-contracts/deploy-scripts/DeployL1.s.sol +++ b/l1-contracts/deploy-scripts/DeployL1.s.sol @@ -10,8 +10,10 @@ import {TransparentUpgradeableProxy} from "@openzeppelin/contracts-v4/proxy/tran import {Utils} from "./Utils.sol"; import {Multicall3} from "contracts/dev-contracts/Multicall3.sol"; -import {Verifier} from "contracts/state-transition/Verifier.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {DualVerifier} from "contracts/state-transition/verifiers/DualVerifier.sol"; +import {VerifierPlonk} from "contracts/state-transition/verifiers/VerifierPlonk.sol"; +import {VerifierFflonk} from "contracts/state-transition/verifiers/VerifierFFLONK.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; import {VerifierParams, IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; import {DefaultUpgrade} from "contracts/upgrades/DefaultUpgrade.sol"; import {Governance} from "contracts/governance/Governance.sol"; @@ -253,17 +255,32 @@ contract DeployL1Script is Script { } function deployVerifier() internal { + address verifierFflonk = deployVerifierFflonk(); + address verifierPlonk = deployVerifierPlonk(); bytes memory code; if (config.testnetVerifier) { code = type(TestnetVerifier).creationCode; } else { - code = type(Verifier).creationCode; + code = type(DualVerifier).creationCode; } + code = abi.encodePacked(code, abi.encode(verifierFflonk, verifierPlonk)); address contractAddress = deployViaCreate2(code); - console.log("Verifier deployed at:", contractAddress); + console.log("Dual verifier deployed at:", contractAddress); addresses.stateTransition.verifier = contractAddress; } + function deployVerifierFflonk() internal returns (address contractAddress) { + bytes memory code = type(VerifierFflonk).creationCode; + contractAddress = deployViaCreate2(code); + console.log("FFLONK verifier deployed at:", contractAddress); + } + + function deployVerifierPlonk() internal returns (address contractAddress) { + bytes memory code = type(VerifierPlonk).creationCode; + contractAddress = deployViaCreate2(code); + console.log("Plonk verifier deployed at:", contractAddress); + } + function deployDefaultUpgrade() internal { address contractAddress = deployViaCreate2(type(DefaultUpgrade).creationCode); console.log("DefaultUpgrade deployed at:", contractAddress); diff --git a/l1-contracts/test/foundry/unit/concrete/Executor/ExecutorProof.t.sol b/l1-contracts/test/foundry/unit/concrete/Executor/ExecutorProof.t.sol index 6c6d8a935..def603caf 100644 --- a/l1-contracts/test/foundry/unit/concrete/Executor/ExecutorProof.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Executor/ExecutorProof.t.sol @@ -9,7 +9,9 @@ import {UtilsFacet} from "foundry-test/unit/concrete/Utils/UtilsFacet.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {ExecutorFacet} from "contracts/state-transition/chain-deps/facets/Executor.sol"; import {IExecutor, LogProcessingOutput} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {IVerifierV2} from "contracts/state-transition/chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; contract TestExecutorFacet is ExecutorFacet { function createBatchCommitment( @@ -42,7 +44,7 @@ contract TestExecutorFacet is ExecutorFacet { contract ExecutorProofTest is Test { UtilsFacet internal utilsFacet; TestExecutorFacet internal executor; - address internal testnetVerifier = address(new TestnetVerifier()); + address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)))); function getTestExecutorFacetSelectors() private pure returns (bytes4[] memory) { bytes4[] memory selectors = new bytes4[](3); diff --git a/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol index d81e9cc30..49bca4eb6 100644 --- a/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Executor/_Executor_Shared.t.sol @@ -18,8 +18,9 @@ import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox import {InitializeData} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; import {IExecutor} from "contracts/state-transition/chain-interfaces/IExecutor.sol"; import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; +import {IVerifierV2} from "contracts/state-transition/chain-interfaces/IVerifierV2.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; contract ExecutorTest is Test { address internal owner; @@ -155,8 +156,7 @@ contract ExecutorTest is Test { timestamp: 0, commitment: bytes32("") }); - - TestnetVerifier testnetVerifier = new TestnetVerifier(); + TestnetVerifier testnetVerifier = new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0))); InitializeData memory params = InitializeData({ // TODO REVIEW diff --git a/l1-contracts/test/foundry/unit/concrete/Verifier/Verifier.t.sol b/l1-contracts/test/foundry/unit/concrete/Verifier/PlonkVerifier.t.sol similarity index 96% rename from l1-contracts/test/foundry/unit/concrete/Verifier/Verifier.t.sol rename to l1-contracts/test/foundry/unit/concrete/Verifier/PlonkVerifier.t.sol index 54ab49974..f6429ac64 100644 --- a/l1-contracts/test/foundry/unit/concrete/Verifier/Verifier.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Verifier/PlonkVerifier.t.sol @@ -3,10 +3,10 @@ pragma solidity 0.8.24; import {Test} from "forge-std/Test.sol"; -import {Verifier} from "contracts/state-transition/Verifier.sol"; -import {VerifierTest} from "contracts/dev-contracts/test/VerifierTest.sol"; +import {VerifierPlonk} from "contracts/state-transition/verifiers/VerifierPlonk.sol"; +import {PlonkVerifierTest} from "contracts/dev-contracts/test/PlonkVerifierTest.sol"; -contract VerifierTestTest is Test { +contract PlonkVerifierTestTest is Test { uint256 Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583; uint256 R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; @@ -14,7 +14,7 @@ contract VerifierTestTest is Test { uint256[] public serializedProof; uint256[] public recursiveAggregationInput; - Verifier public verifier; + VerifierPlonk public verifier; function setUp() public virtual { publicInputs.push(17257057577815541751225964212897374444694342989384539141520877492729); @@ -64,7 +64,7 @@ contract VerifierTestTest is Test { serializedProof.push(7419167499813234488108910149511390953153207250610705609008080038658070088540); serializedProof.push(11628425014048216611195735618191126626331446742771562481735017471681943914146); - verifier = new VerifierTest(); + verifier = new PlonkVerifierTest(); } function testShouldVerify() public view { diff --git a/l1-contracts/test/foundry/unit/concrete/Verifier/VerifierRecursive.t.sol b/l1-contracts/test/foundry/unit/concrete/Verifier/PlonkVerifierRecursive.t.sol similarity index 88% rename from l1-contracts/test/foundry/unit/concrete/Verifier/VerifierRecursive.t.sol rename to l1-contracts/test/foundry/unit/concrete/Verifier/PlonkVerifierRecursive.t.sol index 69bad2303..b87664efa 100644 --- a/l1-contracts/test/foundry/unit/concrete/Verifier/VerifierRecursive.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/Verifier/PlonkVerifierRecursive.t.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.24; -import {VerifierTestTest} from "./Verifier.t.sol"; -import {VerifierRecursiveTest} from "contracts/dev-contracts/test/VerifierRecursiveTest.sol"; +import {PlonkVerifierTestTest} from "./PlonkVerifier.t.sol"; +import {PlonkVerifierRecursiveTest} from "contracts/dev-contracts/test/PlonkVerifierRecursiveTest.sol"; -contract VerifierRecursiveTestTest is VerifierTestTest { +contract PlonkVerifierRecursiveTestTest is PlonkVerifierTestTest { function setUp() public override { super.setUp(); @@ -13,7 +13,7 @@ contract VerifierRecursiveTestTest is VerifierTestTest { recursiveAggregationInput.push(16188304989094043810949359833767911976672882599560690320245309499206765021563); recursiveAggregationInput.push(3201093556796962656759050531176732990872300033146738631772984017549903765305); - verifier = new VerifierRecursiveTest(); + verifier = new PlonkVerifierRecursiveTest(); } function testMoreThan4WordsRecursiveInput_shouldRevert() public { diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol index 999336642..b42493e72 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/StateTransitionManager/_StateTransitionManager_Shared.t.sol @@ -17,8 +17,10 @@ import {GenesisUpgrade} from "contracts/upgrades/GenesisUpgrade.sol"; import {InitializeDataNewChain} from "contracts/state-transition/chain-interfaces/IDiamondInit.sol"; import {StateTransitionManager} from "contracts/state-transition/StateTransitionManager.sol"; import {StateTransitionManagerInitializeData, ChainCreationParams} from "contracts/state-transition/IStateTransitionManager.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; import {ZeroAddress} from "contracts/common/L1ContractErrors.sol"; +import {IVerifierV2} from "contracts/state-transition/chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; contract StateTransitionManagerTest is Test { StateTransitionManager internal stateTransitionManager; @@ -33,7 +35,7 @@ contract StateTransitionManagerTest is Test { address internal constant validator = address(0x5050505); address internal newChainAdmin; uint256 chainId = block.chainid; - address internal testnetVerifier = address(new TestnetVerifier()); + address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)))); Diamond.FacetCut[] internal facetCuts; diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondInit/_DiamondInit_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondInit/_DiamondInit_Shared.t.sol index 8a50fd5d5..732234a40 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondInit/_DiamondInit_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondInit/_DiamondInit_Shared.t.sol @@ -7,11 +7,13 @@ import {Utils} from "foundry-test/unit/concrete/Utils/Utils.sol"; import {UtilsFacet} from "foundry-test/unit/concrete/Utils/UtilsFacet.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; +import {IVerifierV2} from "contracts/state-transition/chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; contract DiamondInitTest is Test { Diamond.FacetCut[] internal facetCuts; - address internal testnetVerifier = address(new TestnetVerifier()); + address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)))); function setUp() public virtual { facetCuts.push( diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondProxy/DiamondProxy.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondProxy/DiamondProxy.t.sol index 4637faabd..a4750542d 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondProxy/DiamondProxy.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/DiamondProxy/DiamondProxy.t.sol @@ -11,8 +11,10 @@ import {DiamondInit} from "contracts/state-transition/chain-deps/DiamondInit.sol import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {DiamondProxy} from "contracts/state-transition/chain-deps/DiamondProxy.sol"; import {ZkSyncHyperchainBase} from "contracts/state-transition/chain-deps/facets/ZkSyncHyperchainBase.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; import {FacetIsFrozen, ValueMismatch, InvalidSelector} from "contracts/common/L1ContractErrors.sol"; +import {IVerifierV2} from "contracts/state-transition/chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; contract TestFacet is ZkSyncHyperchainBase { function func() public pure returns (bool) { @@ -25,7 +27,7 @@ contract TestFacet is ZkSyncHyperchainBase { contract DiamondProxyTest is Test { Diamond.FacetCut[] internal facetCuts; - address internal testnetVerifier = address(new TestnetVerifier()); + address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)))); function getTestFacetSelectors() public pure returns (bytes4[] memory selectors) { selectors = new bytes4[](1); diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/_Admin_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/_Admin_Shared.t.sol index a4419a342..d158ddd90 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/_Admin_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Admin/_Admin_Shared.t.sol @@ -9,12 +9,14 @@ import {UtilsFacet} from "foundry-test/unit/concrete/Utils/UtilsFacet.sol"; import {AdminFacet} from "contracts/state-transition/chain-deps/facets/Admin.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {IAdmin} from "contracts/state-transition/chain-interfaces/IAdmin.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; +import {IVerifierV2} from "contracts/state-transition/chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; contract AdminTest is Test { IAdmin internal adminFacet; UtilsFacet internal utilsFacet; - address internal testnetVerifier = address(new TestnetVerifier()); + address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)))); function getAdminSelectors() public pure returns (bytes4[] memory) { bytes4[] memory selectors = new bytes4[](12); diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Base/_Base_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Base/_Base_Shared.t.sol index 15fa32883..96c3e5c44 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Base/_Base_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Base/_Base_Shared.t.sol @@ -8,7 +8,9 @@ import {UtilsFacet} from "foundry-test/unit/concrete/Utils/UtilsFacet.sol"; import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {ZkSyncHyperchainBase} from "contracts/state-transition/chain-deps/facets/Admin.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; +import {IVerifierV2} from "contracts/state-transition/chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; contract TestBaseFacet is ZkSyncHyperchainBase { function functionWithOnlyAdminModifier() external onlyAdmin {} @@ -40,7 +42,7 @@ bytes constant ERROR_ONLY_VALIDATOR_OR_STATE_TRANSITION_MANAGER = "Hyperchain: O contract ZkSyncHyperchainBaseTest is Test { TestBaseFacet internal testBaseFacet; UtilsFacet internal utilsFacet; - address internal testnetVerifier = address(new TestnetVerifier()); + address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)))); function getTestBaseFacetSelectors() public pure returns (bytes4[] memory selectors) { selectors = new bytes4[](6); diff --git a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol index 32a1e9c55..e963e31b7 100644 --- a/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol +++ b/l1-contracts/test/foundry/unit/concrete/state-transition/chain-deps/facets/Mailbox/_Mailbox_Shared.t.sol @@ -10,7 +10,9 @@ import {MailboxFacet} from "contracts/state-transition/chain-deps/facets/Mailbox import {Diamond} from "contracts/state-transition/libraries/Diamond.sol"; import {IMailbox} from "contracts/state-transition/chain-interfaces/IMailbox.sol"; import {IGetters} from "contracts/state-transition/chain-interfaces/IGetters.sol"; -import {TestnetVerifier} from "contracts/state-transition/TestnetVerifier.sol"; +import {TestnetVerifier} from "contracts/state-transition/verifiers/TestnetVerifier.sol"; +import {IVerifierV2} from "contracts/state-transition/chain-interfaces/IVerifierV2.sol"; +import {IVerifier} from "contracts/state-transition/chain-interfaces/IVerifier.sol"; contract MailboxTest is Test { IMailbox internal mailboxFacet; @@ -18,7 +20,7 @@ contract MailboxTest is Test { IGetters internal gettersFacet; address sender; uint256 constant eraChainId = 9; - address internal testnetVerifier = address(new TestnetVerifier()); + address internal testnetVerifier = address(new TestnetVerifier(IVerifierV2(address(0)), IVerifier(address(0)))); address diamondProxy; function setupDiamondProxy() public { diff --git a/tools/data/fflonk_scheduler_key.json b/tools/data/fflonk_scheduler_key.json new file mode 100644 index 000000000..cdd43ec5f --- /dev/null +++ b/tools/data/fflonk_scheduler_key.json @@ -0,0 +1,399 @@ +{ + "n": 16777215, + "num_inputs": 1, + "state_width": 4, + "num_witness_polys": 0, + "gate_setup_commitments": [ + { + "x": [ + 7041473518753276610, + 14535424708528669582, + 13201344011965459113, + 2897130087748692189 + ], + "y": [ + 7551377240045957203, + 3938680994559003687, + 16706483137576353901, + 2786003470784327326 + ], + "infinity": false + }, + { + "x": [ + 18272574112566878458, + 16137981417959663614, + 15838588009131800405, + 3015283904275055936 + ], + "y": [ + 5587221603595800838, + 4961680924870800295, + 17117659941986630706, + 2617194840215249322 + ], + "infinity": false + }, + { + "x": [ + 13975462584861812129, + 18178670042078411614, + 3386195693215471468, + 2666700336260012533 + ], + "y": [ + 3977105627642414202, + 4537400861054151716, + 15475898821594098620, + 1658247751320851774 + ], + "infinity": false + }, + { + "x": [ + 16277842833814772923, + 4655257551374867597, + 14613527128427906214, + 2384011263837220434 + ], + "y": [ + 13438155507917260925, + 1681961783360029593, + 9838145283630619191, + 3145022577013212364 + ], + "infinity": false + }, + { + "x": [ + 2257074215792094795, + 15840297332048800261, + 7752787513729517208, + 2213812271775260313 + ], + "y": [ + 9504815042706281750, + 3143783907329110636, + 5726996484641831885, + 2348348756186715020 + ], + "infinity": false + }, + { + "x": [ + 9436269926173034716, + 8353505617703691640, + 9263372864563147133, + 452364547608181011 + ], + "y": [ + 53517401948131448, + 8600683373039886778, + 10713988639514953496, + 3091884229552279711 + ], + "infinity": false + }, + { + "x": [ + 2471001934854133497, + 11932587151841940290, + 9727983790633853429, + 743292418711407275 + ], + "y": [ + 2219922039574131913, + 6200487752881169488, + 159222541455338254, + 1981289514965404461 + ], + "infinity": false + }, + { + "x": [ + 10387557038099338908, + 3777779600416753782, + 8279688681580161825, + 2096714248633885271 + ], + "y": [ + 4003894836596889278, + 7566819875065610759, + 6937892309101934040, + 724425532685653216 + ], + "infinity": false + } + ], + "gate_selectors_commitments": [ + { + "x": [ + 14203539687749767931, + 12414461990983607643, + 5173799202403416188, + 2531047567583200315 + ], + "y": [ + 17890925178676127921, + 8067135813786054903, + 10294951679721596259, + 3372244430865439083 + ], + "infinity": false + }, + { + "x": [ + 8250938356182918828, + 8989523127942126907, + 12291296797984538953, + 265866581838824098 + ], + "y": [ + 2552096304808713451, + 4528060267622140868, + 14222476710621591571, + 2515703838465707069 + ], + "infinity": false + } + ], + "permutation_commitments": [ + { + "x": [ + 4760310670502670392, + 9133307033789266781, + 1165299145606630987, + 2333423271383629947 + ], + "y": [ + 8701338046602933584, + 8244575834977400721, + 13923835279411448645, + 590842459332837393 + ], + "infinity": false + }, + { + "x": [ + 16765704004844786377, + 3734854353194303779, + 5208541298592688636, + 2007126263945456660 + ], + "y": [ + 9390331670076615527, + 10546395594767890107, + 16710005739026562911, + 1094385967155653873 + ], + "infinity": false + }, + { + "x": [ + 96544706602781130, + 14501961830701305797, + 15551447137139813444, + 2721397587554959520 + ], + "y": [ + 18248300896616317016, + 6044137253057084277, + 1109466816695564632, + 1681424544305600892 + ], + "infinity": false + }, + { + "x": [ + 636393164683958416, + 2470980311335149449, + 9058696428947095766, + 2865953210317054750 + ], + "y": [ + 14660427536101265956, + 9635151643854767246, + 13687651037244306121, + 1736513100272294968 + ], + "infinity": false + } + ], + "total_lookup_entries_length": 115748, + "lookup_selector_commitment": { + "x": [ + 14450683051820374086, + 14392669012541711696, + 1085264196371072001, + 1432911718574403450 + ], + "y": [ + 2375311355237024951, + 6153452264574448462, + 14765716199346364060, + 2813994675009564617 + ], + "infinity": false + }, + "lookup_tables_commitments": [ + { + "x": [ + 10873859091125335643, + 3906092213625635374, + 17046157606087980048, + 3193402705223440293 + ], + "y": [ + 10158946293873382504, + 2171386304067884865, + 6918663094168980658, + 350601565475975409 + ], + "infinity": false + }, + { + "x": [ + 12822112641313049260, + 3646552465186399021, + 10324071010773924047, + 2209084192380614662 + ], + "y": [ + 11045141628975531869, + 12589678537679955590, + 3065046617868727674, + 2099447669854151830 + ], + "infinity": false + }, + { + "x": [ + 11395032673621937545, + 3000063650268118516, + 7857619430005721792, + 805706808484810738 + ], + "y": [ + 6817063666434679427, + 1646386051225388537, + 4677946977082722827, + 1369650305976868514 + ], + "infinity": false + }, + { + "x": [ + 2885179371868476351, + 159944842081142878, + 6092294387055034894, + 213843603626505240 + ], + "y": [ + 11868113133779277990, + 8509646480531194854, + 14088068011597639414, + 707070630614027545 + ], + "infinity": false + } + ], + "lookup_table_type_commitment": { + "x": [ + 10866682157158839994, + 1596061817389919048, + 4796403318062291708, + 3038810910820814384 + ], + "y": [ + 2859202713989048607, + 17537733215249445364, + 1109622377575874158, + 2641119576344589003 + ], + "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/fflonk_verifier_contract_template.txt b/tools/data/fflonk_verifier_contract_template.txt new file mode 100644 index 000000000..5353032e5 --- /dev/null +++ b/tools/data/fflonk_verifier_contract_template.txt @@ -0,0 +1,1600 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.24; + +import {IVerifierV2} from "../chain-interfaces/IVerifierV2.sol"; + +/// @title Fflonk Verifier Implementation +/// @author Matter Labs +/// @notice FFT inspired version of PlonK to optimize on-chain gas cost +/// @dev For better understanding of the protocol follow the below papers: +/// * Fflonk Paper: https://eprint.iacr.org/2021/1167 +/// @custom:security-contact security@matterlabs.dev +contract VerifierFflonk is IVerifierV2 { + // ================Constants================ + uint32 internal constant DST_0 = 0; + uint32 internal constant DST_1 = 1; + uint32 internal constant DST_CHALLENGE = 2; + uint256 internal constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; + uint256 internal constant Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + uint256 internal constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 internal constant BN254_B_COEFF = 3; + + // ================Verification Key================ + uint256 internal constant VK_NUM_INPUTS = 1; + // [C0]1 = qL(X^8)+ X*qR(X^8)+ X^2*qO(X^8)+ X^3*qM(X^8)+ X^4*qC(X^8)+ X^5*Sσ1(X^8)+ X^6*Sσ2(X^8)+ X^7*Sσ3(X^8) + uint256 internal constant VK_C0_G1_X = 0x15c99dbc62b8191204ff93984b0de4fb7c79ac7a1ef2c94f4ce940319a2408b2; + uint256 internal constant VK_C0_G1_Y = 0x0521b86a104e07c8971bf2e17d7665d59df7566c08e6e0c9750f584bb24084ce; + // k1 = 5, k2 = 7 + uint256 internal constant VK_NON_RESIDUES_0 = 0x0000000000000000000000000000000000000000000000000000000000000005; + uint256 internal constant VK_NON_RESIDUES_1 = 0x0000000000000000000000000000000000000000000000000000000000000007; + // G2 Elements = [1]_2, [s]_2 + uint256 internal constant VK_G2_ELEMENT_0_X1 = 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2; + uint256 internal constant VK_G2_ELEMENT_0_X2 = 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed; + uint256 internal constant VK_G2_ELEMENT_0_Y1 = 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b; + uint256 internal constant VK_G2_ELEMENT_0_Y2 = 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa; + uint256 internal constant VK_G2_ELEMENT_1_X1 = 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1; + uint256 internal constant VK_G2_ELEMENT_1_X2 = 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0; + uint256 internal constant VK_G2_ELEMENT_1_Y1 = 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4; + uint256 internal constant VK_G2_ELEMENT_1_Y2 = 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55; + + // Memory slots from 0x000 to 0x200 are reserved for intermediate computations and call to precompiles. + + // ================Transcript================ + // ================Constants================ + uint256 internal constant ONE = 1; + uint256 internal constant DOMAIN_SIZE = 8388608; + uint256 internal constant OMEGA = 0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863; + // ========================================= + uint256 internal constant TRANSCRIPT_BEGIN_SLOT = 0x200; + uint256 internal constant TRANSCRIPT_DST_BYTE_SLOT = 0x203; + uint256 internal constant TRANSCRIPT_STATE_0_SLOT = 0x204; + uint256 internal constant TRANSCRIPT_STATE_1_SLOT = 0x224; + uint256 internal constant TRANSCRIPT_CHALLENGE_SLOT = 0x244; + + // ================PartialVerifierState================ + // copy-permutation challenges + uint256 internal constant PVS_BETA = 0x264 + 0x00; + uint256 internal constant PVS_GAMMA = 0x264 + 0x20; + // evaluation challenges + uint256 internal constant PVS_R = 0x264 + 0x40; + uint256 internal constant PVS_Z = 0x264 + 0x60; + uint256 internal constant PVS_Z_OMEGA = 0x264 + 0x80; + // aggregation challenge + uint256 internal constant PVS_ALPHA_0 = 0x264 + 0xa0; + uint256 internal constant PVS_ALPHA_1 = 0x264 + 0xc0; + // final evaluation challenge + uint256 internal constant PVS_Y = 0x264 + 0xe0; + // convenience + uint256 internal constant PVS_VANISHING_AT_Z = 0x264 + 0x100; + uint256 internal constant PVS_VANISHING_AT_Z_INV = 0x264 + 0x120; + uint256 internal constant PVS_L_0_AT_Z = 0x264 + 0x140; + uint256 internal constant MAIN_GATE_QUOTIENT_AT_Z = 0x264 + 0x160; + uint256 internal constant COPY_PERM_FIRST_QUOTIENT_AT_Z = 0x264 + 0x180; + uint256 internal constant COPY_PERM_SECOND_QUOTIENT_AT_Z = 0x264 + 0x1a0; + // ================Opening State================ + // h0, h1, h2, h2_shifted + uint256 internal constant OPS_OPENING_POINTS = 0x264 + 0x1c0 + 0x00; // 4 slots + uint256 internal constant OPS_Y_POWS = 0x264 + 0x1c0 + 0x80; // 9 SLOTS + + // ================Pairing State================ + + uint256 internal constant PS_VANISHING_AT_Y = 0x264 + 0x1c0 + 0x1a0; + uint256 internal constant PS_INV_ZTS0_AT_Y = 0x264 + 0x1c0 + 0x1c0; + uint256 internal constant PS_SET_DIFFERENCES_AT_Y = 0x264 + 0x1c0 + 0x1e0; // 3 slots + uint256 internal constant PS_MINUS_Z = 0x264 + 0x1c0 + 0x240; // 2 slots + uint256 internal constant PS_R_EVALS = 0x264 + 0x1c0 + 0x280; // 3 slots + + // ================In Memory(from Proof)================ + uint256 internal constant MEM_PROOF_PUBLIC_INPUT_SLOT = 0x264 + 0x1c0 + 0x2e0; + + uint256 internal constant MEM_PROOF_COMMITMENT_0_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0x20; + uint256 internal constant MEM_PROOF_COMMITMENT_0_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x40; + uint256 internal constant MEM_PROOF_COMMITMENT_1_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0x60; + uint256 internal constant MEM_PROOF_COMMITMENT_1_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x80; + uint256 internal constant MEM_PROOF_COMMITMENT_2_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0xa0; + uint256 internal constant MEM_PROOF_COMMITMENT_2_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0xc0; + uint256 internal constant MEM_PROOF_COMMITMENT_3_G1_X = 0x264 + 0x1c0 + 0x2e0 + 0xe0; + uint256 internal constant MEM_PROOF_COMMITMENT_3_G1_Y = 0x264 + 0x1c0 + 0x2e0 + 0x100; + + uint256 internal constant MEM_PROOF_EVALUATIONS = 0x264 + 0x1c0 + 0x2e0 + 0x120; // 15 slots + + uint256 internal constant MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x1e0; // 1 slots + + uint256 internal constant MEM_LAGRANGE_BASIS_DENOMS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x200; //18 slots + uint256 internal constant MEM_LAGRANGE_BASIS_DENOM_PRODUCTS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x440; // 18 slots + uint256 internal constant MEM_PROOF_LAGRANGE_BASIS_EVALS = 0x264 + 0x1c0 + 0x2e0 + 0x120 + 0x680; // 18 Slots + + // ================Constants================ + uint256 internal constant PROOF_PUBLIC_INPUTS_LENGTH = 1; + uint256 internal constant PROOF_LENGTH = 24; + uint256 internal constant PROOF_EVALUATIONS_LENGTH = 15; + uint256 internal constant TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH = 18; + + /// @inheritdoc IVerifierV2 + function verificationKeyHash() external pure returns (bytes32 vkHash) { + return + keccak256( + abi.encodePacked( + VK_NUM_INPUTS, + VK_C0_G1_X, + VK_C0_G1_Y, + VK_NON_RESIDUES_0, + VK_NON_RESIDUES_1, + VK_G2_ELEMENT_0_X1, + VK_G2_ELEMENT_0_X2, + VK_G2_ELEMENT_0_Y1, + VK_G2_ELEMENT_0_Y2, + VK_G2_ELEMENT_1_X1, + VK_G2_ELEMENT_1_X2, + VK_G2_ELEMENT_1_Y1, + VK_G2_ELEMENT_1_Y2 + ) + ); + } + + /// @inheritdoc IVerifierV2 + function verify( + uint256[] calldata, // _publicInputs + uint256[] calldata // _proof + ) public view virtual returns (bool) { + // Beginning 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 { + // load public inputs and proof from the calldata + load_inputs() + initialize_transcript() + // identities at verifier's point + compute_main_gate_quotient() + compute_copy_permutation_quotients() + // openings + initialize_opening_state() + // final pairing + let result := check_openings() + mstore(0, result) + return(0, 0x20) + + function load_inputs() { + // 1. Load public inputs + let publicInputOffset := calldataload(0x04) + let publicInputLengthInWords := calldataload(add(publicInputOffset, 0x04)) + // We expect only one public input + if iszero(eq(publicInputLengthInWords, PROOF_PUBLIC_INPUTS_LENGTH)) { + revertWithMessage(32, "public input length is incorrect") + } + mstore(MEM_PROOF_PUBLIC_INPUT_SLOT, mod(calldataload(add(publicInputOffset, 0x24)), R_MOD)) + + // 2. Load proof + let proofLengthOffset := calldataload(0x24) + let proofLengthInWords := calldataload(add(proofLengthOffset, 0x04)) + + if iszero(eq(proofLengthInWords, PROOF_LENGTH)) { + revertWithMessage(25, "proof length is incorrect") + } + let proofOffset := add(proofLengthOffset, 0x24) + // Note: We don't accept the point-at-infinity as a valid input for the commitments considering the security risks involved, + // as it may aid in proof manipulation and final pairing computation. + { + let x := mod(calldataload(proofOffset), Q_MOD) + let y := mod(calldataload(add(proofOffset, 0x20)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) { + revertWithMessage(28, "commitment 0 is not on curve") + } + mstore(MEM_PROOF_COMMITMENT_0_G1_Y, y) + mstore(MEM_PROOF_COMMITMENT_0_G1_X, x) + } + { + let x := mod(calldataload(add(proofOffset, 0x40)), Q_MOD) + let y := mod(calldataload(add(proofOffset, 0x60)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) { + revertWithMessage(28, "commitment 1 is not on curve") + } + mstore(MEM_PROOF_COMMITMENT_1_G1_Y, y) + mstore(MEM_PROOF_COMMITMENT_1_G1_X, x) + } + { + let x := mod(calldataload(add(proofOffset, 0x80)), Q_MOD) + let y := mod(calldataload(add(proofOffset, 0xa0)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) { + revertWithMessage(28, "commitment 2 is not on curve") + } + mstore(MEM_PROOF_COMMITMENT_2_G1_Y, y) + mstore(MEM_PROOF_COMMITMENT_2_G1_X, x) + } + { + let x := mod(calldataload(add(proofOffset, 0xc0)), Q_MOD) + let y := mod(calldataload(add(proofOffset, 0xe0)), Q_MOD) + let xx := mulmod(x, x, Q_MOD) + if iszero(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD))) { + revertWithMessage(28, "commitment 3 is not on curve") + } + mstore(MEM_PROOF_COMMITMENT_3_G1_Y, y) + mstore(MEM_PROOF_COMMITMENT_3_G1_X, x) + } + proofOffset := add(proofOffset, 0x100) + + for { + let i := 0 + } lt(i, PROOF_EVALUATIONS_LENGTH) { + i := add(i, 1) + } { + let eval := mod(calldataload(add(proofOffset, mul(i, 0x20))), R_MOD) + let slot := add(MEM_PROOF_EVALUATIONS, mul(i, 0x20)) + mstore(slot, eval) + } + proofOffset := add(proofOffset, mul(PROOF_EVALUATIONS_LENGTH, 0x20)) + + mstore(MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE, mod(calldataload(proofOffset), R_MOD)) + } + + /** + * @dev Commits data in the transcript then gets the challenges + * @notice that at this point, the transcript only has public inputs + * But luckily prover doesn't need any randomness in the first round + * so that prover has no control over the values because quotients are + * separated(there is no quotient aggregation neither in this round nor all rounds) + * + * w = 0x1283ba6f4b7b1a76ba2008fe823128bea4adb9269cbfd7c41c223be65bc60863 + */ + function initialize_transcript() { + if iszero(lt(DOMAIN_SIZE, R_MOD)) { + revertWithMessage(26, "Domain size >= R_MOD [ITS]") + } + if iszero(lt(OMEGA, R_MOD)) { + revertWithMessage(20, "Omega >= R_MOD [ITS]") + } + for { + let i := 0 + } lt(i, VK_NUM_INPUTS) { + i := add(i, 1) + } { + update_transcript(mload(add(MEM_PROOF_PUBLIC_INPUT_SLOT, mul(i, 0x20)))) + } + // commit first round commitment: preprocessed polynomials + update_transcript(VK_C0_G1_X) + update_transcript(VK_C0_G1_Y) + + // commit second round commitment: witnesses and gate identities + update_transcript(mload(MEM_PROOF_COMMITMENT_0_G1_X)) + update_transcript(mload(MEM_PROOF_COMMITMENT_0_G1_Y)) + + // copy-permutation challenges + mstore(PVS_BETA, get_challenge(0)) + mstore(PVS_GAMMA, get_challenge(1)) + // commit third round commitment: copy-perm + update_transcript(mload(MEM_PROOF_COMMITMENT_1_G1_X)) + update_transcript(mload(MEM_PROOF_COMMITMENT_1_G1_Y)) + // get evaluation challenge + // all system polynomials will be evaluated at z + // then combined polynomials will be opened at h_i = r^power_i + // then it becomes e.g C_i(X) = f_0(x^2) + x*f(x^2) in case of two polynomials + mstore(PVS_R, get_challenge(2)) + // commit all evaluations + for { + let i := 0 + } lt(i, PROOF_EVALUATIONS_LENGTH) { + i := add(i, 1) + } { + update_transcript(mload(add(MEM_PROOF_EVALUATIONS, mul(i, 0x20)))) + } + // get aggregation challenge + mstore(PVS_ALPHA_0, get_challenge(3)) + mstore(PVS_ALPHA_1, mulmod(mload(PVS_ALPHA_0), mload(PVS_ALPHA_0), R_MOD)) + // commit w(X) + update_transcript(mload(MEM_PROOF_COMMITMENT_2_G1_X)) + update_transcript(mload(MEM_PROOF_COMMITMENT_2_G1_Y)) + // opening challenge + mstore(PVS_Y, get_challenge(4)) + mstore(PVS_Z, modexp(mload(PVS_R), 24)) + // grand product of copy-permutation needs to be opened at shifted position + mstore(PVS_Z_OMEGA, mulmod(mload(PVS_Z), OMEGA, R_MOD)) + // Z_h(z) = X^N - 1 + mstore(PVS_VANISHING_AT_Z, addmod(modexp(mload(PVS_Z), DOMAIN_SIZE), sub(R_MOD, ONE), R_MOD)) + // L0(z) = 1/(N*(X-1)) * (X^N - 1) + mstore( + PVS_L_0_AT_Z, + modexp(mulmod(addmod(mload(PVS_Z), sub(R_MOD, ONE), R_MOD), DOMAIN_SIZE, R_MOD), sub(R_MOD, 2)) + ) + mstore(PVS_L_0_AT_Z, mulmod(mload(PVS_L_0_AT_Z), mload(PVS_VANISHING_AT_Z), R_MOD)) + mstore(PVS_VANISHING_AT_Z_INV, modexp(mload(PVS_VANISHING_AT_Z), sub(R_MOD, 2))) + } + + /** + * @dev Computes main gate quotient T0(ζ) + * T0(ζ) = (qm(ζ)*a(ζ)*b(ζ) + qa(ζ)*a(ζ) + qb(ζ)*b(ζ) + qc(ζ)*c(ζ) + qconst(ζ) + PI*L0(ζ)) * ZH(ζ)^-1 + */ + function compute_main_gate_quotient() { + // q_const + let rhs := mload(add(MEM_PROOF_EVALUATIONS, mul(4, 0x20))) + rhs := addmod(rhs, mulmod(mload(PVS_L_0_AT_Z), mload(MEM_PROOF_PUBLIC_INPUT_SLOT), R_MOD), R_MOD) + for { + let i := 0 + } lt(i, 3) { + i := add(i, 1) + } { + rhs := addmod( + rhs, + mulmod( + mload(add(MEM_PROOF_EVALUATIONS, mul(i, 0x20))), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, i), 0x20))), + R_MOD + ), + R_MOD + ) + } + // q_m*A*B + rhs := mulmod( + addmod( + rhs, + mulmod( + mulmod( + mload(add(MEM_PROOF_EVALUATIONS, mul(3, 0x20))), + mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(9, 0x20))), + R_MOD + ), + R_MOD + ), + mload(PVS_VANISHING_AT_Z_INV), + R_MOD + ) + mstore(MAIN_GATE_QUOTIENT_AT_Z, rhs) + } + + /** + * @dev Computes copy permutation quotients T1(ζ) & T2(ζ) + * T1(ζ) = ((z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ)) + * −(z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) * ZH(ζ)^-1 + * T2(ζ) = (z(ζ)−1)*L0(ζ)*ZH(ζ)^-1 + */ + function compute_copy_permutation_quotients() { + let tmp + let tmp2 + // (c(ζ)+k2*β*ζ+γ) + let rhs := addmod( + addmod( + mulmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), VK_NON_RESIDUES_1, R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 2), 0x20))), + R_MOD + ) + // (b(ζ)+k1*β*ζ+γ) + tmp := addmod( + addmod( + mulmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), VK_NON_RESIDUES_0, R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 1), 0x20))), + R_MOD + ) + // (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ) + rhs := mulmod(rhs, tmp, R_MOD) + // (z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ) + rhs := mulmod( + mulmod( + rhs, + addmod( + addmod(mulmod(mload(PVS_BETA), mload(PVS_Z), R_MOD), mload(PVS_GAMMA), R_MOD), + mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))), + R_MOD + ), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), + R_MOD + ) + + // (z(ζω) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) + tmp2 := mulmod( + mulmod( + addmod( + addmod( + mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(add(5, 2), 0x20))), R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 2), 0x20))), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), + R_MOD + ), + addmod( + addmod( + mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(add(5, 1), 0x20))), R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(add(8, 1), 0x20))), + R_MOD + ), + R_MOD + ) + // (a(ζ)+β*sσ1(ζ)+γ) + tmp := addmod( + addmod( + mulmod(mload(PVS_BETA), mload(add(MEM_PROOF_EVALUATIONS, mul(5, 0x20))), R_MOD), + mload(PVS_GAMMA), + R_MOD + ), + mload(add(MEM_PROOF_EVALUATIONS, mul(8, 0x20))), + R_MOD + ) + // z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ) + tmp2 := mulmod(tmp2, tmp, R_MOD) + // −(z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) + tmp2 := sub(R_MOD, tmp2) + // ((z(ζ) * (a(ζ)+β*ζ+γ) * (b(ζ)+k1*β*ζ+γ) * (c(ζ)+k2*β*ζ+γ)) − (z(ζω) * (a(ζ)+β*sσ1(ζ)+γ) * (b(ζ)+β*sσ2(ζ)+γ) * (c(ζ)+β*sσ3(ζ)+γ)) * ZH(ζ)^-1 + rhs := mulmod(addmod(rhs, tmp2, R_MOD), mload(PVS_VANISHING_AT_Z_INV), R_MOD) + mstore(COPY_PERM_FIRST_QUOTIENT_AT_Z, rhs) + + // (z(ζ)−1)*L0(ζ)*ZH(ζ)^-1 + rhs := mulmod( + mulmod( + addmod(mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), sub(R_MOD, 1), R_MOD), + mload(PVS_L_0_AT_Z), + R_MOD + ), + mload(PVS_VANISHING_AT_Z_INV), + R_MOD + ) + mstore(COPY_PERM_SECOND_QUOTIENT_AT_Z, rhs) + } + + /** + * @dev Computes partial lagrange basis evaluations Li(y)_numerator {i = [start..(start+num_polys))} using montgomery lagrange basis inverses sent with proof. + * Li(y)_numerator = (w_i * (y^{num_polys} - h^{num_polys})) + * Li(y)_denominator = (num_polys * h^{num_polys-1} * (y - (h * w_i))) + * Li(y) = Li(y)_numerator / Li(y)_denominator = (w_i * (y^{num_polys} - h^{num_polys})) / (num_polys * h^{num_polys-1} * (y - (h * w_i))) + * + * Also calculates the products of the denominators of the lagrange basis evaluations: + * Li(y)_denominators_product = Li(y)_previous_denominators_product * (∏(Li(y)_denominator {i = [start..(start+num_polys))})) + */ + function precompute_partial_lagrange_basis_evaluations(start, num_polys, y, omega, h, product) + -> interim_product + { + if gt(add(start, num_polys), TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH) { + revertWithMessage(31, "Precompute Eval. Error [PLBEI1]") + } + let tmp := h + let loop_length := sub(num_polys, 2) + // h^{num_polys-1} + for { + let i := 0 + } lt(i, loop_length) { + i := add(i, 1) + } { + tmp := mulmod(tmp, h, R_MOD) + } + // num_polys * h^{num_polys-1} + let constant_part := mulmod(num_polys, tmp, R_MOD) + + // y^{num_polys} + let y_pow := mload(add(OPS_Y_POWS, mul(num_polys, 0x20))) + // h^{num_polys} + let num_at_y := mulmod(tmp, h, R_MOD) + // -h^{num_polys} + num_at_y := sub(R_MOD, num_at_y) + // (y^{num_polys} - h^{num_polys}) + num_at_y := addmod(num_at_y, y_pow, R_MOD) + + let current_omega := 1 + for { + let i := 0 + } lt(i, num_polys) { + i := add(i, 1) + } { + // h*w_i + tmp := mulmod(current_omega, h, R_MOD) + // -h*w_i + tmp := sub(R_MOD, tmp) + // y-(h*w_i) + tmp := addmod(tmp, y, R_MOD) + // (num_polys * h^{num_polys-1} * (y - (h * w_i))) + tmp := mulmod(tmp, constant_part, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(start, i), 0x20)), tmp) + + product := mulmod(product, tmp, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(start, i), 0x20)), product) + // Li(y) = (W_i * (y^{num_polys} - h^{num_polys})) + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(start, i), 0x20)), + mulmod(num_at_y, current_omega, R_MOD) + ) + + // w_i {i = i+1} + current_omega := mulmod(current_omega, omega, R_MOD) + } + + interim_product := product + } + + /** + * @dev Computes partial lagrange basis evaluations Li(y)_numerator = {i = [start..(start+num_polys))} & Li(y)_numerator {i = [(start+num_polys)..(start+(2*num_polys)))} using montgomery lagrange basis inverses sent with proof. + * For Li(y)_numerator{i = [start..(start+num_polys))}: + * Li(y)_numerator = w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) + * Li(y)_denominator = (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i))) + * Li(y) = Li(y)_numerator / Li(y)_denominator = (w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})))) / (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i))) + * + * For Li(y)_numerator{i = [(start+num_polys)..(start+(2*num_polys)))} + * Li(y)_numerator = w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) + * Li(y)_denominator = (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i))) + * Li(y) = Li(y)_numerator / Li(y)_denominator = (w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) ) / (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i))) + * + * Also calculates the products of the denominators of the lagrange basis evaluations: + * Li(y)_denominators_product = Li(y)_previous_denominators_product * (∏(Li(y)_denominator {i = [start..(start+num_polys))})) * (∏(Li(y)_denominator {i = [(start+num_polys)..(start+(2*num_polys)))})) + */ + + function precompute_partial_lagrange_basis_evaluations_for_union_set( + start, + num_polys, + y, + omega, + h, + h_shifted, + product + ) -> final_product { + if gt(add(start, mul(2, num_polys)), TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH) { + revertWithMessage(32, "Precompute Eval. Error [PLBEIU1]") + } + let h_pows_0 := h + let h_pows_1 := h_shifted + let loop_length := sub(num_polys, 2) + // h^{num_polys-1} & h_s^{num_polys-1} + for { + let i := 0 + } lt(i, loop_length) { + i := add(i, 1) + } { + h_pows_0 := mulmod(h_pows_0, h, R_MOD) + h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD) + } + let constant_parts_0 := h_pows_0 + let constant_parts_1 := h_pows_1 + // h^{num_polys} + h_pows_0 := mulmod(h_pows_0, h, R_MOD) + // h_s^{num_polys} + h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD) + + // h^{num_polys-1} * h_s^{num_polys} + constant_parts_0 := mulmod(constant_parts_0, h_pows_1, R_MOD) + // -h^{num_polys-1} * h_s^{num_polys} + constant_parts_0 := sub(R_MOD, constant_parts_0) + // h_s^{num_polys-1} * h^{num_polys} + constant_parts_1 := mulmod(constant_parts_1, h_pows_0, R_MOD) + // -h_s^{num_polys-1} * h^{num_polys} + constant_parts_1 := sub(R_MOD, constant_parts_1) + + // y^{num_polys} + let t_2 := mload(add(OPS_Y_POWS, mul(num_polys, 0x20))) + // h^{num_polys} * h_s^{num_polys} + let t_1 := mulmod(h_pows_0, h_pows_1, R_MOD) + // h^{num_polys} + h_s^{num_polys} + let t_0 := addmod(h_pows_0, h_pows_1, R_MOD) + // y^{num_polys} * (h^{num_polys} + h_s^{num_polys}) + t_0 := mulmod(t_0, t_2, R_MOD) + // - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})) + t_0 := sub(R_MOD, t_0) + // h^{num_polys} * h_s^{num_polys} - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})) + t_1 := addmod(t_1, t_0, R_MOD) + // y^{2*num_polys} + t_2 := mulmod(t_2, t_2, R_MOD) + // y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys})) + t_1 := addmod(t_1, t_2, R_MOD) + loop_length := sub(num_polys, 1) + // h^{(2*num_polys)-1} & h_s^{(2*num_polys)-1} + for { + let i := 0 + } lt(i, loop_length) { + i := add(i, 1) + } { + h_pows_0 := mulmod(h_pows_0, h, R_MOD) + h_pows_1 := mulmod(h_pows_1, h_shifted, R_MOD) + } + // h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys}) + constant_parts_0 := addmod(constant_parts_0, h_pows_0, R_MOD) + // num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) + constant_parts_0 := mulmod(constant_parts_0, num_polys, R_MOD) + // h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys}) + constant_parts_1 := addmod(constant_parts_1, h_pows_1, R_MOD) + // num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) + constant_parts_1 := mulmod(constant_parts_1, num_polys, R_MOD) + + let current_omega := 1 + let interim_product := product + for { + let i := 0 + } lt(i, num_polys) { + i := add(i, 1) + } { + t_0 := mulmod(current_omega, h, R_MOD) + t_0 := sub(R_MOD, t_0) + t_0 := addmod(t_0, y, R_MOD) + // (num_polys * (h^{(2*num_polys)-1}-(h^{num_polys-1} * h_s^{num_polys})) * (y-(h*w_i))) + t_0 := mulmod(t_0, constant_parts_0, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(start, i), 0x20)), t_0) + + interim_product := mulmod(interim_product, t_0, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(start, i), 0x20)), interim_product) + // w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(start, i), 0x20)), + mulmod(t_1, current_omega, R_MOD) + ) + // w_i {i = i+1} + current_omega := mulmod(current_omega, omega, R_MOD) + } + + current_omega := 1 + for { + let i := 0 + } lt(i, num_polys) { + i := add(i, 1) + } { + t_0 := mulmod(current_omega, h_shifted, R_MOD) + t_0 := sub(R_MOD, t_0) + t_0 := addmod(t_0, y, R_MOD) + // (num_polys * (h_s^{(2*num_polys)-1}-(h_s^{num_polys-1} * h^{num_polys})) * (y-(h_s*w_i))) + t_0 := mulmod(t_0, constant_parts_1, R_MOD) + + mstore(add(MEM_LAGRANGE_BASIS_DENOMS, mul(add(add(start, num_polys), i), 0x20)), t_0) + + interim_product := mulmod(interim_product, t_0, R_MOD) + + mstore( + add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(add(add(start, num_polys), i), 0x20)), + interim_product + ) + // w_i * (y^{2*num_polys} + (h^{num_polys} * h_s^{num_polys}) - (y^{num_polys} * (h^{num_polys} + h_s^{num_polys}))) + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(start, num_polys), i), 0x20)), + mulmod(t_1, current_omega, R_MOD) + ) + // w_i {i = i+1} + current_omega := mulmod(current_omega, omega, R_MOD) + } + + final_product := interim_product + } + + /** + * @dev Computes lagrange basis evaluations using montgomery lagrange basis inverses sent with proof. + * @notice Check individual functions for more details + */ + function precompute_all_lagrange_basis_evaluations_from_inverses() { + let y := mload(PVS_Y) + // w8 = 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80 + // w4 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636 + // w3 = 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd + let product_0_7 := precompute_partial_lagrange_basis_evaluations( + 0, + 8, + y, + 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + 1 + ) + let product_0_11 := precompute_partial_lagrange_basis_evaluations( + 8, + 4, + y, + 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636, + mload(add(OPS_OPENING_POINTS, mul(1, 0x20))), + product_0_7 + ) + let product_0_17 := precompute_partial_lagrange_basis_evaluations_for_union_set( + add(8, 4), + 3, + y, + 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd, + mload(add(OPS_OPENING_POINTS, mul(2, 0x20))), + mload(add(OPS_OPENING_POINTS, mul(3, 0x20))), + product_0_11 + ) + + let montgomery_inverse := mload(MEM_PROOF_MONTGOMERY_LAGRANGE_BASIS_INVERSE) + + if iszero(eq(mulmod(product_0_17, montgomery_inverse, R_MOD), 1)) { + revertWithMessage(30, "Precompute Eval. Error [PALBE]") + } + let temp := montgomery_inverse + let loop_length := sub(TOTAL_LAGRANGE_BASIS_INVERSES_LENGTH, 1) + for { + let i := loop_length + } gt(i, 0) { + i := sub(i, 1) + } { + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(i, 0x20)), + mulmod( + mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(i, 0x20))), + mulmod(mload(add(MEM_LAGRANGE_BASIS_DENOM_PRODUCTS, mul(sub(i, 1), 0x20))), temp, R_MOD), + R_MOD + ) + ) + temp := mulmod(temp, mload(add(MEM_LAGRANGE_BASIS_DENOMS, mul(i, 0x20))), R_MOD) + } + mstore( + add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20)), + mulmod(mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20))), temp, R_MOD) + ) + } + + /** + * @dev Computes opening points h0, h1, h2, h3 + */ + function compute_opening_points() { + // h = r^{power/num_polys} + let pvs_r := mload(PVS_R) + let r_2 := mulmod(pvs_r, pvs_r, R_MOD) + let r_3 := mulmod(r_2, pvs_r, R_MOD) + let r_6 := mulmod(r_3, r_3, R_MOD) + let r_8 := mulmod(r_6, r_2, R_MOD) + // h0 = pvs_r^3 + mstore(add(OPS_OPENING_POINTS, mul(0, 0x20)), r_3) + // h1 = pvs_r^6 + mstore(add(OPS_OPENING_POINTS, mul(1, 0x20)), r_6) + // h2 = pvs_r^8 + mstore(add(OPS_OPENING_POINTS, mul(2, 0x20)), r_8) + + // h3 (only round 2 needs opening at shifted point) + mstore( + add(OPS_OPENING_POINTS, mul(3, 0x20)), + mulmod(r_8, 0x0925f0bd364638ec3084b45fc27895f8f3f6f079096600fe946c8e9db9a47124, R_MOD) + ) + } + + /** + * @dev Initializes opening state OPS_Y_POWS[i] = y^i + * @notice only 9 powers are computed since the rest stay unused. + */ + function initialize_opening_state() { + compute_opening_points() + let acc := 1 + for { + let i := 0 + } lt(i, 9) { + i := add(i, 1) + } { + mstore(add(OPS_Y_POWS, mul(i, 0x20)), acc) + acc := mulmod(acc, mload(PVS_Y), R_MOD) + } + precompute_all_lagrange_basis_evaluations_from_inverses() + } + + /** + * @dev Computes r polynomial evaluations utilizing horner method + * (r*w)^{i}:{1, w*r, (w*r)^2, .. , (w*r)^{k-1}} + * horner: c0 + c1*(rw) + c2*(rw)^2 + c3*(rw)^3 -> (c0 + (rw)*(c1 + (rw)*(c2 + c3*(rw)))) + */ + function evaluate_r_polys_at_point_unrolled( + main_gate_quotient_at_z, + copy_perm_first_quotient_at_z, + copy_perm_second_quotient_at_z + ) { + let omega_h + let c + + // setup round + // r + + // w8^1 = 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80 + // w8^2 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636 + // w8^3 = 0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566 + // w8^4 = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000 + // w8^5 = 0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181 + // w8^6 = 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb + // w8^7 = 0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b + omega_h := mload(add(OPS_OPENING_POINTS, mul(0, 0x20))) + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(0, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x2b337de1c8c14f22ec9b9e2f96afef3652627366f8170a0a948dad4ac1bd5e80, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(1, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(2, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x1d59376149b959ccbd157ac850893a6f07c2d99b3852513ab8d01be8e846a566, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(3, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(4, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x0530d09118705106cbb4a786ead16926d5d174e181a26686af5448492e42a181, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(5, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(6, 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x130b17119778465cfb3acaee30f81dee20710ead41671f568b11d9ab07b95a9b, + mload(add(OPS_OPENING_POINTS, mul(0, 0x20))), + R_MOD + ) + + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(7, 0x20))), omega_h, R_MOD) + for { + let i := 1 + } lt(i, 7) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(7, i), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(MEM_PROOF_EVALUATIONS), R_MOD) + mstore( + PS_R_EVALS, + addmod( + mload(PS_R_EVALS), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(7, 0x20))), R_MOD), + R_MOD + ) + ) + + // first round + // r + + // w4^1 = 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636 + // w4^2 = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000 + // w4^3 = 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb + omega_h := mload(add(OPS_OPENING_POINTS, mul(1, 0x20))) + + c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD) + for { + let i := 1 + } lt(i, 3) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD) + + mstore( + add(PS_R_EVALS, mul(1, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(1, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 0), 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x30644e72e131a029048b6e193fd841045cea24f6fd736bec231204708f703636, + mload(add(OPS_OPENING_POINTS, mul(1, 0x20))), + R_MOD + ) + + c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD) + for { + let i := 1 + } lt(i, 3) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(1, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(1, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 1), 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000000, + mload(add(OPS_OPENING_POINTS, mul(1, 0x20))), + R_MOD + ) + + c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD) + for { + let i := 1 + } lt(i, 3) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(1, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(1, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 2), 0x20))), R_MOD), + R_MOD + ) + ) + omega_h := mulmod( + 0x0000000000000000b3c4d79d41a91758cb49c3517c4604a520cff123608fc9cb, + mload(add(OPS_OPENING_POINTS, mul(1, 0x20))), + R_MOD + ) + + c := mulmod(main_gate_quotient_at_z, omega_h, R_MOD) + for { + let i := 1 + } lt(i, 3) { + i := add(i, 1) + } { + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), i), 1), 0x20))), R_MOD), + omega_h, + R_MOD + ) + } + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(sub(sub(add(8, 4), 3), 1), 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(1, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(1, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(8, 3), 0x20))), R_MOD), + R_MOD + ) + ) + + // second round + // c2 + // r + omega_h := mload(add(OPS_OPENING_POINTS, mul(2, 0x20))) + let omega_h_shifted := mload(add(OPS_OPENING_POINTS, mul(3, 0x20))) + c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD) + c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 0), 0x20))), R_MOD), + R_MOD + ) + ) + // c2 shifted + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD) + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD), + omega_h_shifted, + R_MOD + ) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod( + c, + mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 0), 0x20))), + R_MOD + ), + R_MOD + ) + ) + // c2 + omega_h := mulmod( + 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd, + mload(add(OPS_OPENING_POINTS, mul(2, 0x20))), + R_MOD + ) + omega_h_shifted := mulmod( + 0x0000000000000000b3c4d79d41a917585bfc41088d8daaa78b17ea66b99c90dd, + mload(add(OPS_OPENING_POINTS, mul(3, 0x20))), + R_MOD + ) + + c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD) + c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 1), 0x20))), R_MOD), + R_MOD + ) + ) + // c2 shifted + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD) + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD), + omega_h_shifted, + R_MOD + ) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod( + c, + mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 1), 0x20))), + R_MOD + ), + R_MOD + ) + ) + // c2 + omega_h := mulmod( + 0x30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23, + mload(add(OPS_OPENING_POINTS, mul(2, 0x20))), + R_MOD + ) + omega_h_shifted := mulmod( + 0x30644e72e131a029048b6e193fd84104cc37a73fec2bc5e9b8ca0b2d36636f23, + mload(add(OPS_OPENING_POINTS, mul(3, 0x20))), + R_MOD + ) + + c := mulmod(copy_perm_second_quotient_at_z, omega_h, R_MOD) + c := mulmod(addmod(c, copy_perm_first_quotient_at_z, R_MOD), omega_h, R_MOD) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(11, 0x20))), R_MOD) + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod(c, mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(8, 4), 2), 0x20))), R_MOD), + R_MOD + ) + ) + // c2 shifted + c := mulmod(mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 2), 0x20))), omega_h_shifted, R_MOD) + c := mulmod( + addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(add(12, 1), 0x20))), R_MOD), + omega_h_shifted, + R_MOD + ) + c := addmod(c, mload(add(MEM_PROOF_EVALUATIONS, mul(12, 0x20))), R_MOD) + + mstore( + add(PS_R_EVALS, mul(2, 0x20)), + addmod( + mload(add(PS_R_EVALS, mul(2, 0x20))), + mulmod( + c, + mload(add(MEM_PROOF_LAGRANGE_BASIS_EVALS, mul(add(add(add(8, 4), 3), 2), 0x20))), + R_MOD + ), + R_MOD + ) + ) + } + + /** + * @dev Computes the openings and returns the result of pairing computation + */ + function check_openings() -> out { + // f(X) = (Z_{T\S0}(y) * (C0(X) - r0(y))) + (alpha*(Z_{T\S1}(y)*(C1(X) - r1(y)))) + (alpha^{2}*(Z_{T\S2}(y)*(C2(X) - r2(y)))) + // Note that, in our case set differences(Z_T\{S_i}) are: + // - Z_{T\S0}(y): (y^{k1}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w)) + // - Z_{T\S1}(y): (y^{k0}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w)) + // - Z_{T\S2}(y): (y^{k0}-ζ)*(y^{k1}-ζ) where + // k0=8, k1=4, and k2=3 are number of the polynomials for setup, first and second round respectively + + let tmp + evaluate_r_polys_at_point_unrolled( + mload(MAIN_GATE_QUOTIENT_AT_Z), + mload(COPY_PERM_FIRST_QUOTIENT_AT_Z), + mload(COPY_PERM_SECOND_QUOTIENT_AT_Z) + ) + + // -ζ + mstore(add(PS_MINUS_Z, mul(0, 0x20)), sub(R_MOD, mload(PVS_Z))) + // -(ζ*w) + mstore(add(PS_MINUS_Z, mul(1, 0x20)), sub(R_MOD, mload(PVS_Z_OMEGA))) + + // Z_{T\S0}(y) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)), + addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(1, 0x20))), R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), tmp, R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(4, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), tmp, R_MOD) + ) + mstore(PS_VANISHING_AT_Y, mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20)))) + mstore(PS_INV_ZTS0_AT_Y, modexp(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(0, 0x20))), sub(R_MOD, 2))) + + // Z_{T\S1}(y) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)), + addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(1, 0x20))), R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(3, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), tmp, R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(8, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), tmp, R_MOD) + ) + mstore(PS_VANISHING_AT_Y, mulmod(mload(PS_VANISHING_AT_Y), tmp, R_MOD)) + + // // Z_{T\S2}(y) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20)), + addmod(mload(add(OPS_Y_POWS, mul(4, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + ) + tmp := addmod(mload(add(OPS_Y_POWS, mul(8, 0x20))), mload(add(PS_MINUS_Z, mul(0, 0x20))), R_MOD) + mstore( + add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20)), + mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20))), tmp, R_MOD) + ) + + // W(X) = f(X) / Z_T(y) where Z_T(y) = (y^{k0}-ζ)*(y^{k1}-ζ)*(y^{k2}-ζ)*(y^{k2}-(ζ*w)) + // we need to check that + // f(X) - W(X) * Z_T(y) = 0 + + // W'(X) = L(X) / (Z_{T\S0}(y)*(X-y)) + // L(X)/Z_{T\S0}(y) = (C0(X) - r0(y)) + (alpha*(Z_{T\S1}(y)/Z_{T\S0}(y))*(C1(X) - r1(y))) + (alpha^{2}*(Z_{T\S2}(y)/Z_{T\S0}(y))*(C2(X) - r2(y))) - ((Z_T(y)/Z_{T\S0}(y))*W(X)) + + // the identity check is reduced into following + // L(X) - W'(X)*Z_{T\S0}(y)(X-y) == 0 + // verifier has commitments to the C_i(X) polynomials + // verifer also recomputed r_i(y) + // group constant and commitment parts + // first prepare L(X)/Z_{T\S0}(y) + // C(X) = C0(X) + ((alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*C1(X)) + ((alpha^2*Z_{T\S2}(y)/Z_{T\S0}(y))*C2(X)) + // r(y) = r0(y) + ((alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*r1(y)) + ((alpha^2*Z_{T\S2}(y)/Z_{T\S0}(y))*r2(y)) + // now construct + // L(X)/Z_{T\S0}(y) = C(X) - r(y) - ((Z_T(y)/Z_{T\S0}(y))*W(X)) + // now check following identity + // C(X) - r(y) - ((Z_t(y)/Z_{T\S0}(y))*W(X)) - (W'(X)*(X-y)) = 0 + // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\S0}(y))*[W] - [(X-y)*W'] = 0 + // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\S0}(y))*[W] - [X*W'] + [y*W]' = 0 + // [C(X)] - [r(y)*G1] - (Z_T(y)/Z_{T\S0}(y))*[W] + [y*W'] - [X*W'] = 0 + // points with X will be multiplied in the exponent via pairing + // so final pairing would ne + // e([C(X)] - [r(y)*G1] - [Z_T(y)/(Z_{T\S0}(y)*W)] + [y*W'], G2)*e(-W', X*G2) = 1 + + // C0 + let ps_aggregated_commitment_g1_x := VK_C0_G1_X + let ps_aggregated_commitment_g1_y := VK_C0_G1_Y + + // ((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y)) + let aggregated_r_at_y := mulmod( + mload(add(PS_SET_DIFFERENCES_AT_Y, mul(2, 0x20))), + mload(PS_INV_ZTS0_AT_Y), + R_MOD + ) + aggregated_r_at_y := mulmod(aggregated_r_at_y, mload(PVS_ALPHA_1), R_MOD) + + // ((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y))*C2 + let tp_g1_x, tp_g1_y := point_mul( + mload(MEM_PROOF_COMMITMENT_1_G1_X), + mload(MEM_PROOF_COMMITMENT_1_G1_Y), + aggregated_r_at_y + ) + // c0 + (((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y))*C2) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + // ((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y))*r2 + aggregated_r_at_y := mulmod(aggregated_r_at_y, mload(add(PS_R_EVALS, mul(2, 0x20))), R_MOD) + + // (alpha*Z_{T\S1}(y)/Z_{T\S0}(y)) + tmp := mulmod(mload(add(PS_SET_DIFFERENCES_AT_Y, mul(1, 0x20))), mload(PS_INV_ZTS0_AT_Y), R_MOD) + tmp := mulmod(tmp, mload(PVS_ALPHA_0), R_MOD) + + // (alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*C1 + tp_g1_x, tp_g1_y := point_mul( + mload(MEM_PROOF_COMMITMENT_0_G1_X), + mload(MEM_PROOF_COMMITMENT_0_G1_Y), + tmp + ) + // c0 + ((alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*C1) + (((alpha^{2}*Z_{T\S2}(y))/Z_{T\S0}(y))*C2) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + // (alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*r1 + tmp := mulmod(tmp, mload(add(PS_R_EVALS, mul(1, 0x20))), R_MOD) + // ((alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*r1) + ((alpha^{2}*Z_{T\S2}(y)/Z_{T\S0}(y))*r2) + aggregated_r_at_y := addmod(aggregated_r_at_y, tmp, R_MOD) + // r0 + (alpha*Z_{T\S1}(y)/Z_{T\S0}(y))*r1 + ((alpha^{2}*Z_{T\S2}(y)/Z_{T\S0}(y))*r2) + aggregated_r_at_y := addmod(aggregated_r_at_y, mload(PS_R_EVALS), R_MOD) + tp_g1_x, tp_g1_y := point_mul(1, 2, aggregated_r_at_y) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_sub( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + // - ((Z_T(y)/Z_{T\S0}(y))*W(X)) + mstore(PS_VANISHING_AT_Y, mulmod(mload(PS_VANISHING_AT_Y), mload(PS_INV_ZTS0_AT_Y), R_MOD)) + tp_g1_x, tp_g1_y := point_mul( + mload(MEM_PROOF_COMMITMENT_2_G1_X), + mload(MEM_PROOF_COMMITMENT_2_G1_Y), + mload(PS_VANISHING_AT_Y) + ) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_sub( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + // L(X)/Z_{T\S0}(y) is aggregated + + // Now check W'(X) = L(X) / (Z_{T\S0}(y)*(x-y)) + // L(X)/Z_{T\S0}(y) + (y*W'(X)) - (x*W'(X)) = 0 + tp_g1_x, tp_g1_y := point_mul( + mload(MEM_PROOF_COMMITMENT_3_G1_X), + mload(MEM_PROOF_COMMITMENT_3_G1_Y), + mload(PVS_Y) + ) + ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y := point_add( + ps_aggregated_commitment_g1_x, + ps_aggregated_commitment_g1_y, + tp_g1_x, + tp_g1_y + ) + let is_zero_commitment + if iszero(mload(MEM_PROOF_COMMITMENT_3_G1_Y)) { + if gt(mload(MEM_PROOF_COMMITMENT_3_G1_X), 0) { + revertWithMessage(21, "non zero x value [CO]") + } + is_zero_commitment := 1 + } + + out := pairing_check(ps_aggregated_commitment_g1_x, ps_aggregated_commitment_g1_y, is_zero_commitment) + } + + /** + * @dev Generates the rolling hash using `val` and updates the transcript. + * The computation is done as follows: + * new_state_0 = keccak256(uint32(0) || old_state_0 || old_state_1 || value) + * new_state_1 = keccak256(uint32(1) || old_state_0 || old_state_1 || value) + * + * @notice The computation assumes that the memory slots 0x200 - 0x202 are clean and doesn't explicitly clean them + */ + function update_transcript(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 Generates a new challenge with (uint32(2) || state_0 || state_1 || uint32(challenge_counter)) + * The challenge_counter is incremented after every challenge + */ + function get_challenge(challenge_counter) -> challenge { + mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x02) + mstore(TRANSCRIPT_CHALLENGE_SLOT, shl(224, challenge_counter)) + challenge := and(keccak256(TRANSCRIPT_BEGIN_SLOT, 0x48), FR_MASK) + } + + /** + * @dev Performs scalar multiplication: point * scalar -> t + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + */ + function point_mul(p_x, p_y, s) -> t_x, t_y { + mstore(0x80, p_x) + mstore(0xa0, p_y) + mstore(0xc0, s) + + let success := staticcall(gas(), 7, 0x80, 0x60, 0x80, 0x40) + if iszero(success) { + revertWithMessage(27, "point multiplication failed") + } + t_x := mload(0x80) + t_y := mload(add(0x80, 0x20)) + } + + /** + * @dev Performs point addition: point 1 + point 2 -> t + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + */ + function point_add(p1_x, p1_y, p2_x, p2_y) -> t_x, t_y { + mstore(0x80, p1_x) + mstore(0xa0, p1_y) + mstore(0xc0, p2_x) + mstore(0xe0, p2_y) + + let success := staticcall(gas(), 6, 0x80, 0x80, 0x80, 0x40) + if iszero(success) { + revertWithMessage(21, "point addition failed") + } + + t_x := mload(0x80) + t_y := mload(add(0x80, 0x20)) + } + + /** + * @dev Performs point subtraction: point 1 + point 2 -> t + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + * @notice We don't consider the highly unlikely case where p2 can be a point-at-infinity and the function would revert. + */ + function point_sub(p1_x, p1_y, p2_x, p2_y) -> t_x, t_y { + mstore(0x80, p1_x) + mstore(0xa0, p1_y) + mstore(0xc0, p2_x) + mstore(0xe0, sub(Q_MOD, p2_y)) + + let success := staticcall(gas(), 6, 0x80, 0x80, 0x80, 0x40) + if iszero(success) { + revertWithMessage(24, "point subtraction failed") + } + + t_x := mload(0x80) + t_y := mload(add(0x80, 0x20)) + } + + /** + * @dev Calculates EC Pairing result following the EIP-197: https://eips.ethereum.org/EIPS/eip-197 + * Performs point negation before pairing calculation, if the flag `is_zero_commitment` is true + * + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + * While code reformatting consider not to overwrite the first constant-defined memory location, which is currently + * TRANSCRIPT_BEGIN_SLOT = 0x200 + */ + function pairing_check(p1_x, p1_y, is_zero_commitment) -> res { + mstore(0x80, p1_x) + mstore(0xa0, p1_y) + mstore(0xc0, VK_G2_ELEMENT_0_X1) + mstore(0xe0, VK_G2_ELEMENT_0_X2) + mstore(0x100, VK_G2_ELEMENT_0_Y1) + mstore(0x120, VK_G2_ELEMENT_0_Y2) + mstore(0x140, mload(MEM_PROOF_COMMITMENT_3_G1_X)) + mstore(0x160, mload(MEM_PROOF_COMMITMENT_3_G1_Y)) + if iszero(is_zero_commitment) { + mstore(0x160, sub(Q_MOD, mload(MEM_PROOF_COMMITMENT_3_G1_Y))) + } + mstore(0x180, VK_G2_ELEMENT_1_X1) + mstore(0x1a0, VK_G2_ELEMENT_1_X2) + mstore(0x1c0, VK_G2_ELEMENT_1_Y1) + mstore(0x1e0, VK_G2_ELEMENT_1_Y2) + + let success := staticcall(gas(), 8, 0x80, mul(12, 0x20), 0x80, 0x20) + + if iszero(success) { + revertWithMessage(20, "pairing check failed") + } + res := mload(0x80) + } + + /** + * @dev Reverts with the desired custom error string. + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + */ + function revertWithMessage(len, reason) { + // "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) + mstore(0x80, 0x08c379a000000000000000000000000000000000000000000000000000000000) + // Data offset + mstore(0x84, 0x0000000000000000000000000000000000000000000000000000000000000020) + // Length of revert string + mstore(0xa4, len) + // Revert reason + mstore(0xc4, reason) + // Revert + revert(0x80, 0x64) + } + + /** + * @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD. + * @notice Stores values starting from the initial free memory pointer i.e., 0x80. + * The free memory pointer is not updated as it stays unused throughout the code execution. + */ + function modexp(value, power) -> res { + mstore(0x80, 0x20) + mstore(0xa0, 0x20) + mstore(0xc0, 0x20) + mstore(0xe0, value) + mstore(0x100, power) + mstore(0x120, R_MOD) + if iszero(staticcall(gas(), 5, 0x80, 0xc0, 0x80, 0x20)) { + revertWithMessage(24, "modexp precompile failed") + } + res := mload(0x80) + } + } + } +} diff --git a/tools/data/scheduler_key.json b/tools/data/plonk_scheduler_key.json similarity index 82% rename from tools/data/scheduler_key.json rename to tools/data/plonk_scheduler_key.json index acb7e3fe8..5d1e4668b 100644 --- a/tools/data/scheduler_key.json +++ b/tools/data/plonk_scheduler_key.json @@ -6,31 +6,31 @@ "gate_setup_commitments": [ { "x": [ - 14543631136906534221, - 11532161447842416044, - 11114175029926010938, - 1228896787564295039 + 9369242392284320144, + 6675816858104016523, + 14858001051416907000, + 1225669426425319311 ], "y": [ - 13293602262342424489, - 8897930584356943159, - 13256028170406220369, - 3214939367598363288 + 897720706316680261, + 18195860010054430178, + 16160026306869138511, + 1356550183856877875 ], "infinity": false }, { "x": [ - 11488992528554025682, - 12016824828223971094, - 11942004360057333370, - 316831626296641307 + 13351117607150022275, + 2933944144818388240, + 5793025266725453005, + 2556701126328621680 ], "y": [ - 304673622018339856, - 7139037552557818730, - 12475560967982555143, - 1055588351918295250 + 11734966568724119012, + 10190935222331090906, + 3787185007465109138, + 2738968085076409877 ], "infinity": false }, @@ -51,16 +51,16 @@ }, { "x": [ - 14005362797509427677, - 2662603874351919260, - 14261489165672308143, - 1470528288349794782 + 9218031701224450589, + 1534624245124893249, + 9487357625416639257, + 647718627141905922 ], "y": [ - 11144229651170108862, - 11439490264313454962, - 114993091474760680, - 1037267173208738614 + 17236328806143473432, + 8632015779980017059, + 11243854438144489053, + 550609446344292359 ], "infinity": false }, @@ -160,31 +160,31 @@ "permutation_commitments": [ { "x": [ - 14761045450946573029, - 17157644513453531531, - 2555518804134782053, - 1415819224310783987 + 3381581189461180576, + 10195903826114386111, + 8297057778034101109, + 2324302591285452919 ], "y": [ - 17265629196749977462, - 4128711855633066822, - 8435602817910411328, - 1408116296902303196 + 10421881628401567792, + 2477974325209761168, + 14091332093800636238, + 38389216099900129 ], "infinity": false }, { "x": [ - 3307267823832528482, - 2406249680085831639, - 9091964031261402109, - 2846274000290842933 + 16312762970049520418, + 17708498487240008455, + 2090500726309035458, + 798851154880350532 ], "y": [ - 17374905554931807856, - 6690578002079222163, - 11809376320193686210, - 2676076649992974574 + 18255293396520572182, + 13666524753490475927, + 6924585208062867717, + 2325827847806608329 ], "infinity": false }, @@ -205,16 +205,16 @@ }, { "x": [ - 18349397811516917436, - 4473982696343317918, - 13070312540813307819, - 2109468484629113245 + 10372574323545302620, + 13217158678971910881, + 14902220059266038709, + 2349385863635190438 ], "y": [ - 13254534552549721008, - 17388411854346636521, - 17875890960520499518, - 1062184221180884481 + 16050259647084466717, + 15943269640590918056, + 9762020112148613496, + 1321808533889686645 ], "infinity": false } diff --git a/tools/data/verifier_contract_template.txt b/tools/data/plonk_verifier_contract_template.txt similarity index 99% rename from tools/data/verifier_contract_template.txt rename to tools/data/plonk_verifier_contract_template.txt index 5ef32b2c5..dc3b64d53 100644 --- a/tools/data/verifier_contract_template.txt +++ b/tools/data/plonk_verifier_contract_template.txt @@ -2,7 +2,7 @@ pragma solidity 0.8.24; -import {IVerifier} from "./chain-interfaces/IVerifier.sol"; +import {IVerifier} from "../chain-interfaces/IVerifier.sol"; /* solhint-disable max-line-length */ /// @author Matter Labs @@ -18,7 +18,7 @@ import {IVerifier} from "./chain-interfaces/IVerifier.sol"; /// * 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. /* solhint-enable max-line-length */ -contract Verifier is IVerifier { +contract VerifierPlonk is IVerifier { /*////////////////////////////////////////////////////////////// Verification keys //////////////////////////////////////////////////////////////*/ diff --git a/tools/src/main.rs b/tools/src/main.rs index 746373fe4..f69448d87 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -106,38 +106,55 @@ lazy_static! { struct Opt { /// Input path to scheduler verification key file. #[structopt( - short = "i", - long = "input_path", - default_value = "data/scheduler_key.json" + long = "plonk_input_path", + default_value = "data/plonk_scheduler_key.json" )] - input_path: String, + plonk_input_path: String, + + /// Input path to scheduler verification key file for . + #[structopt( + long = "fflonk_input_path", + default_value = "data/fflonk_scheduler_key.json" + )] + fflonk_input_path: String, + + /// Output path to verifier contract file. + #[structopt(long = "fflonk_output_path", default_value = "data/VerifierFflonk.sol")] + fflonk_output_path: String, /// Output path to verifier contract file. - #[structopt(short = "o", long = "output_path", default_value = "data/Verifier.sol")] - output_path: String, + #[structopt(long = "plonk_output_path", default_value = "data/VerifierPlonk.sol")] + plonk_output_path: String, } fn main() -> Result<(), Box> { let opt = Opt::from_args(); - let reader = BufReader::new(File::open(&opt.input_path)?); + let plonk_reader = BufReader::new(File::open(&opt.plonk_input_path)?); + let fflonk_reader = BufReader::new(File::open(&opt.fflonk_input_path)?); - let vk: HashMap = from_reader(reader)?; + let plonk_vk: HashMap = from_reader(plonk_reader)?; + let fflonk_vk: HashMap = from_reader(fflonk_reader)?; - let verifier_contract_template = fs::read_to_string("data/verifier_contract_template.txt")?; + let plonk_verifier_contract_template = fs::read_to_string("data/plonk_verifier_contract_template.txt")?; + let fflonk_verifier_contract_template = fs::read_to_string("data/fflonk_verifier_contract_template.txt")?; - let verification_key = fs::read_to_string(&opt.input_path) - .expect(&format!("Unable to read from {}", &opt.input_path)); + let plonk_verification_key = fs::read_to_string(&opt.plonk_input_path) + .expect(&format!("Unable to read from {}", &opt.plonk_input_path)); + let fflonk_verification_key = fs::read_to_string(&opt.fflonk_input_path) + .expect(&format!("Unable to read from {}", &opt.fflonk_input_path)); - let verification_key: VerificationKey = - serde_json::from_str(&verification_key).unwrap(); + let plonk_verification_key: VerificationKey = + serde_json::from_str(&plonk_verification_key).unwrap(); + let fflonk_verification_key: VerificationKey = + serde_json::from_str(&fflonk_verification_key).unwrap(); - let vk_hash = hex::encode(calculate_verification_key_hash(verification_key).to_fixed_bytes()); + let plonk_vk_hash = hex::encode(calculate_verification_key_hash(verification_key).to_fixed_bytes()); let verifier_contract_template = - insert_residue_elements_and_commitments(&verifier_contract_template, &vk, &vk_hash)?; + insert_residue_elements_and_commitments(&verifier_contract_template, &vk, &plonk_vk_hash)?; - let mut file = File::create(opt.output_path)?; + let mut file = File::create(opt.plonk_output_path)?; file.write_all(verifier_contract_template.as_bytes())?; Ok(()) diff --git a/yarn.lock b/yarn.lock index 66cd67b1a..6ab96b427 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7931,10 +7931,3 @@ zksync-ethers@^5.9.0: integrity sha512-Y2Mx6ovvxO6UdC2dePLguVzvNToOY8iLWeq5ne+jgGSJxAi/f4He/NF6FNsf6x1aWX0o8dy4Df8RcOQXAkj5qw== dependencies: ethers "~5.7.0" - -zksync-web3@^0.15.4: - version "0.15.5" - resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.15.5.tgz#aabe379464963ab573e15948660a709f409b5316" - integrity sha512-97gB7OKJL4spegl8fGO54g6cvTd/75G6yFWZWEa2J09zhjTrfqabbwE/GwiUJkFQ5BbzoH4JaTlVz1hoYZI+DQ== - dependencies: - ethers "~5.7.0"