Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
18676596 | 426 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
Verifier
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 9999999 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.8.20; // SPDX-License-Identifier: MIT import "./interfaces/IVerifier.sol"; /* solhint-disable max-line-length */ /// @author Matter Labs /// @notice Modified version of the Permutations over Lagrange-bases for Oecumenical Noninteractive arguments of /// Knowledge (PLONK) verifier. /// Modifications have been made to optimize the proof system for zkSync Era circuits. /// @dev It uses a custom memory layout inside the inline assembly block. Each reserved memory cell is declared in the /// constants below. /// @dev For a better understanding of the verifier algorithm please refer to the following papers: /// * Original Plonk Article: https://eprint.iacr.org/2019/953.pdf /// * Original LookUp Article: https://eprint.iacr.org/2020/315.pdf /// * Plonk for zkSync v1.1: https://github.com/matter-labs/solidity_plonk_verifier/raw/recursive/bellman_vk_codegen_recursive/RecursivePlonkUnrolledForEthereum.pdf /// The notation used in the code is the same as in the papers. /* solhint-enable max-line-length */ contract Verifier is IVerifier { /*////////////////////////////////////////////////////////////// Verification keys //////////////////////////////////////////////////////////////*/ // Memory slots from 0x000 to 0x200 are reserved for intermediate computations and call to precompiles. uint256 internal constant VK_GATE_SETUP_0_X_SLOT = 0x200 + 0x000; uint256 internal constant VK_GATE_SETUP_0_Y_SLOT = 0x200 + 0x020; uint256 internal constant VK_GATE_SETUP_1_X_SLOT = 0x200 + 0x040; uint256 internal constant VK_GATE_SETUP_1_Y_SLOT = 0x200 + 0x060; uint256 internal constant VK_GATE_SETUP_2_X_SLOT = 0x200 + 0x080; uint256 internal constant VK_GATE_SETUP_2_Y_SLOT = 0x200 + 0x0a0; uint256 internal constant VK_GATE_SETUP_3_X_SLOT = 0x200 + 0x0c0; uint256 internal constant VK_GATE_SETUP_3_Y_SLOT = 0x200 + 0x0e0; uint256 internal constant VK_GATE_SETUP_4_X_SLOT = 0x200 + 0x100; uint256 internal constant VK_GATE_SETUP_4_Y_SLOT = 0x200 + 0x120; uint256 internal constant VK_GATE_SETUP_5_X_SLOT = 0x200 + 0x140; uint256 internal constant VK_GATE_SETUP_5_Y_SLOT = 0x200 + 0x160; uint256 internal constant VK_GATE_SETUP_6_X_SLOT = 0x200 + 0x180; uint256 internal constant VK_GATE_SETUP_6_Y_SLOT = 0x200 + 0x1a0; uint256 internal constant VK_GATE_SETUP_7_X_SLOT = 0x200 + 0x1c0; uint256 internal constant VK_GATE_SETUP_7_Y_SLOT = 0x200 + 0x1e0; uint256 internal constant VK_GATE_SELECTORS_0_X_SLOT = 0x200 + 0x200; uint256 internal constant VK_GATE_SELECTORS_0_Y_SLOT = 0x200 + 0x220; uint256 internal constant VK_GATE_SELECTORS_1_X_SLOT = 0x200 + 0x240; uint256 internal constant VK_GATE_SELECTORS_1_Y_SLOT = 0x200 + 0x260; uint256 internal constant VK_PERMUTATION_0_X_SLOT = 0x200 + 0x280; uint256 internal constant VK_PERMUTATION_0_Y_SLOT = 0x200 + 0x2a0; uint256 internal constant VK_PERMUTATION_1_X_SLOT = 0x200 + 0x2c0; uint256 internal constant VK_PERMUTATION_1_Y_SLOT = 0x200 + 0x2e0; uint256 internal constant VK_PERMUTATION_2_X_SLOT = 0x200 + 0x300; uint256 internal constant VK_PERMUTATION_2_Y_SLOT = 0x200 + 0x320; uint256 internal constant VK_PERMUTATION_3_X_SLOT = 0x200 + 0x340; uint256 internal constant VK_PERMUTATION_3_Y_SLOT = 0x200 + 0x360; uint256 internal constant VK_LOOKUP_SELECTOR_X_SLOT = 0x200 + 0x380; uint256 internal constant VK_LOOKUP_SELECTOR_Y_SLOT = 0x200 + 0x3a0; uint256 internal constant VK_LOOKUP_TABLE_0_X_SLOT = 0x200 + 0x3c0; uint256 internal constant VK_LOOKUP_TABLE_0_Y_SLOT = 0x200 + 0x3e0; uint256 internal constant VK_LOOKUP_TABLE_1_X_SLOT = 0x200 + 0x400; uint256 internal constant VK_LOOKUP_TABLE_1_Y_SLOT = 0x200 + 0x420; uint256 internal constant VK_LOOKUP_TABLE_2_X_SLOT = 0x200 + 0x440; uint256 internal constant VK_LOOKUP_TABLE_2_Y_SLOT = 0x200 + 0x460; uint256 internal constant VK_LOOKUP_TABLE_3_X_SLOT = 0x200 + 0x480; uint256 internal constant VK_LOOKUP_TABLE_3_Y_SLOT = 0x200 + 0x4a0; uint256 internal constant VK_LOOKUP_TABLE_TYPE_X_SLOT = 0x200 + 0x4c0; uint256 internal constant VK_LOOKUP_TABLE_TYPE_Y_SLOT = 0x200 + 0x4e0; uint256 internal constant VK_RECURSIVE_FLAG_SLOT = 0x200 + 0x500; /*////////////////////////////////////////////////////////////// Proof //////////////////////////////////////////////////////////////*/ uint256 internal constant PROOF_PUBLIC_INPUT = 0x200 + 0x520 + 0x000; uint256 internal constant PROOF_STATE_POLYS_0_X_SLOT = 0x200 + 0x520 + 0x020; uint256 internal constant PROOF_STATE_POLYS_0_Y_SLOT = 0x200 + 0x520 + 0x040; uint256 internal constant PROOF_STATE_POLYS_1_X_SLOT = 0x200 + 0x520 + 0x060; uint256 internal constant PROOF_STATE_POLYS_1_Y_SLOT = 0x200 + 0x520 + 0x080; uint256 internal constant PROOF_STATE_POLYS_2_X_SLOT = 0x200 + 0x520 + 0x0a0; uint256 internal constant PROOF_STATE_POLYS_2_Y_SLOT = 0x200 + 0x520 + 0x0c0; uint256 internal constant PROOF_STATE_POLYS_3_X_SLOT = 0x200 + 0x520 + 0x0e0; uint256 internal constant PROOF_STATE_POLYS_3_Y_SLOT = 0x200 + 0x520 + 0x100; uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT = 0x200 + 0x520 + 0x120; uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x520 + 0x140; uint256 internal constant PROOF_LOOKUP_S_POLY_X_SLOT = 0x200 + 0x520 + 0x160; uint256 internal constant PROOF_LOOKUP_S_POLY_Y_SLOT = 0x200 + 0x520 + 0x180; uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT = 0x200 + 0x520 + 0x1a0; uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT = 0x200 + 0x520 + 0x1c0; uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT = 0x200 + 0x520 + 0x1e0; uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT = 0x200 + 0x520 + 0x200; uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT = 0x200 + 0x520 + 0x220; uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT = 0x200 + 0x520 + 0x240; uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT = 0x200 + 0x520 + 0x260; uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT = 0x200 + 0x520 + 0x280; uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT = 0x200 + 0x520 + 0x2a0; uint256 internal constant PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT = 0x200 + 0x520 + 0x2c0; uint256 internal constant PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x2e0; uint256 internal constant PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x300; uint256 internal constant PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x320; uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x340; uint256 internal constant PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x360; uint256 internal constant PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x380; uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3a0; uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3c0; uint256 internal constant PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x3e0; uint256 internal constant PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x400; uint256 internal constant PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x420; uint256 internal constant PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x440; uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x460; uint256 internal constant PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x480; uint256 internal constant PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4a0; uint256 internal constant PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4c0; uint256 internal constant PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x4e0; uint256 internal constant PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x500; uint256 internal constant PROOF_OPENING_PROOF_AT_Z_X_SLOT = 0x200 + 0x520 + 0x520; uint256 internal constant PROOF_OPENING_PROOF_AT_Z_Y_SLOT = 0x200 + 0x520 + 0x540; uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT = 0x200 + 0x520 + 0x560; uint256 internal constant PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x520 + 0x580; uint256 internal constant PROOF_RECURSIVE_PART_P1_X_SLOT = 0x200 + 0x520 + 0x5a0; uint256 internal constant PROOF_RECURSIVE_PART_P1_Y_SLOT = 0x200 + 0x520 + 0x5c0; uint256 internal constant PROOF_RECURSIVE_PART_P2_X_SLOT = 0x200 + 0x520 + 0x5e0; uint256 internal constant PROOF_RECURSIVE_PART_P2_Y_SLOT = 0x200 + 0x520 + 0x600; /*////////////////////////////////////////////////////////////// Transcript slot //////////////////////////////////////////////////////////////*/ uint256 internal constant TRANSCRIPT_BEGIN_SLOT = 0x200 + 0x520 + 0x620 + 0x00; uint256 internal constant TRANSCRIPT_DST_BYTE_SLOT = 0x200 + 0x520 + 0x620 + 0x03; uint256 internal constant TRANSCRIPT_STATE_0_SLOT = 0x200 + 0x520 + 0x620 + 0x04; uint256 internal constant TRANSCRIPT_STATE_1_SLOT = 0x200 + 0x520 + 0x620 + 0x24; uint256 internal constant TRANSCRIPT_CHALLENGE_SLOT = 0x200 + 0x520 + 0x620 + 0x44; /*////////////////////////////////////////////////////////////// Partial verifier state //////////////////////////////////////////////////////////////*/ uint256 internal constant STATE_ALPHA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x000; uint256 internal constant STATE_BETA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x020; uint256 internal constant STATE_GAMMA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x040; uint256 internal constant STATE_POWER_OF_ALPHA_2_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x060; uint256 internal constant STATE_POWER_OF_ALPHA_3_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x080; uint256 internal constant STATE_POWER_OF_ALPHA_4_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0a0; uint256 internal constant STATE_POWER_OF_ALPHA_5_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0c0; uint256 internal constant STATE_POWER_OF_ALPHA_6_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x0e0; uint256 internal constant STATE_POWER_OF_ALPHA_7_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x100; uint256 internal constant STATE_POWER_OF_ALPHA_8_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x120; uint256 internal constant STATE_ETA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x140; uint256 internal constant STATE_BETA_LOOKUP_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x160; uint256 internal constant STATE_GAMMA_LOOKUP_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x180; uint256 internal constant STATE_BETA_PLUS_ONE_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1a0; uint256 internal constant STATE_BETA_GAMMA_PLUS_GAMMA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1c0; uint256 internal constant STATE_V_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x1e0; uint256 internal constant STATE_U_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x200; uint256 internal constant STATE_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x220; uint256 internal constant STATE_Z_MINUS_LAST_OMEGA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x240; uint256 internal constant STATE_L_0_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x260; uint256 internal constant STATE_L_N_MINUS_ONE_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x280; uint256 internal constant STATE_Z_IN_DOMAIN_SIZE = 0x200 + 0x520 + 0x620 + 0x80 + 0x2a0; /*////////////////////////////////////////////////////////////// Queries //////////////////////////////////////////////////////////////*/ uint256 internal constant QUERIES_BUFFER_POINT_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x00; uint256 internal constant QUERIES_AT_Z_0_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x40; uint256 internal constant QUERIES_AT_Z_0_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x60; uint256 internal constant QUERIES_AT_Z_1_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x80; uint256 internal constant QUERIES_AT_Z_1_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xa0; uint256 internal constant QUERIES_T_POLY_AGGREGATED_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xc0; uint256 internal constant QUERIES_T_POLY_AGGREGATED_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0xe0; /*////////////////////////////////////////////////////////////// Aggregated commitment //////////////////////////////////////////////////////////////*/ uint256 internal constant AGGREGATED_AT_Z_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x00; uint256 internal constant AGGREGATED_AT_Z_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x20; uint256 internal constant AGGREGATED_AT_Z_OMEGA_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x40; uint256 internal constant AGGREGATED_AT_Z_OMEGA_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x60; uint256 internal constant AGGREGATED_OPENING_AT_Z_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x80; uint256 internal constant AGGREGATED_OPENING_AT_Z_OMEGA_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xa0; /*////////////////////////////////////////////////////////////// Pairing data //////////////////////////////////////////////////////////////*/ uint256 internal constant PAIRING_BUFFER_POINT_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x00; uint256 internal constant PAIRING_BUFFER_POINT_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x20; uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x40; uint256 internal constant PAIRING_PAIR_WITH_GENERATOR_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0xc0 + 0x60; uint256 internal constant PAIRING_PAIR_WITH_X_X_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x80; uint256 internal constant PAIRING_PAIR_WITH_X_Y_SLOT = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xa0; /*////////////////////////////////////////////////////////////// Slots for scalar multiplication optimizations //////////////////////////////////////////////////////////////*/ uint256 internal constant COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xc0; uint256 internal constant LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0xe0; uint256 internal constant LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF = 0x200 + 0x520 + 0x620 + 0x80 + 0x2c0 + 0x100 + 0x100 + 0x100; /*////////////////////////////////////////////////////////////// Constants //////////////////////////////////////////////////////////////*/ uint256 internal constant OMEGA = 0x1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb; uint256 internal constant DOMAIN_SIZE = 0x1000000; // 2^24 uint256 internal constant Q_MOD = 21888242871839275222246405745257275088696311157297823662689037894645226208583; uint256 internal constant R_MOD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; /// @dev flip of 0xe000000000000000000000000000000000000000000000000000000000000000; uint256 internal constant FR_MASK = 0x1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff; // non residues uint256 internal constant NON_RESIDUES_0 = 0x05; uint256 internal constant NON_RESIDUES_1 = 0x07; uint256 internal constant NON_RESIDUES_2 = 0x0a; // trusted setup g2 elements uint256 internal constant G2_ELEMENTS_0_X1 = 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2; uint256 internal constant G2_ELEMENTS_0_X2 = 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed; uint256 internal constant G2_ELEMENTS_0_Y1 = 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b; uint256 internal constant G2_ELEMENTS_0_Y2 = 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa; uint256 internal constant G2_ELEMENTS_1_X1 = 0x260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1; uint256 internal constant G2_ELEMENTS_1_X2 = 0x0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0; uint256 internal constant G2_ELEMENTS_1_Y1 = 0x04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4; uint256 internal constant G2_ELEMENTS_1_Y2 = 0x22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55; /// @notice Calculates a keccak256 hash of the runtime loaded verification keys. /// @return vkHash The keccak256 hash of the loaded verification keys. function verificationKeyHash() external pure returns (bytes32 vkHash) { _loadVerificationKey(); assembly { let start := VK_GATE_SETUP_0_X_SLOT let end := VK_RECURSIVE_FLAG_SLOT let length := add(sub(end, start), 0x20) vkHash := keccak256(start, length) } } /// @notice Load verification keys to memory in runtime. /// @dev The constants are loaded into memory in a specific layout declared in the constants starting from /// `VK_` prefix. /// NOTE: Function may corrupt the memory state if some memory was used before this function was called. /// The VK consists of commitments to setup polynomials: /// [q_a], [q_b], [q_c], [q_d], - main gate setup commitments /// [q_{d_next}], [q_ab], [q_ac], [q_const] / /// [main_gate_selector], [custom_gate_selector] - gate selectors commitments /// [sigma_0], [sigma_1], [sigma_2], [sigma_3] - permutation polynomials commitments /// [lookup_selector] - lookup selector commitment /// [col_0], [col_1], [col_2], [col_3] - lookup columns commitments /// [table_type] - lookup table type commitment function _loadVerificationKey() internal pure virtual { assembly { // gate setup commitments mstore(VK_GATE_SETUP_0_X_SLOT, 0x236c54d6ae3745765605abfdd3d1533aaf27c583bbac66c43f5555e2b087db42) mstore(VK_GATE_SETUP_0_Y_SLOT, 0x0b2b13c749c19a0be311c0eec76abddbc0848e2454cc7323605a714256ef101a) mstore(VK_GATE_SETUP_1_X_SLOT, 0x04659caf7b05471ba5ba85b1ab62267aa6c456836e625f169f7119d55b9462d2) mstore(VK_GATE_SETUP_1_Y_SLOT, 0x0ea63403692148d2ad22189a1e5420076312f4d46e62036a043a6b0b84d5b410) mstore(VK_GATE_SETUP_2_X_SLOT, 0x0e6696d09d65fce1e42805be03fca1f14aea247281f688981f925e77d4ce2291) mstore(VK_GATE_SETUP_2_Y_SLOT, 0x0228f6cf8fe20c1e07e5b78bf8c41d50e55975a126d22a198d1e56acd4bbb3dd) mstore(VK_GATE_SETUP_3_X_SLOT, 0x14685dafe340b1dec5eafcd5e7faddaf24f3781ddc53309cc25d0b42c00541dd) mstore(VK_GATE_SETUP_3_Y_SLOT, 0x0e651cff9447cb360198899b80fa23e89ec13bc94ff161729aa841d2b55ea5be) mstore(VK_GATE_SETUP_4_X_SLOT, 0x16e9ef76cb68f2750eb0ee72382dd9911a982308d0ab10ef94dada13c382ae73) mstore(VK_GATE_SETUP_4_Y_SLOT, 0x22e404bc91350f3bc7daad1d1025113742436983c85eac5ab7b42221a181b81e) mstore(VK_GATE_SETUP_5_X_SLOT, 0x0d9b29613037a5025655c82b143d2b7449c98f3aea358307c8529249cc54f3b9) mstore(VK_GATE_SETUP_5_Y_SLOT, 0x15b3c4c946ad1babfc4c03ff7c2423fd354af3a9305c499b7fb3aaebe2fee746) mstore(VK_GATE_SETUP_6_X_SLOT, 0x08aa077804a60caf18621f96c63d7d1b532f44b13c273956f81b4c41f1f8f076) mstore(VK_GATE_SETUP_6_Y_SLOT, 0x01fcb7c6e22aab56eb8b61bae6f457411bd41bcc5f1e8311421a98ccee23b72e) mstore(VK_GATE_SETUP_7_X_SLOT, 0x283344a1ab3e55ecfd904d0b8e9f4faea338df5a4ead2fa9a42f0e103da40abc) mstore(VK_GATE_SETUP_7_Y_SLOT, 0x223b37b83b9687512d322993edd70e508dd80adb10bcf7321a3cc8a44c269521) // gate selectors commitments mstore(VK_GATE_SELECTORS_0_X_SLOT, 0x1f67f0ba5f7e837bc680acb4e612ebd938ad35211aa6e05b96cad19e66b82d2d) mstore(VK_GATE_SELECTORS_0_Y_SLOT, 0x2820641a84d2e8298ac2ac42bd4b912c0c37f768ecc83d3a29e7c720763d15a1) mstore(VK_GATE_SELECTORS_1_X_SLOT, 0x0353257957562270292a17860ca8e8827703f828f440ee004848b1e23fdf9de2) mstore(VK_GATE_SELECTORS_1_Y_SLOT, 0x305f4137fee253dff8b2bfe579038e8f25d5bd217865072af5d89fc8800ada24) // permutation commitments mstore(VK_PERMUTATION_0_X_SLOT, 0x13a600154b369ff3237706d00948e465ee1c32c7a6d3e18bccd9c4a15910f2e5) mstore(VK_PERMUTATION_0_Y_SLOT, 0x138aa24fbf4cdddc75114811b3d59040394c218ecef3eb46ef9bd646f7e53776) mstore(VK_PERMUTATION_1_X_SLOT, 0x277fff1f80c409357e2d251d79f6e3fd2164b755ce69cfd72de5c690289df662) mstore(VK_PERMUTATION_1_Y_SLOT, 0x25235588e28c70eea3e35531c80deac25cd9b53ea3f98993f120108bc7abf670) mstore(VK_PERMUTATION_2_X_SLOT, 0x0990e07a9b001048b947d0e5bd6157214c7359b771f01bf52bd771ba563a900e) mstore(VK_PERMUTATION_2_Y_SLOT, 0x05e5fb090dd40914c8606d875e301167ae3047d684a02b44d9d36f1eaf43d0b4) mstore(VK_PERMUTATION_3_X_SLOT, 0x1d4656690b33299db5631401a282afab3e16c78ee2c9ad9efea628171dcbc6bc) mstore(VK_PERMUTATION_3_Y_SLOT, 0x0ebda2ebe582f601f813ec1e3970d13ef1500c742a85cce9b7f190f333de03b0) // lookup tables commitments mstore(VK_LOOKUP_TABLE_0_X_SLOT, 0x2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b) mstore(VK_LOOKUP_TABLE_0_Y_SLOT, 0x04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d472981468) mstore(VK_LOOKUP_TABLE_1_X_SLOT, 0x1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac) mstore(VK_LOOKUP_TABLE_1_Y_SLOT, 0x1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d) mstore(VK_LOOKUP_TABLE_2_X_SLOT, 0x0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89) mstore(VK_LOOKUP_TABLE_2_Y_SLOT, 0x1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283) mstore(VK_LOOKUP_TABLE_3_X_SLOT, 0x02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf) mstore(VK_LOOKUP_TABLE_3_Y_SLOT, 0x09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a6) // lookup selector commitment mstore(VK_LOOKUP_SELECTOR_X_SLOT, 0x2f4d347c7fb61daaadfff881e24f4b5dcfdc0d70a95bcb148168b90ef93e0007) mstore(VK_LOOKUP_SELECTOR_Y_SLOT, 0x2322632465ba8e28cd0a4befd813ea85a972f4f6fa8e8603cf5d062dbcb14065) // table type commitment mstore(VK_LOOKUP_TABLE_TYPE_X_SLOT, 0x1e3c9fc98c118e4bc34f1f93d214a5d86898e980c40d8e2c180c6ada377a7467) mstore(VK_LOOKUP_TABLE_TYPE_Y_SLOT, 0x2260a13535c35a15c173f5e5797d4b675b55d164a9995bfb7624971324bd84a8) // flag for using recursive part mstore(VK_RECURSIVE_FLAG_SLOT, 0) } } /// @dev Verifies a zk-SNARK proof. /// @return A boolean value indicating whether the zk-SNARK proof is valid. /// Note: The function may revert execution instead of returning false in some cases. function verify( uint256[] calldata, // _publicInputs uint256[] calldata, // _proof uint256[] calldata // _recursiveAggregationInput ) external view returns (bool) { // No memory was accessed yet, so keys can be loaded into the right place and not corrupt any other memory. _loadVerificationKey(); // Begining of the big inline assembly block that makes all the verification work. // Note: We use the custom memory layout, so the return value should be returned from the assembly, not // Solidity code. assembly { /*////////////////////////////////////////////////////////////// Utils //////////////////////////////////////////////////////////////*/ /// @dev Reverts execution with a provided revert reason. /// @param len The byte length of the error message string, which is expected to be no more than 32. /// @param reason The 1-word revert reason string, encoded in ASCII. function revertWithMessage(len, reason) { // "Error(string)" signature: bytes32(bytes4(keccak256("Error(string)"))) mstore(0x00, 0x08c379a000000000000000000000000000000000000000000000000000000000) // Data offset mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020) // Length of revert string mstore(0x24, len) // Revert reason mstore(0x44, reason) // Revert revert(0x00, 0x64) } /// @dev Performs modular exponentiation using the formula (value ^ power) mod R_MOD. function modexp(value, power) -> res { mstore(0x00, 0x20) mstore(0x20, 0x20) mstore(0x40, 0x20) mstore(0x60, value) mstore(0x80, power) mstore(0xa0, R_MOD) if iszero(staticcall(gas(), 5, 0, 0xc0, 0x00, 0x20)) { revertWithMessage(24, "modexp precompile failed") } res := mload(0x00) } /// @dev Performs a point multiplication operation and stores the result in a given memory destination. function pointMulIntoDest(point, s, dest) { mstore(0x00, mload(point)) mstore(0x20, mload(add(point, 0x20))) mstore(0x40, s) if iszero(staticcall(gas(), 7, 0, 0x60, dest, 0x40)) { revertWithMessage(30, "pointMulIntoDest: ecMul failed") } } /// @dev Performs a point addition operation and stores the result in a given memory destination. function pointAddIntoDest(p1, p2, dest) { mstore(0x00, mload(p1)) mstore(0x20, mload(add(p1, 0x20))) mstore(0x40, mload(p2)) mstore(0x60, mload(add(p2, 0x20))) if iszero(staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) { revertWithMessage(30, "pointAddIntoDest: ecAdd failed") } } /// @dev Performs a point subtraction operation and updates the first point with the result. function pointSubAssign(p1, p2) { mstore(0x00, mload(p1)) mstore(0x20, mload(add(p1, 0x20))) mstore(0x40, mload(p2)) mstore(0x60, sub(Q_MOD, mload(add(p2, 0x20)))) if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) { revertWithMessage(28, "pointSubAssign: ecAdd failed") } } /// @dev Performs a point addition operation and updates the first point with the result. function pointAddAssign(p1, p2) { mstore(0x00, mload(p1)) mstore(0x20, mload(add(p1, 0x20))) mstore(0x40, mload(p2)) mstore(0x60, mload(add(p2, 0x20))) if iszero(staticcall(gas(), 6, 0x00, 0x80, p1, 0x40)) { revertWithMessage(28, "pointAddAssign: ecAdd failed") } } /// @dev Performs a point multiplication operation and then adds the result to the destination point. function pointMulAndAddIntoDest(point, s, dest) { mstore(0x00, mload(point)) mstore(0x20, mload(add(point, 0x20))) mstore(0x40, s) let success := staticcall(gas(), 7, 0, 0x60, 0, 0x40) mstore(0x40, mload(dest)) mstore(0x60, mload(add(dest, 0x20))) success := and(success, staticcall(gas(), 6, 0x00, 0x80, dest, 0x40)) if iszero(success) { revertWithMessage(22, "pointMulAndAddIntoDest") } } /// @dev Negates an elliptic curve point by changing the sign of the y-coordinate. function pointNegate(point) { let pY := mload(add(point, 0x20)) switch pY case 0 { if mload(point) { revertWithMessage(26, "pointNegate: invalid point") } } default { mstore(add(point, 0x20), sub(Q_MOD, pY)) } } /*////////////////////////////////////////////////////////////// Transcript helpers //////////////////////////////////////////////////////////////*/ /// @dev Updates the transcript state with a new challenge value. function updateTranscript(value) { mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x00) mstore(TRANSCRIPT_CHALLENGE_SLOT, value) let newState0 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64) mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x01) let newState1 := keccak256(TRANSCRIPT_BEGIN_SLOT, 0x64) mstore(TRANSCRIPT_STATE_1_SLOT, newState1) mstore(TRANSCRIPT_STATE_0_SLOT, newState0) } /// @dev Retrieves a transcript challenge. function getTranscriptChallenge(numberOfChallenge) -> challenge { mstore8(TRANSCRIPT_DST_BYTE_SLOT, 0x02) mstore(TRANSCRIPT_CHALLENGE_SLOT, shl(224, numberOfChallenge)) challenge := and(keccak256(TRANSCRIPT_BEGIN_SLOT, 0x48), FR_MASK) } /*////////////////////////////////////////////////////////////// 1. Load Proof //////////////////////////////////////////////////////////////*/ /// @dev This function loads a zk-SNARK proof, ensures it's properly formatted, and stores it in memory. /// It ensures the number of inputs and the elliptic curve point's validity. /// Note: It does NOT reject inputs that exceed these module sizes, but rather wraps them within the /// module bounds. /// The proof consists of: /// 1. Public input: (1 field element from F_r) /// /// 2. Polynomial commitments (elliptic curve points over F_q): /// [a], [b], [c], [d] - state polynomials commitments /// [z_perm] - copy-permutation grand product commitment /// [s] - polynomial for lookup argument commitment /// [z_lookup] - lookup grand product commitment /// [t_0], [t_1], [t_2], [t_3] - quotient polynomial parts commitments /// [W], [W'] - proof openings commitments /// /// 3. Polynomial evaluations at z and z*omega (field elements from F_r): /// t(z) - quotient polynomial opening /// a(z), b(z), c(z), d(z), d(z*omega) - state polynomials openings /// main_gate_selector(z) - main gate selector opening /// sigma_0(z), sigma_1(z), sigma_2(z) - permutation polynomials openings /// z_perm(z*omega) - copy-permutation grand product opening /// z_lookup(z*omega) - lookup grand product opening /// lookup_selector(z) - lookup selector opening /// s(x*omega), t(z*omega), table_type(z) - lookup argument polynomial openings /// r(z) - linearisation polynomial opening /// /// 4. Recursive proof (0 or 2 elliptic curve points over F_q) function loadProof() { // 1. Load public input let offset := calldataload(0x04) let publicInputLengthInWords := calldataload(add(offset, 0x04)) let isValid := eq(publicInputLengthInWords, 1) // We expect only one public input mstore(PROOF_PUBLIC_INPUT, and(calldataload(add(offset, 0x24)), FR_MASK)) // 2. Load the proof (except for the recursive part) offset := calldataload(0x24) let proofLengthInWords := calldataload(add(offset, 0x04)) isValid := and(eq(proofLengthInWords, 44), isValid) // PROOF_STATE_POLYS_0 { let x := mod(calldataload(add(offset, 0x024)), Q_MOD) let y := mod(calldataload(add(offset, 0x044)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_STATE_POLYS_0_X_SLOT, x) mstore(PROOF_STATE_POLYS_0_Y_SLOT, y) } // PROOF_STATE_POLYS_1 { let x := mod(calldataload(add(offset, 0x064)), Q_MOD) let y := mod(calldataload(add(offset, 0x084)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_STATE_POLYS_1_X_SLOT, x) mstore(PROOF_STATE_POLYS_1_Y_SLOT, y) } // PROOF_STATE_POLYS_2 { let x := mod(calldataload(add(offset, 0x0a4)), Q_MOD) let y := mod(calldataload(add(offset, 0x0c4)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_STATE_POLYS_2_X_SLOT, x) mstore(PROOF_STATE_POLYS_2_Y_SLOT, y) } // PROOF_STATE_POLYS_3 { let x := mod(calldataload(add(offset, 0x0e4)), Q_MOD) let y := mod(calldataload(add(offset, 0x104)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_STATE_POLYS_3_X_SLOT, x) mstore(PROOF_STATE_POLYS_3_Y_SLOT, y) } // PROOF_COPY_PERMUTATION_GRAND_PRODUCT { let x := mod(calldataload(add(offset, 0x124)), Q_MOD) let y := mod(calldataload(add(offset, 0x144)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, x) mstore(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT, y) } // PROOF_LOOKUP_S_POLY { let x := mod(calldataload(add(offset, 0x164)), Q_MOD) let y := mod(calldataload(add(offset, 0x184)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_LOOKUP_S_POLY_X_SLOT, x) mstore(PROOF_LOOKUP_S_POLY_Y_SLOT, y) } // PROOF_LOOKUP_GRAND_PRODUCT { let x := mod(calldataload(add(offset, 0x1a4)), Q_MOD) let y := mod(calldataload(add(offset, 0x1c4)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, x) mstore(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT, y) } // PROOF_QUOTIENT_POLY_PARTS_0 { let x := mod(calldataload(add(offset, 0x1e4)), Q_MOD) let y := mod(calldataload(add(offset, 0x204)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT, x) mstore(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT, y) } // PROOF_QUOTIENT_POLY_PARTS_1 { let x := mod(calldataload(add(offset, 0x224)), Q_MOD) let y := mod(calldataload(add(offset, 0x244)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, x) mstore(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT, y) } // PROOF_QUOTIENT_POLY_PARTS_2 { let x := mod(calldataload(add(offset, 0x264)), Q_MOD) let y := mod(calldataload(add(offset, 0x284)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, x) mstore(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT, y) } // PROOF_QUOTIENT_POLY_PARTS_3 { let x := mod(calldataload(add(offset, 0x2a4)), Q_MOD) let y := mod(calldataload(add(offset, 0x2c4)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, x) mstore(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT, y) } mstore(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x2e4)), R_MOD)) mstore(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x304)), R_MOD)) mstore(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x324)), R_MOD)) mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x344)), R_MOD)) mstore(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x364)), R_MOD)) mstore(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x384)), R_MOD)) mstore(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3a4)), R_MOD)) mstore(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3c4)), R_MOD)) mstore(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x3e4)), R_MOD)) mstore( PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x404)), R_MOD) ) mstore(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x424)), R_MOD)) mstore(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x444)), R_MOD)) mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x464)), R_MOD)) mstore(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, mod(calldataload(add(offset, 0x484)), R_MOD)) mstore(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4a4)), R_MOD)) mstore(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4c4)), R_MOD)) mstore(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x4e4)), R_MOD)) mstore(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT, mod(calldataload(add(offset, 0x504)), R_MOD)) // PROOF_OPENING_PROOF_AT_Z { let x := mod(calldataload(add(offset, 0x524)), Q_MOD) let y := mod(calldataload(add(offset, 0x544)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_OPENING_PROOF_AT_Z_X_SLOT, x) mstore(PROOF_OPENING_PROOF_AT_Z_Y_SLOT, y) } // PROOF_OPENING_PROOF_AT_Z_OMEGA { let x := mod(calldataload(add(offset, 0x564)), Q_MOD) let y := mod(calldataload(add(offset, 0x584)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, x) mstore(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT, y) } // 3. Load the recursive part of the proof offset := calldataload(0x44) let recursiveProofLengthInWords := calldataload(add(offset, 0x04)) switch mload(VK_RECURSIVE_FLAG_SLOT) case 0 { // recursive part should be empty isValid := and(iszero(recursiveProofLengthInWords), isValid) } default { // recursive part should be consist of 2 points isValid := and(eq(recursiveProofLengthInWords, 4), isValid) // PROOF_RECURSIVE_PART_P1 { let x := mod(calldataload(add(offset, 0x024)), Q_MOD) let y := mod(calldataload(add(offset, 0x044)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_RECURSIVE_PART_P1_X_SLOT, x) mstore(PROOF_RECURSIVE_PART_P1_Y_SLOT, y) } // PROOF_RECURSIVE_PART_P2 { let x := mod(calldataload(add(offset, 0x064)), Q_MOD) let y := mod(calldataload(add(offset, 0x084)), Q_MOD) let xx := mulmod(x, x, Q_MOD) isValid := and(eq(mulmod(y, y, Q_MOD), addmod(mulmod(x, xx, Q_MOD), 3, Q_MOD)), isValid) mstore(PROOF_RECURSIVE_PART_P2_X_SLOT, x) mstore(PROOF_RECURSIVE_PART_P2_Y_SLOT, y) } } // Revert if a proof is not valid if iszero(isValid) { revertWithMessage(27, "loadProof: Proof is invalid") } } /*////////////////////////////////////////////////////////////// 2. Transcript initialization //////////////////////////////////////////////////////////////*/ /// @notice Recomputes all challenges /// @dev The process is the following: /// Commit: PI, [a], [b], [c], [d] /// Get: eta /// Commit: [s] /// Get: beta, gamma /// Commit: [z_perm] /// Get: beta', gamma' /// Commit: [z_lookup] /// Get: alpha /// Commit: [t_0], [t_1], [t_2], [t_3] /// Get: z /// Commit: t(z), a(z), b(z), c(z), d(z), d(z*omega), /// main_gate_selector(z), /// sigma_0(z), sigma_1(z), sigma_2(z), /// z_perm(z*omega), /// t(z), lookup_selector(z), table_type(z), /// s(x*omega), z_lookup(z*omega), t(z*omega), /// r(z) /// Get: v /// Commit: [W], [W'] /// Get: u function initializeTranscript() { // Round 1 updateTranscript(mload(PROOF_PUBLIC_INPUT)) updateTranscript(mload(PROOF_STATE_POLYS_0_X_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_0_Y_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_1_X_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_1_Y_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_2_X_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_2_Y_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_3_X_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_3_Y_SLOT)) mstore(STATE_ETA_SLOT, getTranscriptChallenge(0)) // Round 1.5 updateTranscript(mload(PROOF_LOOKUP_S_POLY_X_SLOT)) updateTranscript(mload(PROOF_LOOKUP_S_POLY_Y_SLOT)) mstore(STATE_BETA_SLOT, getTranscriptChallenge(1)) mstore(STATE_GAMMA_SLOT, getTranscriptChallenge(2)) // Round 2 updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT)) updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_Y_SLOT)) mstore(STATE_BETA_LOOKUP_SLOT, getTranscriptChallenge(3)) mstore(STATE_GAMMA_LOOKUP_SLOT, getTranscriptChallenge(4)) // Round 2.5 updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT)) updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_Y_SLOT)) mstore(STATE_ALPHA_SLOT, getTranscriptChallenge(5)) // Round 3 updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT)) updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT)) updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT)) updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_1_Y_SLOT)) updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT)) updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_2_Y_SLOT)) updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT)) updateTranscript(mload(PROOF_QUOTIENT_POLY_PARTS_3_Y_SLOT)) { let z := getTranscriptChallenge(6) mstore(STATE_Z_SLOT, z) mstore(STATE_Z_IN_DOMAIN_SIZE, modexp(z, DOMAIN_SIZE)) } // Round 4 updateTranscript(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT)) updateTranscript(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)) updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT)) updateTranscript(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT)) updateTranscript(mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT)) updateTranscript(mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT)) updateTranscript(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT)) mstore(STATE_V_SLOT, getTranscriptChallenge(7)) // Round 5 updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT)) updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT)) updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT)) updateTranscript(mload(PROOF_OPENING_PROOF_AT_Z_OMEGA_Y_SLOT)) mstore(STATE_U_SLOT, getTranscriptChallenge(8)) } /*////////////////////////////////////////////////////////////// 3. Verifying quotient evaluation //////////////////////////////////////////////////////////////*/ /// @notice Compute linearisation polynomial's constant term: r_0 /// @dev To save a verifier scalar multiplication, we split linearisation polynomial /// into its constant and non-constant terms. The constant term is computed with the formula: /// /// r_0 = alpha^0 * L_0(z) * PI * q_{main selector}(z) + r(z) -- main gate contribution /// /// - alpha^4 * z_perm(z*omega)(sigma_0(z) * beta + gamma + a(z)) \ /// (sigma_1(z) * beta + gamma + b(z)) | /// (sigma_2(z) * beta + gamma + c(z)) | - permutation contribution /// (sigma_3(z) + gamma) | /// - alpha^5 * L_0(z) / /// /// + alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) \ /// * (z - omega^{n-1}) * z_lookup(z*omega) | - lookup contribution /// - alpha^7 * L_0(z) | /// - alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1} / /// /// In the end we should check that t(z)*Z_H(z) = r(z) + r_0! function verifyQuotientEvaluation() { // Compute power of alpha { let alpha := mload(STATE_ALPHA_SLOT) let currentAlpha := mulmod(alpha, alpha, R_MOD) mstore(STATE_POWER_OF_ALPHA_2_SLOT, currentAlpha) currentAlpha := mulmod(currentAlpha, alpha, R_MOD) mstore(STATE_POWER_OF_ALPHA_3_SLOT, currentAlpha) currentAlpha := mulmod(currentAlpha, alpha, R_MOD) mstore(STATE_POWER_OF_ALPHA_4_SLOT, currentAlpha) currentAlpha := mulmod(currentAlpha, alpha, R_MOD) mstore(STATE_POWER_OF_ALPHA_5_SLOT, currentAlpha) currentAlpha := mulmod(currentAlpha, alpha, R_MOD) mstore(STATE_POWER_OF_ALPHA_6_SLOT, currentAlpha) currentAlpha := mulmod(currentAlpha, alpha, R_MOD) mstore(STATE_POWER_OF_ALPHA_7_SLOT, currentAlpha) currentAlpha := mulmod(currentAlpha, alpha, R_MOD) mstore(STATE_POWER_OF_ALPHA_8_SLOT, currentAlpha) } // z let stateZ := mload(STATE_Z_SLOT) // L_0(z) mstore(STATE_L_0_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(0, stateZ)) // L_{n-1}(z) mstore(STATE_L_N_MINUS_ONE_AT_Z_SLOT, evaluateLagrangePolyOutOfDomain(sub(DOMAIN_SIZE, 1), stateZ)) // L_0(z) * PI let stateT := mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(PROOF_PUBLIC_INPUT), R_MOD) // Compute main gate contribution let result := mulmod(stateT, mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), R_MOD) // Compute permutation contribution result := addmod(result, permutationQuotientContribution(), R_MOD) // Compute lookup contribution result := addmod(result, lookupQuotientContribution(), R_MOD) // Check that r(z) + r_0 = t(z) * Z_H(z) result := addmod(mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), result, R_MOD) let vanishing := addmod(mload(STATE_Z_IN_DOMAIN_SIZE), sub(R_MOD, 1), R_MOD) let lhs := mulmod(mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT), vanishing, R_MOD) if iszero(eq(lhs, result)) { revertWithMessage(27, "invalid quotient evaluation") } } /// @notice Evaluating L_{polyNum}(at) out of domain /// @dev L_i is a Lagrange polynomial for our domain such that: /// L_i(omega^i) = 1 and L_i(omega^j) = 0 for all j != i function evaluateLagrangePolyOutOfDomain(polyNum, at) -> res { let omegaPower := 1 if polyNum { omegaPower := modexp(OMEGA, polyNum) } res := addmod(modexp(at, DOMAIN_SIZE), sub(R_MOD, 1), R_MOD) // Vanishing polynomial can not be zero at point `at` if iszero(res) { revertWithMessage(28, "invalid vanishing polynomial") } res := mulmod(res, omegaPower, R_MOD) let denominator := addmod(at, sub(R_MOD, omegaPower), R_MOD) denominator := mulmod(denominator, DOMAIN_SIZE, R_MOD) denominator := modexp(denominator, sub(R_MOD, 2)) res := mulmod(res, denominator, R_MOD) } /// @notice Compute permutation contribution to linearisation polynomial's constant term function permutationQuotientContribution() -> res { // res = alpha^4 * z_perm(z*omega) res := mulmod( mload(STATE_POWER_OF_ALPHA_4_SLOT), mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD ) { let gamma := mload(STATE_GAMMA_SLOT) let beta := mload(STATE_BETA_SLOT) let factorMultiplier { // res *= sigma_0(z) * beta + gamma + a(z) factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD) factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) factorMultiplier := addmod( factorMultiplier, mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT), R_MOD ) res := mulmod(res, factorMultiplier, R_MOD) } { // res *= sigma_1(z) * beta + gamma + b(z) factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD) factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) factorMultiplier := addmod( factorMultiplier, mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT), R_MOD ) res := mulmod(res, factorMultiplier, R_MOD) } { // res *= sigma_2(z) * beta + gamma + c(z) factorMultiplier := mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD) factorMultiplier := addmod(factorMultiplier, gamma, R_MOD) factorMultiplier := addmod( factorMultiplier, mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT), R_MOD ) res := mulmod(res, factorMultiplier, R_MOD) } // res *= sigma_3(z) + gamma res := mulmod(res, addmod(mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), gamma, R_MOD), R_MOD) } // res = -res res := sub(R_MOD, res) // -= L_0(z) * alpha^5 let l0AtZ := mload(STATE_L_0_AT_Z_SLOT) l0AtZ := mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD) res := addmod(res, sub(R_MOD, l0AtZ), R_MOD) } /// @notice Compute lookup contribution to linearisation polynomial's constant term function lookupQuotientContribution() -> res { let betaLookup := mload(STATE_BETA_LOOKUP_SLOT) let gammaLookup := mload(STATE_GAMMA_LOOKUP_SLOT) let betaPlusOne := addmod(betaLookup, 1, R_MOD) let betaGamma := mulmod(betaPlusOne, gammaLookup, R_MOD) mstore(STATE_BETA_PLUS_ONE_SLOT, betaPlusOne) mstore(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT, betaGamma) // res = alpha^6 * (s(z*omega) * beta' + gamma' (beta' + 1)) * z_lookup(z*omega) res := mulmod(mload(PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT), betaLookup, R_MOD) res := addmod(res, betaGamma, R_MOD) res := mulmod(res, mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD) res := mulmod(res, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) // res *= z - omega^{n-1} { let lastOmega := modexp(OMEGA, sub(DOMAIN_SIZE, 1)) let zMinusLastOmega := addmod(mload(STATE_Z_SLOT), sub(R_MOD, lastOmega), R_MOD) mstore(STATE_Z_MINUS_LAST_OMEGA_SLOT, zMinusLastOmega) res := mulmod(res, zMinusLastOmega, R_MOD) } // res -= alpha^7 * L_{0}(z) { let intermediateValue := mulmod( mload(STATE_L_0_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_7_SLOT), R_MOD ) res := addmod(res, sub(R_MOD, intermediateValue), R_MOD) } // res -= alpha^8 * L_{n-1}(z) * (gamma' (beta' + 1))^{n-1} { let lnMinusOneAtZ := mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT) let betaGammaPowered := modexp(betaGamma, sub(DOMAIN_SIZE, 1)) let alphaPower8 := mload(STATE_POWER_OF_ALPHA_8_SLOT) let subtrahend := mulmod(mulmod(lnMinusOneAtZ, betaGammaPowered, R_MOD), alphaPower8, R_MOD) res := addmod(res, sub(R_MOD, subtrahend), R_MOD) } } /// @notice Compute main gate contribution to linearisation polynomial commitment multiplied by v function mainGateLinearisationContributionWithV( dest, stateOpening0AtZ, stateOpening1AtZ, stateOpening2AtZ, stateOpening3AtZ ) { // += a(z) * [q_a] pointMulIntoDest(VK_GATE_SETUP_0_X_SLOT, stateOpening0AtZ, dest) // += b(z) * [q_b] pointMulAndAddIntoDest(VK_GATE_SETUP_1_X_SLOT, stateOpening1AtZ, dest) // += c(z) * [q_c] pointMulAndAddIntoDest(VK_GATE_SETUP_2_X_SLOT, stateOpening2AtZ, dest) // += d(z) * [q_d] pointMulAndAddIntoDest(VK_GATE_SETUP_3_X_SLOT, stateOpening3AtZ, dest) // += a(z) * b(z) * [q_ab] pointMulAndAddIntoDest(VK_GATE_SETUP_4_X_SLOT, mulmod(stateOpening0AtZ, stateOpening1AtZ, R_MOD), dest) // += a(z) * c(z) * [q_ac] pointMulAndAddIntoDest(VK_GATE_SETUP_5_X_SLOT, mulmod(stateOpening0AtZ, stateOpening2AtZ, R_MOD), dest) // += [q_const] pointAddAssign(dest, VK_GATE_SETUP_6_X_SLOT) // += d(z*omega) * [q_{d_next}] pointMulAndAddIntoDest(VK_GATE_SETUP_7_X_SLOT, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT), dest) // *= v * main_gate_selector(z) let coeff := mulmod(mload(PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT), mload(STATE_V_SLOT), R_MOD) pointMulIntoDest(dest, coeff, dest) } /// @notice Compute custom gate contribution to linearisation polynomial commitment multiplied by v function addAssignRescueCustomGateLinearisationContributionWithV( dest, stateOpening0AtZ, stateOpening1AtZ, stateOpening2AtZ, stateOpening3AtZ ) { let accumulator let intermediateValue // = alpha * (a(z)^2 - b(z)) accumulator := mulmod(stateOpening0AtZ, stateOpening0AtZ, R_MOD) accumulator := addmod(accumulator, sub(R_MOD, stateOpening1AtZ), R_MOD) accumulator := mulmod(accumulator, mload(STATE_ALPHA_SLOT), R_MOD) // += alpha^2 * (b(z)^2 - c(z)) intermediateValue := mulmod(stateOpening1AtZ, stateOpening1AtZ, R_MOD) intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening2AtZ), R_MOD) intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_2_SLOT), R_MOD) accumulator := addmod(accumulator, intermediateValue, R_MOD) // += alpha^3 * (c(z) * a(z) - d(z)) intermediateValue := mulmod(stateOpening2AtZ, stateOpening0AtZ, R_MOD) intermediateValue := addmod(intermediateValue, sub(R_MOD, stateOpening3AtZ), R_MOD) intermediateValue := mulmod(intermediateValue, mload(STATE_POWER_OF_ALPHA_3_SLOT), R_MOD) accumulator := addmod(accumulator, intermediateValue, R_MOD) // *= v * [custom_gate_selector] accumulator := mulmod(accumulator, mload(STATE_V_SLOT), R_MOD) pointMulAndAddIntoDest(VK_GATE_SELECTORS_1_X_SLOT, accumulator, dest) } /// @notice Compute copy-permutation contribution to linearisation polynomial commitment multiplied by v function addAssignPermutationLinearisationContributionWithV( dest, stateOpening0AtZ, stateOpening1AtZ, stateOpening2AtZ, stateOpening3AtZ ) { // alpha^4 let factor := mload(STATE_POWER_OF_ALPHA_4_SLOT) // Calculate the factor { // *= (a(z) + beta * z + gamma) let zMulBeta := mulmod(mload(STATE_Z_SLOT), mload(STATE_BETA_SLOT), R_MOD) let gamma := mload(STATE_GAMMA_SLOT) let intermediateValue := addmod(addmod(zMulBeta, gamma, R_MOD), stateOpening0AtZ, R_MOD) factor := mulmod(factor, intermediateValue, R_MOD) // (b(z) + beta * z * k0 + gamma) intermediateValue := addmod( addmod(mulmod(zMulBeta, NON_RESIDUES_0, R_MOD), gamma, R_MOD), stateOpening1AtZ, R_MOD ) factor := mulmod(factor, intermediateValue, R_MOD) // (c(z) + beta * z * k1 + gamma) intermediateValue := addmod( addmod(mulmod(zMulBeta, NON_RESIDUES_1, R_MOD), gamma, R_MOD), stateOpening2AtZ, R_MOD ) factor := mulmod(factor, intermediateValue, R_MOD) // (d(z) + beta * z * k2 + gamma) intermediateValue := addmod( addmod(mulmod(zMulBeta, NON_RESIDUES_2, R_MOD), gamma, R_MOD), stateOpening3AtZ, R_MOD ) factor := mulmod(factor, intermediateValue, R_MOD) } // += alpha^5 * L_0(z) let l0AtZ := mload(STATE_L_0_AT_Z_SLOT) factor := addmod(factor, mulmod(l0AtZ, mload(STATE_POWER_OF_ALPHA_5_SLOT), R_MOD), R_MOD) // Here we can optimize one scalar multiplication by aggregating coefficients near [z_perm] during // computing [F] // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_perm] factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) mstore(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) // alpha^4 * beta * z_perm(z*omega) factor := mulmod(mload(STATE_POWER_OF_ALPHA_4_SLOT), mload(STATE_BETA_SLOT), R_MOD) factor := mulmod(factor, mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), R_MOD) { // *= (a(z) + beta * sigma_0(z) + gamma) let beta := mload(STATE_BETA_SLOT) let gamma := mload(STATE_GAMMA_SLOT) let intermediateValue := addmod( addmod( mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT), beta, R_MOD), gamma, R_MOD ), stateOpening0AtZ, R_MOD ) factor := mulmod(factor, intermediateValue, R_MOD) // *= (b(z) + beta * sigma_1(z) + gamma) intermediateValue := addmod( addmod( mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT), beta, R_MOD), gamma, R_MOD ), stateOpening1AtZ, R_MOD ) factor := mulmod(factor, intermediateValue, R_MOD) // *= (c(z) + beta * sigma_2(z) + gamma) intermediateValue := addmod( addmod( mulmod(mload(PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT), beta, R_MOD), gamma, R_MOD ), stateOpening2AtZ, R_MOD ) factor := mulmod(factor, intermediateValue, R_MOD) } // *= v * [sigma_3] factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) pointMulIntoDest(VK_PERMUTATION_3_X_SLOT, factor, QUERIES_BUFFER_POINT_SLOT) pointSubAssign(dest, QUERIES_BUFFER_POINT_SLOT) } /// @notice Compute lookup contribution to linearisation polynomial commitment multiplied by v function addAssignLookupLinearisationContributionWithV( dest, stateOpening0AtZ, stateOpening1AtZ, stateOpening2AtZ ) { // alpha^6 * v * z_lookup(z*omega) * (z - omega^{n-1}) * [s] let factor := mload(PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT) factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD) factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) // Here we can optimize one scalar multiplication by aggregating coefficients near [s] during // computing [F] // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [s] mstore(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) // gamma(1 + beta) + t(x) + beta * t(x*omega) factor := mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT) factor := mulmod(factor, mload(STATE_BETA_LOOKUP_SLOT), R_MOD) factor := addmod(factor, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD) factor := addmod(factor, mload(STATE_BETA_GAMMA_PLUS_GAMMA_SLOT), R_MOD) // *= (gamma + f(z)) // We should use fact that f(x) = // lookup_selector(x) * (a(x) + eta * b(x) + eta^2 * c(x) + eta^3 * table_type(x)) // to restore f(z) let fReconstructed { fReconstructed := stateOpening0AtZ let eta := mload(STATE_ETA_SLOT) let currentEta := eta fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening1AtZ, R_MOD), R_MOD) currentEta := mulmod(currentEta, eta, R_MOD) fReconstructed := addmod(fReconstructed, mulmod(currentEta, stateOpening2AtZ, R_MOD), R_MOD) currentEta := mulmod(currentEta, eta, R_MOD) // add type of table fReconstructed := addmod( fReconstructed, mulmod(mload(PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT), currentEta, R_MOD), R_MOD ) fReconstructed := mulmod(fReconstructed, mload(PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT), R_MOD) fReconstructed := addmod(fReconstructed, mload(STATE_GAMMA_LOOKUP_SLOT), R_MOD) } // *= -alpha^6 * (beta + 1) * (z - omega^{n-1}) factor := mulmod(factor, fReconstructed, R_MOD) factor := mulmod(factor, mload(STATE_BETA_PLUS_ONE_SLOT), R_MOD) factor := sub(R_MOD, factor) factor := mulmod(factor, mload(STATE_POWER_OF_ALPHA_6_SLOT), R_MOD) factor := mulmod(factor, mload(STATE_Z_MINUS_LAST_OMEGA_SLOT), R_MOD) // += alpha^7 * L_0(z) factor := addmod( factor, mulmod(mload(STATE_L_0_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_7_SLOT), R_MOD), R_MOD ) // += alpha^8 * L_{n-1}(z) factor := addmod( factor, mulmod(mload(STATE_L_N_MINUS_ONE_AT_Z_SLOT), mload(STATE_POWER_OF_ALPHA_8_SLOT), R_MOD), R_MOD ) // Here we can optimize one scalar multiplication by aggregating coefficients near [z_lookup] during // computing [F] // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [z_lookup] factor := mulmod(factor, mload(STATE_V_SLOT), R_MOD) mstore(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF, factor) } /*////////////////////////////////////////////////////////////// 4. Prepare queries //////////////////////////////////////////////////////////////*/ /// @dev Here we compute the first and second parts of batched polynomial commitment /// We use the formula: /// [D0] = [t_0] + z^n * [t_1] + z^{2n} * [t_2] + z^{3n} * [t_3] /// and /// [D1] = main_gate_selector(z) * ( \ /// a(z) * [q_a] + b(z) * [q_b] + c(z) * [q_c] + d(z) * [q_d] + | - main gate contribution /// a(z) * b(z) * [q_ab] + a(z) * c(z) * [q_ac] + | /// [q_const] + d(z*omega) * [q_{d_next}]) / /// /// + alpha * [custom_gate_selector] * ( \ /// (a(z)^2 - b(z)) + | - custom gate contribution /// (b(z)^2 - c(z)) * alpha + | /// (a(z)*c(z) - d(z)) * alpha^2 ) / /// /// + alpha^4 * [z_perm] * \ /// (a(z) + beta * z + gamma) * | /// (b(z) + beta * z * k0 + gamma) * | /// (c(z) + beta * z * k1 + gamma) * | /// (d(z) + beta * z * k2 + gamma) | - permutation contribution /// - alpha^4 * z_perm(z*omega) * beta * [sigma_3] * | /// (a(z) + beta * sigma_0(z) + gamma) * | /// (b(z) + beta * sigma_1(z) + gamma) * | /// (c(z) + beta * sigma_2(z) + gamma) * | /// + alpha^5 * L_0(z) * [z_perm] / /// /// - alpha^6 * (1 + beta') * (gamma' + f(z)) * (z - omega^{n-1}) * \ /// (gamma'(1 + beta') + t(z) + beta' * t(z*omega)) * [z_lookup] | /// + alpha^6 * z_lookup(z*omega) * (z - omega^{n-1}) * [s] | - lookup contribution /// + alpha^7 * L_0(z) * [z_lookup] | /// + alpha^8 * L_{n-1}(z) * [z_lookup] / function prepareQueries() { // Calculate [D0] { let zInDomainSize := mload(STATE_Z_IN_DOMAIN_SIZE) let currentZ := zInDomainSize mstore(QUERIES_AT_Z_0_X_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_X_SLOT)) mstore(QUERIES_AT_Z_0_Y_SLOT, mload(PROOF_QUOTIENT_POLY_PARTS_0_Y_SLOT)) pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_1_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) currentZ := mulmod(currentZ, zInDomainSize, R_MOD) pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_2_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) currentZ := mulmod(currentZ, zInDomainSize, R_MOD) pointMulAndAddIntoDest(PROOF_QUOTIENT_POLY_PARTS_3_X_SLOT, currentZ, QUERIES_AT_Z_0_X_SLOT) } // Calculate v * [D1] // We are going to multiply all the points in the sum by v to save // one scalar multiplication during [F] computation { let stateOpening0AtZ := mload(PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT) let stateOpening1AtZ := mload(PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT) let stateOpening2AtZ := mload(PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT) let stateOpening3AtZ := mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT) mainGateLinearisationContributionWithV( QUERIES_AT_Z_1_X_SLOT, stateOpening0AtZ, stateOpening1AtZ, stateOpening2AtZ, stateOpening3AtZ ) addAssignRescueCustomGateLinearisationContributionWithV( QUERIES_AT_Z_1_X_SLOT, stateOpening0AtZ, stateOpening1AtZ, stateOpening2AtZ, stateOpening3AtZ ) addAssignPermutationLinearisationContributionWithV( QUERIES_AT_Z_1_X_SLOT, stateOpening0AtZ, stateOpening1AtZ, stateOpening2AtZ, stateOpening3AtZ ) addAssignLookupLinearisationContributionWithV( QUERIES_AT_Z_1_X_SLOT, stateOpening0AtZ, stateOpening1AtZ, stateOpening2AtZ ) } // Also we should restore [t] for future computations // [t] = [col_0] + eta*[col_1] + eta^2*[col_2] + eta^3*[col_3] { mstore(QUERIES_T_POLY_AGGREGATED_X_SLOT, mload(VK_LOOKUP_TABLE_0_X_SLOT)) mstore(QUERIES_T_POLY_AGGREGATED_Y_SLOT, mload(VK_LOOKUP_TABLE_0_Y_SLOT)) let eta := mload(STATE_ETA_SLOT) let currentEta := eta pointMulAndAddIntoDest(VK_LOOKUP_TABLE_1_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) currentEta := mulmod(currentEta, eta, R_MOD) pointMulAndAddIntoDest(VK_LOOKUP_TABLE_2_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) currentEta := mulmod(currentEta, eta, R_MOD) pointMulAndAddIntoDest(VK_LOOKUP_TABLE_3_X_SLOT, currentEta, QUERIES_T_POLY_AGGREGATED_X_SLOT) } } /*////////////////////////////////////////////////////////////// 5. Prepare aggregated commitment //////////////////////////////////////////////////////////////*/ /// @dev Here we compute aggregated commitment for the final pairing /// We use the formula: /// [E] = ( t(z) + v * r(z) /// + v^2*a(z) + v^3*b(z) + v^4*c(z) + v^5*d(z) /// + v^6*main_gate_selector(z) /// + v^7*sigma_0(z) + v^8*sigma_1(z) + v^9*sigma_2(z) /// + v^10*t(z) + v^11*lookup_selector(z) + v^12*table_type(z) /// + u * (v^13*z_perm(z*omega) + v^14*d(z*omega) /// + v^15*s(z*omega) + v^16*z_lookup(z*omega) + v^17*t(z*omega) /// ) /// ) * [1] /// and /// [F] = [D0] + v * [D1] /// + v^2*[a] + v^3*[b] + v^4*[c] + v^5*[d] /// + v^6*[main_gate_selector] /// + v^7*[sigma_0] + v^8*[sigma_1] + v^9*[sigma_2] /// + v^10*[t] + v^11*[lookup_selector] + v^12*[table_type] /// + u * ( v^13*[z_perm] + v^14*[d] /// + v^15*[s] + v^16*[z_lookup] + v^17*[t] /// ) function prepareAggregatedCommitment() { // Here we compute parts of [E] and [F] without u multiplier let aggregationChallenge := 1 let firstDCoeff let firstTCoeff mstore(AGGREGATED_AT_Z_X_SLOT, mload(QUERIES_AT_Z_0_X_SLOT)) mstore(AGGREGATED_AT_Z_Y_SLOT, mload(QUERIES_AT_Z_0_Y_SLOT)) let aggregatedOpeningAtZ := mload(PROOF_QUOTIENT_POLY_OPENING_AT_Z_SLOT) { function updateAggregationChallenge( queriesCommitmentPoint, valueAtZ, curAggregationChallenge, curAggregatedOpeningAtZ ) -> newAggregationChallenge, newAggregatedOpeningAtZ { newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD) pointMulAndAddIntoDest(queriesCommitmentPoint, newAggregationChallenge, AGGREGATED_AT_Z_X_SLOT) newAggregatedOpeningAtZ := addmod( curAggregatedOpeningAtZ, mulmod(newAggregationChallenge, mload(valueAtZ), R_MOD), R_MOD ) } // We don't need to multiply by v, because we have already computed v * [D1] pointAddIntoDest(AGGREGATED_AT_Z_X_SLOT, QUERIES_AT_Z_1_X_SLOT, AGGREGATED_AT_Z_X_SLOT) aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) aggregatedOpeningAtZ := addmod( aggregatedOpeningAtZ, mulmod(aggregationChallenge, mload(PROOF_LINEARISATION_POLY_OPENING_AT_Z_SLOT), R_MOD), R_MOD ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( PROOF_STATE_POLYS_0_X_SLOT, PROOF_STATE_POLYS_0_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( PROOF_STATE_POLYS_1_X_SLOT, PROOF_STATE_POLYS_1_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( PROOF_STATE_POLYS_2_X_SLOT, PROOF_STATE_POLYS_2_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) // Here we can optimize one scalar multiplication by aggregating coefficients near [d] // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [d] aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) firstDCoeff := aggregationChallenge aggregatedOpeningAtZ := addmod( aggregatedOpeningAtZ, mulmod(aggregationChallenge, mload(PROOF_STATE_POLYS_3_OPENING_AT_Z_SLOT), R_MOD), R_MOD ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( VK_GATE_SELECTORS_0_X_SLOT, PROOF_GATE_SELECTORS_0_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( VK_PERMUTATION_0_X_SLOT, PROOF_COPY_PERMUTATION_POLYS_0_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( VK_PERMUTATION_1_X_SLOT, PROOF_COPY_PERMUTATION_POLYS_1_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( VK_PERMUTATION_2_X_SLOT, PROOF_COPY_PERMUTATION_POLYS_2_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) // Here we can optimize one scalar multiplication by aggregating coefficients near [t] // We will sum them and add and make one scalar multiplication: (coeff1 + coeff2) * [t] aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) firstTCoeff := aggregationChallenge aggregatedOpeningAtZ := addmod( aggregatedOpeningAtZ, mulmod(aggregationChallenge, mload(PROOF_LOOKUP_T_POLY_OPENING_AT_Z_SLOT), R_MOD), R_MOD ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( VK_LOOKUP_SELECTOR_X_SLOT, PROOF_LOOKUP_SELECTOR_POLY_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) aggregationChallenge, aggregatedOpeningAtZ := updateAggregationChallenge( VK_LOOKUP_TABLE_TYPE_X_SLOT, PROOF_LOOKUP_TABLE_TYPE_POLY_OPENING_AT_Z_SLOT, aggregationChallenge, aggregatedOpeningAtZ ) } mstore(AGGREGATED_OPENING_AT_Z_SLOT, aggregatedOpeningAtZ) // Here we compute parts of [E] and [F] with u multiplier aggregationChallenge := mulmod(aggregationChallenge, mload(STATE_V_SLOT), R_MOD) let copyPermutationCoeff := addmod( mload(COPY_PERMUTATION_FIRST_AGGREGATED_COMMITMENT_COEFF), mulmod(aggregationChallenge, mload(STATE_U_SLOT), R_MOD), R_MOD ) pointMulIntoDest( PROOF_COPY_PERMUTATION_GRAND_PRODUCT_X_SLOT, copyPermutationCoeff, AGGREGATED_AT_Z_OMEGA_X_SLOT ) let aggregatedOpeningAtZOmega := mulmod( mload(PROOF_COPY_PERMUTATION_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT), aggregationChallenge, R_MOD ) { function updateAggregationChallenge( queriesCommitmentPoint, valueAtZ_Omega, previousCoeff, curAggregationChallenge, curAggregatedOpeningAtZ_Omega ) -> newAggregationChallenge, newAggregatedOpeningAtZ_Omega { newAggregationChallenge := mulmod(curAggregationChallenge, mload(STATE_V_SLOT), R_MOD) let finalCoeff := addmod( previousCoeff, mulmod(newAggregationChallenge, mload(STATE_U_SLOT), R_MOD), R_MOD ) pointMulAndAddIntoDest(queriesCommitmentPoint, finalCoeff, AGGREGATED_AT_Z_OMEGA_X_SLOT) newAggregatedOpeningAtZ_Omega := addmod( curAggregatedOpeningAtZ_Omega, mulmod(newAggregationChallenge, mload(valueAtZ_Omega), R_MOD), R_MOD ) } aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( PROOF_STATE_POLYS_3_X_SLOT, PROOF_STATE_POLYS_3_OPENING_AT_Z_OMEGA_SLOT, firstDCoeff, aggregationChallenge, aggregatedOpeningAtZOmega ) aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( PROOF_LOOKUP_S_POLY_X_SLOT, PROOF_LOOKUP_S_POLY_OPENING_AT_Z_OMEGA_SLOT, mload(LOOKUP_S_FIRST_AGGREGATED_COMMITMENT_COEFF), aggregationChallenge, aggregatedOpeningAtZOmega ) aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( PROOF_LOOKUP_GRAND_PRODUCT_X_SLOT, PROOF_LOOKUP_GRAND_PRODUCT_OPENING_AT_Z_OMEGA_SLOT, mload(LOOKUP_GRAND_PRODUCT_FIRST_AGGREGATED_COMMITMENT_COEFF), aggregationChallenge, aggregatedOpeningAtZOmega ) aggregationChallenge, aggregatedOpeningAtZOmega := updateAggregationChallenge( QUERIES_T_POLY_AGGREGATED_X_SLOT, PROOF_LOOKUP_T_POLY_OPENING_AT_Z_OMEGA_SLOT, firstTCoeff, aggregationChallenge, aggregatedOpeningAtZOmega ) } mstore(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT, aggregatedOpeningAtZOmega) // Now we can merge both parts and get [E] and [F] let u := mload(STATE_U_SLOT) // [F] pointAddIntoDest( AGGREGATED_AT_Z_X_SLOT, AGGREGATED_AT_Z_OMEGA_X_SLOT, PAIRING_PAIR_WITH_GENERATOR_X_SLOT ) // [E] = (aggregatedOpeningAtZ + u * aggregatedOpeningAtZOmega) * [1] let aggregatedValue := addmod( mulmod(mload(AGGREGATED_OPENING_AT_Z_OMEGA_SLOT), u, R_MOD), mload(AGGREGATED_OPENING_AT_Z_SLOT), R_MOD ) mstore(PAIRING_BUFFER_POINT_X_SLOT, 1) mstore(PAIRING_BUFFER_POINT_Y_SLOT, 2) pointMulIntoDest(PAIRING_BUFFER_POINT_X_SLOT, aggregatedValue, PAIRING_BUFFER_POINT_X_SLOT) } /*////////////////////////////////////////////////////////////// 5. Pairing //////////////////////////////////////////////////////////////*/ /// @notice Checks the final pairing /// @dev We should check the equation: /// e([W] + u * [W'], [x]_2) = e(z * [W] + u * z * omega * [W'] + [F] - [E], [1]_2), /// where [F] and [E] were computed previously /// /// Also we need to check that e([P1], [x]_2) = e([P2], [1]_2) /// if we have the recursive part of the proof /// where [P1] and [P2] are parts of the recursive proof /// /// We can aggregate both pairings into one for gas optimization: /// e([W] + u * [W'] + u^2 * [P1], [x]_2) = /// e(z * [W] + u * z * omega * [W'] + [F] - [E] + u^2 * [P2], [1]_2) /// /// u is a valid challenge for such aggregation, /// because [P1] and [P2] are used in PI function finalPairing() { let u := mload(STATE_U_SLOT) let z := mload(STATE_Z_SLOT) let zOmega := mulmod(mload(STATE_Z_SLOT), OMEGA, R_MOD) // [F] - [E] pointSubAssign(PAIRING_PAIR_WITH_GENERATOR_X_SLOT, PAIRING_BUFFER_POINT_X_SLOT) // +z * [W] + u * z * omega * [W'] pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_X_SLOT, z, PAIRING_PAIR_WITH_GENERATOR_X_SLOT) pointMulAndAddIntoDest( PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, mulmod(zOmega, u, R_MOD), PAIRING_PAIR_WITH_GENERATOR_X_SLOT ) // [W] + u * [W'] mstore(PAIRING_PAIR_WITH_X_X_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_X_SLOT)) mstore(PAIRING_PAIR_WITH_X_Y_SLOT, mload(PROOF_OPENING_PROOF_AT_Z_Y_SLOT)) pointMulAndAddIntoDest(PROOF_OPENING_PROOF_AT_Z_OMEGA_X_SLOT, u, PAIRING_PAIR_WITH_X_X_SLOT) pointNegate(PAIRING_PAIR_WITH_X_X_SLOT) // Add recursive proof part if needed if mload(VK_RECURSIVE_FLAG_SLOT) { let uu := mulmod(u, u, R_MOD) pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P1_X_SLOT, uu, PAIRING_PAIR_WITH_GENERATOR_X_SLOT) pointMulAndAddIntoDest(PROOF_RECURSIVE_PART_P2_X_SLOT, uu, PAIRING_PAIR_WITH_X_X_SLOT) } // Calculate pairing { mstore(0x000, mload(PAIRING_PAIR_WITH_GENERATOR_X_SLOT)) mstore(0x020, mload(PAIRING_PAIR_WITH_GENERATOR_Y_SLOT)) mstore(0x040, G2_ELEMENTS_0_X1) mstore(0x060, G2_ELEMENTS_0_X2) mstore(0x080, G2_ELEMENTS_0_Y1) mstore(0x0a0, G2_ELEMENTS_0_Y2) mstore(0x0c0, mload(PAIRING_PAIR_WITH_X_X_SLOT)) mstore(0x0e0, mload(PAIRING_PAIR_WITH_X_Y_SLOT)) mstore(0x100, G2_ELEMENTS_1_X1) mstore(0x120, G2_ELEMENTS_1_X2) mstore(0x140, G2_ELEMENTS_1_Y1) mstore(0x160, G2_ELEMENTS_1_Y2) let success := staticcall(gas(), 8, 0, 0x180, 0x00, 0x20) if iszero(success) { revertWithMessage(32, "finalPairing: precompile failure") } if iszero(mload(0)) { revertWithMessage(29, "finalPairing: pairing failure") } } } /*////////////////////////////////////////////////////////////// Verification //////////////////////////////////////////////////////////////*/ // Step 1: Load the proof and check the correctness of its parts loadProof() // Step 2: Recompute all the challenges with the transcript initializeTranscript() // Step 3: Check the quotient equality verifyQuotientEvaluation() // Step 4: Compute queries [D0] and v * [D1] prepareQueries() // Step 5: Compute [E] and [F] prepareAggregatedCommitment() // Step 6: Check the final pairing with aggregated recursive proof finalPairing() mstore(0, true) return(0, 32) } } }
pragma solidity 0.8.20; // SPDX-License-Identifier: MIT interface IVerifier { function verify( uint256[] calldata _publicInputs, uint256[] calldata _proof, uint256[] calldata _recursiveAggregationInput ) external view returns (bool); function verificationKeyHash() external pure returns (bytes32); }
{ "optimizer": { "enabled": true, "runs": 9999999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"verificationKeyHash","outputs":[{"internalType":"bytes32","name":"vkHash","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"},{"internalType":"uint256[]","name":"","type":"uint256[]"}],"name":"verify","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b506143ef806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c806387d9d0231461003b5780639e8945d214610063575b600080fd5b61004e61004936600461431f565b610079565b60405190151581526020015b60405180910390f35b61006b613cf3565b60405190815260200161005a565b600061064e7f236c54d6ae3745765605abfdd3d1533aaf27c583bbac66c43f5555e2b087db42610200527f0b2b13c749c19a0be311c0eec76abddbc0848e2454cc7323605a714256ef101a610220527f04659caf7b05471ba5ba85b1ab62267aa6c456836e625f169f7119d55b9462d2610240527f0ea63403692148d2ad22189a1e5420076312f4d46e62036a043a6b0b84d5b410610260527f0e6696d09d65fce1e42805be03fca1f14aea247281f688981f925e77d4ce2291610280527f0228f6cf8fe20c1e07e5b78bf8c41d50e55975a126d22a198d1e56acd4bbb3dd6102a0527f14685dafe340b1dec5eafcd5e7faddaf24f3781ddc53309cc25d0b42c00541dd6102c0527f0e651cff9447cb360198899b80fa23e89ec13bc94ff161729aa841d2b55ea5be6102e0527f16e9ef76cb68f2750eb0ee72382dd9911a982308d0ab10ef94dada13c382ae73610300527f22e404bc91350f3bc7daad1d1025113742436983c85eac5ab7b42221a181b81e610320527f0d9b29613037a5025655c82b143d2b7449c98f3aea358307c8529249cc54f3b9610340527f15b3c4c946ad1babfc4c03ff7c2423fd354af3a9305c499b7fb3aaebe2fee746610360527f08aa077804a60caf18621f96c63d7d1b532f44b13c273956f81b4c41f1f8f076610380527f01fcb7c6e22aab56eb8b61bae6f457411bd41bcc5f1e8311421a98ccee23b72e6103a0527f283344a1ab3e55ecfd904d0b8e9f4faea338df5a4ead2fa9a42f0e103da40abc6103c0527f223b37b83b9687512d322993edd70e508dd80adb10bcf7321a3cc8a44c2695216103e0527f1f67f0ba5f7e837bc680acb4e612ebd938ad35211aa6e05b96cad19e66b82d2d610400527f2820641a84d2e8298ac2ac42bd4b912c0c37f768ecc83d3a29e7c720763d15a1610420527f0353257957562270292a17860ca8e8827703f828f440ee004848b1e23fdf9de2610440527f305f4137fee253dff8b2bfe579038e8f25d5bd217865072af5d89fc8800ada24610460527f13a600154b369ff3237706d00948e465ee1c32c7a6d3e18bccd9c4a15910f2e5610480527f138aa24fbf4cdddc75114811b3d59040394c218ecef3eb46ef9bd646f7e537766104a0527f277fff1f80c409357e2d251d79f6e3fd2164b755ce69cfd72de5c690289df6626104c0527f25235588e28c70eea3e35531c80deac25cd9b53ea3f98993f120108bc7abf6706104e0527f0990e07a9b001048b947d0e5bd6157214c7359b771f01bf52bd771ba563a900e610500527f05e5fb090dd40914c8606d875e301167ae3047d684a02b44d9d36f1eaf43d0b4610520527f1d4656690b33299db5631401a282afab3e16c78ee2c9ad9efea628171dcbc6bc610540527f0ebda2ebe582f601f813ec1e3970d13ef1500c742a85cce9b7f190f333de03b0610560527f2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b6105c0527f04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d4729814686105e0527f1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac610600527f1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d610620527f0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89610640527f1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283610660527f02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf610680527f09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a66106a0527f2f4d347c7fb61daaadfff881e24f4b5dcfdc0d70a95bcb148168b90ef93e0007610580527f2322632465ba8e28cd0a4befd813ea85a972f4f6fa8e8603cf5d062dbcb140656105a0527f1e3c9fc98c118e4bc34f1f93d214a5d86898e980c40d8e2c180c6ada377a74676106c0527f2260a13535c35a15c173f5e5797d4b675b55d164a9995bfb7624971324bd84a86106e052600061070052565b613cb8565b7f08c379a000000000000000000000000000000000000000000000000000000000600052602060045280602452508060445260646000fd5b6020600052602080526020604052806060525080608052507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a05260006020600060c0600060055afa610704576107047f6d6f6465787020707265636f6d70696c65206661696c656400000000000000006018610653565b5060005190565b805160005260208101516020525080604052506040816060600060075afa610758576107587f706f696e744d756c496e746f446573743a2065634d756c206661696c65640000601e610653565b50565b805160005260208101516020525080516040526020810151606052506040816080600060065afa610758576107587f706f696e74416464496e746f446573743a206563416464206661696c65640000601e610653565b80516000526020810151602052815160405260208201517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47036060526040816080600060065afa610827576108277f706f696e7453756241737369676e3a206563416464206661696c656400000000601c610653565b5050565b80516000526020810151602052815160405260208201516060526040816080600060065afa610827576108277f706f696e7441646441737369676e3a206563416464206661696c656400000000601c610653565b80516000526020810151602052508060405250604060006060600060075afa815160405260208201516060526040826080600060065afa1680610827576108277f706f696e744d756c416e64416464496e746f44657374000000000000000000006016610653565b602081015180801561091f57507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470360209190910152565b825115610951576109517f706f696e744e65676174653a20696e76616c696420706f696e74000000000000601a610653565b505050565b6000610d4353610d84526064610d40206001610d43536064610d4020610d6452610d4452565b6002610d435360e01b610d84526048610d40207f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b60043560048101356001811490507f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602483013516610720526024359150600482013581602c8214169150507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476024830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476044840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161074052806107605250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476064830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476084840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161078052806107a05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760a4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760c4840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478485091416935050816107c052806107e05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760e4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610104840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161080052806108205250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610124830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610144840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161084052806108605250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610164830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610184840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161088052806108a05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476101a4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476101c4840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478485091416935050816108c052806108e05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476101e4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610204840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161090052806109205250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610224830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610244840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161094052806109605250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610264830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610284840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161098052806109a05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476102a4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476102c4840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478485091416935050816109c052806109e05250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102e483013506610a00527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161030483013506610a20527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161032483013506610a40527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161034483013506610a60527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161036483013506610a80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161038483013506610aa0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103a483013506610ac0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103c483013506610ae0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103e483013506610b00527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161040483013506610b20527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161042483013506610b40527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161044483013506610b60527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161046483013506610b80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161048483013506610ba0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104a483013506610bc0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104c483013506610be0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104e483013506610c00527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161050483013506610c20527f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610524830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610544840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848509141693505081610c405280610c605250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610564830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610584840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848509141693505081610c805280610ca0525050604435915060048201356107005160008114611b5e5782600483141692507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476024850135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476044860135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309857f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848509141695505081610cc05280610ce05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476064850135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476084860135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309857f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848509141695505081610d005280610d20525050611b65565b8282151692505b505080610827576108277f6c6f616450726f6f663a2050726f6f6620697320696e76616c69640000000000601b610653565b611ba361072051610956565b611baf61074051610956565b611bbb61076051610956565b611bc761078051610956565b611bd36107a051610956565b611bdf6107c051610956565b611beb6107e051610956565b611bf761080051610956565b611c0361082051610956565b611c0d600061097c565b610f0052611c1d61088051610956565b611c296108a051610956565b611c33600161097c565b610de052611c41600261097c565b610e0052611c5161084051610956565b611c5d61086051610956565b611c67600361097c565b610f2052611c75600461097c565b610f4052611c856108c051610956565b611c916108e051610956565b611c9b600561097c565b610dc052611cab61090051610956565b611cb761092051610956565b611cc361094051610956565b611ccf61096051610956565b611cdb61098051610956565b611ce76109a051610956565b611cf36109c051610956565b611cff6109e051610956565b611d09600661097c565b80610fe052611d1c63010000008261068b565b6110605250611d2d610c0051610956565b611d39610a0051610956565b611d45610a2051610956565b611d51610a4051610956565b611d5d610a6051610956565b611d69610a8051610956565b611d75610aa051610956565b611d81610ac051610956565b611d8d610ae051610956565b611d99610b0051610956565b611da5610b2051610956565b611db1610b8051610956565b611dbd610bc051610956565b611dc9610be051610956565b611dd5610b4051610956565b611de1610b6051610956565b611ded610ba051610956565b611df9610c2051610956565b611e03600761097c565b610fa052611e13610c4051610956565b611e1f610c6051610956565b611e2b610c8051610956565b611e37610ca051610956565b611e41600861097c565b610fc052565b610dc0517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820980610e20527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610e40527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610e60527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610e80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610ea0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610ec0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209610ee0525050610fe051611f84816000612115565b61102052611f958162ffffff612115565b61104052507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016107205161102051097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610aa051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016120166122bf565b820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161204361259e565b820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181610c20510890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010361106051087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181610c0051099050818114610827576108277f696e76616c69642071756f7469656e74206576616c756174696f6e0000000000601b610653565b60006001821561214c57612149837f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb61068b565b90505b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000061219c63010000008761068b565b089150816121cf576121cf7f696e76616c69642076616e697368696e6720706f6c796e6f6d69616c00000000601c610653565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016301000000820990506122917f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff8261068b565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818309949350505050565b60007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610b2051610e6051099050610e0051610de05160007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182610ac0510990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610a0051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182610ae0510990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610a2051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182610b00510990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610a4051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818509935050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082610a6051088309915050807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001039050611020517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610e8051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103830891505090565b6000610f2051610f40517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600183087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209915080610f60525080610f80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182610b40510992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610b6051840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610ea051840992506126cc62ffffff7f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb61068b565b91507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103610fe05108915081611000527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610ec051611020510991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010384086110405190935091506127cc62ffffff8261068b565b610ee0517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184870909935050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010384089250505090565b612875818361020061070b565b612882818461024061087f565b61288f818561028061087f565b61289c81866102c061087f565b6128cc817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185850961030061087f565b6128fc817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186850961034061087f565b6129086103808261082b565b61291881610a80516103c061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051610aa0510961294d82828461070b565b505050505050565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184850991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610dc051830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185860990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001867f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610e2051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184870990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001877f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610e4051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183089150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa0518209905061294d828261044061087f565b610e60517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610de051610fe05109610e00517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838508087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001877f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160058809080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001887f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160078809080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600a8809080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018185099350505050611020517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610e8051830983089150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa0518209905080611340527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610de051610e60510990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610b205182099050610de051610e00517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186610ac0510908087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001877f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187610ae05109080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001887f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187610b005109080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993505050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051820990506130af6110808261054061070b565b506130bc611080826107b1565b5050505050565b50610b60517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610ea051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161100051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa05182096113805250610ba051610f20517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610b8051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610f80518208610f00519091508290807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180888309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082610be051098408925050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610bc051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610f4051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183099150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610f60518209610ea0517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019182039250820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161100051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610ec0516110205109820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610ee0516110405109820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa05182096113605250505050565b6110605180610900516110c052610920516110e0526134446110c08261094061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990506134796110c08261098061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990506134ae6110c0826109c061087f565b5050610a0051610a2051610a4051610a60516134cf81838587611100612868565b6134de81838587611100612955565b6134ed81838587611100612ba0565b506134fc8183856111006130c3565b5050506105c051611140526105e05161116052610f0051806135236111408261060061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990506135586111408261064061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990506108276111408261068061087f565b60016000806110c051611180526110e0516111a052610c0051613617565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051860991506135e4611180838561087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180855184098708905094509492505050565b61362861118061110061118061075b565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610c205186098208905061368d8185610a006107406135ab565b915093506136a18185610a206107806135ab565b915093506136b58185610a406107c06135ab565b915093507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051850993508392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610a60518609820890506137218185610aa06104006135ab565b915093506137358185610ac06104806135ab565b915093506137498185610ae06104c06135ab565b9150935061375d8185610b006105006135ab565b915093507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051850993508391507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610b80518609820890506137c98185610bc06105806135ab565b915093506137dd8185610be06106c06135ab565b61120052610fa0519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019150840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610fc0518509611340510861384b6111c08261084061070b565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183610b205109613910565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051870991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610fc051840986086138db6111c0828661087f565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018085518409880890509550959350505050565b613921818585610a80610800613878565b91509350613939818561138051610b40610880613878565b91509350613951818561136051610b606108c0613878565b91509350613966818584610ba0611140613878565b611220525050610fc0519250613987915061128090506111c061118061075b565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001611200517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018361122051090890506001611240526002611260526107586112408261124061070b565b610fc051610fe0517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb8209613a4b6112406112806107b1565b613a5a61128083610c4061087f565b613a8c6112807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001858409610c8061087f565b5050610c405161130052610c605161132052613aad61130082610c8061087f565b613ab86113006108e7565b6107005115613b05577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818209613af461128082610cc061087f565b613b0361130082610d0061087f565b505b50611280516000526112a0516020527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26040527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6060527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b6080527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a0526113005160c0526113205160e0527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1610100527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0610120527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4610140527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101605260206000610180600060085afa80613c8357613c837f66696e616c50616972696e673a20707265636f6d70696c65206661696c7572656020610653565b50600051613cb657613cb67f66696e616c50616972696e673a2070616972696e67206661696c757265000000601d610653565b565b613cc06109b4565b613cc8611b97565b613cd0611e47565b613cd8613420565b613ce061358d565b613ce86139f1565b600160005260206000f35b60006142c87f236c54d6ae3745765605abfdd3d1533aaf27c583bbac66c43f5555e2b087db42610200527f0b2b13c749c19a0be311c0eec76abddbc0848e2454cc7323605a714256ef101a610220527f04659caf7b05471ba5ba85b1ab62267aa6c456836e625f169f7119d55b9462d2610240527f0ea63403692148d2ad22189a1e5420076312f4d46e62036a043a6b0b84d5b410610260527f0e6696d09d65fce1e42805be03fca1f14aea247281f688981f925e77d4ce2291610280527f0228f6cf8fe20c1e07e5b78bf8c41d50e55975a126d22a198d1e56acd4bbb3dd6102a0527f14685dafe340b1dec5eafcd5e7faddaf24f3781ddc53309cc25d0b42c00541dd6102c0527f0e651cff9447cb360198899b80fa23e89ec13bc94ff161729aa841d2b55ea5be6102e0527f16e9ef76cb68f2750eb0ee72382dd9911a982308d0ab10ef94dada13c382ae73610300527f22e404bc91350f3bc7daad1d1025113742436983c85eac5ab7b42221a181b81e610320527f0d9b29613037a5025655c82b143d2b7449c98f3aea358307c8529249cc54f3b9610340527f15b3c4c946ad1babfc4c03ff7c2423fd354af3a9305c499b7fb3aaebe2fee746610360527f08aa077804a60caf18621f96c63d7d1b532f44b13c273956f81b4c41f1f8f076610380527f01fcb7c6e22aab56eb8b61bae6f457411bd41bcc5f1e8311421a98ccee23b72e6103a0527f283344a1ab3e55ecfd904d0b8e9f4faea338df5a4ead2fa9a42f0e103da40abc6103c0527f223b37b83b9687512d322993edd70e508dd80adb10bcf7321a3cc8a44c2695216103e0527f1f67f0ba5f7e837bc680acb4e612ebd938ad35211aa6e05b96cad19e66b82d2d610400527f2820641a84d2e8298ac2ac42bd4b912c0c37f768ecc83d3a29e7c720763d15a1610420527f0353257957562270292a17860ca8e8827703f828f440ee004848b1e23fdf9de2610440527f305f4137fee253dff8b2bfe579038e8f25d5bd217865072af5d89fc8800ada24610460527f13a600154b369ff3237706d00948e465ee1c32c7a6d3e18bccd9c4a15910f2e5610480527f138aa24fbf4cdddc75114811b3d59040394c218ecef3eb46ef9bd646f7e537766104a0527f277fff1f80c409357e2d251d79f6e3fd2164b755ce69cfd72de5c690289df6626104c0527f25235588e28c70eea3e35531c80deac25cd9b53ea3f98993f120108bc7abf6706104e0527f0990e07a9b001048b947d0e5bd6157214c7359b771f01bf52bd771ba563a900e610500527f05e5fb090dd40914c8606d875e301167ae3047d684a02b44d9d36f1eaf43d0b4610520527f1d4656690b33299db5631401a282afab3e16c78ee2c9ad9efea628171dcbc6bc610540527f0ebda2ebe582f601f813ec1e3970d13ef1500c742a85cce9b7f190f333de03b0610560527f2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b6105c0527f04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d4729814686105e0527f1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac610600527f1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d610620527f0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89610640527f1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283610660527f02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf610680527f09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a66106a0527f2f4d347c7fb61daaadfff881e24f4b5dcfdc0d70a95bcb148168b90ef93e0007610580527f2322632465ba8e28cd0a4befd813ea85a972f4f6fa8e8603cf5d062dbcb140656105a0527f1e3c9fc98c118e4bc34f1f93d214a5d86898e980c40d8e2c180c6ada377a74676106c0527f2260a13535c35a15c173f5e5797d4b675b55d164a9995bfb7624971324bd84a86106e052600061070052565b506105206102002090565b60008083601f8401126142e557600080fd5b50813567ffffffffffffffff8111156142fd57600080fd5b6020830191508360208260051b850101111561431857600080fd5b9250929050565b6000806000806000806060878903121561433857600080fd5b863567ffffffffffffffff8082111561435057600080fd5b61435c8a838b016142d3565b9098509650602089013591508082111561437557600080fd5b6143818a838b016142d3565b9096509450604089013591508082111561439a57600080fd5b506143a789828a016142d3565b979a969950949750929593949250505056fea2646970667358221220ddbc99b1b4e4dcc95826fb34755df3c790bb81b098b5c3e093a88924455772d964736f6c63430008140033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106100365760003560e01c806387d9d0231461003b5780639e8945d214610063575b600080fd5b61004e61004936600461431f565b610079565b60405190151581526020015b60405180910390f35b61006b613cf3565b60405190815260200161005a565b600061064e7f236c54d6ae3745765605abfdd3d1533aaf27c583bbac66c43f5555e2b087db42610200527f0b2b13c749c19a0be311c0eec76abddbc0848e2454cc7323605a714256ef101a610220527f04659caf7b05471ba5ba85b1ab62267aa6c456836e625f169f7119d55b9462d2610240527f0ea63403692148d2ad22189a1e5420076312f4d46e62036a043a6b0b84d5b410610260527f0e6696d09d65fce1e42805be03fca1f14aea247281f688981f925e77d4ce2291610280527f0228f6cf8fe20c1e07e5b78bf8c41d50e55975a126d22a198d1e56acd4bbb3dd6102a0527f14685dafe340b1dec5eafcd5e7faddaf24f3781ddc53309cc25d0b42c00541dd6102c0527f0e651cff9447cb360198899b80fa23e89ec13bc94ff161729aa841d2b55ea5be6102e0527f16e9ef76cb68f2750eb0ee72382dd9911a982308d0ab10ef94dada13c382ae73610300527f22e404bc91350f3bc7daad1d1025113742436983c85eac5ab7b42221a181b81e610320527f0d9b29613037a5025655c82b143d2b7449c98f3aea358307c8529249cc54f3b9610340527f15b3c4c946ad1babfc4c03ff7c2423fd354af3a9305c499b7fb3aaebe2fee746610360527f08aa077804a60caf18621f96c63d7d1b532f44b13c273956f81b4c41f1f8f076610380527f01fcb7c6e22aab56eb8b61bae6f457411bd41bcc5f1e8311421a98ccee23b72e6103a0527f283344a1ab3e55ecfd904d0b8e9f4faea338df5a4ead2fa9a42f0e103da40abc6103c0527f223b37b83b9687512d322993edd70e508dd80adb10bcf7321a3cc8a44c2695216103e0527f1f67f0ba5f7e837bc680acb4e612ebd938ad35211aa6e05b96cad19e66b82d2d610400527f2820641a84d2e8298ac2ac42bd4b912c0c37f768ecc83d3a29e7c720763d15a1610420527f0353257957562270292a17860ca8e8827703f828f440ee004848b1e23fdf9de2610440527f305f4137fee253dff8b2bfe579038e8f25d5bd217865072af5d89fc8800ada24610460527f13a600154b369ff3237706d00948e465ee1c32c7a6d3e18bccd9c4a15910f2e5610480527f138aa24fbf4cdddc75114811b3d59040394c218ecef3eb46ef9bd646f7e537766104a0527f277fff1f80c409357e2d251d79f6e3fd2164b755ce69cfd72de5c690289df6626104c0527f25235588e28c70eea3e35531c80deac25cd9b53ea3f98993f120108bc7abf6706104e0527f0990e07a9b001048b947d0e5bd6157214c7359b771f01bf52bd771ba563a900e610500527f05e5fb090dd40914c8606d875e301167ae3047d684a02b44d9d36f1eaf43d0b4610520527f1d4656690b33299db5631401a282afab3e16c78ee2c9ad9efea628171dcbc6bc610540527f0ebda2ebe582f601f813ec1e3970d13ef1500c742a85cce9b7f190f333de03b0610560527f2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b6105c0527f04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d4729814686105e0527f1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac610600527f1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d610620527f0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89610640527f1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283610660527f02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf610680527f09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a66106a0527f2f4d347c7fb61daaadfff881e24f4b5dcfdc0d70a95bcb148168b90ef93e0007610580527f2322632465ba8e28cd0a4befd813ea85a972f4f6fa8e8603cf5d062dbcb140656105a0527f1e3c9fc98c118e4bc34f1f93d214a5d86898e980c40d8e2c180c6ada377a74676106c0527f2260a13535c35a15c173f5e5797d4b675b55d164a9995bfb7624971324bd84a86106e052600061070052565b613cb8565b7f08c379a000000000000000000000000000000000000000000000000000000000600052602060045280602452508060445260646000fd5b6020600052602080526020604052806060525080608052507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160a05260006020600060c0600060055afa610704576107047f6d6f6465787020707265636f6d70696c65206661696c656400000000000000006018610653565b5060005190565b805160005260208101516020525080604052506040816060600060075afa610758576107587f706f696e744d756c496e746f446573743a2065634d756c206661696c65640000601e610653565b50565b805160005260208101516020525080516040526020810151606052506040816080600060065afa610758576107587f706f696e74416464496e746f446573743a206563416464206661696c65640000601e610653565b80516000526020810151602052815160405260208201517f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47036060526040816080600060065afa610827576108277f706f696e7453756241737369676e3a206563416464206661696c656400000000601c610653565b5050565b80516000526020810151602052815160405260208201516060526040816080600060065afa610827576108277f706f696e7441646441737369676e3a206563416464206661696c656400000000601c610653565b80516000526020810151602052508060405250604060006060600060075afa815160405260208201516060526040826080600060065afa1680610827576108277f706f696e744d756c416e64416464496e746f44657374000000000000000000006016610653565b602081015180801561091f57507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470360209190910152565b825115610951576109517f706f696e744e65676174653a20696e76616c696420706f696e74000000000000601a610653565b505050565b6000610d4353610d84526064610d40206001610d43536064610d4020610d6452610d4452565b6002610d435360e01b610d84526048610d40207f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1690565b60043560048101356001811490507f1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602483013516610720526024359150600482013581602c8214169150507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476024830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476044840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161074052806107605250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476064830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476084840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161078052806107a05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760a4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760c4840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478485091416935050816107c052806107e05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760e4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610104840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161080052806108205250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610124830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610144840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161084052806108605250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610164830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610184840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161088052806108a05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476101a4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476101c4840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478485091416935050816108c052806108e05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476101e4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610204840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161090052806109205250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610224830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610244840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161094052806109605250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610264830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610284840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4784850914169350508161098052806109a05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476102a4830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476102c4840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd478485091416935050816109c052806109e05250507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016102e483013506610a00527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161030483013506610a20527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161032483013506610a40527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161034483013506610a60527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161036483013506610a80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161038483013506610aa0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103a483013506610ac0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103c483013506610ae0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016103e483013506610b00527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161040483013506610b20527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161042483013506610b40527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161044483013506610b60527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161046483013506610b80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161048483013506610ba0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104a483013506610bc0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104c483013506610be0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016104e483013506610c00527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161050483013506610c20527f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610524830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610544840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848509141693505081610c405280610c605250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610564830135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47610584840135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309837f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848509141693505081610c805280610ca0525050604435915060048201356107005160008114611b5e5782600483141692507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476024850135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476044860135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309857f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848509141695505081610cc05280610ce05250507f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476064850135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd476084860135067f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47828309857f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4760037f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848709087f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47848509141695505081610d005280610d20525050611b65565b8282151692505b505080610827576108277f6c6f616450726f6f663a2050726f6f6620697320696e76616c69640000000000601b610653565b611ba361072051610956565b611baf61074051610956565b611bbb61076051610956565b611bc761078051610956565b611bd36107a051610956565b611bdf6107c051610956565b611beb6107e051610956565b611bf761080051610956565b611c0361082051610956565b611c0d600061097c565b610f0052611c1d61088051610956565b611c296108a051610956565b611c33600161097c565b610de052611c41600261097c565b610e0052611c5161084051610956565b611c5d61086051610956565b611c67600361097c565b610f2052611c75600461097c565b610f4052611c856108c051610956565b611c916108e051610956565b611c9b600561097c565b610dc052611cab61090051610956565b611cb761092051610956565b611cc361094051610956565b611ccf61096051610956565b611cdb61098051610956565b611ce76109a051610956565b611cf36109c051610956565b611cff6109e051610956565b611d09600661097c565b80610fe052611d1c63010000008261068b565b6110605250611d2d610c0051610956565b611d39610a0051610956565b611d45610a2051610956565b611d51610a4051610956565b611d5d610a6051610956565b611d69610a8051610956565b611d75610aa051610956565b611d81610ac051610956565b611d8d610ae051610956565b611d99610b0051610956565b611da5610b2051610956565b611db1610b8051610956565b611dbd610bc051610956565b611dc9610be051610956565b611dd5610b4051610956565b611de1610b6051610956565b611ded610ba051610956565b611df9610c2051610956565b611e03600761097c565b610fa052611e13610c4051610956565b611e1f610c6051610956565b611e2b610c8051610956565b611e37610ca051610956565b611e41600861097c565b610fc052565b610dc0517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181820980610e20527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610e40527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610e60527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610e80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610ea0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209905080610ec0527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209610ee0525050610fe051611f84816000612115565b61102052611f958162ffffff612115565b61104052507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016107205161102051097f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610aa051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016120166122bf565b820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161204361259e565b820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181610c20510890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010361106051087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181610c0051099050818114610827576108277f696e76616c69642071756f7469656e74206576616c756174696f6e0000000000601b610653565b60006001821561214c57612149837f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb61068b565b90505b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000061219c63010000008761068b565b089150816121cf576121cf7f696e76616c69642076616e697368696e6720706f6c796e6f6d69616c00000000601c610653565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103850890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000016301000000820990506122917f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593efffffff8261068b565b90507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818309949350505050565b60007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610b2051610e6051099050610e0051610de05160007f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182610ac0510990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610a0051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182610ae0510990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610a2051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182610b00510990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610a4051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818509935050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082610a6051088309915050807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001039050611020517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610e8051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103830891505090565b6000610f2051610f40517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600183087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001828209915080610f60525080610f80527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182610b40510992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610b6051840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610ea051840992506126cc62ffffff7f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb61068b565b91507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103610fe05108915081611000527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610ec051611020510991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010384086110405190935091506127cc62ffffff8261068b565b610ee0517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184870909935050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001827f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010384089250505090565b612875818361020061070b565b612882818461024061087f565b61288f818561028061087f565b61289c81866102c061087f565b6128cc817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185850961030061087f565b6128fc817f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186850961034061087f565b6129086103808261082b565b61291881610a80516103c061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051610aa0510961294d82828461070b565b505050505050565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184850991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610dc051830991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000185860990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001867f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610e2051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181830891507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000184870990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001877f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000103820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610e4051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183089150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa0518209905061294d828261044061087f565b610e60517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610de051610fe05109610e00517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001838508087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001877f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160058809080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001887f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000160078809080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001897f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001600a8809080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018185099350505050611020517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610e8051830983089150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa0518209905080611340527f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610de051610e60510990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610b205182099050610de051610e00517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001857f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001837f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000186610ac0510908087f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001877f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187610ae05109080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001887f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001847f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000187610b005109080890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000181850993505050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051820990506130af6110808261054061070b565b506130bc611080826107b1565b5050505050565b50610b60517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610ea051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161100051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa05182096113805250610ba051610f20517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000190820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610b8051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610f80518208610f00519091508290807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180878309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180888309840892507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018082610be051098408925050507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610bc051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610f4051820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018183099150507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610f60518209610ea0517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019182039250820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000161100051820990507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610ec0516110205109820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610ee0516110405109820890507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa05182096113605250505050565b6110605180610900516110c052610920516110e0526134446110c08261094061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990506134796110c08261098061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990506134ae6110c0826109c061087f565b5050610a0051610a2051610a4051610a60516134cf81838587611100612868565b6134de81838587611100612955565b6134ed81838587611100612ba0565b506134fc8183856111006130c3565b5050506105c051611140526105e05161116052610f0051806135236111408261060061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990506135586111408261064061087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000182820990506108276111408261068061087f565b60016000806110c051611180526110e0516111a052610c0051613617565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051860991506135e4611180838561087f565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180855184098708905094509492505050565b61362861118061110061118061075b565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051850993507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610c205186098208905061368d8185610a006107406135ab565b915093506136a18185610a206107806135ab565b915093506136b58185610a406107c06135ab565b915093507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051850993508392507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610a60518609820890506137218185610aa06104006135ab565b915093506137358185610ac06104806135ab565b915093506137498185610ae06104c06135ab565b9150935061375d8185610b006105006135ab565b915093507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051850993508391507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610b80518609820890506137c98185610bc06105806135ab565b915093506137dd8185610be06106c06135ab565b61120052610fa0519094507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000019150840992507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610fc0518509611340510861384b6111c08261084061070b565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000183610b205109613910565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001610fa051870991507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f000000180610fc051840986086138db6111c0828661087f565b507f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018085518409880890509550959350505050565b613921818585610a80610800613878565b91509350613939818561138051610b40610880613878565b91509350613951818561136051610b606108c0613878565b91509350613966818584610ba0611140613878565b611220525050610fc0519250613987915061128090506111c061118061075b565b7f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001611200517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000018361122051090890506001611240526002611260526107586112408261124061070b565b610fc051610fe0517f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000017f1951441010b2b95a6e47a6075066a50a036f5ba978c050f2821df86636c0facb8209613a4b6112406112806107b1565b613a5a61128083610c4061087f565b613a8c6112807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001858409610c8061087f565b5050610c405161130052610c605161132052613aad61130082610c8061087f565b613ab86113006108e7565b6107005115613b05577f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001818209613af461128082610cc061087f565b613b0361130082610d0061087f565b505b50611280516000526112a0516020527f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c26040527f1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed6060527f090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b6080527f12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa60a0526113005160c0526113205160e0527f260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c1610100527f0118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b0610120527f04fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe4610140527f22febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e556101605260206000610180600060085afa80613c8357613c837f66696e616c50616972696e673a20707265636f6d70696c65206661696c7572656020610653565b50600051613cb657613cb67f66696e616c50616972696e673a2070616972696e67206661696c757265000000601d610653565b565b613cc06109b4565b613cc8611b97565b613cd0611e47565b613cd8613420565b613ce061358d565b613ce86139f1565b600160005260206000f35b60006142c87f236c54d6ae3745765605abfdd3d1533aaf27c583bbac66c43f5555e2b087db42610200527f0b2b13c749c19a0be311c0eec76abddbc0848e2454cc7323605a714256ef101a610220527f04659caf7b05471ba5ba85b1ab62267aa6c456836e625f169f7119d55b9462d2610240527f0ea63403692148d2ad22189a1e5420076312f4d46e62036a043a6b0b84d5b410610260527f0e6696d09d65fce1e42805be03fca1f14aea247281f688981f925e77d4ce2291610280527f0228f6cf8fe20c1e07e5b78bf8c41d50e55975a126d22a198d1e56acd4bbb3dd6102a0527f14685dafe340b1dec5eafcd5e7faddaf24f3781ddc53309cc25d0b42c00541dd6102c0527f0e651cff9447cb360198899b80fa23e89ec13bc94ff161729aa841d2b55ea5be6102e0527f16e9ef76cb68f2750eb0ee72382dd9911a982308d0ab10ef94dada13c382ae73610300527f22e404bc91350f3bc7daad1d1025113742436983c85eac5ab7b42221a181b81e610320527f0d9b29613037a5025655c82b143d2b7449c98f3aea358307c8529249cc54f3b9610340527f15b3c4c946ad1babfc4c03ff7c2423fd354af3a9305c499b7fb3aaebe2fee746610360527f08aa077804a60caf18621f96c63d7d1b532f44b13c273956f81b4c41f1f8f076610380527f01fcb7c6e22aab56eb8b61bae6f457411bd41bcc5f1e8311421a98ccee23b72e6103a0527f283344a1ab3e55ecfd904d0b8e9f4faea338df5a4ead2fa9a42f0e103da40abc6103c0527f223b37b83b9687512d322993edd70e508dd80adb10bcf7321a3cc8a44c2695216103e0527f1f67f0ba5f7e837bc680acb4e612ebd938ad35211aa6e05b96cad19e66b82d2d610400527f2820641a84d2e8298ac2ac42bd4b912c0c37f768ecc83d3a29e7c720763d15a1610420527f0353257957562270292a17860ca8e8827703f828f440ee004848b1e23fdf9de2610440527f305f4137fee253dff8b2bfe579038e8f25d5bd217865072af5d89fc8800ada24610460527f13a600154b369ff3237706d00948e465ee1c32c7a6d3e18bccd9c4a15910f2e5610480527f138aa24fbf4cdddc75114811b3d59040394c218ecef3eb46ef9bd646f7e537766104a0527f277fff1f80c409357e2d251d79f6e3fd2164b755ce69cfd72de5c690289df6626104c0527f25235588e28c70eea3e35531c80deac25cd9b53ea3f98993f120108bc7abf6706104e0527f0990e07a9b001048b947d0e5bd6157214c7359b771f01bf52bd771ba563a900e610500527f05e5fb090dd40914c8606d875e301167ae3047d684a02b44d9d36f1eaf43d0b4610520527f1d4656690b33299db5631401a282afab3e16c78ee2c9ad9efea628171dcbc6bc610540527f0ebda2ebe582f601f813ec1e3970d13ef1500c742a85cce9b7f190f333de03b0610560527f2c513ed74d9d57a5ec901e074032741036353a2c4513422e96e7b53b302d765b6105c0527f04dd964427e430f16004076d708c0cb21e225056cc1d57418cfbd3d4729814686105e0527f1ea83e5e65c6f8068f4677e2911678cf329b28259642a32db1f14b8347828aac610600527f1d22bc884a2da4962a893ba8de13f57aaeb785ed52c5e686994839cab8f7475d610620527f0b2e7212d0d9cff26d0bdf3d79b2cac029a25dfeb1cafdf49e2349d7db348d89610640527f1301f9b252419ea240eb67fda720ca0b16d92364027285f95e9b1349490fa283610660527f02f7b99fdfa5b418548c2d777785820e02383cfc87e7085e280a375a358153bf610680527f09d004fe08dc4d19c382df36fad22ef676185663543703e6a4b40203e50fd8a66106a0527f2f4d347c7fb61daaadfff881e24f4b5dcfdc0d70a95bcb148168b90ef93e0007610580527f2322632465ba8e28cd0a4befd813ea85a972f4f6fa8e8603cf5d062dbcb140656105a0527f1e3c9fc98c118e4bc34f1f93d214a5d86898e980c40d8e2c180c6ada377a74676106c0527f2260a13535c35a15c173f5e5797d4b675b55d164a9995bfb7624971324bd84a86106e052600061070052565b506105206102002090565b60008083601f8401126142e557600080fd5b50813567ffffffffffffffff8111156142fd57600080fd5b6020830191508360208260051b850101111561431857600080fd5b9250929050565b6000806000806000806060878903121561433857600080fd5b863567ffffffffffffffff8082111561435057600080fd5b61435c8a838b016142d3565b9098509650602089013591508082111561437557600080fd5b6143818a838b016142d3565b9096509450604089013591508082111561439a57600080fd5b506143a789828a016142d3565b979a969950949750929593949250505056fea2646970667358221220ddbc99b1b4e4dcc95826fb34755df3c790bb81b098b5c3e093a88924455772d964736f6c63430008140033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ZKSYNC | 100.00% | $3,118.13 | 0.00007 | $0.218269 |
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.