From 5f2c0cf785eb162547a370ce5e6bcbfb24eb09e7 Mon Sep 17 00:00:00 2001 From: HrikB <23041116+HrikB@users.noreply.github.com> Date: Wed, 16 Apr 2025 03:02:33 -0400 Subject: [PATCH 1/8] feat: v1.0.1 verifier (#2) * feat: v1.0.1 verifier * test: add tests * chore: remove solady * feat: add consistency check workflow --- foundry.toml | 6 +- remappings.txt | 1 + src/Halo2Verifier.sol | 2035 ---------------- src/v1.0.1/Halo2Verifier.sol | 2045 +++++++++++++++++ src/{ => v1.0.1}/OpenVmHalo2Verifier.sol | 94 +- .../interfaces/IOpenVmHalo2Verifier.sol | 6 +- test/OpenVmHalo2Verifier.t.sol | 135 -- test/helpers/LibString.sol | 1628 +++++++++++++ test/v1.0.1/OpenVmHalo2Verifer.t.sol | 109 + test/v1.0.1/evm.proof | 9 + 10 files changed, 3847 insertions(+), 2221 deletions(-) create mode 100644 remappings.txt delete mode 100644 src/Halo2Verifier.sol create mode 100644 src/v1.0.1/Halo2Verifier.sol rename src/{ => v1.0.1}/OpenVmHalo2Verifier.sol (63%) rename src/{ => v1.0.1}/interfaces/IOpenVmHalo2Verifier.sol (54%) delete mode 100644 test/OpenVmHalo2Verifier.t.sol create mode 100644 test/helpers/LibString.sol create mode 100644 test/v1.0.1/OpenVmHalo2Verifer.t.sol create mode 100644 test/v1.0.1/evm.proof diff --git a/foundry.toml b/foundry.toml index 52753ce..ebbf0b6 100644 --- a/foundry.toml +++ b/foundry.toml @@ -6,17 +6,19 @@ verbosity = 2 solc = "0.8.19" optimizer = true optimizer_runs = 100000 -evm_version = "cancun" +evm_version = "paris" show_progress = true +fs_permissions = [{ access = "read", path = "./test/v1.0.1/evm.proof"}] [profile.default.optimizer_details] constantOptimizer = false yul = false [fuzz] - runs = 1000 + runs = 256 [fmt] + sort_imports = true bracket_spacing = true int_types = "long" line_length = 120 diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 0000000..feaba2d --- /dev/null +++ b/remappings.txt @@ -0,0 +1 @@ +forge-std/=lib/forge-std/src/ diff --git a/src/Halo2Verifier.sol b/src/Halo2Verifier.sol deleted file mode 100644 index f2163cd..0000000 --- a/src/Halo2Verifier.sol +++ /dev/null @@ -1,2035 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.19; - -contract Halo2Verifier { - fallback(bytes calldata) external returns (bytes memory) { - assembly ("memory-safe") { - // Enforce that Solidity memory layout is respected - let data := mload(0x40) - if iszero(eq(data, 0x80)) { revert(0, 0) } - - let success := true - let f_p := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 - let f_q := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 - function validate_ec_point(x, y) -> valid { - { - let x_lt_p := lt(x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) - let y_lt_p := lt(y, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) - valid := and(x_lt_p, y_lt_p) - } - { - let y_square := mulmod(y, y, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) - let x_square := mulmod(x, x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) - let x_cube := - mulmod(x_square, x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) - let x_cube_plus_3 := - addmod(x_cube, 3, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) - let is_affine := eq(x_cube_plus_3, y_square) - valid := and(valid, is_affine) - } - } - mstore(0xa0, mod(calldataload(0x0), f_q)) - mstore(0xc0, mod(calldataload(0x20), f_q)) - mstore(0xe0, mod(calldataload(0x40), f_q)) - mstore(0x100, mod(calldataload(0x60), f_q)) - mstore(0x120, mod(calldataload(0x80), f_q)) - mstore(0x140, mod(calldataload(0xa0), f_q)) - mstore(0x160, mod(calldataload(0xc0), f_q)) - mstore(0x180, mod(calldataload(0xe0), f_q)) - mstore(0x1a0, mod(calldataload(0x100), f_q)) - mstore(0x1c0, mod(calldataload(0x120), f_q)) - mstore(0x1e0, mod(calldataload(0x140), f_q)) - mstore(0x200, mod(calldataload(0x160), f_q)) - mstore(0x220, mod(calldataload(0x180), f_q)) - mstore(0x240, mod(calldataload(0x1a0), f_q)) - mstore(0x260, mod(calldataload(0x1c0), f_q)) - mstore(0x280, mod(calldataload(0x1e0), f_q)) - mstore(0x2a0, mod(calldataload(0x200), f_q)) - mstore(0x2c0, mod(calldataload(0x220), f_q)) - mstore(0x2e0, mod(calldataload(0x240), f_q)) - mstore(0x300, mod(calldataload(0x260), f_q)) - mstore(0x320, mod(calldataload(0x280), f_q)) - mstore(0x340, mod(calldataload(0x2a0), f_q)) - mstore(0x360, mod(calldataload(0x2c0), f_q)) - mstore(0x380, mod(calldataload(0x2e0), f_q)) - mstore(0x3a0, mod(calldataload(0x300), f_q)) - mstore(0x3c0, mod(calldataload(0x320), f_q)) - mstore(0x3e0, mod(calldataload(0x340), f_q)) - mstore(0x400, mod(calldataload(0x360), f_q)) - mstore(0x420, mod(calldataload(0x380), f_q)) - mstore(0x440, mod(calldataload(0x3a0), f_q)) - mstore(0x460, mod(calldataload(0x3c0), f_q)) - mstore(0x480, mod(calldataload(0x3e0), f_q)) - mstore(0x4a0, mod(calldataload(0x400), f_q)) - mstore(0x4c0, mod(calldataload(0x420), f_q)) - mstore(0x4e0, mod(calldataload(0x440), f_q)) - mstore(0x500, mod(calldataload(0x460), f_q)) - mstore(0x520, mod(calldataload(0x480), f_q)) - mstore(0x540, mod(calldataload(0x4a0), f_q)) - mstore(0x560, mod(calldataload(0x4c0), f_q)) - mstore(0x580, mod(calldataload(0x4e0), f_q)) - mstore(0x5a0, mod(calldataload(0x500), f_q)) - mstore(0x5c0, mod(calldataload(0x520), f_q)) - mstore(0x5e0, mod(calldataload(0x540), f_q)) - mstore(0x600, mod(calldataload(0x560), f_q)) - mstore(0x620, mod(calldataload(0x580), f_q)) - mstore(0x640, mod(calldataload(0x5a0), f_q)) - mstore(0x80, 15110407967775938799674765595297831082501283201004702666478789912569454392502) - - { - let x := calldataload(0x5c0) - mstore(0x660, x) - let y := calldataload(0x5e0) - mstore(0x680, y) - success := and(validate_ec_point(x, y), success) - } - mstore(0x6a0, keccak256(0x80, 1568)) - { - let hash := mload(0x6a0) - mstore(0x6c0, mod(hash, f_q)) - mstore(0x6e0, hash) - } - - { - let x := calldataload(0x600) - mstore(0x700, x) - let y := calldataload(0x620) - mstore(0x720, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x640) - mstore(0x740, x) - let y := calldataload(0x660) - mstore(0x760, y) - success := and(validate_ec_point(x, y), success) - } - mstore(0x780, keccak256(0x6e0, 160)) - { - let hash := mload(0x780) - mstore(0x7a0, mod(hash, f_q)) - mstore(0x7c0, hash) - } - mstore8(2016, 1) - mstore(0x7e0, keccak256(0x7c0, 33)) - { - let hash := mload(0x7e0) - mstore(0x800, mod(hash, f_q)) - mstore(0x820, hash) - } - - { - let x := calldataload(0x680) - mstore(0x840, x) - let y := calldataload(0x6a0) - mstore(0x860, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x6c0) - mstore(0x880, x) - let y := calldataload(0x6e0) - mstore(0x8a0, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x700) - mstore(0x8c0, x) - let y := calldataload(0x720) - mstore(0x8e0, y) - success := and(validate_ec_point(x, y), success) - } - mstore(0x900, keccak256(0x820, 224)) - { - let hash := mload(0x900) - mstore(0x920, mod(hash, f_q)) - mstore(0x940, hash) - } - - { - let x := calldataload(0x740) - mstore(0x960, x) - let y := calldataload(0x760) - mstore(0x980, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x780) - mstore(0x9a0, x) - let y := calldataload(0x7a0) - mstore(0x9c0, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x7c0) - mstore(0x9e0, x) - let y := calldataload(0x7e0) - mstore(0xa00, y) - success := and(validate_ec_point(x, y), success) - } - - { - let x := calldataload(0x800) - mstore(0xa20, x) - let y := calldataload(0x820) - mstore(0xa40, y) - success := and(validate_ec_point(x, y), success) - } - mstore(0xa60, keccak256(0x940, 288)) - { - let hash := mload(0xa60) - mstore(0xa80, mod(hash, f_q)) - mstore(0xaa0, hash) - } - mstore(0xac0, mod(calldataload(0x840), f_q)) - mstore(0xae0, mod(calldataload(0x860), f_q)) - mstore(0xb00, mod(calldataload(0x880), f_q)) - mstore(0xb20, mod(calldataload(0x8a0), f_q)) - mstore(0xb40, mod(calldataload(0x8c0), f_q)) - mstore(0xb60, mod(calldataload(0x8e0), f_q)) - mstore(0xb80, mod(calldataload(0x900), f_q)) - mstore(0xba0, mod(calldataload(0x920), f_q)) - mstore(0xbc0, mod(calldataload(0x940), f_q)) - mstore(0xbe0, mod(calldataload(0x960), f_q)) - mstore(0xc00, mod(calldataload(0x980), f_q)) - mstore(0xc20, mod(calldataload(0x9a0), f_q)) - mstore(0xc40, mod(calldataload(0x9c0), f_q)) - mstore(0xc60, mod(calldataload(0x9e0), f_q)) - mstore(0xc80, mod(calldataload(0xa00), f_q)) - mstore(0xca0, mod(calldataload(0xa20), f_q)) - mstore(0xcc0, mod(calldataload(0xa40), f_q)) - mstore(0xce0, mod(calldataload(0xa60), f_q)) - mstore(0xd00, mod(calldataload(0xa80), f_q)) - mstore(0xd20, keccak256(0xaa0, 640)) - { - let hash := mload(0xd20) - mstore(0xd40, mod(hash, f_q)) - mstore(0xd60, hash) - } - mstore8(3456, 1) - mstore(0xd80, keccak256(0xd60, 33)) - { - let hash := mload(0xd80) - mstore(0xda0, mod(hash, f_q)) - mstore(0xdc0, hash) - } - - { - let x := calldataload(0xaa0) - mstore(0xde0, x) - let y := calldataload(0xac0) - mstore(0xe00, y) - success := and(validate_ec_point(x, y), success) - } - mstore(0xe20, keccak256(0xdc0, 96)) - { - let hash := mload(0xe20) - mstore(0xe40, mod(hash, f_q)) - mstore(0xe60, hash) - } - - { - let x := calldataload(0xae0) - mstore(0xe80, x) - let y := calldataload(0xb00) - mstore(0xea0, y) - success := and(validate_ec_point(x, y), success) - } - { - let x := mload(0xa0) - x := add(x, shl(88, mload(0xc0))) - x := add(x, shl(176, mload(0xe0))) - mstore(3776, x) - let y := mload(0x100) - y := add(y, shl(88, mload(0x120))) - y := add(y, shl(176, mload(0x140))) - mstore(3808, y) - - success := and(validate_ec_point(x, y), success) - } - { - let x := mload(0x160) - x := add(x, shl(88, mload(0x180))) - x := add(x, shl(176, mload(0x1a0))) - mstore(3840, x) - let y := mload(0x1c0) - y := add(y, shl(88, mload(0x1e0))) - y := add(y, shl(176, mload(0x200))) - mstore(3872, y) - - success := and(validate_ec_point(x, y), success) - } - mstore(0xf40, mulmod(mload(0xa80), mload(0xa80), f_q)) - mstore(0xf60, mulmod(mload(0xf40), mload(0xf40), f_q)) - mstore(0xf80, mulmod(mload(0xf60), mload(0xf60), f_q)) - mstore(0xfa0, mulmod(mload(0xf80), mload(0xf80), f_q)) - mstore(0xfc0, mulmod(mload(0xfa0), mload(0xfa0), f_q)) - mstore(0xfe0, mulmod(mload(0xfc0), mload(0xfc0), f_q)) - mstore(0x1000, mulmod(mload(0xfe0), mload(0xfe0), f_q)) - mstore(0x1020, mulmod(mload(0x1000), mload(0x1000), f_q)) - mstore(0x1040, mulmod(mload(0x1020), mload(0x1020), f_q)) - mstore(0x1060, mulmod(mload(0x1040), mload(0x1040), f_q)) - mstore(0x1080, mulmod(mload(0x1060), mload(0x1060), f_q)) - mstore(0x10a0, mulmod(mload(0x1080), mload(0x1080), f_q)) - mstore(0x10c0, mulmod(mload(0x10a0), mload(0x10a0), f_q)) - mstore(0x10e0, mulmod(mload(0x10c0), mload(0x10c0), f_q)) - mstore(0x1100, mulmod(mload(0x10e0), mload(0x10e0), f_q)) - mstore(0x1120, mulmod(mload(0x1100), mload(0x1100), f_q)) - mstore(0x1140, mulmod(mload(0x1120), mload(0x1120), f_q)) - mstore(0x1160, mulmod(mload(0x1140), mload(0x1140), f_q)) - mstore(0x1180, mulmod(mload(0x1160), mload(0x1160), f_q)) - mstore(0x11a0, mulmod(mload(0x1180), mload(0x1180), f_q)) - mstore(0x11c0, mulmod(mload(0x11a0), mload(0x11a0), f_q)) - mstore(0x11e0, mulmod(mload(0x11c0), mload(0x11c0), f_q)) - mstore(0x1200, mulmod(mload(0x11e0), mload(0x11e0), f_q)) - mstore( - 0x1220, - addmod( - mload(0x1200), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q - ) - ) - mstore( - 0x1240, - mulmod( - mload(0x1220), 21888240262557392955334514970720457388010314637169927192662615958087340972065, f_q - ) - ) - mstore( - 0x1260, - mulmod(mload(0x1240), 4506835738822104338668100540817374747935106310012997856968187171738630203507, f_q) - ) - mstore( - 0x1280, - addmod(mload(0xa80), 17381407133017170883578305204439900340613258090403036486730017014837178292110, f_q) - ) - mstore( - 0x12a0, - mulmod( - mload(0x1240), 21710372849001950800533397158415938114909991150039389063546734567764856596059, f_q - ) - ) - mstore( - 0x12c0, - addmod(mload(0xa80), 177870022837324421713008586841336973638373250376645280151469618810951899558, f_q) - ) - mstore( - 0x12e0, - mulmod(mload(0x1240), 1887003188133998471169152042388914354640772748308168868301418279904560637395, f_q) - ) - mstore( - 0x1300, - addmod(mload(0xa80), 20001239683705276751077253702868360733907591652107865475396785906671247858222, f_q) - ) - mstore( - 0x1320, - mulmod(mload(0x1240), 2785514556381676080176937710880804108647911392478702105860685610379369825016, f_q) - ) - mstore( - 0x1340, - addmod(mload(0xa80), 19102728315457599142069468034376470979900453007937332237837518576196438670601, f_q) - ) - mstore( - 0x1360, - mulmod( - mload(0x1240), 14655294445420895451632927078981340937842238432098198055057679026789553137428, f_q - ) - ) - mstore( - 0x1380, - addmod(mload(0xa80), 7232948426418379770613478666275934150706125968317836288640525159786255358189, f_q) - ) - mstore( - 0x13a0, - mulmod(mload(0x1240), 8734126352828345679573237859165904705806588461301144420590422589042130041188, f_q) - ) - mstore( - 0x13c0, - addmod(mload(0xa80), 13154116519010929542673167886091370382741775939114889923107781597533678454429, f_q) - ) - mstore( - 0x13e0, - mulmod(mload(0x1240), 9741553891420464328295280489650144566903017206473301385034033384879943874347, f_q) - ) - mstore( - 0x1400, - addmod(mload(0xa80), 12146688980418810893951125255607130521645347193942732958664170801695864621270, f_q) - ) - mstore(0x1420, mulmod(mload(0x1240), 1, f_q)) - mstore( - 0x1440, - addmod(mload(0xa80), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q) - ) - mstore( - 0x1460, - mulmod(mload(0x1240), 8374374965308410102411073611984011876711565317741801500439755773472076597347, f_q) - ) - mstore( - 0x1480, - addmod(mload(0xa80), 13513867906530865119835332133273263211836799082674232843258448413103731898270, f_q) - ) - mstore( - 0x14a0, - mulmod( - mload(0x1240), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q - ) - ) - mstore( - 0x14c0, - addmod(mload(0xa80), 10676941854703594198666993839846402519342119846958189386823924046696287912227, f_q) - ) - mstore( - 0x14e0, - mulmod(mload(0x1240), 3615478808282855240548287271348143516886772452944084747768312988864436725401, f_q) - ) - mstore( - 0x1500, - addmod(mload(0xa80), 18272764063556419981698118473909131571661591947471949595929891197711371770216, f_q) - ) - mstore( - 0x1520, - mulmod(mload(0x1240), 1426404432721484388505361748317961535523355871255605456897797744433766488507, f_q) - ) - mstore( - 0x1540, - addmod(mload(0xa80), 20461838439117790833741043996939313553025008529160428886800406442142042007110, f_q) - ) - mstore( - 0x1560, - mulmod(mload(0x1240), 216092043779272773661818549620449970334216366264741118684015851799902419467, f_q) - ) - mstore( - 0x1580, - addmod(mload(0xa80), 21672150828060002448584587195636825118214148034151293225014188334775906076150, f_q) - ) - mstore( - 0x15a0, - mulmod( - mload(0x1240), 12619617507853212586156872920672483948819476989779550311307282715684870266992, f_q - ) - ) - mstore( - 0x15c0, - addmod(mload(0xa80), 9268625363986062636089532824584791139728887410636484032390921470890938228625, f_q) - ) - mstore( - 0x15e0, - mulmod( - mload(0x1240), 18610195890048912503953886742825279624920778288956610528523679659246523534888, f_q - ) - ) - mstore( - 0x1600, - addmod(mload(0xa80), 3278046981790362718292519002431995463627586111459423815174524527329284960729, f_q) - ) - mstore( - 0x1620, - mulmod( - mload(0x1240), 19032961837237948602743626455740240236231119053033140765040043513661803148152, f_q - ) - ) - mstore( - 0x1640, - addmod(mload(0xa80), 2855281034601326619502779289517034852317245347382893578658160672914005347465, f_q) - ) - mstore( - 0x1660, - mulmod( - mload(0x1240), 14875928112196239563830800280253496262679717528621719058794366823499719730250, f_q - ) - ) - mstore( - 0x1680, - addmod(mload(0xa80), 7012314759643035658415605465003778825868646871794315284903837363076088765367, f_q) - ) - mstore( - 0x16a0, - mulmod(mload(0x1240), 915149353520972163646494413843788069594022902357002628455555785223409501882, f_q) - ) - mstore( - 0x16c0, - addmod(mload(0xa80), 20973093518318303058599911331413487018954341498059031715242648401352398993735, f_q) - ) - mstore( - 0x16e0, - mulmod(mload(0x1240), 5522161504810533295870699551020523636289972223872138525048055197429246400245, f_q) - ) - mstore( - 0x1700, - addmod(mload(0xa80), 16366081367028741926375706194236751452258392176543895818650148989146562095372, f_q) - ) - mstore( - 0x1720, - mulmod(mload(0x1240), 3766081621734395783232337525162072736827576297943013392955872170138036189193, f_q) - ) - mstore( - 0x1740, - addmod(mload(0xa80), 18122161250104879439014068220095202351720788102473020950742332016437772306424, f_q) - ) - mstore( - 0x1760, - mulmod(mload(0x1240), 9100833993744738801214480881117348002768153232283708533639316963648253510584, f_q) - ) - mstore( - 0x1780, - addmod(mload(0xa80), 12787408878094536421031924864139927085780211168132325810058887222927554985033, f_q) - ) - mstore( - 0x17a0, - mulmod(mload(0x1240), 4245441013247250116003069945606352967193023389718465410501109428393342802981, f_q) - ) - mstore( - 0x17c0, - addmod(mload(0xa80), 17642801858592025106243335799650922121355341010697568933197094758182465692636, f_q) - ) - mstore( - 0x17e0, - mulmod(mload(0x1240), 6132660129994545119218258312491950835441607143741804980633129304664017206141, f_q) - ) - mstore( - 0x1800, - addmod(mload(0xa80), 15755582741844730103028147432765324253106757256674229363065074881911791289476, f_q) - ) - mstore( - 0x1820, - mulmod(mload(0x1240), 5854133144571823792863860130267644613802765696134002830362054821530146160770, f_q) - ) - mstore( - 0x1840, - addmod(mload(0xa80), 16034109727267451429382545614989630474745598704282031513336149365045662334847, f_q) - ) - mstore( - 0x1860, - mulmod(mload(0x1240), 515148244606945972463850631189471072103916690263705052318085725998468254533, f_q) - ) - mstore( - 0x1880, - addmod(mload(0xa80), 21373094627232329249782555114067804016444447710152329291380118460577340241084, f_q) - ) - mstore( - 0x18a0, - mulmod(mload(0x1240), 5980488956150442207659150513163747165544364597008566989111579977672498964212, f_q) - ) - mstore( - 0x18c0, - addmod(mload(0xa80), 15907753915688833014587255232093527923003999803407467354586624208903309531405, f_q) - ) - mstore( - 0x18e0, - mulmod(mload(0x1240), 5223738580615264174925218065001555728265216895679471490312087802465486318994, f_q) - ) - mstore( - 0x1900, - addmod(mload(0xa80), 16664504291224011047321187680255719360283147504736562853386116384110322176623, f_q) - ) - mstore( - 0x1920, - mulmod( - mload(0x1240), 14557038802599140430182096396825290815503940951075961210638273254419942783582, f_q - ) - ) - mstore( - 0x1940, - addmod(mload(0xa80), 7331204069240134792064309348431984273044423449340073133059930932155865712035, f_q) - ) - mstore( - 0x1960, - mulmod( - mload(0x1240), 16976236069879939850923145256911338076234942200101755618884183331004076579046, f_q - ) - ) - mstore( - 0x1980, - addmod(mload(0xa80), 4912006801959335371323260488345937012313422200314278724814020855571731916571, f_q) - ) - mstore( - 0x19a0, - mulmod( - mload(0x1240), 13553911191894110065493137367144919847521088405945523452288398666974237857208, f_q - ) - ) - mstore( - 0x19c0, - addmod(mload(0xa80), 8334331679945165156753268378112355241027275994470510891409805519601570638409, f_q) - ) - mstore( - 0x19e0, - mulmod( - mload(0x1240), 12222687719926148270818604386979005738180875192307070468454582955273533101023, f_q - ) - ) - mstore( - 0x1a00, - addmod(mload(0xa80), 9665555151913126951427801358278269350367489208108963875243621231302275394594, f_q) - ) - mstore( - 0x1a20, - mulmod(mload(0x1240), 9697063347556872083384215826199993067635178715531258559890418744774301211662, f_q) - ) - mstore( - 0x1a40, - addmod(mload(0xa80), 12191179524282403138862189919057282020913185684884775783807785441801507283955, f_q) - ) - mstore( - 0x1a60, - mulmod( - mload(0x1240), 13783318220968413117070077848579881425001701814458176881760898225529300547844, f_q - ) - ) - mstore( - 0x1a80, - addmod(mload(0xa80), 8104924650870862105176327896677393663546662585957857461937305961046507947773, f_q) - ) - mstore( - 0x1aa0, - mulmod( - mload(0x1240), 10807735674816066981985242612061336605021639643453679977988966079770672437131, f_q - ) - ) - mstore( - 0x1ac0, - addmod(mload(0xa80), 11080507197023208240261163133195938483526724756962354365709238106805136058486, f_q) - ) - mstore( - 0x1ae0, - mulmod( - mload(0x1240), 15487660954688013862248478071816391715224351867581977083810729441220383572585, f_q - ) - ) - mstore( - 0x1b00, - addmod(mload(0xa80), 6400581917151261359997927673440883373324012532834057259887474745355424923032, f_q) - ) - mstore( - 0x1b20, - mulmod( - mload(0x1240), 12459868075641381822485233712013080087763946065665469821362892189399541605692, f_q - ) - ) - mstore( - 0x1b40, - addmod(mload(0xa80), 9428374796197893399761172033244195000784418334750564522335311997176266889925, f_q) - ) - mstore( - 0x1b60, - mulmod( - mload(0x1240), 12562571400845953139885120066983392294851269266041089223701347829190217414825, f_q - ) - ) - mstore( - 0x1b80, - addmod(mload(0xa80), 9325671470993322082361285678273882793697095134374945119996856357385591080792, f_q) - ) - mstore( - 0x1ba0, - mulmod( - mload(0x1240), 16038300751658239075779628684257016433412502747804121525056508685985277092575, f_q - ) - ) - mstore( - 0x1bc0, - addmod(mload(0xa80), 5849942120181036146466777061000258655135861652611912818641695500590531403042, f_q) - ) - mstore( - 0x1be0, - mulmod( - mload(0x1240), 17665522928519859765452767154433594409738037332395989540221744312194874941704, f_q - ) - ) - mstore( - 0x1c00, - addmod(mload(0xa80), 4222719943319415456793638590823680678810327068020044803476459874380933553913, f_q) - ) - mstore( - 0x1c20, - mulmod(mload(0x1240), 6955697244493336113861667751840378876927906302623587437721024018233754910398, f_q) - ) - mstore( - 0x1c40, - addmod(mload(0xa80), 14932545627345939108384737993416896211620458097792446905977180168342053585219, f_q) - ) - mstore( - 0x1c60, - mulmod(mload(0x1240), 1918679275621049296283934091410967415474987212511681231948800935495808101054, f_q) - ) - mstore( - 0x1c80, - addmod(mload(0xa80), 19969563596218225925962471653846307673073377187904353111749403251080000394563, f_q) - ) - mstore( - 0x1ca0, - mulmod( - mload(0x1240), 13498745591877810872211159461644682954739332524336278910448604883789771736885, f_q - ) - ) - mstore( - 0x1cc0, - addmod(mload(0xa80), 8389497279961464350035246283612592133809031876079755433249599302786036758732, f_q) - ) - mstore( - 0x1ce0, - mulmod(mload(0x1240), 6604851689411953560355663038203889299997924520355363678860500374111951937637, f_q) - ) - mstore( - 0x1d00, - addmod(mload(0xa80), 15283391182427321661890742707053385788550439880060670664837703812463856557980, f_q) - ) - mstore( - 0x1d20, - mulmod( - mload(0x1240), 20345677989844117909528750049476969581182118546166966482506114734614108237981, f_q - ) - ) - mstore( - 0x1d40, - addmod(mload(0xa80), 1542564881995157312717655695780305507366245854249067861192089451961700257636, f_q) - ) - mstore( - 0x1d60, - mulmod( - mload(0x1240), 11244009323710436498447061620026171700033960328162115124806024297270121927878, f_q - ) - ) - mstore( - 0x1d80, - addmod(mload(0xa80), 10644233548128838723799344125231103388514404072253919218892179889305686567739, f_q) - ) - mstore( - 0x1da0, - mulmod(mload(0x1240), 790608022292213379425324383664216541739009722347092850716054055768832299157, f_q) - ) - mstore( - 0x1dc0, - addmod(mload(0xa80), 21097634849547061842821081361593058546809354678068941492982150130806976196460, f_q) - ) - mstore( - 0x1de0, - mulmod( - mload(0x1240), 13894403229372218245111098554468346933152618215322268934207074514797092422856, f_q - ) - ) - mstore( - 0x1e00, - addmod(mload(0xa80), 7993839642467056977135307190788928155395746185093765409491129671778716072761, f_q) - ) - mstore( - 0x1e20, - mulmod(mload(0x1240), 5289443209903185443361862148540090689648485914368835830972895623576469023722, f_q) - ) - mstore( - 0x1e40, - addmod(mload(0xa80), 16598799661936089778884543596717184398899878486047198512725308562999339471895, f_q) - ) - mstore( - 0x1e60, - mulmod( - mload(0x1240), 19715528266218439644661892824912275086257866064695767122686506494361332681035, f_q - ) - ) - mstore( - 0x1e80, - addmod(mload(0xa80), 2172714605620835577584512920345000002290498335720267221011697692214475814582, f_q) - ) - mstore( - 0x1ea0, - mulmod( - mload(0x1240), 15161189183906287273290738379431332336600234154579306802151507052820126345529, f_q - ) - ) - mstore( - 0x1ec0, - addmod(mload(0xa80), 6727053687932987948955667365825942751948130245836727541546697133755682150088, f_q) - ) - mstore( - 0x1ee0, - mulmod( - mload(0x1240), 12456424076401232823832128238027368612265814450984711658287606686035629293382, f_q - ) - ) - mstore( - 0x1f00, - addmod(mload(0xa80), 9431818795438042398414277507229906476282549949431322685410597500540179202235, f_q) - ) - mstore( - 0x1f20, - mulmod(mload(0x1240), 557567375339945239933617516585967620814823575807691402619711360028043331811, f_q) - ) - mstore( - 0x1f40, - addmod(mload(0xa80), 21330675496499329982312788228671307467733540824608342941078492826547765163806, f_q) - ) - mstore( - 0x1f60, - mulmod(mload(0x1240), 3675353143102618619098608207619541954347747556257261634661810167705798540391, f_q) - ) - mstore( - 0x1f80, - addmod(mload(0xa80), 18212889728736656603147797537637733134200616844158772709036394018870009955226, f_q) - ) - { - let prod := mload(0x1280) - - prod := mulmod(mload(0x12c0), prod, f_q) - mstore(0x1fa0, prod) - - prod := mulmod(mload(0x1300), prod, f_q) - mstore(0x1fc0, prod) - - prod := mulmod(mload(0x1340), prod, f_q) - mstore(0x1fe0, prod) - - prod := mulmod(mload(0x1380), prod, f_q) - mstore(0x2000, prod) - - prod := mulmod(mload(0x13c0), prod, f_q) - mstore(0x2020, prod) - - prod := mulmod(mload(0x1400), prod, f_q) - mstore(0x2040, prod) - - prod := mulmod(mload(0x1440), prod, f_q) - mstore(0x2060, prod) - - prod := mulmod(mload(0x1480), prod, f_q) - mstore(0x2080, prod) - - prod := mulmod(mload(0x14c0), prod, f_q) - mstore(0x20a0, prod) - - prod := mulmod(mload(0x1500), prod, f_q) - mstore(0x20c0, prod) - - prod := mulmod(mload(0x1540), prod, f_q) - mstore(0x20e0, prod) - - prod := mulmod(mload(0x1580), prod, f_q) - mstore(0x2100, prod) - - prod := mulmod(mload(0x15c0), prod, f_q) - mstore(0x2120, prod) - - prod := mulmod(mload(0x1600), prod, f_q) - mstore(0x2140, prod) - - prod := mulmod(mload(0x1640), prod, f_q) - mstore(0x2160, prod) - - prod := mulmod(mload(0x1680), prod, f_q) - mstore(0x2180, prod) - - prod := mulmod(mload(0x16c0), prod, f_q) - mstore(0x21a0, prod) - - prod := mulmod(mload(0x1700), prod, f_q) - mstore(0x21c0, prod) - - prod := mulmod(mload(0x1740), prod, f_q) - mstore(0x21e0, prod) - - prod := mulmod(mload(0x1780), prod, f_q) - mstore(0x2200, prod) - - prod := mulmod(mload(0x17c0), prod, f_q) - mstore(0x2220, prod) - - prod := mulmod(mload(0x1800), prod, f_q) - mstore(0x2240, prod) - - prod := mulmod(mload(0x1840), prod, f_q) - mstore(0x2260, prod) - - prod := mulmod(mload(0x1880), prod, f_q) - mstore(0x2280, prod) - - prod := mulmod(mload(0x18c0), prod, f_q) - mstore(0x22a0, prod) - - prod := mulmod(mload(0x1900), prod, f_q) - mstore(0x22c0, prod) - - prod := mulmod(mload(0x1940), prod, f_q) - mstore(0x22e0, prod) - - prod := mulmod(mload(0x1980), prod, f_q) - mstore(0x2300, prod) - - prod := mulmod(mload(0x19c0), prod, f_q) - mstore(0x2320, prod) - - prod := mulmod(mload(0x1a00), prod, f_q) - mstore(0x2340, prod) - - prod := mulmod(mload(0x1a40), prod, f_q) - mstore(0x2360, prod) - - prod := mulmod(mload(0x1a80), prod, f_q) - mstore(0x2380, prod) - - prod := mulmod(mload(0x1ac0), prod, f_q) - mstore(0x23a0, prod) - - prod := mulmod(mload(0x1b00), prod, f_q) - mstore(0x23c0, prod) - - prod := mulmod(mload(0x1b40), prod, f_q) - mstore(0x23e0, prod) - - prod := mulmod(mload(0x1b80), prod, f_q) - mstore(0x2400, prod) - - prod := mulmod(mload(0x1bc0), prod, f_q) - mstore(0x2420, prod) - - prod := mulmod(mload(0x1c00), prod, f_q) - mstore(0x2440, prod) - - prod := mulmod(mload(0x1c40), prod, f_q) - mstore(0x2460, prod) - - prod := mulmod(mload(0x1c80), prod, f_q) - mstore(0x2480, prod) - - prod := mulmod(mload(0x1cc0), prod, f_q) - mstore(0x24a0, prod) - - prod := mulmod(mload(0x1d00), prod, f_q) - mstore(0x24c0, prod) - - prod := mulmod(mload(0x1d40), prod, f_q) - mstore(0x24e0, prod) - - prod := mulmod(mload(0x1d80), prod, f_q) - mstore(0x2500, prod) - - prod := mulmod(mload(0x1dc0), prod, f_q) - mstore(0x2520, prod) - - prod := mulmod(mload(0x1e00), prod, f_q) - mstore(0x2540, prod) - - prod := mulmod(mload(0x1e40), prod, f_q) - mstore(0x2560, prod) - - prod := mulmod(mload(0x1e80), prod, f_q) - mstore(0x2580, prod) - - prod := mulmod(mload(0x1ec0), prod, f_q) - mstore(0x25a0, prod) - - prod := mulmod(mload(0x1f00), prod, f_q) - mstore(0x25c0, prod) - - prod := mulmod(mload(0x1f40), prod, f_q) - mstore(0x25e0, prod) - - prod := mulmod(mload(0x1f80), prod, f_q) - mstore(0x2600, prod) - - prod := mulmod(mload(0x1220), prod, f_q) - mstore(0x2620, prod) - } - mstore(0x2660, 32) - mstore(0x2680, 32) - mstore(0x26a0, 32) - mstore(0x26c0, mload(0x2620)) - mstore(0x26e0, 21888242871839275222246405745257275088548364400416034343698204186575808495615) - mstore(0x2700, 21888242871839275222246405745257275088548364400416034343698204186575808495617) - success := and(eq(staticcall(gas(), 0x5, 0x2660, 0xc0, 0x2640, 0x20), 1), success) - { - let inv := mload(0x2640) - let v - - v := mload(0x1220) - mstore(4640, mulmod(mload(0x2600), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1f80) - mstore(8064, mulmod(mload(0x25e0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1f40) - mstore(8000, mulmod(mload(0x25c0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1f00) - mstore(7936, mulmod(mload(0x25a0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1ec0) - mstore(7872, mulmod(mload(0x2580), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1e80) - mstore(7808, mulmod(mload(0x2560), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1e40) - mstore(7744, mulmod(mload(0x2540), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1e00) - mstore(7680, mulmod(mload(0x2520), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1dc0) - mstore(7616, mulmod(mload(0x2500), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1d80) - mstore(7552, mulmod(mload(0x24e0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1d40) - mstore(7488, mulmod(mload(0x24c0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1d00) - mstore(7424, mulmod(mload(0x24a0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1cc0) - mstore(7360, mulmod(mload(0x2480), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1c80) - mstore(7296, mulmod(mload(0x2460), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1c40) - mstore(7232, mulmod(mload(0x2440), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1c00) - mstore(7168, mulmod(mload(0x2420), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1bc0) - mstore(7104, mulmod(mload(0x2400), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1b80) - mstore(7040, mulmod(mload(0x23e0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1b40) - mstore(6976, mulmod(mload(0x23c0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1b00) - mstore(6912, mulmod(mload(0x23a0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1ac0) - mstore(6848, mulmod(mload(0x2380), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1a80) - mstore(6784, mulmod(mload(0x2360), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1a40) - mstore(6720, mulmod(mload(0x2340), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1a00) - mstore(6656, mulmod(mload(0x2320), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x19c0) - mstore(6592, mulmod(mload(0x2300), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1980) - mstore(6528, mulmod(mload(0x22e0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1940) - mstore(6464, mulmod(mload(0x22c0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1900) - mstore(6400, mulmod(mload(0x22a0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x18c0) - mstore(6336, mulmod(mload(0x2280), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1880) - mstore(6272, mulmod(mload(0x2260), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1840) - mstore(6208, mulmod(mload(0x2240), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1800) - mstore(6144, mulmod(mload(0x2220), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x17c0) - mstore(6080, mulmod(mload(0x2200), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1780) - mstore(6016, mulmod(mload(0x21e0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1740) - mstore(5952, mulmod(mload(0x21c0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1700) - mstore(5888, mulmod(mload(0x21a0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x16c0) - mstore(5824, mulmod(mload(0x2180), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1680) - mstore(5760, mulmod(mload(0x2160), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1640) - mstore(5696, mulmod(mload(0x2140), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1600) - mstore(5632, mulmod(mload(0x2120), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x15c0) - mstore(5568, mulmod(mload(0x2100), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1580) - mstore(5504, mulmod(mload(0x20e0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1540) - mstore(5440, mulmod(mload(0x20c0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1500) - mstore(5376, mulmod(mload(0x20a0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x14c0) - mstore(5312, mulmod(mload(0x2080), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1480) - mstore(5248, mulmod(mload(0x2060), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1440) - mstore(5184, mulmod(mload(0x2040), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1400) - mstore(5120, mulmod(mload(0x2020), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x13c0) - mstore(5056, mulmod(mload(0x2000), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1380) - mstore(4992, mulmod(mload(0x1fe0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1340) - mstore(4928, mulmod(mload(0x1fc0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x1300) - mstore(4864, mulmod(mload(0x1fa0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x12c0) - mstore(4800, mulmod(mload(0x1280), inv, f_q)) - inv := mulmod(v, inv, f_q) - mstore(0x1280, inv) - } - mstore(0x2720, mulmod(mload(0x1260), mload(0x1280), f_q)) - mstore(0x2740, mulmod(mload(0x12a0), mload(0x12c0), f_q)) - mstore(0x2760, mulmod(mload(0x12e0), mload(0x1300), f_q)) - mstore(0x2780, mulmod(mload(0x1320), mload(0x1340), f_q)) - mstore(0x27a0, mulmod(mload(0x1360), mload(0x1380), f_q)) - mstore(0x27c0, mulmod(mload(0x13a0), mload(0x13c0), f_q)) - mstore(0x27e0, mulmod(mload(0x13e0), mload(0x1400), f_q)) - mstore(0x2800, mulmod(mload(0x1420), mload(0x1440), f_q)) - mstore(0x2820, mulmod(mload(0x1460), mload(0x1480), f_q)) - mstore(0x2840, mulmod(mload(0x14a0), mload(0x14c0), f_q)) - mstore(0x2860, mulmod(mload(0x14e0), mload(0x1500), f_q)) - mstore(0x2880, mulmod(mload(0x1520), mload(0x1540), f_q)) - mstore(0x28a0, mulmod(mload(0x1560), mload(0x1580), f_q)) - mstore(0x28c0, mulmod(mload(0x15a0), mload(0x15c0), f_q)) - mstore(0x28e0, mulmod(mload(0x15e0), mload(0x1600), f_q)) - mstore(0x2900, mulmod(mload(0x1620), mload(0x1640), f_q)) - mstore(0x2920, mulmod(mload(0x1660), mload(0x1680), f_q)) - mstore(0x2940, mulmod(mload(0x16a0), mload(0x16c0), f_q)) - mstore(0x2960, mulmod(mload(0x16e0), mload(0x1700), f_q)) - mstore(0x2980, mulmod(mload(0x1720), mload(0x1740), f_q)) - mstore(0x29a0, mulmod(mload(0x1760), mload(0x1780), f_q)) - mstore(0x29c0, mulmod(mload(0x17a0), mload(0x17c0), f_q)) - mstore(0x29e0, mulmod(mload(0x17e0), mload(0x1800), f_q)) - mstore(0x2a00, mulmod(mload(0x1820), mload(0x1840), f_q)) - mstore(0x2a20, mulmod(mload(0x1860), mload(0x1880), f_q)) - mstore(0x2a40, mulmod(mload(0x18a0), mload(0x18c0), f_q)) - mstore(0x2a60, mulmod(mload(0x18e0), mload(0x1900), f_q)) - mstore(0x2a80, mulmod(mload(0x1920), mload(0x1940), f_q)) - mstore(0x2aa0, mulmod(mload(0x1960), mload(0x1980), f_q)) - mstore(0x2ac0, mulmod(mload(0x19a0), mload(0x19c0), f_q)) - mstore(0x2ae0, mulmod(mload(0x19e0), mload(0x1a00), f_q)) - mstore(0x2b00, mulmod(mload(0x1a20), mload(0x1a40), f_q)) - mstore(0x2b20, mulmod(mload(0x1a60), mload(0x1a80), f_q)) - mstore(0x2b40, mulmod(mload(0x1aa0), mload(0x1ac0), f_q)) - mstore(0x2b60, mulmod(mload(0x1ae0), mload(0x1b00), f_q)) - mstore(0x2b80, mulmod(mload(0x1b20), mload(0x1b40), f_q)) - mstore(0x2ba0, mulmod(mload(0x1b60), mload(0x1b80), f_q)) - mstore(0x2bc0, mulmod(mload(0x1ba0), mload(0x1bc0), f_q)) - mstore(0x2be0, mulmod(mload(0x1be0), mload(0x1c00), f_q)) - mstore(0x2c00, mulmod(mload(0x1c20), mload(0x1c40), f_q)) - mstore(0x2c20, mulmod(mload(0x1c60), mload(0x1c80), f_q)) - mstore(0x2c40, mulmod(mload(0x1ca0), mload(0x1cc0), f_q)) - mstore(0x2c60, mulmod(mload(0x1ce0), mload(0x1d00), f_q)) - mstore(0x2c80, mulmod(mload(0x1d20), mload(0x1d40), f_q)) - mstore(0x2ca0, mulmod(mload(0x1d60), mload(0x1d80), f_q)) - mstore(0x2cc0, mulmod(mload(0x1da0), mload(0x1dc0), f_q)) - mstore(0x2ce0, mulmod(mload(0x1de0), mload(0x1e00), f_q)) - mstore(0x2d00, mulmod(mload(0x1e20), mload(0x1e40), f_q)) - mstore(0x2d20, mulmod(mload(0x1e60), mload(0x1e80), f_q)) - mstore(0x2d40, mulmod(mload(0x1ea0), mload(0x1ec0), f_q)) - mstore(0x2d60, mulmod(mload(0x1ee0), mload(0x1f00), f_q)) - mstore(0x2d80, mulmod(mload(0x1f20), mload(0x1f40), f_q)) - mstore(0x2da0, mulmod(mload(0x1f60), mload(0x1f80), f_q)) - { - let result := mulmod(mload(0x2800), mload(0xa0), f_q) - result := addmod(mulmod(mload(0x2820), mload(0xc0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2840), mload(0xe0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2860), mload(0x100), f_q), result, f_q) - result := addmod(mulmod(mload(0x2880), mload(0x120), f_q), result, f_q) - result := addmod(mulmod(mload(0x28a0), mload(0x140), f_q), result, f_q) - result := addmod(mulmod(mload(0x28c0), mload(0x160), f_q), result, f_q) - result := addmod(mulmod(mload(0x28e0), mload(0x180), f_q), result, f_q) - result := addmod(mulmod(mload(0x2900), mload(0x1a0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2920), mload(0x1c0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2940), mload(0x1e0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2960), mload(0x200), f_q), result, f_q) - result := addmod(mulmod(mload(0x2980), mload(0x220), f_q), result, f_q) - result := addmod(mulmod(mload(0x29a0), mload(0x240), f_q), result, f_q) - result := addmod(mulmod(mload(0x29c0), mload(0x260), f_q), result, f_q) - result := addmod(mulmod(mload(0x29e0), mload(0x280), f_q), result, f_q) - result := addmod(mulmod(mload(0x2a00), mload(0x2a0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2a20), mload(0x2c0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2a40), mload(0x2e0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2a60), mload(0x300), f_q), result, f_q) - result := addmod(mulmod(mload(0x2a80), mload(0x320), f_q), result, f_q) - result := addmod(mulmod(mload(0x2aa0), mload(0x340), f_q), result, f_q) - result := addmod(mulmod(mload(0x2ac0), mload(0x360), f_q), result, f_q) - result := addmod(mulmod(mload(0x2ae0), mload(0x380), f_q), result, f_q) - result := addmod(mulmod(mload(0x2b00), mload(0x3a0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2b20), mload(0x3c0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2b40), mload(0x3e0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2b60), mload(0x400), f_q), result, f_q) - result := addmod(mulmod(mload(0x2b80), mload(0x420), f_q), result, f_q) - result := addmod(mulmod(mload(0x2ba0), mload(0x440), f_q), result, f_q) - result := addmod(mulmod(mload(0x2bc0), mload(0x460), f_q), result, f_q) - result := addmod(mulmod(mload(0x2be0), mload(0x480), f_q), result, f_q) - result := addmod(mulmod(mload(0x2c00), mload(0x4a0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2c20), mload(0x4c0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2c40), mload(0x4e0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2c60), mload(0x500), f_q), result, f_q) - result := addmod(mulmod(mload(0x2c80), mload(0x520), f_q), result, f_q) - result := addmod(mulmod(mload(0x2ca0), mload(0x540), f_q), result, f_q) - result := addmod(mulmod(mload(0x2cc0), mload(0x560), f_q), result, f_q) - result := addmod(mulmod(mload(0x2ce0), mload(0x580), f_q), result, f_q) - result := addmod(mulmod(mload(0x2d00), mload(0x5a0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2d20), mload(0x5c0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2d40), mload(0x5e0), f_q), result, f_q) - result := addmod(mulmod(mload(0x2d60), mload(0x600), f_q), result, f_q) - result := addmod(mulmod(mload(0x2d80), mload(0x620), f_q), result, f_q) - result := addmod(mulmod(mload(0x2da0), mload(0x640), f_q), result, f_q) - mstore(11712, result) - } - mstore(0x2de0, mulmod(mload(0xb00), mload(0xae0), f_q)) - mstore(0x2e00, addmod(mload(0xac0), mload(0x2de0), f_q)) - mstore(0x2e20, addmod(mload(0x2e00), sub(f_q, mload(0xb20)), f_q)) - mstore(0x2e40, mulmod(mload(0x2e20), mload(0xb80), f_q)) - mstore(0x2e60, mulmod(mload(0x920), mload(0x2e40), f_q)) - mstore(0x2e80, addmod(1, sub(f_q, mload(0xc40)), f_q)) - mstore(0x2ea0, mulmod(mload(0x2e80), mload(0x2800), f_q)) - mstore(0x2ec0, addmod(mload(0x2e60), mload(0x2ea0), f_q)) - mstore(0x2ee0, mulmod(mload(0x920), mload(0x2ec0), f_q)) - mstore(0x2f00, mulmod(mload(0xc40), mload(0xc40), f_q)) - mstore(0x2f20, addmod(mload(0x2f00), sub(f_q, mload(0xc40)), f_q)) - mstore(0x2f40, mulmod(mload(0x2f20), mload(0x2720), f_q)) - mstore(0x2f60, addmod(mload(0x2ee0), mload(0x2f40), f_q)) - mstore(0x2f80, mulmod(mload(0x920), mload(0x2f60), f_q)) - mstore(0x2fa0, addmod(1, sub(f_q, mload(0x2720)), f_q)) - mstore(0x2fc0, addmod(mload(0x2740), mload(0x2760), f_q)) - mstore(0x2fe0, addmod(mload(0x2fc0), mload(0x2780), f_q)) - mstore(0x3000, addmod(mload(0x2fe0), mload(0x27a0), f_q)) - mstore(0x3020, addmod(mload(0x3000), mload(0x27c0), f_q)) - mstore(0x3040, addmod(mload(0x3020), mload(0x27e0), f_q)) - mstore(0x3060, addmod(mload(0x2fa0), sub(f_q, mload(0x3040)), f_q)) - mstore(0x3080, mulmod(mload(0xbe0), mload(0x7a0), f_q)) - mstore(0x30a0, addmod(mload(0xb40), mload(0x3080), f_q)) - mstore(0x30c0, addmod(mload(0x30a0), mload(0x800), f_q)) - mstore(0x30e0, mulmod(mload(0xc00), mload(0x7a0), f_q)) - mstore(0x3100, addmod(mload(0xac0), mload(0x30e0), f_q)) - mstore(0x3120, addmod(mload(0x3100), mload(0x800), f_q)) - mstore(0x3140, mulmod(mload(0x3120), mload(0x30c0), f_q)) - mstore(0x3160, mulmod(mload(0xc20), mload(0x7a0), f_q)) - mstore(0x3180, addmod(mload(0x2dc0), mload(0x3160), f_q)) - mstore(0x31a0, addmod(mload(0x3180), mload(0x800), f_q)) - mstore(0x31c0, mulmod(mload(0x31a0), mload(0x3140), f_q)) - mstore(0x31e0, mulmod(mload(0x31c0), mload(0xc60), f_q)) - mstore(0x3200, mulmod(1, mload(0x7a0), f_q)) - mstore(0x3220, mulmod(mload(0xa80), mload(0x3200), f_q)) - mstore(0x3240, addmod(mload(0xb40), mload(0x3220), f_q)) - mstore(0x3260, addmod(mload(0x3240), mload(0x800), f_q)) - mstore( - 0x3280, - mulmod(4131629893567559867359510883348571134090853742863529169391034518566172092834, mload(0x7a0), f_q) - ) - mstore(0x32a0, mulmod(mload(0xa80), mload(0x3280), f_q)) - mstore(0x32c0, addmod(mload(0xac0), mload(0x32a0), f_q)) - mstore(0x32e0, addmod(mload(0x32c0), mload(0x800), f_q)) - mstore(0x3300, mulmod(mload(0x32e0), mload(0x3260), f_q)) - mstore( - 0x3320, - mulmod(8910878055287538404433155982483128285667088683464058436815641868457422632747, mload(0x7a0), f_q) - ) - mstore(0x3340, mulmod(mload(0xa80), mload(0x3320), f_q)) - mstore(0x3360, addmod(mload(0x2dc0), mload(0x3340), f_q)) - mstore(0x3380, addmod(mload(0x3360), mload(0x800), f_q)) - mstore(0x33a0, mulmod(mload(0x3380), mload(0x3300), f_q)) - mstore(0x33c0, mulmod(mload(0x33a0), mload(0xc40), f_q)) - mstore(0x33e0, addmod(mload(0x31e0), sub(f_q, mload(0x33c0)), f_q)) - mstore(0x3400, mulmod(mload(0x33e0), mload(0x3060), f_q)) - mstore(0x3420, addmod(mload(0x2f80), mload(0x3400), f_q)) - mstore(0x3440, mulmod(mload(0x920), mload(0x3420), f_q)) - mstore(0x3460, addmod(1, sub(f_q, mload(0xc80)), f_q)) - mstore(0x3480, mulmod(mload(0x3460), mload(0x2800), f_q)) - mstore(0x34a0, addmod(mload(0x3440), mload(0x3480), f_q)) - mstore(0x34c0, mulmod(mload(0x920), mload(0x34a0), f_q)) - mstore(0x34e0, mulmod(mload(0xc80), mload(0xc80), f_q)) - mstore(0x3500, addmod(mload(0x34e0), sub(f_q, mload(0xc80)), f_q)) - mstore(0x3520, mulmod(mload(0x3500), mload(0x2720), f_q)) - mstore(0x3540, addmod(mload(0x34c0), mload(0x3520), f_q)) - mstore(0x3560, mulmod(mload(0x920), mload(0x3540), f_q)) - mstore(0x3580, addmod(mload(0xcc0), mload(0x7a0), f_q)) - mstore(0x35a0, mulmod(mload(0x3580), mload(0xca0), f_q)) - mstore(0x35c0, addmod(mload(0xd00), mload(0x800), f_q)) - mstore(0x35e0, mulmod(mload(0x35c0), mload(0x35a0), f_q)) - mstore(0x3600, mulmod(mload(0xac0), mload(0xba0), f_q)) - mstore(0x3620, addmod(mload(0x3600), mload(0x7a0), f_q)) - mstore(0x3640, mulmod(mload(0x3620), mload(0xc80), f_q)) - mstore(0x3660, addmod(mload(0xb60), mload(0x800), f_q)) - mstore(0x3680, mulmod(mload(0x3660), mload(0x3640), f_q)) - mstore(0x36a0, addmod(mload(0x35e0), sub(f_q, mload(0x3680)), f_q)) - mstore(0x36c0, mulmod(mload(0x36a0), mload(0x3060), f_q)) - mstore(0x36e0, addmod(mload(0x3560), mload(0x36c0), f_q)) - mstore(0x3700, mulmod(mload(0x920), mload(0x36e0), f_q)) - mstore(0x3720, addmod(mload(0xcc0), sub(f_q, mload(0xd00)), f_q)) - mstore(0x3740, mulmod(mload(0x3720), mload(0x2800), f_q)) - mstore(0x3760, addmod(mload(0x3700), mload(0x3740), f_q)) - mstore(0x3780, mulmod(mload(0x920), mload(0x3760), f_q)) - mstore(0x37a0, mulmod(mload(0x3720), mload(0x3060), f_q)) - mstore(0x37c0, addmod(mload(0xcc0), sub(f_q, mload(0xce0)), f_q)) - mstore(0x37e0, mulmod(mload(0x37c0), mload(0x37a0), f_q)) - mstore(0x3800, addmod(mload(0x3780), mload(0x37e0), f_q)) - mstore(0x3820, mulmod(mload(0x1200), mload(0x1200), f_q)) - mstore(0x3840, mulmod(mload(0x3820), mload(0x1200), f_q)) - mstore(0x3860, mulmod(mload(0x3840), mload(0x1200), f_q)) - mstore(0x3880, mulmod(1, mload(0x1200), f_q)) - mstore(0x38a0, mulmod(1, mload(0x3820), f_q)) - mstore(0x38c0, mulmod(1, mload(0x3840), f_q)) - mstore(0x38e0, mulmod(mload(0x3800), mload(0x1220), f_q)) - mstore(0x3900, mulmod(mload(0xf40), mload(0xa80), f_q)) - mstore(0x3920, mulmod(mload(0x3900), mload(0xa80), f_q)) - mstore( - 0x3940, - mulmod(mload(0xa80), 9741553891420464328295280489650144566903017206473301385034033384879943874347, f_q) - ) - mstore(0x3960, addmod(mload(0xe40), sub(f_q, mload(0x3940)), f_q)) - mstore(0x3980, mulmod(mload(0xa80), 1, f_q)) - mstore(0x39a0, addmod(mload(0xe40), sub(f_q, mload(0x3980)), f_q)) - mstore( - 0x39c0, - mulmod(mload(0xa80), 8374374965308410102411073611984011876711565317741801500439755773472076597347, f_q) - ) - mstore(0x39e0, addmod(mload(0xe40), sub(f_q, mload(0x39c0)), f_q)) - mstore( - 0x3a00, - mulmod(mload(0xa80), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q) - ) - mstore(0x3a20, addmod(mload(0xe40), sub(f_q, mload(0x3a00)), f_q)) - mstore( - 0x3a40, - mulmod(mload(0xa80), 3615478808282855240548287271348143516886772452944084747768312988864436725401, f_q) - ) - mstore(0x3a60, addmod(mload(0xe40), sub(f_q, mload(0x3a40)), f_q)) - mstore( - 0x3a80, - mulmod( - 13213688729882003894512633350385593288217014177373218494356903340348818451480, mload(0x3900), f_q - ) - ) - mstore(0x3aa0, mulmod(mload(0x3a80), 1, f_q)) - { - let result := mulmod(mload(0xe40), mload(0x3a80), f_q) - result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3aa0)), f_q), result, f_q) - mstore(15040, result) - } - mstore( - 0x3ae0, - mulmod(8207090019724696496350398458716998472718344609680392612601596849934418295470, mload(0x3900), f_q) - ) - mstore( - 0x3b00, - mulmod(mload(0x3ae0), 8374374965308410102411073611984011876711565317741801500439755773472076597347, f_q) - ) - { - let result := mulmod(mload(0xe40), mload(0x3ae0), f_q) - result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3b00)), f_q), result, f_q) - mstore(15136, result) - } - mstore( - 0x3b40, - mulmod(7391709068497399131897422873231908718558236401035363928063603272120120747483, mload(0x3900), f_q) - ) - mstore( - 0x3b60, - mulmod( - mload(0x3b40), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q - ) - ) - { - let result := mulmod(mload(0xe40), mload(0x3b40), f_q) - result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3b60)), f_q), result, f_q) - mstore(15232, result) - } - mstore( - 0x3ba0, - mulmod( - 19036273796805830823244991598792794567595348772040298280440552631112242221017, mload(0x3900), f_q - ) - ) - mstore( - 0x3bc0, - mulmod(mload(0x3ba0), 3615478808282855240548287271348143516886772452944084747768312988864436725401, f_q) - ) - { - let result := mulmod(mload(0xe40), mload(0x3ba0), f_q) - result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3bc0)), f_q), result, f_q) - mstore(15328, result) - } - mstore(0x3c00, mulmod(1, mload(0x39a0), f_q)) - mstore(0x3c20, mulmod(mload(0x3c00), mload(0x39e0), f_q)) - mstore(0x3c40, mulmod(mload(0x3c20), mload(0x3a20), f_q)) - mstore(0x3c60, mulmod(mload(0x3c40), mload(0x3a60), f_q)) - mstore( - 0x3c80, - mulmod(13513867906530865119835332133273263211836799082674232843258448413103731898271, mload(0xa80), f_q) - ) - mstore(0x3ca0, mulmod(mload(0x3c80), 1, f_q)) - { - let result := mulmod(mload(0xe40), mload(0x3c80), f_q) - result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3ca0)), f_q), result, f_q) - mstore(15552, result) - } - mstore( - 0x3ce0, - mulmod(8374374965308410102411073611984011876711565317741801500439755773472076597346, mload(0xa80), f_q) - ) - mstore( - 0x3d00, - mulmod(mload(0x3ce0), 8374374965308410102411073611984011876711565317741801500439755773472076597347, f_q) - ) - { - let result := mulmod(mload(0xe40), mload(0x3ce0), f_q) - result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3d00)), f_q), result, f_q) - mstore(15648, result) - } - mstore( - 0x3d40, - mulmod(12146688980418810893951125255607130521645347193942732958664170801695864621271, mload(0xa80), f_q) - ) - mstore(0x3d60, mulmod(mload(0x3d40), 1, f_q)) - { - let result := mulmod(mload(0xe40), mload(0x3d40), f_q) - result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3d60)), f_q), result, f_q) - mstore(15744, result) - } - mstore( - 0x3da0, - mulmod(9741553891420464328295280489650144566903017206473301385034033384879943874346, mload(0xa80), f_q) - ) - mstore( - 0x3dc0, - mulmod(mload(0x3da0), 9741553891420464328295280489650144566903017206473301385034033384879943874347, f_q) - ) - { - let result := mulmod(mload(0xe40), mload(0x3da0), f_q) - result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3dc0)), f_q), result, f_q) - mstore(15840, result) - } - mstore(0x3e00, mulmod(mload(0x3c00), mload(0x3960), f_q)) - { - let result := mulmod(mload(0xe40), 1, f_q) - result := - addmod( - mulmod( - mload(0xa80), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q - ), - result, - f_q - ) - mstore(15904, result) - } - { - let prod := mload(0x3ac0) - - prod := mulmod(mload(0x3b20), prod, f_q) - mstore(0x3e40, prod) - - prod := mulmod(mload(0x3b80), prod, f_q) - mstore(0x3e60, prod) - - prod := mulmod(mload(0x3be0), prod, f_q) - mstore(0x3e80, prod) - - prod := mulmod(mload(0x3cc0), prod, f_q) - mstore(0x3ea0, prod) - - prod := mulmod(mload(0x3d20), prod, f_q) - mstore(0x3ec0, prod) - - prod := mulmod(mload(0x3c20), prod, f_q) - mstore(0x3ee0, prod) - - prod := mulmod(mload(0x3d80), prod, f_q) - mstore(0x3f00, prod) - - prod := mulmod(mload(0x3de0), prod, f_q) - mstore(0x3f20, prod) - - prod := mulmod(mload(0x3e00), prod, f_q) - mstore(0x3f40, prod) - - prod := mulmod(mload(0x3e20), prod, f_q) - mstore(0x3f60, prod) - - prod := mulmod(mload(0x3c00), prod, f_q) - mstore(0x3f80, prod) - } - mstore(0x3fc0, 32) - mstore(0x3fe0, 32) - mstore(0x4000, 32) - mstore(0x4020, mload(0x3f80)) - mstore(0x4040, 21888242871839275222246405745257275088548364400416034343698204186575808495615) - mstore(0x4060, 21888242871839275222246405745257275088548364400416034343698204186575808495617) - success := and(eq(staticcall(gas(), 0x5, 0x3fc0, 0xc0, 0x3fa0, 0x20), 1), success) - { - let inv := mload(0x3fa0) - let v - - v := mload(0x3c00) - mstore(15360, mulmod(mload(0x3f60), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3e20) - mstore(15904, mulmod(mload(0x3f40), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3e00) - mstore(15872, mulmod(mload(0x3f20), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3de0) - mstore(15840, mulmod(mload(0x3f00), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3d80) - mstore(15744, mulmod(mload(0x3ee0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3c20) - mstore(15392, mulmod(mload(0x3ec0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3d20) - mstore(15648, mulmod(mload(0x3ea0), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3cc0) - mstore(15552, mulmod(mload(0x3e80), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3be0) - mstore(15328, mulmod(mload(0x3e60), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3b80) - mstore(15232, mulmod(mload(0x3e40), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x3b20) - mstore(15136, mulmod(mload(0x3ac0), inv, f_q)) - inv := mulmod(v, inv, f_q) - mstore(0x3ac0, inv) - } - { - let result := mload(0x3ac0) - result := addmod(mload(0x3b20), result, f_q) - result := addmod(mload(0x3b80), result, f_q) - result := addmod(mload(0x3be0), result, f_q) - mstore(16512, result) - } - mstore(0x40a0, mulmod(mload(0x3c60), mload(0x3c20), f_q)) - { - let result := mload(0x3cc0) - result := addmod(mload(0x3d20), result, f_q) - mstore(16576, result) - } - mstore(0x40e0, mulmod(mload(0x3c60), mload(0x3e00), f_q)) - { - let result := mload(0x3d80) - result := addmod(mload(0x3de0), result, f_q) - mstore(16640, result) - } - mstore(0x4120, mulmod(mload(0x3c60), mload(0x3c00), f_q)) - { - let result := mload(0x3e20) - mstore(16704, result) - } - { - let prod := mload(0x4080) - - prod := mulmod(mload(0x40c0), prod, f_q) - mstore(0x4160, prod) - - prod := mulmod(mload(0x4100), prod, f_q) - mstore(0x4180, prod) - - prod := mulmod(mload(0x4140), prod, f_q) - mstore(0x41a0, prod) - } - mstore(0x41e0, 32) - mstore(0x4200, 32) - mstore(0x4220, 32) - mstore(0x4240, mload(0x41a0)) - mstore(0x4260, 21888242871839275222246405745257275088548364400416034343698204186575808495615) - mstore(0x4280, 21888242871839275222246405745257275088548364400416034343698204186575808495617) - success := and(eq(staticcall(gas(), 0x5, 0x41e0, 0xc0, 0x41c0, 0x20), 1), success) - { - let inv := mload(0x41c0) - let v - - v := mload(0x4140) - mstore(16704, mulmod(mload(0x4180), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x4100) - mstore(16640, mulmod(mload(0x4160), inv, f_q)) - inv := mulmod(v, inv, f_q) - - v := mload(0x40c0) - mstore(16576, mulmod(mload(0x4080), inv, f_q)) - inv := mulmod(v, inv, f_q) - mstore(0x4080, inv) - } - mstore(0x42a0, mulmod(mload(0x40a0), mload(0x40c0), f_q)) - mstore(0x42c0, mulmod(mload(0x40e0), mload(0x4100), f_q)) - mstore(0x42e0, mulmod(mload(0x4120), mload(0x4140), f_q)) - mstore(0x4300, mulmod(mload(0xd40), mload(0xd40), f_q)) - mstore(0x4320, mulmod(mload(0x4300), mload(0xd40), f_q)) - mstore(0x4340, mulmod(mload(0x4320), mload(0xd40), f_q)) - mstore(0x4360, mulmod(mload(0x4340), mload(0xd40), f_q)) - mstore(0x4380, mulmod(mload(0x4360), mload(0xd40), f_q)) - mstore(0x43a0, mulmod(mload(0x4380), mload(0xd40), f_q)) - mstore(0x43c0, mulmod(mload(0x43a0), mload(0xd40), f_q)) - mstore(0x43e0, mulmod(mload(0x43c0), mload(0xd40), f_q)) - mstore(0x4400, mulmod(mload(0x43e0), mload(0xd40), f_q)) - mstore(0x4420, mulmod(mload(0xda0), mload(0xda0), f_q)) - mstore(0x4440, mulmod(mload(0x4420), mload(0xda0), f_q)) - mstore(0x4460, mulmod(mload(0x4440), mload(0xda0), f_q)) - { - let result := mulmod(mload(0xac0), mload(0x3ac0), f_q) - result := addmod(mulmod(mload(0xae0), mload(0x3b20), f_q), result, f_q) - result := addmod(mulmod(mload(0xb00), mload(0x3b80), f_q), result, f_q) - result := addmod(mulmod(mload(0xb20), mload(0x3be0), f_q), result, f_q) - mstore(17536, result) - } - mstore(0x44a0, mulmod(mload(0x4480), mload(0x4080), f_q)) - mstore(0x44c0, mulmod(sub(f_q, mload(0x44a0)), 1, f_q)) - mstore(0x44e0, mulmod(mload(0x44c0), 1, f_q)) - mstore(0x4500, mulmod(1, mload(0x40a0), f_q)) - { - let result := mulmod(mload(0xc40), mload(0x3cc0), f_q) - result := addmod(mulmod(mload(0xc60), mload(0x3d20), f_q), result, f_q) - mstore(17696, result) - } - mstore(0x4540, mulmod(mload(0x4520), mload(0x42a0), f_q)) - mstore(0x4560, mulmod(sub(f_q, mload(0x4540)), 1, f_q)) - mstore(0x4580, mulmod(mload(0x4500), 1, f_q)) - { - let result := mulmod(mload(0xc80), mload(0x3cc0), f_q) - result := addmod(mulmod(mload(0xca0), mload(0x3d20), f_q), result, f_q) - mstore(17824, result) - } - mstore(0x45c0, mulmod(mload(0x45a0), mload(0x42a0), f_q)) - mstore(0x45e0, mulmod(sub(f_q, mload(0x45c0)), mload(0xd40), f_q)) - mstore(0x4600, mulmod(mload(0x4500), mload(0xd40), f_q)) - mstore(0x4620, addmod(mload(0x4560), mload(0x45e0), f_q)) - mstore(0x4640, mulmod(mload(0x4620), mload(0xda0), f_q)) - mstore(0x4660, mulmod(mload(0x4580), mload(0xda0), f_q)) - mstore(0x4680, mulmod(mload(0x4600), mload(0xda0), f_q)) - mstore(0x46a0, addmod(mload(0x44e0), mload(0x4640), f_q)) - mstore(0x46c0, mulmod(1, mload(0x40e0), f_q)) - { - let result := mulmod(mload(0xcc0), mload(0x3d80), f_q) - result := addmod(mulmod(mload(0xce0), mload(0x3de0), f_q), result, f_q) - mstore(18144, result) - } - mstore(0x4700, mulmod(mload(0x46e0), mload(0x42c0), f_q)) - mstore(0x4720, mulmod(sub(f_q, mload(0x4700)), 1, f_q)) - mstore(0x4740, mulmod(mload(0x46c0), 1, f_q)) - mstore(0x4760, mulmod(mload(0x4720), mload(0x4420), f_q)) - mstore(0x4780, mulmod(mload(0x4740), mload(0x4420), f_q)) - mstore(0x47a0, addmod(mload(0x46a0), mload(0x4760), f_q)) - mstore(0x47c0, mulmod(1, mload(0x4120), f_q)) - { - let result := mulmod(mload(0xd00), mload(0x3e20), f_q) - mstore(18400, result) - } - mstore(0x4800, mulmod(mload(0x47e0), mload(0x42e0), f_q)) - mstore(0x4820, mulmod(sub(f_q, mload(0x4800)), 1, f_q)) - mstore(0x4840, mulmod(mload(0x47c0), 1, f_q)) - { - let result := mulmod(mload(0xb40), mload(0x3e20), f_q) - mstore(18528, result) - } - mstore(0x4880, mulmod(mload(0x4860), mload(0x42e0), f_q)) - mstore(0x48a0, mulmod(sub(f_q, mload(0x4880)), mload(0xd40), f_q)) - mstore(0x48c0, mulmod(mload(0x47c0), mload(0xd40), f_q)) - mstore(0x48e0, addmod(mload(0x4820), mload(0x48a0), f_q)) - { - let result := mulmod(mload(0xb60), mload(0x3e20), f_q) - mstore(18688, result) - } - mstore(0x4920, mulmod(mload(0x4900), mload(0x42e0), f_q)) - mstore(0x4940, mulmod(sub(f_q, mload(0x4920)), mload(0x4300), f_q)) - mstore(0x4960, mulmod(mload(0x47c0), mload(0x4300), f_q)) - mstore(0x4980, addmod(mload(0x48e0), mload(0x4940), f_q)) - { - let result := mulmod(mload(0xb80), mload(0x3e20), f_q) - mstore(18848, result) - } - mstore(0x49c0, mulmod(mload(0x49a0), mload(0x42e0), f_q)) - mstore(0x49e0, mulmod(sub(f_q, mload(0x49c0)), mload(0x4320), f_q)) - mstore(0x4a00, mulmod(mload(0x47c0), mload(0x4320), f_q)) - mstore(0x4a20, addmod(mload(0x4980), mload(0x49e0), f_q)) - { - let result := mulmod(mload(0xba0), mload(0x3e20), f_q) - mstore(19008, result) - } - mstore(0x4a60, mulmod(mload(0x4a40), mload(0x42e0), f_q)) - mstore(0x4a80, mulmod(sub(f_q, mload(0x4a60)), mload(0x4340), f_q)) - mstore(0x4aa0, mulmod(mload(0x47c0), mload(0x4340), f_q)) - mstore(0x4ac0, addmod(mload(0x4a20), mload(0x4a80), f_q)) - { - let result := mulmod(mload(0xbe0), mload(0x3e20), f_q) - mstore(19168, result) - } - mstore(0x4b00, mulmod(mload(0x4ae0), mload(0x42e0), f_q)) - mstore(0x4b20, mulmod(sub(f_q, mload(0x4b00)), mload(0x4360), f_q)) - mstore(0x4b40, mulmod(mload(0x47c0), mload(0x4360), f_q)) - mstore(0x4b60, addmod(mload(0x4ac0), mload(0x4b20), f_q)) - { - let result := mulmod(mload(0xc00), mload(0x3e20), f_q) - mstore(19328, result) - } - mstore(0x4ba0, mulmod(mload(0x4b80), mload(0x42e0), f_q)) - mstore(0x4bc0, mulmod(sub(f_q, mload(0x4ba0)), mload(0x4380), f_q)) - mstore(0x4be0, mulmod(mload(0x47c0), mload(0x4380), f_q)) - mstore(0x4c00, addmod(mload(0x4b60), mload(0x4bc0), f_q)) - { - let result := mulmod(mload(0xc20), mload(0x3e20), f_q) - mstore(19488, result) - } - mstore(0x4c40, mulmod(mload(0x4c20), mload(0x42e0), f_q)) - mstore(0x4c60, mulmod(sub(f_q, mload(0x4c40)), mload(0x43a0), f_q)) - mstore(0x4c80, mulmod(mload(0x47c0), mload(0x43a0), f_q)) - mstore(0x4ca0, addmod(mload(0x4c00), mload(0x4c60), f_q)) - mstore(0x4cc0, mulmod(mload(0x3880), mload(0x4120), f_q)) - mstore(0x4ce0, mulmod(mload(0x38a0), mload(0x4120), f_q)) - mstore(0x4d00, mulmod(mload(0x38c0), mload(0x4120), f_q)) - { - let result := mulmod(mload(0x38e0), mload(0x3e20), f_q) - mstore(19744, result) - } - mstore(0x4d40, mulmod(mload(0x4d20), mload(0x42e0), f_q)) - mstore(0x4d60, mulmod(sub(f_q, mload(0x4d40)), mload(0x43c0), f_q)) - mstore(0x4d80, mulmod(mload(0x47c0), mload(0x43c0), f_q)) - mstore(0x4da0, mulmod(mload(0x4cc0), mload(0x43c0), f_q)) - mstore(0x4dc0, mulmod(mload(0x4ce0), mload(0x43c0), f_q)) - mstore(0x4de0, mulmod(mload(0x4d00), mload(0x43c0), f_q)) - mstore(0x4e00, addmod(mload(0x4ca0), mload(0x4d60), f_q)) - { - let result := mulmod(mload(0xbc0), mload(0x3e20), f_q) - mstore(20000, result) - } - mstore(0x4e40, mulmod(mload(0x4e20), mload(0x42e0), f_q)) - mstore(0x4e60, mulmod(sub(f_q, mload(0x4e40)), mload(0x43e0), f_q)) - mstore(0x4e80, mulmod(mload(0x47c0), mload(0x43e0), f_q)) - mstore(0x4ea0, addmod(mload(0x4e00), mload(0x4e60), f_q)) - mstore(0x4ec0, mulmod(mload(0x4ea0), mload(0x4440), f_q)) - mstore(0x4ee0, mulmod(mload(0x4840), mload(0x4440), f_q)) - mstore(0x4f00, mulmod(mload(0x48c0), mload(0x4440), f_q)) - mstore(0x4f20, mulmod(mload(0x4960), mload(0x4440), f_q)) - mstore(0x4f40, mulmod(mload(0x4a00), mload(0x4440), f_q)) - mstore(0x4f60, mulmod(mload(0x4aa0), mload(0x4440), f_q)) - mstore(0x4f80, mulmod(mload(0x4b40), mload(0x4440), f_q)) - mstore(0x4fa0, mulmod(mload(0x4be0), mload(0x4440), f_q)) - mstore(0x4fc0, mulmod(mload(0x4c80), mload(0x4440), f_q)) - mstore(0x4fe0, mulmod(mload(0x4d80), mload(0x4440), f_q)) - mstore(0x5000, mulmod(mload(0x4da0), mload(0x4440), f_q)) - mstore(0x5020, mulmod(mload(0x4dc0), mload(0x4440), f_q)) - mstore(0x5040, mulmod(mload(0x4de0), mload(0x4440), f_q)) - mstore(0x5060, mulmod(mload(0x4e80), mload(0x4440), f_q)) - mstore(0x5080, addmod(mload(0x47a0), mload(0x4ec0), f_q)) - mstore(0x50a0, mulmod(1, mload(0x3c60), f_q)) - mstore(0x50c0, mulmod(1, mload(0xe40), f_q)) - mstore(0x50e0, 0x0000000000000000000000000000000000000000000000000000000000000001) - mstore(0x5100, 0x0000000000000000000000000000000000000000000000000000000000000002) - mstore(0x5120, mload(0x5080)) - success := and(eq(staticcall(gas(), 0x7, 0x50e0, 0x60, 0x50e0, 0x40), 1), success) - mstore(0x5140, mload(0x50e0)) - mstore(0x5160, mload(0x5100)) - mstore(0x5180, mload(0x660)) - mstore(0x51a0, mload(0x680)) - success := and(eq(staticcall(gas(), 0x6, 0x5140, 0x80, 0x5140, 0x40), 1), success) - mstore(0x51c0, mload(0x840)) - mstore(0x51e0, mload(0x860)) - mstore(0x5200, mload(0x4660)) - success := and(eq(staticcall(gas(), 0x7, 0x51c0, 0x60, 0x51c0, 0x40), 1), success) - mstore(0x5220, mload(0x5140)) - mstore(0x5240, mload(0x5160)) - mstore(0x5260, mload(0x51c0)) - mstore(0x5280, mload(0x51e0)) - success := and(eq(staticcall(gas(), 0x6, 0x5220, 0x80, 0x5220, 0x40), 1), success) - mstore(0x52a0, mload(0x880)) - mstore(0x52c0, mload(0x8a0)) - mstore(0x52e0, mload(0x4680)) - success := and(eq(staticcall(gas(), 0x7, 0x52a0, 0x60, 0x52a0, 0x40), 1), success) - mstore(0x5300, mload(0x5220)) - mstore(0x5320, mload(0x5240)) - mstore(0x5340, mload(0x52a0)) - mstore(0x5360, mload(0x52c0)) - success := and(eq(staticcall(gas(), 0x6, 0x5300, 0x80, 0x5300, 0x40), 1), success) - mstore(0x5380, mload(0x700)) - mstore(0x53a0, mload(0x720)) - mstore(0x53c0, mload(0x4780)) - success := and(eq(staticcall(gas(), 0x7, 0x5380, 0x60, 0x5380, 0x40), 1), success) - mstore(0x53e0, mload(0x5300)) - mstore(0x5400, mload(0x5320)) - mstore(0x5420, mload(0x5380)) - mstore(0x5440, mload(0x53a0)) - success := and(eq(staticcall(gas(), 0x6, 0x53e0, 0x80, 0x53e0, 0x40), 1), success) - mstore(0x5460, mload(0x740)) - mstore(0x5480, mload(0x760)) - mstore(0x54a0, mload(0x4ee0)) - success := and(eq(staticcall(gas(), 0x7, 0x5460, 0x60, 0x5460, 0x40), 1), success) - mstore(0x54c0, mload(0x53e0)) - mstore(0x54e0, mload(0x5400)) - mstore(0x5500, mload(0x5460)) - mstore(0x5520, mload(0x5480)) - success := and(eq(staticcall(gas(), 0x6, 0x54c0, 0x80, 0x54c0, 0x40), 1), success) - mstore(0x5540, 0x10903f13ba202328af3a3e13021579d67557dcf3bfe705d1b7989ed2f4e4c2c4) - mstore(0x5560, 0x190b6fb9d28400025be71d2314d98e4e9a8eeb0de7130496f1b622d55abf6ff6) - mstore(0x5580, mload(0x4f00)) - success := and(eq(staticcall(gas(), 0x7, 0x5540, 0x60, 0x5540, 0x40), 1), success) - mstore(0x55a0, mload(0x54c0)) - mstore(0x55c0, mload(0x54e0)) - mstore(0x55e0, mload(0x5540)) - mstore(0x5600, mload(0x5560)) - success := and(eq(staticcall(gas(), 0x6, 0x55a0, 0x80, 0x55a0, 0x40), 1), success) - mstore(0x5620, 0x2eb40e2b0c13a6f4b989cffa9dbc452447bfd9f04a79f6379aefea8c9850a550) - mstore(0x5640, 0x0efe5496541e2bd648d490f11ad542e1dec3127f818b8065843d0dd81358416c) - mstore(0x5660, mload(0x4f20)) - success := and(eq(staticcall(gas(), 0x7, 0x5620, 0x60, 0x5620, 0x40), 1), success) - mstore(0x5680, mload(0x55a0)) - mstore(0x56a0, mload(0x55c0)) - mstore(0x56c0, mload(0x5620)) - mstore(0x56e0, mload(0x5640)) - success := and(eq(staticcall(gas(), 0x6, 0x5680, 0x80, 0x5680, 0x40), 1), success) - mstore(0x5700, 0x18dca54423b6fa7932c92beff56ce260a3c726e3613c92fe0843d86b92199bfd) - mstore(0x5720, 0x0f6641ca942a4541625b14adb25ab0fd978060c98d01eb1b036fcdb1b8f77be1) - mstore(0x5740, mload(0x4f40)) - success := and(eq(staticcall(gas(), 0x7, 0x5700, 0x60, 0x5700, 0x40), 1), success) - mstore(0x5760, mload(0x5680)) - mstore(0x5780, mload(0x56a0)) - mstore(0x57a0, mload(0x5700)) - mstore(0x57c0, mload(0x5720)) - success := and(eq(staticcall(gas(), 0x6, 0x5760, 0x80, 0x5760, 0x40), 1), success) - mstore(0x57e0, 0x23809cc9d17a8cb32381764c6234d3038148b8bec5d8573fb3470486feedd968) - mstore(0x5800, 0x1debabb870ec20dc2c4df55cbd88205b6dd234ad5805faf3a72e14b6f934f238) - mstore(0x5820, mload(0x4f60)) - success := and(eq(staticcall(gas(), 0x7, 0x57e0, 0x60, 0x57e0, 0x40), 1), success) - mstore(0x5840, mload(0x5760)) - mstore(0x5860, mload(0x5780)) - mstore(0x5880, mload(0x57e0)) - mstore(0x58a0, mload(0x5800)) - success := and(eq(staticcall(gas(), 0x6, 0x5840, 0x80, 0x5840, 0x40), 1), success) - mstore(0x58c0, 0x2b65ba9ff654f7feb0cc2b9609adc1e982ca4e01f037521c7c4196277deea800) - mstore(0x58e0, 0x0cf0f87bb9200057e5f6331cd1d7b4dc4dd1cf1fcfceb3a7eb36cad45ba6ea02) - mstore(0x5900, mload(0x4f80)) - success := and(eq(staticcall(gas(), 0x7, 0x58c0, 0x60, 0x58c0, 0x40), 1), success) - mstore(0x5920, mload(0x5840)) - mstore(0x5940, mload(0x5860)) - mstore(0x5960, mload(0x58c0)) - mstore(0x5980, mload(0x58e0)) - success := and(eq(staticcall(gas(), 0x6, 0x5920, 0x80, 0x5920, 0x40), 1), success) - mstore(0x59a0, 0x23f7bb2254ad1c43019cf50cb00a9c7354bc55d444647ce7d5345e4db3eac1ba) - mstore(0x59c0, 0x14505e073a9e6cb05d697b73577eb7cda821bff988201b7e37ccb224eba4d9d2) - mstore(0x59e0, mload(0x4fa0)) - success := and(eq(staticcall(gas(), 0x7, 0x59a0, 0x60, 0x59a0, 0x40), 1), success) - mstore(0x5a00, mload(0x5920)) - mstore(0x5a20, mload(0x5940)) - mstore(0x5a40, mload(0x59a0)) - mstore(0x5a60, mload(0x59c0)) - success := and(eq(staticcall(gas(), 0x6, 0x5a00, 0x80, 0x5a00, 0x40), 1), success) - mstore(0x5a80, 0x23eb59f4643a8f86f1bb54ebc274b4810126c9c2fae5d8de472ef0566afaa14c) - mstore(0x5aa0, 0x13b28c9220c717cac368247913075f4bb8da09cd9d0145c1292810854089c39c) - mstore(0x5ac0, mload(0x4fc0)) - success := and(eq(staticcall(gas(), 0x7, 0x5a80, 0x60, 0x5a80, 0x40), 1), success) - mstore(0x5ae0, mload(0x5a00)) - mstore(0x5b00, mload(0x5a20)) - mstore(0x5b20, mload(0x5a80)) - mstore(0x5b40, mload(0x5aa0)) - success := and(eq(staticcall(gas(), 0x6, 0x5ae0, 0x80, 0x5ae0, 0x40), 1), success) - mstore(0x5b60, mload(0x960)) - mstore(0x5b80, mload(0x980)) - mstore(0x5ba0, mload(0x4fe0)) - success := and(eq(staticcall(gas(), 0x7, 0x5b60, 0x60, 0x5b60, 0x40), 1), success) - mstore(0x5bc0, mload(0x5ae0)) - mstore(0x5be0, mload(0x5b00)) - mstore(0x5c00, mload(0x5b60)) - mstore(0x5c20, mload(0x5b80)) - success := and(eq(staticcall(gas(), 0x6, 0x5bc0, 0x80, 0x5bc0, 0x40), 1), success) - mstore(0x5c40, mload(0x9a0)) - mstore(0x5c60, mload(0x9c0)) - mstore(0x5c80, mload(0x5000)) - success := and(eq(staticcall(gas(), 0x7, 0x5c40, 0x60, 0x5c40, 0x40), 1), success) - mstore(0x5ca0, mload(0x5bc0)) - mstore(0x5cc0, mload(0x5be0)) - mstore(0x5ce0, mload(0x5c40)) - mstore(0x5d00, mload(0x5c60)) - success := and(eq(staticcall(gas(), 0x6, 0x5ca0, 0x80, 0x5ca0, 0x40), 1), success) - mstore(0x5d20, mload(0x9e0)) - mstore(0x5d40, mload(0xa00)) - mstore(0x5d60, mload(0x5020)) - success := and(eq(staticcall(gas(), 0x7, 0x5d20, 0x60, 0x5d20, 0x40), 1), success) - mstore(0x5d80, mload(0x5ca0)) - mstore(0x5da0, mload(0x5cc0)) - mstore(0x5dc0, mload(0x5d20)) - mstore(0x5de0, mload(0x5d40)) - success := and(eq(staticcall(gas(), 0x6, 0x5d80, 0x80, 0x5d80, 0x40), 1), success) - mstore(0x5e00, mload(0xa20)) - mstore(0x5e20, mload(0xa40)) - mstore(0x5e40, mload(0x5040)) - success := and(eq(staticcall(gas(), 0x7, 0x5e00, 0x60, 0x5e00, 0x40), 1), success) - mstore(0x5e60, mload(0x5d80)) - mstore(0x5e80, mload(0x5da0)) - mstore(0x5ea0, mload(0x5e00)) - mstore(0x5ec0, mload(0x5e20)) - success := and(eq(staticcall(gas(), 0x6, 0x5e60, 0x80, 0x5e60, 0x40), 1), success) - mstore(0x5ee0, mload(0x8c0)) - mstore(0x5f00, mload(0x8e0)) - mstore(0x5f20, mload(0x5060)) - success := and(eq(staticcall(gas(), 0x7, 0x5ee0, 0x60, 0x5ee0, 0x40), 1), success) - mstore(0x5f40, mload(0x5e60)) - mstore(0x5f60, mload(0x5e80)) - mstore(0x5f80, mload(0x5ee0)) - mstore(0x5fa0, mload(0x5f00)) - success := and(eq(staticcall(gas(), 0x6, 0x5f40, 0x80, 0x5f40, 0x40), 1), success) - mstore(0x5fc0, mload(0xde0)) - mstore(0x5fe0, mload(0xe00)) - mstore(0x6000, sub(f_q, mload(0x50a0))) - success := and(eq(staticcall(gas(), 0x7, 0x5fc0, 0x60, 0x5fc0, 0x40), 1), success) - mstore(0x6020, mload(0x5f40)) - mstore(0x6040, mload(0x5f60)) - mstore(0x6060, mload(0x5fc0)) - mstore(0x6080, mload(0x5fe0)) - success := and(eq(staticcall(gas(), 0x6, 0x6020, 0x80, 0x6020, 0x40), 1), success) - mstore(0x60a0, mload(0xe80)) - mstore(0x60c0, mload(0xea0)) - mstore(0x60e0, mload(0x50c0)) - success := and(eq(staticcall(gas(), 0x7, 0x60a0, 0x60, 0x60a0, 0x40), 1), success) - mstore(0x6100, mload(0x6020)) - mstore(0x6120, mload(0x6040)) - mstore(0x6140, mload(0x60a0)) - mstore(0x6160, mload(0x60c0)) - success := and(eq(staticcall(gas(), 0x6, 0x6100, 0x80, 0x6100, 0x40), 1), success) - mstore(0x6180, mload(0x6100)) - mstore(0x61a0, mload(0x6120)) - mstore(0x61c0, mload(0xe80)) - mstore(0x61e0, mload(0xea0)) - mstore(0x6200, mload(0xec0)) - mstore(0x6220, mload(0xee0)) - mstore(0x6240, mload(0xf00)) - mstore(0x6260, mload(0xf20)) - mstore(0x6280, keccak256(0x6180, 256)) - mstore(25248, mod(mload(25216), f_q)) - mstore(0x62c0, mulmod(mload(0x62a0), mload(0x62a0), f_q)) - mstore(0x62e0, mulmod(1, mload(0x62a0), f_q)) - mstore(0x6300, mload(0x6200)) - mstore(0x6320, mload(0x6220)) - mstore(0x6340, mload(0x62e0)) - success := and(eq(staticcall(gas(), 0x7, 0x6300, 0x60, 0x6300, 0x40), 1), success) - mstore(0x6360, mload(0x6180)) - mstore(0x6380, mload(0x61a0)) - mstore(0x63a0, mload(0x6300)) - mstore(0x63c0, mload(0x6320)) - success := and(eq(staticcall(gas(), 0x6, 0x6360, 0x80, 0x6360, 0x40), 1), success) - mstore(0x63e0, mload(0x6240)) - mstore(0x6400, mload(0x6260)) - mstore(0x6420, mload(0x62e0)) - success := and(eq(staticcall(gas(), 0x7, 0x63e0, 0x60, 0x63e0, 0x40), 1), success) - mstore(0x6440, mload(0x61c0)) - mstore(0x6460, mload(0x61e0)) - mstore(0x6480, mload(0x63e0)) - mstore(0x64a0, mload(0x6400)) - success := and(eq(staticcall(gas(), 0x6, 0x6440, 0x80, 0x6440, 0x40), 1), success) - mstore(0x64c0, mload(0x6360)) - mstore(0x64e0, mload(0x6380)) - mstore(0x6500, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) - mstore(0x6520, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) - mstore(0x6540, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) - mstore(0x6560, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa) - mstore(0x6580, mload(0x6440)) - mstore(0x65a0, mload(0x6460)) - mstore(0x65c0, 0x172aa93c41f16e1e04d62ac976a5d945f4be0acab990c6dc19ac4a7cf68bf77b) - mstore(0x65e0, 0x2ae0c8c3a090f7200ff398ee9845bbae8f8c1445ae7b632212775f60a0e21600) - mstore(0x6600, 0x190fa476a5b352809ed41d7a0d7fe12b8f685e3c12a6d83855dba27aaf469643) - mstore(0x6620, 0x1c0a500618907df9e4273d5181e31088deb1f05132de037cbfe73888f97f77c9) - success := and(eq(staticcall(gas(), 0x8, 0x64c0, 0x180, 0x64c0, 0x20), 1), success) - success := and(eq(mload(0x64c0), 1), success) - - // Revert if anything fails - if iszero(success) { revert(0, 0) } - - // Return empty bytes on success - return(0, 0) - } - } -} diff --git a/src/v1.0.1/Halo2Verifier.sol b/src/v1.0.1/Halo2Verifier.sol new file mode 100644 index 0000000..71ce4bb --- /dev/null +++ b/src/v1.0.1/Halo2Verifier.sol @@ -0,0 +1,2045 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.19; + +contract Halo2Verifier { + fallback(bytes calldata) external returns (bytes memory) { + assembly ("memory-safe") { + // Enforce that Solidity memory layout is respected + let data := mload(0x40) + if iszero(eq(data, 0x80)) { revert(0, 0) } + + let success := true + let f_p := 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 + let f_q := 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 + function validate_ec_point(x, y) -> valid { + { + let x_lt_p := lt(x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) + let y_lt_p := lt(y, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) + valid := and(x_lt_p, y_lt_p) + } + { + let y_square := mulmod(y, y, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) + let x_square := mulmod(x, x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) + let x_cube := + mulmod(x_square, x, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) + let x_cube_plus_3 := + addmod(x_cube, 3, 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47) + let is_affine := eq(x_cube_plus_3, y_square) + valid := and(valid, is_affine) + } + } + mstore(0xa0, mod(calldataload(0x0), f_q)) + mstore(0xc0, mod(calldataload(0x20), f_q)) + mstore(0xe0, mod(calldataload(0x40), f_q)) + mstore(0x100, mod(calldataload(0x60), f_q)) + mstore(0x120, mod(calldataload(0x80), f_q)) + mstore(0x140, mod(calldataload(0xa0), f_q)) + mstore(0x160, mod(calldataload(0xc0), f_q)) + mstore(0x180, mod(calldataload(0xe0), f_q)) + mstore(0x1a0, mod(calldataload(0x100), f_q)) + mstore(0x1c0, mod(calldataload(0x120), f_q)) + mstore(0x1e0, mod(calldataload(0x140), f_q)) + mstore(0x200, mod(calldataload(0x160), f_q)) + mstore(0x220, mod(calldataload(0x180), f_q)) + mstore(0x240, mod(calldataload(0x1a0), f_q)) + mstore(0x260, mod(calldataload(0x1c0), f_q)) + mstore(0x280, mod(calldataload(0x1e0), f_q)) + mstore(0x2a0, mod(calldataload(0x200), f_q)) + mstore(0x2c0, mod(calldataload(0x220), f_q)) + mstore(0x2e0, mod(calldataload(0x240), f_q)) + mstore(0x300, mod(calldataload(0x260), f_q)) + mstore(0x320, mod(calldataload(0x280), f_q)) + mstore(0x340, mod(calldataload(0x2a0), f_q)) + mstore(0x360, mod(calldataload(0x2c0), f_q)) + mstore(0x380, mod(calldataload(0x2e0), f_q)) + mstore(0x3a0, mod(calldataload(0x300), f_q)) + mstore(0x3c0, mod(calldataload(0x320), f_q)) + mstore(0x3e0, mod(calldataload(0x340), f_q)) + mstore(0x400, mod(calldataload(0x360), f_q)) + mstore(0x420, mod(calldataload(0x380), f_q)) + mstore(0x440, mod(calldataload(0x3a0), f_q)) + mstore(0x460, mod(calldataload(0x3c0), f_q)) + mstore(0x480, mod(calldataload(0x3e0), f_q)) + mstore(0x4a0, mod(calldataload(0x400), f_q)) + mstore(0x4c0, mod(calldataload(0x420), f_q)) + mstore(0x4e0, mod(calldataload(0x440), f_q)) + mstore(0x500, mod(calldataload(0x460), f_q)) + mstore(0x520, mod(calldataload(0x480), f_q)) + mstore(0x540, mod(calldataload(0x4a0), f_q)) + mstore(0x560, mod(calldataload(0x4c0), f_q)) + mstore(0x580, mod(calldataload(0x4e0), f_q)) + mstore(0x5a0, mod(calldataload(0x500), f_q)) + mstore(0x5c0, mod(calldataload(0x520), f_q)) + mstore(0x5e0, mod(calldataload(0x540), f_q)) + mstore(0x600, mod(calldataload(0x560), f_q)) + mstore(0x620, mod(calldataload(0x580), f_q)) + mstore(0x640, mod(calldataload(0x5a0), f_q)) + mstore(0x80, 19438946737798499885558692066632745375001658993335175299244276035086184769592) + + { + let x := calldataload(0x5c0) + mstore(0x660, x) + let y := calldataload(0x5e0) + mstore(0x680, y) + success := and(validate_ec_point(x, y), success) + } + mstore(0x6a0, keccak256(0x80, 1568)) + { + let hash := mload(0x6a0) + mstore(0x6c0, mod(hash, f_q)) + mstore(0x6e0, hash) + } + + { + let x := calldataload(0x600) + mstore(0x700, x) + let y := calldataload(0x620) + mstore(0x720, y) + success := and(validate_ec_point(x, y), success) + } + + { + let x := calldataload(0x640) + mstore(0x740, x) + let y := calldataload(0x660) + mstore(0x760, y) + success := and(validate_ec_point(x, y), success) + } + mstore(0x780, keccak256(0x6e0, 160)) + { + let hash := mload(0x780) + mstore(0x7a0, mod(hash, f_q)) + mstore(0x7c0, hash) + } + mstore8(2016, 1) + mstore(0x7e0, keccak256(0x7c0, 33)) + { + let hash := mload(0x7e0) + mstore(0x800, mod(hash, f_q)) + mstore(0x820, hash) + } + + { + let x := calldataload(0x680) + mstore(0x840, x) + let y := calldataload(0x6a0) + mstore(0x860, y) + success := and(validate_ec_point(x, y), success) + } + + { + let x := calldataload(0x6c0) + mstore(0x880, x) + let y := calldataload(0x6e0) + mstore(0x8a0, y) + success := and(validate_ec_point(x, y), success) + } + + { + let x := calldataload(0x700) + mstore(0x8c0, x) + let y := calldataload(0x720) + mstore(0x8e0, y) + success := and(validate_ec_point(x, y), success) + } + mstore(0x900, keccak256(0x820, 224)) + { + let hash := mload(0x900) + mstore(0x920, mod(hash, f_q)) + mstore(0x940, hash) + } + + { + let x := calldataload(0x740) + mstore(0x960, x) + let y := calldataload(0x760) + mstore(0x980, y) + success := and(validate_ec_point(x, y), success) + } + + { + let x := calldataload(0x780) + mstore(0x9a0, x) + let y := calldataload(0x7a0) + mstore(0x9c0, y) + success := and(validate_ec_point(x, y), success) + } + + { + let x := calldataload(0x7c0) + mstore(0x9e0, x) + let y := calldataload(0x7e0) + mstore(0xa00, y) + success := and(validate_ec_point(x, y), success) + } + + { + let x := calldataload(0x800) + mstore(0xa20, x) + let y := calldataload(0x820) + mstore(0xa40, y) + success := and(validate_ec_point(x, y), success) + } + mstore(0xa60, keccak256(0x940, 288)) + { + let hash := mload(0xa60) + mstore(0xa80, mod(hash, f_q)) + mstore(0xaa0, hash) + } + mstore(0xac0, mod(calldataload(0x840), f_q)) + mstore(0xae0, mod(calldataload(0x860), f_q)) + mstore(0xb00, mod(calldataload(0x880), f_q)) + mstore(0xb20, mod(calldataload(0x8a0), f_q)) + mstore(0xb40, mod(calldataload(0x8c0), f_q)) + mstore(0xb60, mod(calldataload(0x8e0), f_q)) + mstore(0xb80, mod(calldataload(0x900), f_q)) + mstore(0xba0, mod(calldataload(0x920), f_q)) + mstore(0xbc0, mod(calldataload(0x940), f_q)) + mstore(0xbe0, mod(calldataload(0x960), f_q)) + mstore(0xc00, mod(calldataload(0x980), f_q)) + mstore(0xc20, mod(calldataload(0x9a0), f_q)) + mstore(0xc40, mod(calldataload(0x9c0), f_q)) + mstore(0xc60, mod(calldataload(0x9e0), f_q)) + mstore(0xc80, mod(calldataload(0xa00), f_q)) + mstore(0xca0, mod(calldataload(0xa20), f_q)) + mstore(0xcc0, mod(calldataload(0xa40), f_q)) + mstore(0xce0, mod(calldataload(0xa60), f_q)) + mstore(0xd00, mod(calldataload(0xa80), f_q)) + mstore(0xd20, keccak256(0xaa0, 640)) + { + let hash := mload(0xd20) + mstore(0xd40, mod(hash, f_q)) + mstore(0xd60, hash) + } + mstore8(3456, 1) + mstore(0xd80, keccak256(0xd60, 33)) + { + let hash := mload(0xd80) + mstore(0xda0, mod(hash, f_q)) + mstore(0xdc0, hash) + } + + { + let x := calldataload(0xaa0) + mstore(0xde0, x) + let y := calldataload(0xac0) + mstore(0xe00, y) + success := and(validate_ec_point(x, y), success) + } + mstore(0xe20, keccak256(0xdc0, 96)) + { + let hash := mload(0xe20) + mstore(0xe40, mod(hash, f_q)) + mstore(0xe60, hash) + } + + { + let x := calldataload(0xae0) + mstore(0xe80, x) + let y := calldataload(0xb00) + mstore(0xea0, y) + success := and(validate_ec_point(x, y), success) + } + { + let x := mload(0xa0) + x := add(x, shl(88, mload(0xc0))) + x := add(x, shl(176, mload(0xe0))) + mstore(3776, x) + let y := mload(0x100) + y := add(y, shl(88, mload(0x120))) + y := add(y, shl(176, mload(0x140))) + mstore(3808, y) + + success := and(validate_ec_point(x, y), success) + } + { + let x := mload(0x160) + x := add(x, shl(88, mload(0x180))) + x := add(x, shl(176, mload(0x1a0))) + mstore(3840, x) + let y := mload(0x1c0) + y := add(y, shl(88, mload(0x1e0))) + y := add(y, shl(176, mload(0x200))) + mstore(3872, y) + + success := and(validate_ec_point(x, y), success) + } + mstore(0xf40, mulmod(mload(0xa80), mload(0xa80), f_q)) + mstore(0xf60, mulmod(mload(0xf40), mload(0xf40), f_q)) + mstore(0xf80, mulmod(mload(0xf60), mload(0xf60), f_q)) + mstore(0xfa0, mulmod(mload(0xf80), mload(0xf80), f_q)) + mstore(0xfc0, mulmod(mload(0xfa0), mload(0xfa0), f_q)) + mstore(0xfe0, mulmod(mload(0xfc0), mload(0xfc0), f_q)) + mstore(0x1000, mulmod(mload(0xfe0), mload(0xfe0), f_q)) + mstore(0x1020, mulmod(mload(0x1000), mload(0x1000), f_q)) + mstore(0x1040, mulmod(mload(0x1020), mload(0x1020), f_q)) + mstore(0x1060, mulmod(mload(0x1040), mload(0x1040), f_q)) + mstore(0x1080, mulmod(mload(0x1060), mload(0x1060), f_q)) + mstore(0x10a0, mulmod(mload(0x1080), mload(0x1080), f_q)) + mstore(0x10c0, mulmod(mload(0x10a0), mload(0x10a0), f_q)) + mstore(0x10e0, mulmod(mload(0x10c0), mload(0x10c0), f_q)) + mstore(0x1100, mulmod(mload(0x10e0), mload(0x10e0), f_q)) + mstore(0x1120, mulmod(mload(0x1100), mload(0x1100), f_q)) + mstore(0x1140, mulmod(mload(0x1120), mload(0x1120), f_q)) + mstore(0x1160, mulmod(mload(0x1140), mload(0x1140), f_q)) + mstore(0x1180, mulmod(mload(0x1160), mload(0x1160), f_q)) + mstore(0x11a0, mulmod(mload(0x1180), mload(0x1180), f_q)) + mstore(0x11c0, mulmod(mload(0x11a0), mload(0x11a0), f_q)) + mstore(0x11e0, mulmod(mload(0x11c0), mload(0x11c0), f_q)) + mstore( + 0x1200, + addmod( + mload(0x11e0), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q + ) + ) + mstore( + 0x1220, + mulmod( + mload(0x1200), 21888237653275510688422624196183639687472264873923820041627027729598873448513, f_q + ) + ) + mstore( + 0x1240, + mulmod( + mload(0x1220), 13225785879531581993054172815365636627224369411478295502904397545373139154045, f_q + ) + ) + mstore( + 0x1260, + addmod(mload(0xa80), 8662456992307693229192232929891638461323994988937738840793806641202669341572, f_q) + ) + mstore( + 0x1280, + mulmod( + mload(0x1220), 10939663269433627367777756708678102241564365262857670666700619874077960926249, f_q + ) + ) + mstore( + 0x12a0, + addmod(mload(0xa80), 10948579602405647854468649036579172846983999137558363676997584312497847569368, f_q) + ) + mstore( + 0x12c0, + mulmod( + mload(0x1220), 11016257578652593686382655500910603527869149377564754001549454008164059876499, f_q + ) + ) + mstore( + 0x12e0, + addmod(mload(0xa80), 10871985293186681535863750244346671560679215022851280342148750178411748619118, f_q) + ) + mstore( + 0x1300, + mulmod( + mload(0x1220), 15402826414547299628414612080036060696555554914079673875872749760617770134879, f_q + ) + ) + mstore( + 0x1320, + addmod(mload(0xa80), 6485416457291975593831793665221214391992809486336360467825454425958038360738, f_q) + ) + mstore( + 0x1340, + mulmod( + mload(0x1220), 21710372849001950800533397158415938114909991150039389063546734567764856596059, f_q + ) + ) + mstore( + 0x1360, + addmod(mload(0xa80), 177870022837324421713008586841336973638373250376645280151469618810951899558, f_q) + ) + mstore( + 0x1380, + mulmod(mload(0x1220), 2785514556381676080176937710880804108647911392478702105860685610379369825016, f_q) + ) + mstore( + 0x13a0, + addmod(mload(0xa80), 19102728315457599142069468034376470979900453007937332237837518576196438670601, f_q) + ) + mstore( + 0x13c0, + mulmod(mload(0x1220), 8734126352828345679573237859165904705806588461301144420590422589042130041188, f_q) + ) + mstore( + 0x13e0, + addmod(mload(0xa80), 13154116519010929542673167886091370382741775939114889923107781597533678454429, f_q) + ) + mstore(0x1400, mulmod(mload(0x1220), 1, f_q)) + mstore( + 0x1420, + addmod(mload(0xa80), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q) + ) + mstore( + 0x1440, + mulmod( + mload(0x1220), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q + ) + ) + mstore( + 0x1460, + addmod(mload(0xa80), 10676941854703594198666993839846402519342119846958189386823924046696287912227, f_q) + ) + mstore( + 0x1480, + mulmod(mload(0x1220), 1426404432721484388505361748317961535523355871255605456897797744433766488507, f_q) + ) + mstore( + 0x14a0, + addmod(mload(0xa80), 20461838439117790833741043996939313553025008529160428886800406442142042007110, f_q) + ) + mstore( + 0x14c0, + mulmod( + mload(0x1220), 12619617507853212586156872920672483948819476989779550311307282715684870266992, f_q + ) + ) + mstore( + 0x14e0, + addmod(mload(0xa80), 9268625363986062636089532824584791139728887410636484032390921470890938228625, f_q) + ) + mstore( + 0x1500, + mulmod( + mload(0x1220), 19032961837237948602743626455740240236231119053033140765040043513661803148152, f_q + ) + ) + mstore( + 0x1520, + addmod(mload(0xa80), 2855281034601326619502779289517034852317245347382893578658160672914005347465, f_q) + ) + mstore( + 0x1540, + mulmod(mload(0x1220), 915149353520972163646494413843788069594022902357002628455555785223409501882, f_q) + ) + mstore( + 0x1560, + addmod(mload(0xa80), 20973093518318303058599911331413487018954341498059031715242648401352398993735, f_q) + ) + mstore( + 0x1580, + mulmod(mload(0x1220), 3766081621734395783232337525162072736827576297943013392955872170138036189193, f_q) + ) + mstore( + 0x15a0, + addmod(mload(0xa80), 18122161250104879439014068220095202351720788102473020950742332016437772306424, f_q) + ) + mstore( + 0x15c0, + mulmod(mload(0x1220), 4245441013247250116003069945606352967193023389718465410501109428393342802981, f_q) + ) + mstore( + 0x15e0, + addmod(mload(0xa80), 17642801858592025106243335799650922121355341010697568933197094758182465692636, f_q) + ) + mstore( + 0x1600, + mulmod(mload(0x1220), 5854133144571823792863860130267644613802765696134002830362054821530146160770, f_q) + ) + mstore( + 0x1620, + addmod(mload(0xa80), 16034109727267451429382545614989630474745598704282031513336149365045662334847, f_q) + ) + mstore( + 0x1640, + mulmod(mload(0x1220), 5980488956150442207659150513163747165544364597008566989111579977672498964212, f_q) + ) + mstore( + 0x1660, + addmod(mload(0xa80), 15907753915688833014587255232093527923003999803407467354586624208903309531405, f_q) + ) + mstore( + 0x1680, + mulmod( + mload(0x1220), 14557038802599140430182096396825290815503940951075961210638273254419942783582, f_q + ) + ) + mstore( + 0x16a0, + addmod(mload(0xa80), 7331204069240134792064309348431984273044423449340073133059930932155865712035, f_q) + ) + mstore( + 0x16c0, + mulmod( + mload(0x1220), 13553911191894110065493137367144919847521088405945523452288398666974237857208, f_q + ) + ) + mstore( + 0x16e0, + addmod(mload(0xa80), 8334331679945165156753268378112355241027275994470510891409805519601570638409, f_q) + ) + mstore( + 0x1700, + mulmod(mload(0x1220), 9697063347556872083384215826199993067635178715531258559890418744774301211662, f_q) + ) + mstore( + 0x1720, + addmod(mload(0xa80), 12191179524282403138862189919057282020913185684884775783807785441801507283955, f_q) + ) + mstore( + 0x1740, + mulmod( + mload(0x1220), 10807735674816066981985242612061336605021639643453679977988966079770672437131, f_q + ) + ) + mstore( + 0x1760, + addmod(mload(0xa80), 11080507197023208240261163133195938483526724756962354365709238106805136058486, f_q) + ) + mstore( + 0x1780, + mulmod( + mload(0x1220), 12459868075641381822485233712013080087763946065665469821362892189399541605692, f_q + ) + ) + mstore( + 0x17a0, + addmod(mload(0xa80), 9428374796197893399761172033244195000784418334750564522335311997176266889925, f_q) + ) + mstore( + 0x17c0, + mulmod( + mload(0x1220), 16038300751658239075779628684257016433412502747804121525056508685985277092575, f_q + ) + ) + mstore( + 0x17e0, + addmod(mload(0xa80), 5849942120181036146466777061000258655135861652611912818641695500590531403042, f_q) + ) + mstore( + 0x1800, + mulmod(mload(0x1220), 6955697244493336113861667751840378876927906302623587437721024018233754910398, f_q) + ) + mstore( + 0x1820, + addmod(mload(0xa80), 14932545627345939108384737993416896211620458097792446905977180168342053585219, f_q) + ) + mstore( + 0x1840, + mulmod( + mload(0x1220), 13498745591877810872211159461644682954739332524336278910448604883789771736885, f_q + ) + ) + mstore( + 0x1860, + addmod(mload(0xa80), 8389497279961464350035246283612592133809031876079755433249599302786036758732, f_q) + ) + mstore( + 0x1880, + mulmod( + mload(0x1220), 20345677989844117909528750049476969581182118546166966482506114734614108237981, f_q + ) + ) + mstore( + 0x18a0, + addmod(mload(0xa80), 1542564881995157312717655695780305507366245854249067861192089451961700257636, f_q) + ) + mstore( + 0x18c0, + mulmod(mload(0x1220), 790608022292213379425324383664216541739009722347092850716054055768832299157, f_q) + ) + mstore( + 0x18e0, + addmod(mload(0xa80), 21097634849547061842821081361593058546809354678068941492982150130806976196460, f_q) + ) + mstore( + 0x1900, + mulmod(mload(0x1220), 5289443209903185443361862148540090689648485914368835830972895623576469023722, f_q) + ) + mstore( + 0x1920, + addmod(mload(0xa80), 16598799661936089778884543596717184398899878486047198512725308562999339471895, f_q) + ) + mstore( + 0x1940, + mulmod( + mload(0x1220), 15161189183906287273290738379431332336600234154579306802151507052820126345529, f_q + ) + ) + mstore( + 0x1960, + addmod(mload(0xa80), 6727053687932987948955667365825942751948130245836727541546697133755682150088, f_q) + ) + mstore( + 0x1980, + mulmod(mload(0x1220), 557567375339945239933617516585967620814823575807691402619711360028043331811, f_q) + ) + mstore( + 0x19a0, + addmod(mload(0xa80), 21330675496499329982312788228671307467733540824608342941078492826547765163806, f_q) + ) + mstore( + 0x19c0, + mulmod( + mload(0x1220), 16611719114775828483319365659907682366622074960672212059891361227499450055959, f_q + ) + ) + mstore( + 0x19e0, + addmod(mload(0xa80), 5276523757063446738927040085349592721926289439743822283806842959076358439658, f_q) + ) + mstore( + 0x1a00, + mulmod(mload(0x1220), 4509404676247677387317362072810231899718070082381452255950861037254608304934, f_q) + ) + mstore( + 0x1a20, + addmod(mload(0xa80), 17378838195591597834929043672447043188830294318034582087747343149321200190683, f_q) + ) + mstore( + 0x1a40, + mulmod(mload(0x1220), 6866457077948847028333856457654941632900463970069876241424363695212127143359, f_q) + ) + mstore( + 0x1a60, + addmod(mload(0xa80), 15021785793890428193912549287602333455647900430346158102273840491363681352258, f_q) + ) + mstore( + 0x1a80, + mulmod( + mload(0x1220), 20169013865622130318472103510465966222180994822334426398191891983290742724178, f_q + ) + ) + mstore( + 0x1aa0, + addmod(mload(0xa80), 1719229006217144903774302234791308866367369578081607945506312203285065771439, f_q) + ) + mstore( + 0x1ac0, + mulmod( + mload(0x1220), 14874205783542236433261764022044465911656512639684999678853651860683757650009, f_q + ) + ) + mstore( + 0x1ae0, + addmod(mload(0xa80), 7014037088297038788984641723212809176891851760731034664844552325892050845608, f_q) + ) + mstore( + 0x1b00, + mulmod(mload(0x1220), 2579947959091681244170407980400327834520881737801886423874592072501514087543, f_q) + ) + mstore( + 0x1b20, + addmod(mload(0xa80), 19308294912747593978075997764856947254027482662614147919823612114074294408074, f_q) + ) + mstore( + 0x1b40, + mulmod( + mload(0x1220), 17011225028452114973964561549541821925778010085385130152192105634715080939230, f_q + ) + ) + mstore( + 0x1b60, + addmod(mload(0xa80), 4877017843387160248281844195715453162770354315030904191506098551860727556387, f_q) + ) + mstore( + 0x1b80, + mulmod(mload(0x1220), 1881761935718519990121799628252273658786792458106649887437395059872945867717, f_q) + ) + mstore( + 0x1ba0, + addmod(mload(0xa80), 20006480936120755232124606117005001429761571942309384456260809126702862627900, f_q) + ) + mstore( + 0x1bc0, + mulmod( + mload(0x1220), 21662285561588145310352318480822402603888953131447478827940284064946709915517, f_q + ) + ) + mstore( + 0x1be0, + addmod(mload(0xa80), 225957310251129911894087264434872484659411268968555515757920121629098580100, f_q) + ) + mstore( + 0x1c00, + mulmod( + mload(0x1220), 21846745818185811051373434299876022191132089169516983080959277716660228899818, f_q + ) + ) + mstore( + 0x1c20, + addmod(mload(0xa80), 41497053653464170872971445381252897416275230899051262738926469915579595799, f_q) + ) + mstore( + 0x1c40, + mulmod( + mload(0x1220), 11770617947510597378885200406447716404126404817511323735042103519754393416137, f_q + ) + ) + mstore( + 0x1c60, + addmod(mload(0xa80), 10117624924328677843361205338809558684421959582904710608656100666821415079480, f_q) + ) + mstore( + 0x1c80, + mulmod( + mload(0x1220), 13018529307372270489258244406856841315962482733096074798317807775255504614069, f_q + ) + ) + mstore( + 0x1ca0, + addmod(mload(0xa80), 8869713564467004732988161338400433772585881667319959545380396411320303881548, f_q) + ) + mstore( + 0x1cc0, + mulmod(mload(0x1220), 5276270562549512946272803945594037128265390012927669941530122528135796334063, f_q) + ) + mstore( + 0x1ce0, + addmod(mload(0xa80), 16611972309289762275973601799663237960282974387488364402168081658440012161554, f_q) + ) + mstore( + 0x1d00, + mulmod(mload(0x1220), 1459528961030896569807206253631725410868595642414057264270714861278164633285, f_q) + ) + mstore( + 0x1d20, + addmod(mload(0xa80), 20428713910808378652439199491625549677679768758001977079427489325297643862332, f_q) + ) + mstore( + 0x1d40, + mulmod(mload(0x1220), 3194789416964050406424265110350613664596286587119568977604859939037397011192, f_q) + ) + mstore( + 0x1d60, + addmod(mload(0xa80), 18693453454875224815822140634906661423952077813296465366093344247538411484425, f_q) + ) + mstore( + 0x1d80, + mulmod(mload(0x1220), 3090451643741879200285099477849831179472024364989630500355756836624424014697, f_q) + ) + mstore( + 0x1da0, + addmod(mload(0xa80), 18797791228097396021961306267407443909076340035426403843342447349951384480920, f_q) + ) + mstore( + 0x1dc0, + mulmod( + mload(0x1220), 15927748781034921005593027077824543133423706442106451156060388409950986747549, f_q + ) + ) + mstore( + 0x1de0, + addmod(mload(0xa80), 5960494090804354216653378667432731955124657958309583187637815776624821748068, f_q) + ) + mstore( + 0x1e00, + mulmod( + mload(0x1220), 21594472933355353940227302948201802990541640451776958309590170926766063614527, f_q + ) + ) + mstore( + 0x1e20, + addmod(mload(0xa80), 293769938483921282019102797055472098006723948639076034108033259809744881090, f_q) + ) + mstore( + 0x1e40, + mulmod( + mload(0x1220), 18627493688178473377890450102960302362510276568110871848038317193719995024144, f_q + ) + ) + mstore( + 0x1e60, + addmod(mload(0xa80), 3260749183660801844355955642296972726038087832305162495659886992855813471473, f_q) + ) + mstore( + 0x1e80, + mulmod( + mload(0x1220), 15233875724801927436678555222002139405060841628305391430751578735629430475003, f_q + ) + ) + mstore( + 0x1ea0, + addmod(mload(0xa80), 6654367147037347785567850523255135683487522772110642912946625450946378020614, f_q) + ) + mstore( + 0x1ec0, + mulmod( + mload(0x1220), 12662796367122493153085459582914902083443981635312477834616629373139110863873, f_q + ) + ) + mstore( + 0x1ee0, + addmod(mload(0xa80), 9225446504716782069160946162342373005104382765103556509081574813436697631744, f_q) + ) + mstore( + 0x1f00, + mulmod(mload(0x1220), 9228489335593836417731216695316971397516686186585289059470421738439643366942, f_q) + ) + mstore( + 0x1f20, + addmod(mload(0xa80), 12659753536245438804515189049940303691031678213830745284227782448136165128675, f_q) + ) + mstore( + 0x1f40, + mulmod(mload(0x1220), 6904960663187367776878651408524770307710353971752548687936010869699798414796, f_q) + ) + mstore( + 0x1f60, + addmod(mload(0xa80), 14983282208651907445367754336732504780838010428663485655762193316876010080821, f_q) + ) + { + let prod := mload(0x1260) + + prod := mulmod(mload(0x12a0), prod, f_q) + mstore(0x1f80, prod) + + prod := mulmod(mload(0x12e0), prod, f_q) + mstore(0x1fa0, prod) + + prod := mulmod(mload(0x1320), prod, f_q) + mstore(0x1fc0, prod) + + prod := mulmod(mload(0x1360), prod, f_q) + mstore(0x1fe0, prod) + + prod := mulmod(mload(0x13a0), prod, f_q) + mstore(0x2000, prod) + + prod := mulmod(mload(0x13e0), prod, f_q) + mstore(0x2020, prod) + + prod := mulmod(mload(0x1420), prod, f_q) + mstore(0x2040, prod) + + prod := mulmod(mload(0x1460), prod, f_q) + mstore(0x2060, prod) + + prod := mulmod(mload(0x14a0), prod, f_q) + mstore(0x2080, prod) + + prod := mulmod(mload(0x14e0), prod, f_q) + mstore(0x20a0, prod) + + prod := mulmod(mload(0x1520), prod, f_q) + mstore(0x20c0, prod) + + prod := mulmod(mload(0x1560), prod, f_q) + mstore(0x20e0, prod) + + prod := mulmod(mload(0x15a0), prod, f_q) + mstore(0x2100, prod) + + prod := mulmod(mload(0x15e0), prod, f_q) + mstore(0x2120, prod) + + prod := mulmod(mload(0x1620), prod, f_q) + mstore(0x2140, prod) + + prod := mulmod(mload(0x1660), prod, f_q) + mstore(0x2160, prod) + + prod := mulmod(mload(0x16a0), prod, f_q) + mstore(0x2180, prod) + + prod := mulmod(mload(0x16e0), prod, f_q) + mstore(0x21a0, prod) + + prod := mulmod(mload(0x1720), prod, f_q) + mstore(0x21c0, prod) + + prod := mulmod(mload(0x1760), prod, f_q) + mstore(0x21e0, prod) + + prod := mulmod(mload(0x17a0), prod, f_q) + mstore(0x2200, prod) + + prod := mulmod(mload(0x17e0), prod, f_q) + mstore(0x2220, prod) + + prod := mulmod(mload(0x1820), prod, f_q) + mstore(0x2240, prod) + + prod := mulmod(mload(0x1860), prod, f_q) + mstore(0x2260, prod) + + prod := mulmod(mload(0x18a0), prod, f_q) + mstore(0x2280, prod) + + prod := mulmod(mload(0x18e0), prod, f_q) + mstore(0x22a0, prod) + + prod := mulmod(mload(0x1920), prod, f_q) + mstore(0x22c0, prod) + + prod := mulmod(mload(0x1960), prod, f_q) + mstore(0x22e0, prod) + + prod := mulmod(mload(0x19a0), prod, f_q) + mstore(0x2300, prod) + + prod := mulmod(mload(0x19e0), prod, f_q) + mstore(0x2320, prod) + + prod := mulmod(mload(0x1a20), prod, f_q) + mstore(0x2340, prod) + + prod := mulmod(mload(0x1a60), prod, f_q) + mstore(0x2360, prod) + + prod := mulmod(mload(0x1aa0), prod, f_q) + mstore(0x2380, prod) + + prod := mulmod(mload(0x1ae0), prod, f_q) + mstore(0x23a0, prod) + + prod := mulmod(mload(0x1b20), prod, f_q) + mstore(0x23c0, prod) + + prod := mulmod(mload(0x1b60), prod, f_q) + mstore(0x23e0, prod) + + prod := mulmod(mload(0x1ba0), prod, f_q) + mstore(0x2400, prod) + + prod := mulmod(mload(0x1be0), prod, f_q) + mstore(0x2420, prod) + + prod := mulmod(mload(0x1c20), prod, f_q) + mstore(0x2440, prod) + + prod := mulmod(mload(0x1c60), prod, f_q) + mstore(0x2460, prod) + + prod := mulmod(mload(0x1ca0), prod, f_q) + mstore(0x2480, prod) + + prod := mulmod(mload(0x1ce0), prod, f_q) + mstore(0x24a0, prod) + + prod := mulmod(mload(0x1d20), prod, f_q) + mstore(0x24c0, prod) + + prod := mulmod(mload(0x1d60), prod, f_q) + mstore(0x24e0, prod) + + prod := mulmod(mload(0x1da0), prod, f_q) + mstore(0x2500, prod) + + prod := mulmod(mload(0x1de0), prod, f_q) + mstore(0x2520, prod) + + prod := mulmod(mload(0x1e20), prod, f_q) + mstore(0x2540, prod) + + prod := mulmod(mload(0x1e60), prod, f_q) + mstore(0x2560, prod) + + prod := mulmod(mload(0x1ea0), prod, f_q) + mstore(0x2580, prod) + + prod := mulmod(mload(0x1ee0), prod, f_q) + mstore(0x25a0, prod) + + prod := mulmod(mload(0x1f20), prod, f_q) + mstore(0x25c0, prod) + + prod := mulmod(mload(0x1f60), prod, f_q) + mstore(0x25e0, prod) + + prod := mulmod(mload(0x1200), prod, f_q) + mstore(0x2600, prod) + } + mstore(0x2640, 32) + mstore(0x2660, 32) + mstore(0x2680, 32) + mstore(0x26a0, mload(0x2600)) + mstore(0x26c0, 21888242871839275222246405745257275088548364400416034343698204186575808495615) + mstore(0x26e0, 21888242871839275222246405745257275088548364400416034343698204186575808495617) + success := and(eq(staticcall(gas(), 0x5, 0x2640, 0xc0, 0x2620, 0x20), 1), success) + { + let inv := mload(0x2620) + let v + + v := mload(0x1200) + mstore(4608, mulmod(mload(0x25e0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1f60) + mstore(8032, mulmod(mload(0x25c0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1f20) + mstore(7968, mulmod(mload(0x25a0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1ee0) + mstore(7904, mulmod(mload(0x2580), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1ea0) + mstore(7840, mulmod(mload(0x2560), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1e60) + mstore(7776, mulmod(mload(0x2540), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1e20) + mstore(7712, mulmod(mload(0x2520), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1de0) + mstore(7648, mulmod(mload(0x2500), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1da0) + mstore(7584, mulmod(mload(0x24e0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1d60) + mstore(7520, mulmod(mload(0x24c0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1d20) + mstore(7456, mulmod(mload(0x24a0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1ce0) + mstore(7392, mulmod(mload(0x2480), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1ca0) + mstore(7328, mulmod(mload(0x2460), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1c60) + mstore(7264, mulmod(mload(0x2440), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1c20) + mstore(7200, mulmod(mload(0x2420), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1be0) + mstore(7136, mulmod(mload(0x2400), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1ba0) + mstore(7072, mulmod(mload(0x23e0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1b60) + mstore(7008, mulmod(mload(0x23c0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1b20) + mstore(6944, mulmod(mload(0x23a0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1ae0) + mstore(6880, mulmod(mload(0x2380), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1aa0) + mstore(6816, mulmod(mload(0x2360), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1a60) + mstore(6752, mulmod(mload(0x2340), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1a20) + mstore(6688, mulmod(mload(0x2320), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x19e0) + mstore(6624, mulmod(mload(0x2300), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x19a0) + mstore(6560, mulmod(mload(0x22e0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1960) + mstore(6496, mulmod(mload(0x22c0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1920) + mstore(6432, mulmod(mload(0x22a0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x18e0) + mstore(6368, mulmod(mload(0x2280), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x18a0) + mstore(6304, mulmod(mload(0x2260), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1860) + mstore(6240, mulmod(mload(0x2240), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1820) + mstore(6176, mulmod(mload(0x2220), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x17e0) + mstore(6112, mulmod(mload(0x2200), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x17a0) + mstore(6048, mulmod(mload(0x21e0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1760) + mstore(5984, mulmod(mload(0x21c0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1720) + mstore(5920, mulmod(mload(0x21a0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x16e0) + mstore(5856, mulmod(mload(0x2180), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x16a0) + mstore(5792, mulmod(mload(0x2160), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1660) + mstore(5728, mulmod(mload(0x2140), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1620) + mstore(5664, mulmod(mload(0x2120), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x15e0) + mstore(5600, mulmod(mload(0x2100), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x15a0) + mstore(5536, mulmod(mload(0x20e0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1560) + mstore(5472, mulmod(mload(0x20c0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1520) + mstore(5408, mulmod(mload(0x20a0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x14e0) + mstore(5344, mulmod(mload(0x2080), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x14a0) + mstore(5280, mulmod(mload(0x2060), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1460) + mstore(5216, mulmod(mload(0x2040), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1420) + mstore(5152, mulmod(mload(0x2020), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x13e0) + mstore(5088, mulmod(mload(0x2000), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x13a0) + mstore(5024, mulmod(mload(0x1fe0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1360) + mstore(4960, mulmod(mload(0x1fc0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x1320) + mstore(4896, mulmod(mload(0x1fa0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x12e0) + mstore(4832, mulmod(mload(0x1f80), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x12a0) + mstore(4768, mulmod(mload(0x1260), inv, f_q)) + inv := mulmod(v, inv, f_q) + mstore(0x1260, inv) + } + mstore(0x2700, mulmod(mload(0x1240), mload(0x1260), f_q)) + mstore(0x2720, mulmod(mload(0x1280), mload(0x12a0), f_q)) + mstore(0x2740, mulmod(mload(0x12c0), mload(0x12e0), f_q)) + mstore(0x2760, mulmod(mload(0x1300), mload(0x1320), f_q)) + mstore(0x2780, mulmod(mload(0x1340), mload(0x1360), f_q)) + mstore(0x27a0, mulmod(mload(0x1380), mload(0x13a0), f_q)) + mstore(0x27c0, mulmod(mload(0x13c0), mload(0x13e0), f_q)) + mstore(0x27e0, mulmod(mload(0x1400), mload(0x1420), f_q)) + mstore(0x2800, mulmod(mload(0x1440), mload(0x1460), f_q)) + mstore(0x2820, mulmod(mload(0x1480), mload(0x14a0), f_q)) + mstore(0x2840, mulmod(mload(0x14c0), mload(0x14e0), f_q)) + mstore(0x2860, mulmod(mload(0x1500), mload(0x1520), f_q)) + mstore(0x2880, mulmod(mload(0x1540), mload(0x1560), f_q)) + mstore(0x28a0, mulmod(mload(0x1580), mload(0x15a0), f_q)) + mstore(0x28c0, mulmod(mload(0x15c0), mload(0x15e0), f_q)) + mstore(0x28e0, mulmod(mload(0x1600), mload(0x1620), f_q)) + mstore(0x2900, mulmod(mload(0x1640), mload(0x1660), f_q)) + mstore(0x2920, mulmod(mload(0x1680), mload(0x16a0), f_q)) + mstore(0x2940, mulmod(mload(0x16c0), mload(0x16e0), f_q)) + mstore(0x2960, mulmod(mload(0x1700), mload(0x1720), f_q)) + mstore(0x2980, mulmod(mload(0x1740), mload(0x1760), f_q)) + mstore(0x29a0, mulmod(mload(0x1780), mload(0x17a0), f_q)) + mstore(0x29c0, mulmod(mload(0x17c0), mload(0x17e0), f_q)) + mstore(0x29e0, mulmod(mload(0x1800), mload(0x1820), f_q)) + mstore(0x2a00, mulmod(mload(0x1840), mload(0x1860), f_q)) + mstore(0x2a20, mulmod(mload(0x1880), mload(0x18a0), f_q)) + mstore(0x2a40, mulmod(mload(0x18c0), mload(0x18e0), f_q)) + mstore(0x2a60, mulmod(mload(0x1900), mload(0x1920), f_q)) + mstore(0x2a80, mulmod(mload(0x1940), mload(0x1960), f_q)) + mstore(0x2aa0, mulmod(mload(0x1980), mload(0x19a0), f_q)) + mstore(0x2ac0, mulmod(mload(0x19c0), mload(0x19e0), f_q)) + mstore(0x2ae0, mulmod(mload(0x1a00), mload(0x1a20), f_q)) + mstore(0x2b00, mulmod(mload(0x1a40), mload(0x1a60), f_q)) + mstore(0x2b20, mulmod(mload(0x1a80), mload(0x1aa0), f_q)) + mstore(0x2b40, mulmod(mload(0x1ac0), mload(0x1ae0), f_q)) + mstore(0x2b60, mulmod(mload(0x1b00), mload(0x1b20), f_q)) + mstore(0x2b80, mulmod(mload(0x1b40), mload(0x1b60), f_q)) + mstore(0x2ba0, mulmod(mload(0x1b80), mload(0x1ba0), f_q)) + mstore(0x2bc0, mulmod(mload(0x1bc0), mload(0x1be0), f_q)) + mstore(0x2be0, mulmod(mload(0x1c00), mload(0x1c20), f_q)) + mstore(0x2c00, mulmod(mload(0x1c40), mload(0x1c60), f_q)) + mstore(0x2c20, mulmod(mload(0x1c80), mload(0x1ca0), f_q)) + mstore(0x2c40, mulmod(mload(0x1cc0), mload(0x1ce0), f_q)) + mstore(0x2c60, mulmod(mload(0x1d00), mload(0x1d20), f_q)) + mstore(0x2c80, mulmod(mload(0x1d40), mload(0x1d60), f_q)) + mstore(0x2ca0, mulmod(mload(0x1d80), mload(0x1da0), f_q)) + mstore(0x2cc0, mulmod(mload(0x1dc0), mload(0x1de0), f_q)) + mstore(0x2ce0, mulmod(mload(0x1e00), mload(0x1e20), f_q)) + mstore(0x2d00, mulmod(mload(0x1e40), mload(0x1e60), f_q)) + mstore(0x2d20, mulmod(mload(0x1e80), mload(0x1ea0), f_q)) + mstore(0x2d40, mulmod(mload(0x1ec0), mload(0x1ee0), f_q)) + mstore(0x2d60, mulmod(mload(0x1f00), mload(0x1f20), f_q)) + mstore(0x2d80, mulmod(mload(0x1f40), mload(0x1f60), f_q)) + { + let result := mulmod(mload(0x27e0), mload(0xa0), f_q) + result := addmod(mulmod(mload(0x2800), mload(0xc0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2820), mload(0xe0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2840), mload(0x100), f_q), result, f_q) + result := addmod(mulmod(mload(0x2860), mload(0x120), f_q), result, f_q) + result := addmod(mulmod(mload(0x2880), mload(0x140), f_q), result, f_q) + result := addmod(mulmod(mload(0x28a0), mload(0x160), f_q), result, f_q) + result := addmod(mulmod(mload(0x28c0), mload(0x180), f_q), result, f_q) + result := addmod(mulmod(mload(0x28e0), mload(0x1a0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2900), mload(0x1c0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2920), mload(0x1e0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2940), mload(0x200), f_q), result, f_q) + result := addmod(mulmod(mload(0x2960), mload(0x220), f_q), result, f_q) + result := addmod(mulmod(mload(0x2980), mload(0x240), f_q), result, f_q) + result := addmod(mulmod(mload(0x29a0), mload(0x260), f_q), result, f_q) + result := addmod(mulmod(mload(0x29c0), mload(0x280), f_q), result, f_q) + result := addmod(mulmod(mload(0x29e0), mload(0x2a0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2a00), mload(0x2c0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2a20), mload(0x2e0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2a40), mload(0x300), f_q), result, f_q) + result := addmod(mulmod(mload(0x2a60), mload(0x320), f_q), result, f_q) + result := addmod(mulmod(mload(0x2a80), mload(0x340), f_q), result, f_q) + result := addmod(mulmod(mload(0x2aa0), mload(0x360), f_q), result, f_q) + result := addmod(mulmod(mload(0x2ac0), mload(0x380), f_q), result, f_q) + result := addmod(mulmod(mload(0x2ae0), mload(0x3a0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2b00), mload(0x3c0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2b20), mload(0x3e0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2b40), mload(0x400), f_q), result, f_q) + result := addmod(mulmod(mload(0x2b60), mload(0x420), f_q), result, f_q) + result := addmod(mulmod(mload(0x2b80), mload(0x440), f_q), result, f_q) + result := addmod(mulmod(mload(0x2ba0), mload(0x460), f_q), result, f_q) + result := addmod(mulmod(mload(0x2bc0), mload(0x480), f_q), result, f_q) + result := addmod(mulmod(mload(0x2be0), mload(0x4a0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2c00), mload(0x4c0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2c20), mload(0x4e0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2c40), mload(0x500), f_q), result, f_q) + result := addmod(mulmod(mload(0x2c60), mload(0x520), f_q), result, f_q) + result := addmod(mulmod(mload(0x2c80), mload(0x540), f_q), result, f_q) + result := addmod(mulmod(mload(0x2ca0), mload(0x560), f_q), result, f_q) + result := addmod(mulmod(mload(0x2cc0), mload(0x580), f_q), result, f_q) + result := addmod(mulmod(mload(0x2ce0), mload(0x5a0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2d00), mload(0x5c0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2d20), mload(0x5e0), f_q), result, f_q) + result := addmod(mulmod(mload(0x2d40), mload(0x600), f_q), result, f_q) + result := addmod(mulmod(mload(0x2d60), mload(0x620), f_q), result, f_q) + result := addmod(mulmod(mload(0x2d80), mload(0x640), f_q), result, f_q) + mstore(11680, result) + } + mstore(0x2dc0, mulmod(mload(0xb00), mload(0xae0), f_q)) + mstore(0x2de0, addmod(mload(0xac0), mload(0x2dc0), f_q)) + mstore(0x2e00, addmod(mload(0x2de0), sub(f_q, mload(0xb20)), f_q)) + mstore(0x2e20, mulmod(mload(0x2e00), mload(0xb80), f_q)) + mstore(0x2e40, mulmod(mload(0x920), mload(0x2e20), f_q)) + mstore(0x2e60, addmod(1, sub(f_q, mload(0xc40)), f_q)) + mstore(0x2e80, mulmod(mload(0x2e60), mload(0x27e0), f_q)) + mstore(0x2ea0, addmod(mload(0x2e40), mload(0x2e80), f_q)) + mstore(0x2ec0, mulmod(mload(0x920), mload(0x2ea0), f_q)) + mstore(0x2ee0, mulmod(mload(0xc40), mload(0xc40), f_q)) + mstore(0x2f00, addmod(mload(0x2ee0), sub(f_q, mload(0xc40)), f_q)) + mstore(0x2f20, mulmod(mload(0x2f00), mload(0x2700), f_q)) + mstore(0x2f40, addmod(mload(0x2ec0), mload(0x2f20), f_q)) + mstore(0x2f60, mulmod(mload(0x920), mload(0x2f40), f_q)) + mstore(0x2f80, addmod(1, sub(f_q, mload(0x2700)), f_q)) + mstore(0x2fa0, addmod(mload(0x2720), mload(0x2740), f_q)) + mstore(0x2fc0, addmod(mload(0x2fa0), mload(0x2760), f_q)) + mstore(0x2fe0, addmod(mload(0x2fc0), mload(0x2780), f_q)) + mstore(0x3000, addmod(mload(0x2fe0), mload(0x27a0), f_q)) + mstore(0x3020, addmod(mload(0x3000), mload(0x27c0), f_q)) + mstore(0x3040, addmod(mload(0x2f80), sub(f_q, mload(0x3020)), f_q)) + mstore(0x3060, mulmod(mload(0xbe0), mload(0x7a0), f_q)) + mstore(0x3080, addmod(mload(0xb40), mload(0x3060), f_q)) + mstore(0x30a0, addmod(mload(0x3080), mload(0x800), f_q)) + mstore(0x30c0, mulmod(mload(0xc00), mload(0x7a0), f_q)) + mstore(0x30e0, addmod(mload(0xac0), mload(0x30c0), f_q)) + mstore(0x3100, addmod(mload(0x30e0), mload(0x800), f_q)) + mstore(0x3120, mulmod(mload(0x3100), mload(0x30a0), f_q)) + mstore(0x3140, mulmod(mload(0xc20), mload(0x7a0), f_q)) + mstore(0x3160, addmod(mload(0x2da0), mload(0x3140), f_q)) + mstore(0x3180, addmod(mload(0x3160), mload(0x800), f_q)) + mstore(0x31a0, mulmod(mload(0x3180), mload(0x3120), f_q)) + mstore(0x31c0, mulmod(mload(0x31a0), mload(0xc60), f_q)) + mstore(0x31e0, mulmod(1, mload(0x7a0), f_q)) + mstore(0x3200, mulmod(mload(0xa80), mload(0x31e0), f_q)) + mstore(0x3220, addmod(mload(0xb40), mload(0x3200), f_q)) + mstore(0x3240, addmod(mload(0x3220), mload(0x800), f_q)) + mstore( + 0x3260, + mulmod(4131629893567559867359510883348571134090853742863529169391034518566172092834, mload(0x7a0), f_q) + ) + mstore(0x3280, mulmod(mload(0xa80), mload(0x3260), f_q)) + mstore(0x32a0, addmod(mload(0xac0), mload(0x3280), f_q)) + mstore(0x32c0, addmod(mload(0x32a0), mload(0x800), f_q)) + mstore(0x32e0, mulmod(mload(0x32c0), mload(0x3240), f_q)) + mstore( + 0x3300, + mulmod(8910878055287538404433155982483128285667088683464058436815641868457422632747, mload(0x7a0), f_q) + ) + mstore(0x3320, mulmod(mload(0xa80), mload(0x3300), f_q)) + mstore(0x3340, addmod(mload(0x2da0), mload(0x3320), f_q)) + mstore(0x3360, addmod(mload(0x3340), mload(0x800), f_q)) + mstore(0x3380, mulmod(mload(0x3360), mload(0x32e0), f_q)) + mstore(0x33a0, mulmod(mload(0x3380), mload(0xc40), f_q)) + mstore(0x33c0, addmod(mload(0x31c0), sub(f_q, mload(0x33a0)), f_q)) + mstore(0x33e0, mulmod(mload(0x33c0), mload(0x3040), f_q)) + mstore(0x3400, addmod(mload(0x2f60), mload(0x33e0), f_q)) + mstore(0x3420, mulmod(mload(0x920), mload(0x3400), f_q)) + mstore(0x3440, addmod(1, sub(f_q, mload(0xc80)), f_q)) + mstore(0x3460, mulmod(mload(0x3440), mload(0x27e0), f_q)) + mstore(0x3480, addmod(mload(0x3420), mload(0x3460), f_q)) + mstore(0x34a0, mulmod(mload(0x920), mload(0x3480), f_q)) + mstore(0x34c0, mulmod(mload(0xc80), mload(0xc80), f_q)) + mstore(0x34e0, addmod(mload(0x34c0), sub(f_q, mload(0xc80)), f_q)) + mstore(0x3500, mulmod(mload(0x34e0), mload(0x2700), f_q)) + mstore(0x3520, addmod(mload(0x34a0), mload(0x3500), f_q)) + mstore(0x3540, mulmod(mload(0x920), mload(0x3520), f_q)) + mstore(0x3560, addmod(mload(0xcc0), mload(0x7a0), f_q)) + mstore(0x3580, mulmod(mload(0x3560), mload(0xca0), f_q)) + mstore(0x35a0, addmod(mload(0xd00), mload(0x800), f_q)) + mstore(0x35c0, mulmod(mload(0x35a0), mload(0x3580), f_q)) + mstore(0x35e0, mulmod(mload(0xac0), mload(0xba0), f_q)) + mstore(0x3600, addmod(mload(0x35e0), mload(0x7a0), f_q)) + mstore(0x3620, mulmod(mload(0x3600), mload(0xc80), f_q)) + mstore(0x3640, addmod(mload(0xb60), mload(0x800), f_q)) + mstore(0x3660, mulmod(mload(0x3640), mload(0x3620), f_q)) + mstore(0x3680, addmod(mload(0x35c0), sub(f_q, mload(0x3660)), f_q)) + mstore(0x36a0, mulmod(mload(0x3680), mload(0x3040), f_q)) + mstore(0x36c0, addmod(mload(0x3540), mload(0x36a0), f_q)) + mstore(0x36e0, mulmod(mload(0x920), mload(0x36c0), f_q)) + mstore(0x3700, addmod(mload(0xcc0), sub(f_q, mload(0xd00)), f_q)) + mstore(0x3720, mulmod(mload(0x3700), mload(0x27e0), f_q)) + mstore(0x3740, addmod(mload(0x36e0), mload(0x3720), f_q)) + mstore(0x3760, mulmod(mload(0x920), mload(0x3740), f_q)) + mstore(0x3780, mulmod(mload(0x3700), mload(0x3040), f_q)) + mstore(0x37a0, addmod(mload(0xcc0), sub(f_q, mload(0xce0)), f_q)) + mstore(0x37c0, mulmod(mload(0x37a0), mload(0x3780), f_q)) + mstore(0x37e0, addmod(mload(0x3760), mload(0x37c0), f_q)) + mstore(0x3800, mulmod(mload(0x11e0), mload(0x11e0), f_q)) + mstore(0x3820, mulmod(mload(0x3800), mload(0x11e0), f_q)) + mstore(0x3840, mulmod(mload(0x3820), mload(0x11e0), f_q)) + mstore(0x3860, mulmod(1, mload(0x11e0), f_q)) + mstore(0x3880, mulmod(1, mload(0x3800), f_q)) + mstore(0x38a0, mulmod(1, mload(0x3820), f_q)) + mstore(0x38c0, mulmod(mload(0x37e0), mload(0x1200), f_q)) + mstore(0x38e0, mulmod(mload(0xf40), mload(0xa80), f_q)) + mstore(0x3900, mulmod(mload(0x38e0), mload(0xa80), f_q)) + mstore( + 0x3920, + mulmod(mload(0xa80), 8734126352828345679573237859165904705806588461301144420590422589042130041188, f_q) + ) + mstore(0x3940, addmod(mload(0xe40), sub(f_q, mload(0x3920)), f_q)) + mstore(0x3960, mulmod(mload(0xa80), 1, f_q)) + mstore(0x3980, addmod(mload(0xe40), sub(f_q, mload(0x3960)), f_q)) + mstore( + 0x39a0, + mulmod(mload(0xa80), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q) + ) + mstore(0x39c0, addmod(mload(0xe40), sub(f_q, mload(0x39a0)), f_q)) + mstore( + 0x39e0, + mulmod(mload(0xa80), 1426404432721484388505361748317961535523355871255605456897797744433766488507, f_q) + ) + mstore(0x3a00, addmod(mload(0xe40), sub(f_q, mload(0x39e0)), f_q)) + mstore( + 0x3a20, + mulmod(mload(0xa80), 12619617507853212586156872920672483948819476989779550311307282715684870266992, f_q) + ) + mstore(0x3a40, addmod(mload(0xe40), sub(f_q, mload(0x3a20)), f_q)) + mstore( + 0x3a60, + mulmod(3544324119167359571073009690693121464267965232733679586767649244433889388945, mload(0x38e0), f_q) + ) + mstore(0x3a80, mulmod(mload(0x3a60), 1, f_q)) + { + let result := mulmod(mload(0xe40), mload(0x3a60), f_q) + result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3a80)), f_q), result, f_q) + mstore(15008, result) + } + mstore( + 0x3ac0, + mulmod(3860370625838117017501327045244227871206764201116468958063324100051382735289, mload(0x38e0), f_q) + ) + mstore( + 0x3ae0, + mulmod( + mload(0x3ac0), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q + ) + ) + { + let result := mulmod(mload(0xe40), mload(0x3ac0), f_q) + result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3ae0)), f_q), result, f_q) + mstore(15104, result) + } + mstore( + 0x3b20, + mulmod( + 21616901807277407275624036604424346159916096890712898844034238973395610537327, mload(0x38e0), f_q + ) + ) + mstore( + 0x3b40, + mulmod(mload(0x3b20), 1426404432721484388505361748317961535523355871255605456897797744433766488507, f_q) + ) + { + let result := mulmod(mload(0xe40), mload(0x3b20), f_q) + result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3b40)), f_q), result, f_q) + mstore(15200, result) + } + mstore( + 0x3b80, + mulmod(3209408481237076479025468386201293941554240476766691830436732310949352383503, mload(0x38e0), f_q) + ) + mstore( + 0x3ba0, + mulmod( + mload(0x3b80), 12619617507853212586156872920672483948819476989779550311307282715684870266992, f_q + ) + ) + { + let result := mulmod(mload(0xe40), mload(0x3b80), f_q) + result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3ba0)), f_q), result, f_q) + mstore(15296, result) + } + mstore(0x3be0, mulmod(1, mload(0x3980), f_q)) + mstore(0x3c00, mulmod(mload(0x3be0), mload(0x39c0), f_q)) + mstore(0x3c20, mulmod(mload(0x3c00), mload(0x3a00), f_q)) + mstore(0x3c40, mulmod(mload(0x3c20), mload(0x3a40), f_q)) + mstore( + 0x3c60, + mulmod(10676941854703594198666993839846402519342119846958189386823924046696287912228, mload(0xa80), f_q) + ) + mstore(0x3c80, mulmod(mload(0x3c60), 1, f_q)) + { + let result := mulmod(mload(0xe40), mload(0x3c60), f_q) + result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3c80)), f_q), result, f_q) + mstore(15520, result) + } + mstore( + 0x3cc0, + mulmod(11211301017135681023579411905410872569206244553457844956874280139879520583389, mload(0xa80), f_q) + ) + mstore( + 0x3ce0, + mulmod( + mload(0x3cc0), 11211301017135681023579411905410872569206244553457844956874280139879520583390, f_q + ) + ) + { + let result := mulmod(mload(0xe40), mload(0x3cc0), f_q) + result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3ce0)), f_q), result, f_q) + mstore(15616, result) + } + mstore( + 0x3d20, + mulmod(13154116519010929542673167886091370382741775939114889923107781597533678454430, mload(0xa80), f_q) + ) + mstore(0x3d40, mulmod(mload(0x3d20), 1, f_q)) + { + let result := mulmod(mload(0xe40), mload(0x3d20), f_q) + result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3d40)), f_q), result, f_q) + mstore(15712, result) + } + mstore( + 0x3d80, + mulmod(8734126352828345679573237859165904705806588461301144420590422589042130041187, mload(0xa80), f_q) + ) + mstore( + 0x3da0, + mulmod(mload(0x3d80), 8734126352828345679573237859165904705806588461301144420590422589042130041188, f_q) + ) + { + let result := mulmod(mload(0xe40), mload(0x3d80), f_q) + result := addmod(mulmod(mload(0xa80), sub(f_q, mload(0x3da0)), f_q), result, f_q) + mstore(15808, result) + } + mstore(0x3de0, mulmod(mload(0x3be0), mload(0x3940), f_q)) + { + let result := mulmod(mload(0xe40), 1, f_q) + result := + addmod( + mulmod( + mload(0xa80), 21888242871839275222246405745257275088548364400416034343698204186575808495616, f_q + ), + result, + f_q + ) + mstore(15872, result) + } + { + let prod := mload(0x3aa0) + + prod := mulmod(mload(0x3b00), prod, f_q) + mstore(0x3e20, prod) + + prod := mulmod(mload(0x3b60), prod, f_q) + mstore(0x3e40, prod) + + prod := mulmod(mload(0x3bc0), prod, f_q) + mstore(0x3e60, prod) + + prod := mulmod(mload(0x3ca0), prod, f_q) + mstore(0x3e80, prod) + + prod := mulmod(mload(0x3d00), prod, f_q) + mstore(0x3ea0, prod) + + prod := mulmod(mload(0x3c00), prod, f_q) + mstore(0x3ec0, prod) + + prod := mulmod(mload(0x3d60), prod, f_q) + mstore(0x3ee0, prod) + + prod := mulmod(mload(0x3dc0), prod, f_q) + mstore(0x3f00, prod) + + prod := mulmod(mload(0x3de0), prod, f_q) + mstore(0x3f20, prod) + + prod := mulmod(mload(0x3e00), prod, f_q) + mstore(0x3f40, prod) + + prod := mulmod(mload(0x3be0), prod, f_q) + mstore(0x3f60, prod) + } + mstore(0x3fa0, 32) + mstore(0x3fc0, 32) + mstore(0x3fe0, 32) + mstore(0x4000, mload(0x3f60)) + mstore(0x4020, 21888242871839275222246405745257275088548364400416034343698204186575808495615) + mstore(0x4040, 21888242871839275222246405745257275088548364400416034343698204186575808495617) + success := and(eq(staticcall(gas(), 0x5, 0x3fa0, 0xc0, 0x3f80, 0x20), 1), success) + { + let inv := mload(0x3f80) + let v + + v := mload(0x3be0) + mstore(15328, mulmod(mload(0x3f40), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3e00) + mstore(15872, mulmod(mload(0x3f20), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3de0) + mstore(15840, mulmod(mload(0x3f00), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3dc0) + mstore(15808, mulmod(mload(0x3ee0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3d60) + mstore(15712, mulmod(mload(0x3ec0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3c00) + mstore(15360, mulmod(mload(0x3ea0), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3d00) + mstore(15616, mulmod(mload(0x3e80), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3ca0) + mstore(15520, mulmod(mload(0x3e60), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3bc0) + mstore(15296, mulmod(mload(0x3e40), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3b60) + mstore(15200, mulmod(mload(0x3e20), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x3b00) + mstore(15104, mulmod(mload(0x3aa0), inv, f_q)) + inv := mulmod(v, inv, f_q) + mstore(0x3aa0, inv) + } + { + let result := mload(0x3aa0) + result := addmod(mload(0x3b00), result, f_q) + result := addmod(mload(0x3b60), result, f_q) + result := addmod(mload(0x3bc0), result, f_q) + mstore(16480, result) + } + mstore(0x4080, mulmod(mload(0x3c40), mload(0x3c00), f_q)) + { + let result := mload(0x3ca0) + result := addmod(mload(0x3d00), result, f_q) + mstore(16544, result) + } + mstore(0x40c0, mulmod(mload(0x3c40), mload(0x3de0), f_q)) + { + let result := mload(0x3d60) + result := addmod(mload(0x3dc0), result, f_q) + mstore(16608, result) + } + mstore(0x4100, mulmod(mload(0x3c40), mload(0x3be0), f_q)) + { + let result := mload(0x3e00) + mstore(16672, result) + } + { + let prod := mload(0x4060) + + prod := mulmod(mload(0x40a0), prod, f_q) + mstore(0x4140, prod) + + prod := mulmod(mload(0x40e0), prod, f_q) + mstore(0x4160, prod) + + prod := mulmod(mload(0x4120), prod, f_q) + mstore(0x4180, prod) + } + mstore(0x41c0, 32) + mstore(0x41e0, 32) + mstore(0x4200, 32) + mstore(0x4220, mload(0x4180)) + mstore(0x4240, 21888242871839275222246405745257275088548364400416034343698204186575808495615) + mstore(0x4260, 21888242871839275222246405745257275088548364400416034343698204186575808495617) + success := and(eq(staticcall(gas(), 0x5, 0x41c0, 0xc0, 0x41a0, 0x20), 1), success) + { + let inv := mload(0x41a0) + let v + + v := mload(0x4120) + mstore(16672, mulmod(mload(0x4160), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x40e0) + mstore(16608, mulmod(mload(0x4140), inv, f_q)) + inv := mulmod(v, inv, f_q) + + v := mload(0x40a0) + mstore(16544, mulmod(mload(0x4060), inv, f_q)) + inv := mulmod(v, inv, f_q) + mstore(0x4060, inv) + } + mstore(0x4280, mulmod(mload(0x4080), mload(0x40a0), f_q)) + mstore(0x42a0, mulmod(mload(0x40c0), mload(0x40e0), f_q)) + mstore(0x42c0, mulmod(mload(0x4100), mload(0x4120), f_q)) + mstore(0x42e0, mulmod(mload(0xd40), mload(0xd40), f_q)) + mstore(0x4300, mulmod(mload(0x42e0), mload(0xd40), f_q)) + mstore(0x4320, mulmod(mload(0x4300), mload(0xd40), f_q)) + mstore(0x4340, mulmod(mload(0x4320), mload(0xd40), f_q)) + mstore(0x4360, mulmod(mload(0x4340), mload(0xd40), f_q)) + mstore(0x4380, mulmod(mload(0x4360), mload(0xd40), f_q)) + mstore(0x43a0, mulmod(mload(0x4380), mload(0xd40), f_q)) + mstore(0x43c0, mulmod(mload(0x43a0), mload(0xd40), f_q)) + mstore(0x43e0, mulmod(mload(0x43c0), mload(0xd40), f_q)) + mstore(0x4400, mulmod(mload(0xda0), mload(0xda0), f_q)) + mstore(0x4420, mulmod(mload(0x4400), mload(0xda0), f_q)) + mstore(0x4440, mulmod(mload(0x4420), mload(0xda0), f_q)) + { + let result := mulmod(mload(0xac0), mload(0x3aa0), f_q) + result := addmod(mulmod(mload(0xae0), mload(0x3b00), f_q), result, f_q) + result := addmod(mulmod(mload(0xb00), mload(0x3b60), f_q), result, f_q) + result := addmod(mulmod(mload(0xb20), mload(0x3bc0), f_q), result, f_q) + mstore(17504, result) + } + mstore(0x4480, mulmod(mload(0x4460), mload(0x4060), f_q)) + mstore(0x44a0, mulmod(sub(f_q, mload(0x4480)), 1, f_q)) + mstore(0x44c0, mulmod(mload(0x44a0), 1, f_q)) + mstore(0x44e0, mulmod(1, mload(0x4080), f_q)) + { + let result := mulmod(mload(0xc40), mload(0x3ca0), f_q) + result := addmod(mulmod(mload(0xc60), mload(0x3d00), f_q), result, f_q) + mstore(17664, result) + } + mstore(0x4520, mulmod(mload(0x4500), mload(0x4280), f_q)) + mstore(0x4540, mulmod(sub(f_q, mload(0x4520)), 1, f_q)) + mstore(0x4560, mulmod(mload(0x44e0), 1, f_q)) + { + let result := mulmod(mload(0xc80), mload(0x3ca0), f_q) + result := addmod(mulmod(mload(0xca0), mload(0x3d00), f_q), result, f_q) + mstore(17792, result) + } + mstore(0x45a0, mulmod(mload(0x4580), mload(0x4280), f_q)) + mstore(0x45c0, mulmod(sub(f_q, mload(0x45a0)), mload(0xd40), f_q)) + mstore(0x45e0, mulmod(mload(0x44e0), mload(0xd40), f_q)) + mstore(0x4600, addmod(mload(0x4540), mload(0x45c0), f_q)) + mstore(0x4620, mulmod(mload(0x4600), mload(0xda0), f_q)) + mstore(0x4640, mulmod(mload(0x4560), mload(0xda0), f_q)) + mstore(0x4660, mulmod(mload(0x45e0), mload(0xda0), f_q)) + mstore(0x4680, addmod(mload(0x44c0), mload(0x4620), f_q)) + mstore(0x46a0, mulmod(1, mload(0x40c0), f_q)) + { + let result := mulmod(mload(0xcc0), mload(0x3d60), f_q) + result := addmod(mulmod(mload(0xce0), mload(0x3dc0), f_q), result, f_q) + mstore(18112, result) + } + mstore(0x46e0, mulmod(mload(0x46c0), mload(0x42a0), f_q)) + mstore(0x4700, mulmod(sub(f_q, mload(0x46e0)), 1, f_q)) + mstore(0x4720, mulmod(mload(0x46a0), 1, f_q)) + mstore(0x4740, mulmod(mload(0x4700), mload(0x4400), f_q)) + mstore(0x4760, mulmod(mload(0x4720), mload(0x4400), f_q)) + mstore(0x4780, addmod(mload(0x4680), mload(0x4740), f_q)) + mstore(0x47a0, mulmod(1, mload(0x4100), f_q)) + { + let result := mulmod(mload(0xd00), mload(0x3e00), f_q) + mstore(18368, result) + } + mstore(0x47e0, mulmod(mload(0x47c0), mload(0x42c0), f_q)) + mstore(0x4800, mulmod(sub(f_q, mload(0x47e0)), 1, f_q)) + mstore(0x4820, mulmod(mload(0x47a0), 1, f_q)) + { + let result := mulmod(mload(0xb40), mload(0x3e00), f_q) + mstore(18496, result) + } + mstore(0x4860, mulmod(mload(0x4840), mload(0x42c0), f_q)) + mstore(0x4880, mulmod(sub(f_q, mload(0x4860)), mload(0xd40), f_q)) + mstore(0x48a0, mulmod(mload(0x47a0), mload(0xd40), f_q)) + mstore(0x48c0, addmod(mload(0x4800), mload(0x4880), f_q)) + { + let result := mulmod(mload(0xb60), mload(0x3e00), f_q) + mstore(18656, result) + } + mstore(0x4900, mulmod(mload(0x48e0), mload(0x42c0), f_q)) + mstore(0x4920, mulmod(sub(f_q, mload(0x4900)), mload(0x42e0), f_q)) + mstore(0x4940, mulmod(mload(0x47a0), mload(0x42e0), f_q)) + mstore(0x4960, addmod(mload(0x48c0), mload(0x4920), f_q)) + { + let result := mulmod(mload(0xb80), mload(0x3e00), f_q) + mstore(18816, result) + } + mstore(0x49a0, mulmod(mload(0x4980), mload(0x42c0), f_q)) + mstore(0x49c0, mulmod(sub(f_q, mload(0x49a0)), mload(0x4300), f_q)) + mstore(0x49e0, mulmod(mload(0x47a0), mload(0x4300), f_q)) + mstore(0x4a00, addmod(mload(0x4960), mload(0x49c0), f_q)) + { + let result := mulmod(mload(0xba0), mload(0x3e00), f_q) + mstore(18976, result) + } + mstore(0x4a40, mulmod(mload(0x4a20), mload(0x42c0), f_q)) + mstore(0x4a60, mulmod(sub(f_q, mload(0x4a40)), mload(0x4320), f_q)) + mstore(0x4a80, mulmod(mload(0x47a0), mload(0x4320), f_q)) + mstore(0x4aa0, addmod(mload(0x4a00), mload(0x4a60), f_q)) + { + let result := mulmod(mload(0xbe0), mload(0x3e00), f_q) + mstore(19136, result) + } + mstore(0x4ae0, mulmod(mload(0x4ac0), mload(0x42c0), f_q)) + mstore(0x4b00, mulmod(sub(f_q, mload(0x4ae0)), mload(0x4340), f_q)) + mstore(0x4b20, mulmod(mload(0x47a0), mload(0x4340), f_q)) + mstore(0x4b40, addmod(mload(0x4aa0), mload(0x4b00), f_q)) + { + let result := mulmod(mload(0xc00), mload(0x3e00), f_q) + mstore(19296, result) + } + mstore(0x4b80, mulmod(mload(0x4b60), mload(0x42c0), f_q)) + mstore(0x4ba0, mulmod(sub(f_q, mload(0x4b80)), mload(0x4360), f_q)) + mstore(0x4bc0, mulmod(mload(0x47a0), mload(0x4360), f_q)) + mstore(0x4be0, addmod(mload(0x4b40), mload(0x4ba0), f_q)) + { + let result := mulmod(mload(0xc20), mload(0x3e00), f_q) + mstore(19456, result) + } + mstore(0x4c20, mulmod(mload(0x4c00), mload(0x42c0), f_q)) + mstore(0x4c40, mulmod(sub(f_q, mload(0x4c20)), mload(0x4380), f_q)) + mstore(0x4c60, mulmod(mload(0x47a0), mload(0x4380), f_q)) + mstore(0x4c80, addmod(mload(0x4be0), mload(0x4c40), f_q)) + mstore(0x4ca0, mulmod(mload(0x3860), mload(0x4100), f_q)) + mstore(0x4cc0, mulmod(mload(0x3880), mload(0x4100), f_q)) + mstore(0x4ce0, mulmod(mload(0x38a0), mload(0x4100), f_q)) + { + let result := mulmod(mload(0x38c0), mload(0x3e00), f_q) + mstore(19712, result) + } + mstore(0x4d20, mulmod(mload(0x4d00), mload(0x42c0), f_q)) + mstore(0x4d40, mulmod(sub(f_q, mload(0x4d20)), mload(0x43a0), f_q)) + mstore(0x4d60, mulmod(mload(0x47a0), mload(0x43a0), f_q)) + mstore(0x4d80, mulmod(mload(0x4ca0), mload(0x43a0), f_q)) + mstore(0x4da0, mulmod(mload(0x4cc0), mload(0x43a0), f_q)) + mstore(0x4dc0, mulmod(mload(0x4ce0), mload(0x43a0), f_q)) + mstore(0x4de0, addmod(mload(0x4c80), mload(0x4d40), f_q)) + { + let result := mulmod(mload(0xbc0), mload(0x3e00), f_q) + mstore(19968, result) + } + mstore(0x4e20, mulmod(mload(0x4e00), mload(0x42c0), f_q)) + mstore(0x4e40, mulmod(sub(f_q, mload(0x4e20)), mload(0x43c0), f_q)) + mstore(0x4e60, mulmod(mload(0x47a0), mload(0x43c0), f_q)) + mstore(0x4e80, addmod(mload(0x4de0), mload(0x4e40), f_q)) + mstore(0x4ea0, mulmod(mload(0x4e80), mload(0x4420), f_q)) + mstore(0x4ec0, mulmod(mload(0x4820), mload(0x4420), f_q)) + mstore(0x4ee0, mulmod(mload(0x48a0), mload(0x4420), f_q)) + mstore(0x4f00, mulmod(mload(0x4940), mload(0x4420), f_q)) + mstore(0x4f20, mulmod(mload(0x49e0), mload(0x4420), f_q)) + mstore(0x4f40, mulmod(mload(0x4a80), mload(0x4420), f_q)) + mstore(0x4f60, mulmod(mload(0x4b20), mload(0x4420), f_q)) + mstore(0x4f80, mulmod(mload(0x4bc0), mload(0x4420), f_q)) + mstore(0x4fa0, mulmod(mload(0x4c60), mload(0x4420), f_q)) + mstore(0x4fc0, mulmod(mload(0x4d60), mload(0x4420), f_q)) + mstore(0x4fe0, mulmod(mload(0x4d80), mload(0x4420), f_q)) + mstore(0x5000, mulmod(mload(0x4da0), mload(0x4420), f_q)) + mstore(0x5020, mulmod(mload(0x4dc0), mload(0x4420), f_q)) + mstore(0x5040, mulmod(mload(0x4e60), mload(0x4420), f_q)) + mstore(0x5060, addmod(mload(0x4780), mload(0x4ea0), f_q)) + mstore(0x5080, mulmod(1, mload(0x3c40), f_q)) + mstore(0x50a0, mulmod(1, mload(0xe40), f_q)) + mstore(0x50c0, 0x0000000000000000000000000000000000000000000000000000000000000001) + mstore(0x50e0, 0x0000000000000000000000000000000000000000000000000000000000000002) + mstore(0x5100, mload(0x5060)) + success := and(eq(staticcall(gas(), 0x7, 0x50c0, 0x60, 0x50c0, 0x40), 1), success) + mstore(0x5120, mload(0x50c0)) + mstore(0x5140, mload(0x50e0)) + mstore(0x5160, mload(0x660)) + mstore(0x5180, mload(0x680)) + success := and(eq(staticcall(gas(), 0x6, 0x5120, 0x80, 0x5120, 0x40), 1), success) + mstore(0x51a0, mload(0x840)) + mstore(0x51c0, mload(0x860)) + mstore(0x51e0, mload(0x4640)) + success := and(eq(staticcall(gas(), 0x7, 0x51a0, 0x60, 0x51a0, 0x40), 1), success) + mstore(0x5200, mload(0x5120)) + mstore(0x5220, mload(0x5140)) + mstore(0x5240, mload(0x51a0)) + mstore(0x5260, mload(0x51c0)) + success := and(eq(staticcall(gas(), 0x6, 0x5200, 0x80, 0x5200, 0x40), 1), success) + mstore(0x5280, mload(0x880)) + mstore(0x52a0, mload(0x8a0)) + mstore(0x52c0, mload(0x4660)) + success := and(eq(staticcall(gas(), 0x7, 0x5280, 0x60, 0x5280, 0x40), 1), success) + mstore(0x52e0, mload(0x5200)) + mstore(0x5300, mload(0x5220)) + mstore(0x5320, mload(0x5280)) + mstore(0x5340, mload(0x52a0)) + success := and(eq(staticcall(gas(), 0x6, 0x52e0, 0x80, 0x52e0, 0x40), 1), success) + mstore(0x5360, mload(0x700)) + mstore(0x5380, mload(0x720)) + mstore(0x53a0, mload(0x4760)) + success := and(eq(staticcall(gas(), 0x7, 0x5360, 0x60, 0x5360, 0x40), 1), success) + mstore(0x53c0, mload(0x52e0)) + mstore(0x53e0, mload(0x5300)) + mstore(0x5400, mload(0x5360)) + mstore(0x5420, mload(0x5380)) + success := and(eq(staticcall(gas(), 0x6, 0x53c0, 0x80, 0x53c0, 0x40), 1), success) + mstore(0x5440, mload(0x740)) + mstore(0x5460, mload(0x760)) + mstore(0x5480, mload(0x4ec0)) + success := and(eq(staticcall(gas(), 0x7, 0x5440, 0x60, 0x5440, 0x40), 1), success) + mstore(0x54a0, mload(0x53c0)) + mstore(0x54c0, mload(0x53e0)) + mstore(0x54e0, mload(0x5440)) + mstore(0x5500, mload(0x5460)) + success := and(eq(staticcall(gas(), 0x6, 0x54a0, 0x80, 0x54a0, 0x40), 1), success) + mstore(0x5520, 0x1701d83c9ad410f3d61886c39b7b8a618f42219a1404264208f838c1672b8e71) + mstore(0x5540, 0x0868e69339d5d3bbd747b6e7772a9760a7277c0993a420c2848808b15de0424c) + mstore(0x5560, mload(0x4ee0)) + success := and(eq(staticcall(gas(), 0x7, 0x5520, 0x60, 0x5520, 0x40), 1), success) + mstore(0x5580, mload(0x54a0)) + mstore(0x55a0, mload(0x54c0)) + mstore(0x55c0, mload(0x5520)) + mstore(0x55e0, mload(0x5540)) + success := and(eq(staticcall(gas(), 0x6, 0x5580, 0x80, 0x5580, 0x40), 1), success) + mstore(0x5600, 0x21c6ea7d6dbcd767ffb9d9beeb4f9c2f8243bc65290f2d75a59aea4f65ba8f3d) + mstore(0x5620, 0x24d0a0acb031c9a5687da08cdaf96650aae5c60435739bda8bbd574eb962622c) + mstore(0x5640, mload(0x4f00)) + success := and(eq(staticcall(gas(), 0x7, 0x5600, 0x60, 0x5600, 0x40), 1), success) + mstore(0x5660, mload(0x5580)) + mstore(0x5680, mload(0x55a0)) + mstore(0x56a0, mload(0x5600)) + mstore(0x56c0, mload(0x5620)) + success := and(eq(staticcall(gas(), 0x6, 0x5660, 0x80, 0x5660, 0x40), 1), success) + mstore(0x56e0, 0x289feda4952fe4464c9716d071e291bbecdcd5432356042dc79b76ed38cbbb0d) + mstore(0x5700, 0x07f3ca14a8801fa413462ad72ea448da5d7cf8c5218534cdc39bb23779b70bb9) + mstore(0x5720, mload(0x4f20)) + success := and(eq(staticcall(gas(), 0x7, 0x56e0, 0x60, 0x56e0, 0x40), 1), success) + mstore(0x5740, mload(0x5660)) + mstore(0x5760, mload(0x5680)) + mstore(0x5780, mload(0x56e0)) + mstore(0x57a0, mload(0x5700)) + success := and(eq(staticcall(gas(), 0x6, 0x5740, 0x80, 0x5740, 0x40), 1), success) + mstore(0x57c0, 0x259670bd2f2f6fce3b18100f92a2e59874da3b66a9ddd61e163eb4b071e24a97) + mstore(0x57e0, 0x097f07272f7ca89070ad9c06d9a3da1bb6e91d0e69bf7872f44cc5d332291eb5) + mstore(0x5800, mload(0x4f40)) + success := and(eq(staticcall(gas(), 0x7, 0x57c0, 0x60, 0x57c0, 0x40), 1), success) + mstore(0x5820, mload(0x5740)) + mstore(0x5840, mload(0x5760)) + mstore(0x5860, mload(0x57c0)) + mstore(0x5880, mload(0x57e0)) + success := and(eq(staticcall(gas(), 0x6, 0x5820, 0x80, 0x5820, 0x40), 1), success) + mstore(0x58a0, 0x26eaa658ef237183330c716cf9d5d978d8618788db2551142e1a9af837ecc810) + mstore(0x58c0, 0x1fa793c6b0f3df4a8a1017daa531a45fdfa8b5da433308f71716565f8b85aa7d) + mstore(0x58e0, mload(0x4f60)) + success := and(eq(staticcall(gas(), 0x7, 0x58a0, 0x60, 0x58a0, 0x40), 1), success) + mstore(0x5900, mload(0x5820)) + mstore(0x5920, mload(0x5840)) + mstore(0x5940, mload(0x58a0)) + mstore(0x5960, mload(0x58c0)) + success := and(eq(staticcall(gas(), 0x6, 0x5900, 0x80, 0x5900, 0x40), 1), success) + mstore(0x5980, 0x186694283d847cec7609ca9b642f46fc751a61e3ac330841cdf6a938a3439c02) + mstore(0x59a0, 0x16eb0141342ab5f9ca76fcaef38c222f51dd1afd5d45943e3676ba0c2ceb4f6d) + mstore(0x59c0, mload(0x4f80)) + success := and(eq(staticcall(gas(), 0x7, 0x5980, 0x60, 0x5980, 0x40), 1), success) + mstore(0x59e0, mload(0x5900)) + mstore(0x5a00, mload(0x5920)) + mstore(0x5a20, mload(0x5980)) + mstore(0x5a40, mload(0x59a0)) + success := and(eq(staticcall(gas(), 0x6, 0x59e0, 0x80, 0x59e0, 0x40), 1), success) + mstore(0x5a60, 0x0f76818adad3c635f139fde306f33aba7249952bdbbf72cf477a51f9d84f3ccc) + mstore(0x5a80, 0x23f89f9896b1cc39de92659098ea839186ff2e997ffa3413c1a7af4f31abe4ce) + mstore(0x5aa0, mload(0x4fa0)) + success := and(eq(staticcall(gas(), 0x7, 0x5a60, 0x60, 0x5a60, 0x40), 1), success) + mstore(0x5ac0, mload(0x59e0)) + mstore(0x5ae0, mload(0x5a00)) + mstore(0x5b00, mload(0x5a60)) + mstore(0x5b20, mload(0x5a80)) + success := and(eq(staticcall(gas(), 0x6, 0x5ac0, 0x80, 0x5ac0, 0x40), 1), success) + mstore(0x5b40, mload(0x960)) + mstore(0x5b60, mload(0x980)) + mstore(0x5b80, mload(0x4fc0)) + success := and(eq(staticcall(gas(), 0x7, 0x5b40, 0x60, 0x5b40, 0x40), 1), success) + mstore(0x5ba0, mload(0x5ac0)) + mstore(0x5bc0, mload(0x5ae0)) + mstore(0x5be0, mload(0x5b40)) + mstore(0x5c00, mload(0x5b60)) + success := and(eq(staticcall(gas(), 0x6, 0x5ba0, 0x80, 0x5ba0, 0x40), 1), success) + mstore(0x5c20, mload(0x9a0)) + mstore(0x5c40, mload(0x9c0)) + mstore(0x5c60, mload(0x4fe0)) + success := and(eq(staticcall(gas(), 0x7, 0x5c20, 0x60, 0x5c20, 0x40), 1), success) + mstore(0x5c80, mload(0x5ba0)) + mstore(0x5ca0, mload(0x5bc0)) + mstore(0x5cc0, mload(0x5c20)) + mstore(0x5ce0, mload(0x5c40)) + success := and(eq(staticcall(gas(), 0x6, 0x5c80, 0x80, 0x5c80, 0x40), 1), success) + mstore(0x5d00, mload(0x9e0)) + mstore(0x5d20, mload(0xa00)) + mstore(0x5d40, mload(0x5000)) + success := and(eq(staticcall(gas(), 0x7, 0x5d00, 0x60, 0x5d00, 0x40), 1), success) + mstore(0x5d60, mload(0x5c80)) + mstore(0x5d80, mload(0x5ca0)) + mstore(0x5da0, mload(0x5d00)) + mstore(0x5dc0, mload(0x5d20)) + success := and(eq(staticcall(gas(), 0x6, 0x5d60, 0x80, 0x5d60, 0x40), 1), success) + mstore(0x5de0, mload(0xa20)) + mstore(0x5e00, mload(0xa40)) + mstore(0x5e20, mload(0x5020)) + success := and(eq(staticcall(gas(), 0x7, 0x5de0, 0x60, 0x5de0, 0x40), 1), success) + mstore(0x5e40, mload(0x5d60)) + mstore(0x5e60, mload(0x5d80)) + mstore(0x5e80, mload(0x5de0)) + mstore(0x5ea0, mload(0x5e00)) + success := and(eq(staticcall(gas(), 0x6, 0x5e40, 0x80, 0x5e40, 0x40), 1), success) + mstore(0x5ec0, mload(0x8c0)) + mstore(0x5ee0, mload(0x8e0)) + mstore(0x5f00, mload(0x5040)) + success := and(eq(staticcall(gas(), 0x7, 0x5ec0, 0x60, 0x5ec0, 0x40), 1), success) + mstore(0x5f20, mload(0x5e40)) + mstore(0x5f40, mload(0x5e60)) + mstore(0x5f60, mload(0x5ec0)) + mstore(0x5f80, mload(0x5ee0)) + success := and(eq(staticcall(gas(), 0x6, 0x5f20, 0x80, 0x5f20, 0x40), 1), success) + mstore(0x5fa0, mload(0xde0)) + mstore(0x5fc0, mload(0xe00)) + mstore(0x5fe0, sub(f_q, mload(0x5080))) + success := and(eq(staticcall(gas(), 0x7, 0x5fa0, 0x60, 0x5fa0, 0x40), 1), success) + mstore(0x6000, mload(0x5f20)) + mstore(0x6020, mload(0x5f40)) + mstore(0x6040, mload(0x5fa0)) + mstore(0x6060, mload(0x5fc0)) + success := and(eq(staticcall(gas(), 0x6, 0x6000, 0x80, 0x6000, 0x40), 1), success) + mstore(0x6080, mload(0xe80)) + mstore(0x60a0, mload(0xea0)) + mstore(0x60c0, mload(0x50a0)) + success := and(eq(staticcall(gas(), 0x7, 0x6080, 0x60, 0x6080, 0x40), 1), success) + mstore(0x60e0, mload(0x6000)) + mstore(0x6100, mload(0x6020)) + mstore(0x6120, mload(0x6080)) + mstore(0x6140, mload(0x60a0)) + success := and(eq(staticcall(gas(), 0x6, 0x60e0, 0x80, 0x60e0, 0x40), 1), success) + mstore(0x6160, mload(0x60e0)) + mstore(0x6180, mload(0x6100)) + mstore(0x61a0, mload(0xe80)) + mstore(0x61c0, mload(0xea0)) + mstore(0x61e0, mload(0xec0)) + mstore(0x6200, mload(0xee0)) + mstore(0x6220, mload(0xf00)) + mstore(0x6240, mload(0xf20)) + mstore(0x6260, keccak256(0x6160, 256)) + mstore(25216, mod(mload(25184), f_q)) + mstore(0x62a0, mulmod(mload(0x6280), mload(0x6280), f_q)) + mstore(0x62c0, mulmod(1, mload(0x6280), f_q)) + mstore(0x62e0, mload(0x61e0)) + mstore(0x6300, mload(0x6200)) + mstore(0x6320, mload(0x62c0)) + success := and(eq(staticcall(gas(), 0x7, 0x62e0, 0x60, 0x62e0, 0x40), 1), success) + mstore(0x6340, mload(0x6160)) + mstore(0x6360, mload(0x6180)) + mstore(0x6380, mload(0x62e0)) + mstore(0x63a0, mload(0x6300)) + success := and(eq(staticcall(gas(), 0x6, 0x6340, 0x80, 0x6340, 0x40), 1), success) + mstore(0x63c0, mload(0x6220)) + mstore(0x63e0, mload(0x6240)) + mstore(0x6400, mload(0x62c0)) + success := and(eq(staticcall(gas(), 0x7, 0x63c0, 0x60, 0x63c0, 0x40), 1), success) + mstore(0x6420, mload(0x61a0)) + mstore(0x6440, mload(0x61c0)) + mstore(0x6460, mload(0x63c0)) + mstore(0x6480, mload(0x63e0)) + success := and(eq(staticcall(gas(), 0x6, 0x6420, 0x80, 0x6420, 0x40), 1), success) + mstore(0x64a0, mload(0x6340)) + mstore(0x64c0, mload(0x6360)) + mstore(0x64e0, 0x198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2) + mstore(0x6500, 0x1800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed) + mstore(0x6520, 0x090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b) + mstore(0x6540, 0x12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa) + mstore(0x6560, mload(0x6420)) + mstore(0x6580, mload(0x6440)) + mstore(0x65a0, 0x172aa93c41f16e1e04d62ac976a5d945f4be0acab990c6dc19ac4a7cf68bf77b) + mstore(0x65c0, 0x2ae0c8c3a090f7200ff398ee9845bbae8f8c1445ae7b632212775f60a0e21600) + mstore(0x65e0, 0x190fa476a5b352809ed41d7a0d7fe12b8f685e3c12a6d83855dba27aaf469643) + mstore(0x6600, 0x1c0a500618907df9e4273d5181e31088deb1f05132de037cbfe73888f97f77c9) + success := and(eq(staticcall(gas(), 0x8, 0x64a0, 0x180, 0x64a0, 0x20), 1), success) + success := and(eq(mload(0x64a0), 1), success) + + // Revert if anything fails + if iszero(success) { revert(0, 0) } + + // Return empty bytes on success + return(0, 0) + } + } +} diff --git a/src/OpenVmHalo2Verifier.sol b/src/v1.0.1/OpenVmHalo2Verifier.sol similarity index 63% rename from src/OpenVmHalo2Verifier.sol rename to src/v1.0.1/OpenVmHalo2Verifier.sol index 185ff17..ef41b44 100644 --- a/src/OpenVmHalo2Verifier.sol +++ b/src/v1.0.1/OpenVmHalo2Verifier.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity 0.8.19; import { Halo2Verifier } from "./Halo2Verifier.sol"; @@ -9,58 +9,58 @@ type MemoryPointer is uint256; /// @notice This contract provides a thin wrapper around the Halo2 verifier /// outputted by `snark-verifier`, exposing a more user-friendly interface. contract OpenVmHalo2Verifier is Halo2Verifier, IOpenVmHalo2Verifier { - /// @dev Invalid proof data length - error InvalidProofDataLength(); - /// @dev Invalid public values length - error InvalidPublicValuesLength(); + error InvalidPublicValuesLength(uint256 expected, uint256 actual); + + /// @dev Invalid proof data length + error InvalidProofDataLength(uint256 expected, uint256 actual); /// @dev Proof verification failed error ProofVerificationFailed(); - /// @dev The length of the proof data, in bytes + /// @dev The length of the proof data, in bytes. uint256 private constant PROOF_DATA_LENGTH = (12 + 43) * 32; - /// @dev The length of the public values, in bytes. This value is set by OpenVM. + /// @dev The length of the public values, in bytes. This value is set by + /// OpenVM and is guaranteed to be no larger than 8192. uint256 private constant PUBLIC_VALUES_LENGTH = 32; /// @dev The length of the full proof, in bytes uint256 private constant FULL_PROOF_LENGTH = (12 + 2 + PUBLIC_VALUES_LENGTH + 43) * 32; - /// @dev The leaf verifier commitment. This value is set by OpenVM. - bytes32 public constant LEAF_EXE_COMMIT = - bytes32(0x0071628bff0dcb64201f77ff5c7d869c7073b842e3dadf9e618e8673ef671bfd); - - /// @dev The version of OpenVM that generated the proof. - string public constant OPENVM_VERSION = "v1.0.0"; + /// @dev The version of OpenVM that generated this verifier. + string public constant OPENVM_VERSION = "1.0.1-rc.0"; /// @notice A wrapper that constructs the proof into the right format for /// use with the `snark-verifier` verification. /// - /// @dev This function assumes that `publicValues` encodes one `bytes32` - /// hash which is the hash of the public values. - /// - /// The verifier expected proof format is: - /// proof[..12 * 32]: KZG accumulators + /// @dev The verifier expected proof format is: + /// proof[..12 * 32]: KZG accumulator /// proof[12 * 32..13 * 32]: app exe commit - /// proof[13 * 32..14 * 32]: leaf exe commit + /// proof[13 * 32..14 * 32]: app vm commit /// proof[14 * 32..(14 + PUBLIC_VALUES_LENGTH) * 32]: publicValues[0..PUBLIC_VALUES_LENGTH] - /// proof[(14 + PUBLIC_VALUES_LENGTH) * 32..]: Public Values Suffix + /// proof[(14 + PUBLIC_VALUES_LENGTH) * 32..]: Proof Suffix /// /// @param publicValues The PVs revealed by the OpenVM guest program. - /// @param proofData All components of the proof except the public values, - /// leaf and app exe commits. The expected format is: - /// `abi.encodePacked(kzgAccumulators, proofSuffix)` + /// @param proofData All components of the proof except the public values and + /// app exe and vm commits. The expected format is: + /// `abi.encodePacked(kzgAccumulator, proofSuffix)` /// @param appExeCommit The commitment to the OpenVM application executable whose execution /// is being verified. - function verify(bytes calldata publicValues, bytes calldata proofData, bytes32 appExeCommit) external view { - if (publicValues.length != PUBLIC_VALUES_LENGTH) revert InvalidPublicValuesLength(); - if (proofData.length != PROOF_DATA_LENGTH) revert InvalidProofDataLength(); + /// @param appVmCommit The commitment to the VM configuration. + function verify(bytes calldata publicValues, bytes calldata proofData, bytes32 appExeCommit, bytes32 appVmCommit) + external + view + { + if (publicValues.length != PUBLIC_VALUES_LENGTH) { + revert InvalidPublicValuesLength(PUBLIC_VALUES_LENGTH, publicValues.length); + } + if (proofData.length != PROOF_DATA_LENGTH) revert InvalidProofDataLength(PROOF_DATA_LENGTH, proofData.length); // We will format the public values and construct the full proof payload // below. - MemoryPointer proofPtr = _constructProof(publicValues, proofData, appExeCommit); + MemoryPointer proofPtr = _constructProof(publicValues, proofData, appExeCommit, appVmCommit); uint256 fullProofLength = FULL_PROOF_LENGTH; @@ -79,33 +79,33 @@ contract OpenVmHalo2Verifier is Halo2Verifier, IOpenVmHalo2Verifier { // /// ```solidity /// bytes memory proof = - /// abi.encodePacked(proofData[0:0x180], appExeCommit, leafExeCommit, publicValuesPayload, proofData[0x180:]); + /// abi.encodePacked(proofData[0:0x180], appExeCommit, appVmCommit, publicValuesPayload, proofData[0x180:]); /// ``` // /// where `publicValuesPayload` is a memory payload with each byte in /// `publicValues` separated into its own `bytes32` word. /// /// This function does not clean the memory it allocates. Since it is the - /// only memory allocation that occurs in the call frame, we know that - /// the memory region was not written to before. + /// only memory write that occurs in the call frame, we know that + /// the memory region cannot have been dirtied. /// /// @return proofPtr Memory pointer to the beginning of the constructed - /// proof. - function _constructProof(bytes calldata publicValues, bytes calldata proofData, bytes32 appExeCommit) - internal - pure - returns (MemoryPointer proofPtr) - { + /// proof. This pointer does not follow `bytes memory` semantics. + function _constructProof( + bytes calldata publicValues, + bytes calldata proofData, + bytes32 appExeCommit, + bytes32 appVmCommit + ) internal pure returns (MemoryPointer proofPtr) { uint256 fullProofLength = FULL_PROOF_LENGTH; - bytes32 leafExeCommit = LEAF_EXE_COMMIT; // The expected proof format using hex offsets: // - // proof[..0x180]: KZG accumulators + // proof[..0x180]: KZG accumulator // proof[0x180..0x1a0]: app exe commit - // proof[0x1a0..0x1c0]: leaf exe commit + // proof[0x1a0..0x1c0]: app vm commit // proof[0x1c0..(0x1c0 + PUBLIC_VALUES_LENGTH * 32)]: publicValues[0..PUBLIC_VALUES_LENGTH] - // proof[(0x1c0 + PUBLIC_VALUES_LENGTH * 32)..]: Public Values Suffix + // proof[(0x1c0 + PUBLIC_VALUES_LENGTH * 32)..]: Proof Suffix /// @solidity memory-safe-assembly assembly { @@ -113,22 +113,22 @@ contract OpenVmHalo2Verifier is Halo2Verifier, IOpenVmHalo2Verifier { // Allocate the memory as a safety measure. mstore(0x40, add(proofPtr, fullProofLength)) - // Copy the KZG accumulators (length 0x180) into the beginning of + // Copy the KZG accumulator (length 0x180) into the beginning of // the memory buffer calldatacopy(proofPtr, proofData.offset, 0x180) - // Copy the App Exe Commit and Leaf Exe Commit into the memory buffer + // Copy the App Exe Commit and App Vm Commit into the memory buffer mstore(add(proofPtr, 0x180), appExeCommit) - mstore(add(proofPtr, 0x1a0), leafExeCommit) + mstore(add(proofPtr, 0x1a0), appVmCommit) - // Copy the Public Values Suffix (length 43 * 32 = 0x560) into the + // Copy the Proof Suffix (length 43 * 32 = 0x560) into the // end of the memory buffer, leaving PUBLIC_VALUES_LENGTH words in // between for the publicValuesPayload. // - // Begin copying from the end of the KZG accumulators in the + // Begin copying from the end of the KZG accumulator in the // calldata buffer (0x180) - let suffixProofOffset := add(0x1c0, shl(5, PUBLIC_VALUES_LENGTH)) - calldatacopy(add(proofPtr, suffixProofOffset), add(proofData.offset, 0x180), 0x560) + let proofSuffixOffset := add(0x1c0, shl(5, PUBLIC_VALUES_LENGTH)) + calldatacopy(add(proofPtr, proofSuffixOffset), add(proofData.offset, 0x180), 0x560) // Copy each byte of the public values into the proof. It copies the // most significant bytes of public values first. diff --git a/src/interfaces/IOpenVmHalo2Verifier.sol b/src/v1.0.1/interfaces/IOpenVmHalo2Verifier.sol similarity index 54% rename from src/interfaces/IOpenVmHalo2Verifier.sol rename to src/v1.0.1/interfaces/IOpenVmHalo2Verifier.sol index 231f4de..ac8292c 100644 --- a/src/interfaces/IOpenVmHalo2Verifier.sol +++ b/src/v1.0.1/interfaces/IOpenVmHalo2Verifier.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.0; interface IOpenVmHalo2Verifier { - function verify(bytes calldata publicValues, bytes calldata proofData, bytes32 appExeCommit) external view; + function verify(bytes calldata publicValues, bytes calldata proofData, bytes32 appExeCommit, bytes32 appVmCommit) + external + view; } diff --git a/test/OpenVmHalo2Verifier.t.sol b/test/OpenVmHalo2Verifier.t.sol deleted file mode 100644 index 0346d64..0000000 --- a/test/OpenVmHalo2Verifier.t.sol +++ /dev/null @@ -1,135 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.19; - -import { OpenVmHalo2Verifier, MemoryPointer } from "../src/OpenVmHalo2Verifier.sol"; -import { Test, console2, safeconsole as console } from "forge-std/Test.sol"; - -contract OpenVmHalo2VerifierTest is Test, OpenVmHalo2Verifier { - bytes proofData; - - bytes private constant kzgAccumulators = - hex"000000000000000000000000000000000000000000d1437a9bb9ac3c1a16992c00000000000000000000000000000000000000000021a9ab5617c2d7d6ccf9af0000000000000000000000000000000000000000000008ba19dc0f462a92dc0900000000000000000000000000000000000000000025694ae0246cb4b406b0970000000000000000000000000000000000000000004ec055f528761bb24e7098000000000000000000000000000000000000000000000b0dcb882b8af688ddc4000000000000000000000000000000000000000000e7eb1b7903923438cc8b54000000000000000000000000000000000000000000af1197617343468c85c9d6000000000000000000000000000000000000000000002d22b55036423bb4c266000000000000000000000000000000000000000000280255aa13e24534841355000000000000000000000000000000000000000000bcffb01f01132cb2ea1eb20000000000000000000000000000000000000000000000c69662a512b8d21501"; - bytes private constant suffix = - hex"2775c493e84b4ea63465d98b4666134e274cd3d66eaf6b59a6dbeddb594de2091ae1f48e567caf12f2b888d339ba7f99ab4603cf008397276d562f67ad058c9410cc4bdb94e881438afc407093d722a48089d5784437d52a4bafc6e9819bf4e00bec156cd36cb7f7883d9ed0196b4f2ce825b2b579a08ec670d0b8708d476f610d620bbaac86de1c27ad5dfce81d3479c72e603025c1b3dc30559150467c1680022031fdd52469b8884250650abf7fb3c5a38018c285e3412d266f937aa0ef2c0d07838c09f45be93e8c0f653eddcb3565cb6c124dca1433684aba18f8cb58802af17390906939ca5d7f37bcd2f02f5138c9afcdd6ebe543dbbee0f775e1c40d2722ddbd7b4c08aba7a4f7ebd96b801afb946e453e69ea9ce118d47f6408b21914042c2f38028b0c160541d4fadb04a1a5b647cc8569247377be8202a468b1cb279b82a31e702f57385e36f7734f6b391d6a50fd14d09e0c30eab5cc4c8c6e5029bd146dea01b1b317ec886eaef10e4752867f6a0f88f20ba6e23b5a842e930d2883c12cbcf1306cedc382aa0207cb33cf01435390660f944e69687fb16ce7180c474460f997bd0efff02754973fd493cda33c7b364b89997409dbac35ca54f01b74498360f00fbf7092e589de2020641a954a27c92b862303dad302f258c9530c729d2baeccb32dbce5bf1ddc0758529f020b75806fd0b3cdf61670d88027e5028badbe0381b6eb6b68955284be0f85de4865ffcde32a8ce08182fe3cd433e91074bce48f7cb8295e8af1d1d9ccacce4490506e7adc9b29e88aba93f1b207ed28328bfe9f6cf3b6d3547bdb430f48898f78fa6c3e18234c855e0a52bb78c5d0131533e4a471a039c7cab0f266daf91f1834589a624400edc4398e7e828f582c07aa42c25d1b672cfb45eac915f8c4dbafd3205c29100c2992d45affacb9f19020535efdc88776c1c3aca1260df676ddfe99782c48ec29d38eea6b12f06d536702d6164196e22b4e348c9afe0c8f5572b4651c01cc00314a2291cc27e1763a762c1e3fbb8e98f1b99abdfdfa786508d7cc44df1d5d02f934bf3014a36090e464273f4d8c1b7877eb0b290223dac7d9cf81ec17f791d11aa4a1b68f9693796e7b0a8e03eceb1e616b44d174568825ffaca4cbeb4c4aab4e0e9c6723385e3cdeca273c175f927edc0111bc70e928bb1082a6b835e3ce1e3ed65c0dff86922d451806fb038d5521c9ba7fa99d60dc5f69d561b00799c6daa0ea4e43a3d33a456b2b19ed25f31ceebf094342dc5da14ef72395949b799483d059a7e65a246a8f45ad1c48b3b8a76fb17dc5a80bfe5d7900a9870397d653644d404cde9ab38c8b4aea0d3e6682eb89cfb1f50e1e5a8140543232c48d76b947416ee9950c0f5dc11b200e5bf99a7434f8174b56d22164d8b76bf640310d52d589a046d53b117d2bf3f9112b7a7fce911175c9801442086a89b0973b4d957ae8e947879c51835ef64fa027cbfabaddc03997a2b92fd79f0973f694696f56543e83a8f19119473ebff740014caa87269b99c0adf301358c2f3776c760068bf7b596de58dc752d758e7ed3224cbc8d6edda7bc8c4547db51d9c78ce44a178a6e03cae750dfbe643acd08972f5ae0952cd44c62797787661c879af3a7330a53b23bcdd93aa7e3d61039d95622a882f24f6ecd1fc5400d4c8abf867ec3d5a2de8388fa8d6d1026227c6716a002cbf4a89570842bd00a4c0fda5a055473d973f1115d49e77fef7a69fee3131803e935eaeea4bf493566a48ab82e4bf8c3552cc58376974341021ce82f09f4081ddbcbab8b9c65979a5643d145e8fd093cd3769a353c8c9f58830719ec0683c422b28a02f74ca326db6911af0e9bc67cb42007a65ad3e8942d823ba0c31ae2c322f45f1f54aca1193f948c49885fee5960695994cb73b2f572a8ac2be49192b5"; - bytes32 appExeCommit = bytes32(0x0063802e02e9f8db01adecac53ea5c1db95bdbacf7800b9e29e27527cfd2613b); - bytes32 publicValues = bytes32(uint256(0)); - - uint256 private constant FULL_PROOF_WORDS = (12 + 2 + 32 + 43); - - uint256 private constant FULL_PROOF_LENGTH = (12 + 2 + 32 + 43) * 32; - - function setUp() public { - proofData = abi.encodePacked(kzgAccumulators, suffix); - } - - function test_verifyProof() public view { - this.verify(abi.encodePacked(publicValues), proofData, appExeCommit); - } - - function test_proofFormat() public view { - this.constructAndCheckProof(abi.encodePacked(publicValues), proofData, appExeCommit); - } - - function testFuzz_proofFormat(uint256 proofDataSeed, bytes32 _publicValues, bytes32 _appExeCommit) public { - vm.assume(_publicValues != bytes32(0)); - vm.assume(_appExeCommit != bytes32(0)); - - bytes memory _proofData = new bytes(55 * 32); - for (uint256 i = 0; i < 55 * 32; ++i) { - bytes1 _byte = bytes1(uint8(uint256(keccak256(abi.encodePacked(proofDataSeed, i))))); - _proofData[i] = _byte; - } - - proofData = _proofData; - publicValues = _publicValues; - appExeCommit = _appExeCommit; - - test_proofFormat(); - } - - function constructAndCheckProof(bytes calldata _publicValues, bytes calldata _proofData, bytes32 _appExeCommit) - external - view - { - MemoryPointer proofPtr = _constructProof(_publicValues, _proofData, _appExeCommit); - - // _constructProof will return a pointer to memory that will hold the - // proof data in a block of size FULL_PROOF_LENGTH. However, this won't - // be directly compatible with `bytes memory` since it will be missing - // the length as the first word. So below, we will - // - // 1. Move all the data down by 32 bytes in memory - // 2. Set the length in the first word which we just created the space for - // 3. Allocate the memory for the length (32 bytes) - // 4. Create a `bytes memory` pointer which points to the length - - uint256 i = FULL_PROOF_WORDS - 1; - while (true) { - uint256 currentPtr = MemoryPointer.unwrap(proofPtr) + i * 32; - // console.log("currentPtr", currentPtr); - uint256 destPtr = MemoryPointer.unwrap(proofPtr) + ((i + 1) * 32); - // console.log("destPtr", destPtr); - assembly { - mstore(destPtr, mload(currentPtr)) - } - if (i == 0) break; - i--; - } - - bytes memory proof; - assembly { - // 2. - mstore(proofPtr, FULL_PROOF_LENGTH) - // 3. - mstore(0x40, add(mload(0x40), 0x20)) - - // 4. - proof := proofPtr - } - - this.checkProofFormat(proof); - } - - function checkProofFormat(bytes calldata proof) external view { - bytes memory proofDataExpected = proofData; - - bytes memory _kzgAccumulators = proof[0:0x180]; - bytes memory publicValuesSuffix = proof[0x5c0:]; - bytes memory _proofData = abi.encodePacked(_kzgAccumulators, publicValuesSuffix); - require(keccak256(_proofData) == keccak256(proofDataExpected), "Proof data mismatch"); - - bytes memory _appExeCommit = proof[0x180:0x1a0]; - bytes memory _leafExeCommit = proof[0x1a0:0x1c0]; - - require(bytes32(_appExeCommit) == appExeCommit, "App exe commit mismatch"); - require(bytes32(_leafExeCommit) == LEAF_EXE_COMMIT, "Leaf exe commit mismatch"); - - bytes32 publicValuesExpected = publicValues; - bytes calldata _publicValues = proof[0x1c0:0x5c0]; - for (uint256 i = 0; i < 32; ++i) { - uint256 expected = uint256(uint8(publicValuesExpected[i])); - uint256 actual = uint256(bytes32(_publicValues[i * 32:(i + 1) * 32])); - require(expected == actual, "Public values mismatch"); - } - } - - function test_RevertWhen_InvalidProofDataLength() public { - vm.expectRevert(abi.encodeWithSelector(OpenVmHalo2Verifier.InvalidProofDataLength.selector)); - this.verify(abi.encodePacked(publicValues), hex"aa", appExeCommit); - } - - function test_RevertWhen_InvalidPublicValuesLength() public { - vm.expectRevert(abi.encodeWithSelector(OpenVmHalo2Verifier.InvalidPublicValuesLength.selector)); - this.verify(proofData, hex"aa", appExeCommit); - } - - function test_RevertWhen_ProofVerificationFailed() public { - bytes memory corruptedKzgAccumulators = kzgAccumulators; - corruptedKzgAccumulators[0] = bytes1(uint8(0x01)); - - bytes memory corruptedProofData = abi.encodePacked(corruptedKzgAccumulators, suffix); - - vm.expectRevert(abi.encodeWithSelector(OpenVmHalo2Verifier.ProofVerificationFailed.selector)); - this.verify(abi.encodePacked(publicValues), corruptedProofData, appExeCommit); - } -} diff --git a/test/helpers/LibString.sol b/test/helpers/LibString.sol new file mode 100644 index 0000000..f046fa4 --- /dev/null +++ b/test/helpers/LibString.sol @@ -0,0 +1,1628 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +/// @notice Library for byte related operations. +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibBytes.sol) +library LibBytes { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* STRUCTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Goated bytes storage struct that totally MOGs, no cap, fr. + /// Uses less gas and bytecode than Solidity's native bytes storage. It's meta af. + /// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight. + struct BytesStorage { + bytes32 _spacer; + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTANTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev The constant returned when the `search` is not found in the bytes. + uint256 internal constant NOT_FOUND = type(uint256).max; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* BYTE STORAGE OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Sets the value of the bytes storage `$` to `s`. + function set(BytesStorage storage $, bytes memory s) internal { + /// @solidity memory-safe-assembly + assembly { + let n := mload(s) + let packed := or(0xff, shl(8, n)) + for { let i := 0 } 1 { } { + if iszero(gt(n, 0xfe)) { + i := 0x1f + packed := or(n, shl(8, mload(add(s, i)))) + if iszero(gt(n, i)) { break } + } + let o := add(s, 0x20) + mstore(0x00, $.slot) + for { let p := keccak256(0x00, 0x20) } 1 { } { + sstore(add(p, shr(5, i)), mload(add(o, i))) + i := add(i, 0x20) + if iszero(lt(i, n)) { break } + } + break + } + sstore($.slot, packed) + } + } + + /// @dev Sets the value of the bytes storage `$` to `s`. + function setCalldata(BytesStorage storage $, bytes calldata s) internal { + /// @solidity memory-safe-assembly + assembly { + let packed := or(0xff, shl(8, s.length)) + for { let i := 0 } 1 { } { + if iszero(gt(s.length, 0xfe)) { + i := 0x1f + packed := or(s.length, shl(8, shr(8, calldataload(s.offset)))) + if iszero(gt(s.length, i)) { break } + } + mstore(0x00, $.slot) + for { let p := keccak256(0x00, 0x20) } 1 { } { + sstore(add(p, shr(5, i)), calldataload(add(s.offset, i))) + i := add(i, 0x20) + if iszero(lt(i, s.length)) { break } + } + break + } + sstore($.slot, packed) + } + } + + /// @dev Sets the value of the bytes storage `$` to the empty bytes. + function clear(BytesStorage storage $) internal { + delete $._spacer; + } + + /// @dev Returns whether the value stored is `$` is the empty bytes "". + function isEmpty(BytesStorage storage $) internal view returns (bool) { + return uint256($._spacer) & 0xff == uint256(0); + } + + /// @dev Returns the length of the value stored in `$`. + function length(BytesStorage storage $) internal view returns (uint256 result) { + result = uint256($._spacer); + /// @solidity memory-safe-assembly + assembly { + let n := and(0xff, result) + result := or(mul(shr(8, result), eq(0xff, n)), mul(n, iszero(eq(0xff, n)))) + } + } + + /// @dev Returns the value stored in `$`. + function get(BytesStorage storage $) internal view returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let o := add(result, 0x20) + let packed := sload($.slot) + let n := shr(8, packed) + for { let i := 0 } 1 { } { + if iszero(eq(or(packed, 0xff), packed)) { + mstore(o, packed) + n := and(0xff, packed) + i := 0x1f + if iszero(gt(n, i)) { break } + } + mstore(0x00, $.slot) + for { let p := keccak256(0x00, 0x20) } 1 { } { + mstore(add(o, i), sload(add(p, shr(5, i)))) + i := add(i, 0x20) + if iszero(lt(i, n)) { break } + } + break + } + mstore(result, n) // Store the length of the memory. + mstore(add(o, n), 0) // Zeroize the slot after the bytes. + mstore(0x40, add(add(o, n), 0x20)) // Allocate memory. + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* BYTES OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`. + function replace(bytes memory subject, bytes memory needle, bytes memory replacement) + internal + pure + returns (bytes memory result) + { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let needleLen := mload(needle) + let replacementLen := mload(replacement) + let d := sub(result, subject) // Memory difference. + let i := add(subject, 0x20) // Subject bytes pointer. + mstore(0x00, add(i, mload(subject))) // End of subject. + if iszero(gt(needleLen, mload(subject))) { + let subjectSearchEnd := add(sub(mload(0x00), needleLen), 1) + let h := 0 // The hash of `needle`. + if iszero(lt(needleLen, 0x20)) { h := keccak256(add(needle, 0x20), needleLen) } + let s := mload(add(needle, 0x20)) + for { let m := shl(3, sub(0x20, and(needleLen, 0x1f))) } 1 { } { + let t := mload(i) + // Whether the first `needleLen % 32` bytes of `subject` and `needle` matches. + if iszero(shr(m, xor(t, s))) { + if h { + if iszero(eq(keccak256(i, needleLen), h)) { + mstore(add(i, d), t) + i := add(i, 1) + if iszero(lt(i, subjectSearchEnd)) { break } + continue + } + } + // Copy the `replacement` one word at a time. + for { let j := 0 } 1 { } { + mstore(add(add(i, d), j), mload(add(add(replacement, 0x20), j))) + j := add(j, 0x20) + if iszero(lt(j, replacementLen)) { break } + } + d := sub(add(d, replacementLen), needleLen) + if needleLen { + i := add(i, needleLen) + if iszero(lt(i, subjectSearchEnd)) { break } + continue + } + } + mstore(add(i, d), t) + i := add(i, 1) + if iszero(lt(i, subjectSearchEnd)) { break } + } + } + let end := mload(0x00) + let n := add(sub(d, add(result, 0x20)), end) + // Copy the rest of the bytes one word at a time. + for { } lt(i, end) { i := add(i, 0x20) } { mstore(add(i, d), mload(i)) } + let o := add(i, d) + mstore(o, 0) // Zeroize the slot after the bytes. + mstore(0x40, add(o, 0x20)) // Allocate memory. + mstore(result, n) // Store the length. + } + } + + /// @dev Returns the byte index of the first location of `needle` in `subject`, + /// needleing from left to right, starting from `from`. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. + function indexOf(bytes memory subject, bytes memory needle, uint256 from) internal pure returns (uint256 result) { + /// @solidity memory-safe-assembly + assembly { + result := not(0) // Initialize to `NOT_FOUND`. + for { let subjectLen := mload(subject) } 1 { } { + if iszero(mload(needle)) { + result := from + if iszero(gt(from, subjectLen)) { break } + result := subjectLen + break + } + let needleLen := mload(needle) + let subjectStart := add(subject, 0x20) + + subject := add(subjectStart, from) + let end := add(sub(add(subjectStart, subjectLen), needleLen), 1) + let m := shl(3, sub(0x20, and(needleLen, 0x1f))) + let s := mload(add(needle, 0x20)) + + if iszero(and(lt(subject, end), lt(from, subjectLen))) { break } + + if iszero(lt(needleLen, 0x20)) { + for { let h := keccak256(add(needle, 0x20), needleLen) } 1 { } { + if iszero(shr(m, xor(mload(subject), s))) { + if eq(keccak256(subject, needleLen), h) { + result := sub(subject, subjectStart) + break + } + } + subject := add(subject, 1) + if iszero(lt(subject, end)) { break } + } + break + } + for { } 1 { } { + if iszero(shr(m, xor(mload(subject), s))) { + result := sub(subject, subjectStart) + break + } + subject := add(subject, 1) + if iszero(lt(subject, end)) { break } + } + break + } + } + } + + /// @dev Returns the byte index of the first location of `needle` in `subject`, + /// needleing from left to right. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. + function indexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) { + return indexOf(subject, needle, 0); + } + + /// @dev Returns the byte index of the first location of `needle` in `subject`, + /// needleing from right to left, starting from `from`. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. + function lastIndexOf(bytes memory subject, bytes memory needle, uint256 from) + internal + pure + returns (uint256 result) + { + /// @solidity memory-safe-assembly + assembly { + for { } 1 { } { + result := not(0) // Initialize to `NOT_FOUND`. + let needleLen := mload(needle) + if gt(needleLen, mload(subject)) { break } + let w := result + + let fromMax := sub(mload(subject), needleLen) + if iszero(gt(fromMax, from)) { from := fromMax } + + let end := add(add(subject, 0x20), w) + subject := add(add(subject, 0x20), from) + if iszero(gt(subject, end)) { break } + // As this function is not too often used, + // we shall simply use keccak256 for smaller bytecode size. + for { let h := keccak256(add(needle, 0x20), needleLen) } 1 { } { + if eq(keccak256(subject, needleLen), h) { + result := sub(subject, add(end, 1)) + break + } + subject := add(subject, w) // `sub(subject, 1)`. + if iszero(gt(subject, end)) { break } + } + break + } + } + } + + /// @dev Returns the byte index of the first location of `needle` in `subject`, + /// needleing from right to left. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. + function lastIndexOf(bytes memory subject, bytes memory needle) internal pure returns (uint256) { + return lastIndexOf(subject, needle, type(uint256).max); + } + + /// @dev Returns true if `needle` is found in `subject`, false otherwise. + function contains(bytes memory subject, bytes memory needle) internal pure returns (bool) { + return indexOf(subject, needle) != NOT_FOUND; + } + + /// @dev Returns whether `subject` starts with `needle`. + function startsWith(bytes memory subject, bytes memory needle) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + let n := mload(needle) + // Just using keccak256 directly is actually cheaper. + let t := eq(keccak256(add(subject, 0x20), n), keccak256(add(needle, 0x20), n)) + result := lt(gt(n, mload(subject)), t) + } + } + + /// @dev Returns whether `subject` ends with `needle`. + function endsWith(bytes memory subject, bytes memory needle) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + let n := mload(needle) + let notInRange := gt(n, mload(subject)) + // `subject + 0x20 + max(subject.length - needle.length, 0)`. + let t := add(add(subject, 0x20), mul(iszero(notInRange), sub(mload(subject), n))) + // Just using keccak256 directly is actually cheaper. + result := gt(eq(keccak256(t, n), keccak256(add(needle, 0x20), n)), notInRange) + } + } + + /// @dev Returns `subject` repeated `times`. + function repeat(bytes memory subject, uint256 times) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + let l := mload(subject) // Subject length. + if iszero(or(iszero(times), iszero(l))) { + result := mload(0x40) + subject := add(subject, 0x20) + let o := add(result, 0x20) + for { } 1 { } { + // Copy the `subject` one word at a time. + for { let j := 0 } 1 { } { + mstore(add(o, j), mload(add(subject, j))) + j := add(j, 0x20) + if iszero(lt(j, l)) { break } + } + o := add(o, l) + times := sub(times, 1) + if iszero(times) { break } + } + mstore(o, 0) // Zeroize the slot after the bytes. + mstore(0x40, add(o, 0x20)) // Allocate memory. + mstore(result, sub(o, add(result, 0x20))) // Store the length. + } + } + } + + /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). + /// `start` and `end` are byte offsets. + function slice(bytes memory subject, uint256 start, uint256 end) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + let l := mload(subject) // Subject length. + if iszero(gt(l, end)) { end := l } + if iszero(gt(l, start)) { start := l } + if lt(start, end) { + result := mload(0x40) + let n := sub(end, start) + let i := add(subject, start) + let w := not(0x1f) + // Copy the `subject` one word at a time, backwards. + for { let j := and(add(n, 0x1f), w) } 1 { } { + mstore(add(result, j), mload(add(i, j))) + j := add(j, w) // `sub(j, 0x20)`. + if iszero(j) { break } + } + let o := add(add(result, 0x20), n) + mstore(o, 0) // Zeroize the slot after the bytes. + mstore(0x40, add(o, 0x20)) // Allocate memory. + mstore(result, n) // Store the length. + } + } + } + + /// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes. + /// `start` is a byte offset. + function slice(bytes memory subject, uint256 start) internal pure returns (bytes memory result) { + result = slice(subject, start, type(uint256).max); + } + + /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). + /// `start` and `end` are byte offsets. Faster than Solidity's native slicing. + function sliceCalldata(bytes calldata subject, uint256 start, uint256 end) + internal + pure + returns (bytes calldata result) + { + /// @solidity memory-safe-assembly + assembly { + end := xor(end, mul(xor(end, subject.length), lt(subject.length, end))) + start := xor(start, mul(xor(start, subject.length), lt(subject.length, start))) + result.offset := add(subject.offset, start) + result.length := mul(lt(start, end), sub(end, start)) + } + } + + /// @dev Returns a copy of `subject` sliced from `start` to the end of the bytes. + /// `start` is a byte offset. Faster than Solidity's native slicing. + function sliceCalldata(bytes calldata subject, uint256 start) internal pure returns (bytes calldata result) { + /// @solidity memory-safe-assembly + assembly { + start := xor(start, mul(xor(start, subject.length), lt(subject.length, start))) + result.offset := add(subject.offset, start) + result.length := mul(lt(start, subject.length), sub(subject.length, start)) + } + } + + /// @dev Reduces the size of `subject` to `n`. + /// If `n` is greater than the size of `subject`, this will be a no-op. + function truncate(bytes memory subject, uint256 n) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + result := subject + mstore(mul(lt(n, mload(result)), result), n) + } + } + + /// @dev Returns a copy of `subject`, with the length reduced to `n`. + /// If `n` is greater than the size of `subject`, this will be a no-op. + function truncatedCalldata(bytes calldata subject, uint256 n) internal pure returns (bytes calldata result) { + /// @solidity memory-safe-assembly + assembly { + result.offset := subject.offset + result.length := xor(n, mul(xor(n, subject.length), lt(subject.length, n))) + } + } + + /// @dev Returns all the indices of `needle` in `subject`. + /// The indices are byte offsets. + function indicesOf(bytes memory subject, bytes memory needle) internal pure returns (uint256[] memory result) { + /// @solidity memory-safe-assembly + assembly { + let searchLen := mload(needle) + if iszero(gt(searchLen, mload(subject))) { + result := mload(0x40) + let i := add(subject, 0x20) + let o := add(result, 0x20) + let subjectSearchEnd := add(sub(add(i, mload(subject)), searchLen), 1) + let h := 0 // The hash of `needle`. + if iszero(lt(searchLen, 0x20)) { h := keccak256(add(needle, 0x20), searchLen) } + let s := mload(add(needle, 0x20)) + for { let m := shl(3, sub(0x20, and(searchLen, 0x1f))) } 1 { } { + let t := mload(i) + // Whether the first `searchLen % 32` bytes of `subject` and `needle` matches. + if iszero(shr(m, xor(t, s))) { + if h { + if iszero(eq(keccak256(i, searchLen), h)) { + i := add(i, 1) + if iszero(lt(i, subjectSearchEnd)) { break } + continue + } + } + mstore(o, sub(i, add(subject, 0x20))) // Append to `result`. + o := add(o, 0x20) + i := add(i, searchLen) // Advance `i` by `searchLen`. + if searchLen { + if iszero(lt(i, subjectSearchEnd)) { break } + continue + } + } + i := add(i, 1) + if iszero(lt(i, subjectSearchEnd)) { break } + } + mstore(result, shr(5, sub(o, add(result, 0x20)))) // Store the length of `result`. + // Allocate memory for result. + // We allocate one more word, so this array can be recycled for {split}. + mstore(0x40, add(o, 0x20)) + } + } + } + + /// @dev Returns an arrays of bytess based on the `delimiter` inside of the `subject` bytes. + function split(bytes memory subject, bytes memory delimiter) internal pure returns (bytes[] memory result) { + uint256[] memory indices = indicesOf(subject, delimiter); + /// @solidity memory-safe-assembly + assembly { + let w := not(0x1f) + let indexPtr := add(indices, 0x20) + let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) + mstore(add(indicesEnd, w), mload(subject)) + mstore(indices, add(mload(indices), 1)) + for { let prevIndex := 0 } 1 { } { + let index := mload(indexPtr) + mstore(indexPtr, 0x60) + if iszero(eq(index, prevIndex)) { + let element := mload(0x40) + let l := sub(index, prevIndex) + mstore(element, l) // Store the length of the element. + // Copy the `subject` one word at a time, backwards. + for { let o := and(add(l, 0x1f), w) } 1 { } { + mstore(add(element, o), mload(add(add(subject, prevIndex), o))) + o := add(o, w) // `sub(o, 0x20)`. + if iszero(o) { break } + } + mstore(add(add(element, 0x20), l), 0) // Zeroize the slot after the bytes. + // Allocate memory for the length and the bytes, rounded up to a multiple of 32. + mstore(0x40, add(element, and(add(l, 0x3f), w))) + mstore(indexPtr, element) // Store the `element` into the array. + } + prevIndex := add(index, mload(delimiter)) + indexPtr := add(indexPtr, 0x20) + if iszero(lt(indexPtr, indicesEnd)) { break } + } + result := indices + if iszero(mload(delimiter)) { + result := add(indices, 0x20) + mstore(result, sub(mload(indices), 2)) + } + } + } + + /// @dev Returns a concatenated bytes of `a` and `b`. + /// Cheaper than `bytes.concat()` and does not de-align the free memory pointer. + function concat(bytes memory a, bytes memory b) internal pure returns (bytes memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let w := not(0x1f) + let aLen := mload(a) + // Copy `a` one word at a time, backwards. + for { let o := and(add(aLen, 0x20), w) } 1 { } { + mstore(add(result, o), mload(add(a, o))) + o := add(o, w) // `sub(o, 0x20)`. + if iszero(o) { break } + } + let bLen := mload(b) + let output := add(result, aLen) + // Copy `b` one word at a time, backwards. + for { let o := and(add(bLen, 0x20), w) } 1 { } { + mstore(add(output, o), mload(add(b, o))) + o := add(o, w) // `sub(o, 0x20)`. + if iszero(o) { break } + } + let totalLen := add(aLen, bLen) + let last := add(add(result, 0x20), totalLen) + mstore(last, 0) // Zeroize the slot after the bytes. + mstore(result, totalLen) // Store the length. + mstore(0x40, add(last, 0x20)) // Allocate memory. + } + } + + /// @dev Returns whether `a` equals `b`. + function eq(bytes memory a, bytes memory b) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) + } + } + + /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small bytes. + function eqs(bytes memory a, bytes32 b) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + // These should be evaluated on compile time, as far as possible. + let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`. + let x := not(or(m, or(b, add(m, and(b, m))))) + let r := shl(7, iszero(iszero(shr(128, x)))) + r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) + r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) + r := or(r, shl(4, lt(0xffff, shr(r, x)))) + r := or(r, shl(3, lt(0xff, shr(r, x)))) + // forgefmt: disable-next-item + result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))), + xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20))))) + } + } + + /// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`. + /// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1. + function cmp(bytes memory a, bytes memory b) internal pure returns (int256 result) { + /// @solidity memory-safe-assembly + assembly { + let aLen := mload(a) + let bLen := mload(b) + let n := and(xor(aLen, mul(xor(aLen, bLen), lt(bLen, aLen))), not(0x1f)) + if n { + for { let i := 0x20 } 1 { } { + let x := mload(add(a, i)) + let y := mload(add(b, i)) + if iszero(or(xor(x, y), eq(i, n))) { + i := add(i, 0x20) + continue + } + result := sub(gt(x, y), lt(x, y)) + break + } + } + // forgefmt: disable-next-item + if iszero(result) { + let l := 0x201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201 + let x := and(mload(add(add(a, 0x20), n)), shl(shl(3, byte(sub(aLen, n), l)), not(0))) + let y := and(mload(add(add(b, 0x20), n)), shl(shl(3, byte(sub(bLen, n), l)), not(0))) + result := sub(gt(x, y), lt(x, y)) + if iszero(result) { result := sub(gt(aLen, bLen), lt(aLen, bLen)) } + } + } + } + + /// @dev Directly returns `a` without copying. + function directReturn(bytes memory a) internal pure { + /// @solidity memory-safe-assembly + assembly { + // Assumes that the bytes does not start from the scratch space. + let retStart := sub(a, 0x20) + let retUnpaddedSize := add(mload(a), 0x40) + // Right pad with zeroes. Just in case the bytes is produced + // by a method that doesn't zero right pad. + mstore(add(retStart, retUnpaddedSize), 0) + mstore(retStart, 0x20) // Store the return offset. + // End the transaction, returning the bytes. + return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize))) + } + } + + /// @dev Directly returns `a` with minimal copying. + function directReturn(bytes[] memory a) internal pure { + /// @solidity memory-safe-assembly + assembly { + let n := mload(a) // `a.length`. + let o := add(a, 0x20) // Start of elements in `a`. + let u := a // Highest memory slot. + let w := not(0x1f) + for { let i := 0 } iszero(eq(i, n)) { i := add(i, 1) } { + let c := add(o, shl(5, i)) // Location of pointer to `a[i]`. + let s := mload(c) // `a[i]`. + let l := mload(s) // `a[i].length`. + let r := and(l, 0x1f) // `a[i].length % 32`. + let z := add(0x20, and(l, w)) // Offset of last word in `a[i]` from `s`. + // If `s` comes before `o`, or `s` is not zero right padded. + if iszero(lt(lt(s, o), or(iszero(r), iszero(shl(shl(3, r), mload(add(s, z))))))) { + let m := mload(0x40) + mstore(m, l) // Copy `a[i].length`. + for { } 1 { } { + mstore(add(m, z), mload(add(s, z))) // Copy `a[i]`, backwards. + z := add(z, w) // `sub(z, 0x20)`. + if iszero(z) { break } + } + let e := add(add(m, 0x20), l) + mstore(e, 0) // Zeroize the slot after the copied bytes. + mstore(0x40, add(e, 0x20)) // Allocate memory. + s := m + } + mstore(c, sub(s, o)) // Convert to calldata offset. + let t := add(l, add(s, 0x20)) + if iszero(lt(t, u)) { u := t } + } + let retStart := add(a, w) // Assumes `a` doesn't start from scratch space. + mstore(retStart, 0x20) // Store the return offset. + return(retStart, add(0x40, sub(u, retStart))) // End the transaction. + } + } + + /// @dev Returns the word at `offset`, without any bounds checks. + function load(bytes memory a, uint256 offset) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(add(add(a, 0x20), offset)) + } + } + + /// @dev Returns the word at `offset`, without any bounds checks. + function loadCalldata(bytes calldata a, uint256 offset) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + result := calldataload(add(a.offset, offset)) + } + } + + /// @dev Returns a slice representing a static struct in the calldata. Performs bounds checks. + function staticStructInCalldata(bytes calldata a, uint256 offset) internal pure returns (bytes calldata result) { + /// @solidity memory-safe-assembly + assembly { + let l := sub(a.length, 0x20) + result.offset := add(a.offset, offset) + result.length := sub(a.length, offset) + if or(shr(64, or(l, a.offset)), gt(offset, l)) { revert(l, 0x00) } + } + } + + /// @dev Returns a slice representing a dynamic struct in the calldata. Performs bounds checks. + function dynamicStructInCalldata(bytes calldata a, uint256 offset) internal pure returns (bytes calldata result) { + /// @solidity memory-safe-assembly + assembly { + let l := sub(a.length, 0x20) + let s := calldataload(add(a.offset, offset)) // Relative offset of `result` from `a.offset`. + result.offset := add(a.offset, s) + result.length := sub(a.length, s) + if or(shr(64, or(s, or(l, a.offset))), gt(offset, l)) { revert(l, 0x00) } + } + } + + /// @dev Returns bytes in calldata. Performs bounds checks. + function bytesInCalldata(bytes calldata a, uint256 offset) internal pure returns (bytes calldata result) { + /// @solidity memory-safe-assembly + assembly { + let l := sub(a.length, 0x20) + let s := calldataload(add(a.offset, offset)) // Relative offset of `result` from `a.offset`. + result.offset := add(add(a.offset, s), 0x20) + result.length := calldataload(add(a.offset, s)) + // forgefmt: disable-next-item + if or(shr(64, or(result.length, or(s, or(l, a.offset)))), + or(gt(add(s, result.length), l), gt(offset, l))) { revert(l, 0x00) } + } + } + + /// @dev Returns empty calldata bytes. For silencing the compiler. + function emptyCalldata() internal pure returns (bytes calldata result) { + /// @solidity memory-safe-assembly + assembly { + result.length := 0 + } + } +} + +/// @notice Library for converting numbers into strings and other string operations. +/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) +/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) +/// +/// @dev Note: +/// For performance and bytecode compactness, most of the string operations are restricted to +/// byte strings (7-bit ASCII), except where otherwise specified. +/// Usage of byte string operations on charsets with runes spanning two or more bytes +/// can lead to undefined behavior. +library LibString { + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* STRUCTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Goated string storage struct that totally MOGs, no cap, fr. + /// Uses less gas and bytecode than Solidity's native string storage. It's meta af. + /// Packs length with the first 31 bytes if <255 bytes, so it’s mad tight. + struct StringStorage { + bytes32 _spacer; + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CUSTOM ERRORS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev The length of the output is too small to contain all the hex digits. + error HexLengthInsufficient(); + + /// @dev The length of the string is more than 32 bytes. + error TooBigForSmallString(); + + /// @dev The input string must be a 7-bit ASCII. + error StringNot7BitASCII(); + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* CONSTANTS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev The constant returned when the `search` is not found in the string. + uint256 internal constant NOT_FOUND = type(uint256).max; + + /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'. + uint128 internal constant ALPHANUMERIC_7_BIT_ASCII = 0x7fffffe07fffffe03ff000000000000; + + /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'. + uint128 internal constant LETTERS_7_BIT_ASCII = 0x7fffffe07fffffe0000000000000000; + + /// @dev Lookup for 'abcdefghijklmnopqrstuvwxyz'. + uint128 internal constant LOWERCASE_7_BIT_ASCII = 0x7fffffe000000000000000000000000; + + /// @dev Lookup for 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'. + uint128 internal constant UPPERCASE_7_BIT_ASCII = 0x7fffffe0000000000000000; + + /// @dev Lookup for '0123456789'. + uint128 internal constant DIGITS_7_BIT_ASCII = 0x3ff000000000000; + + /// @dev Lookup for '0123456789abcdefABCDEF'. + uint128 internal constant HEXDIGITS_7_BIT_ASCII = 0x7e0000007e03ff000000000000; + + /// @dev Lookup for '01234567'. + uint128 internal constant OCTDIGITS_7_BIT_ASCII = 0xff000000000000; + + /// @dev Lookup for '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'. + uint128 internal constant PRINTABLE_7_BIT_ASCII = 0x7fffffffffffffffffffffff00003e00; + + /// @dev Lookup for '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'. + uint128 internal constant PUNCTUATION_7_BIT_ASCII = 0x78000001f8000001fc00fffe00000000; + + /// @dev Lookup for ' \t\n\r\x0b\x0c'. + uint128 internal constant WHITESPACE_7_BIT_ASCII = 0x100003e00; + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* STRING STORAGE OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Sets the value of the string storage `$` to `s`. + function set(StringStorage storage $, string memory s) internal { + LibBytes.set(bytesStorage($), bytes(s)); + } + + /// @dev Sets the value of the string storage `$` to `s`. + function setCalldata(StringStorage storage $, string calldata s) internal { + LibBytes.setCalldata(bytesStorage($), bytes(s)); + } + + /// @dev Sets the value of the string storage `$` to the empty string. + function clear(StringStorage storage $) internal { + delete $._spacer; + } + + /// @dev Returns whether the value stored is `$` is the empty string "". + function isEmpty(StringStorage storage $) internal view returns (bool) { + return uint256($._spacer) & 0xff == uint256(0); + } + + /// @dev Returns the length of the value stored in `$`. + function length(StringStorage storage $) internal view returns (uint256) { + return LibBytes.length(bytesStorage($)); + } + + /// @dev Returns the value stored in `$`. + function get(StringStorage storage $) internal view returns (string memory) { + return string(LibBytes.get(bytesStorage($))); + } + + /// @dev Helper to cast `$` to a `BytesStorage`. + function bytesStorage(StringStorage storage $) internal pure returns (LibBytes.BytesStorage storage casted) { + /// @solidity memory-safe-assembly + assembly { + casted.slot := $.slot + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* DECIMAL OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns the base 10 decimal representation of `value`. + function toString(uint256 value) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + // The maximum value of a uint256 contains 78 digits (1 byte per digit), but + // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. + // We will need 1 word for the trailing zeros padding, 1 word for the length, + // and 3 words for a maximum of 78 digits. + result := add(mload(0x40), 0x80) + mstore(0x40, add(result, 0x20)) // Allocate memory. + mstore(result, 0) // Zeroize the slot after the string. + + let end := result // Cache the end of the memory to calculate the length later. + let w := not(0) // Tsk. + // We write the string from rightmost digit to leftmost digit. + // The following is essentially a do-while loop that also handles the zero case. + for { let temp := value } 1 { } { + result := add(result, w) // `sub(result, 1)`. + // Store the character to the pointer. + // The ASCII index of the '0' character is 48. + mstore8(result, add(48, mod(temp, 10))) + temp := div(temp, 10) // Keep dividing `temp` until zero. + if iszero(temp) { break } + } + let n := sub(end, result) + result := sub(result, 0x20) // Move the pointer 32 bytes back to make room for the length. + mstore(result, n) // Store the length. + } + } + + /// @dev Returns the base 10 decimal representation of `value`. + function toString(int256 value) internal pure returns (string memory result) { + if (value >= 0) return toString(uint256(value)); + unchecked { + result = toString(~uint256(value) + 1); + } + /// @solidity memory-safe-assembly + assembly { + // We still have some spare memory space on the left, + // as we have allocated 3 words (96 bytes) for up to 78 digits. + let n := mload(result) // Load the string length. + mstore(result, 0x2d) // Store the '-' character. + result := sub(result, 1) // Move back the string pointer by a byte. + mstore(result, add(n, 1)) // Update the string length. + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* HEXADECIMAL OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns the hexadecimal representation of `value`, + /// left-padded to an input length of `byteCount` bytes. + /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, + /// giving a total length of `byteCount * 2 + 2` bytes. + /// Reverts if `byteCount` is too small for the output to contain all the digits. + function toHexString(uint256 value, uint256 byteCount) internal pure returns (string memory result) { + result = toHexStringNoPrefix(value, byteCount); + /// @solidity memory-safe-assembly + assembly { + let n := add(mload(result), 2) // Compute the length. + mstore(result, 0x3078) // Store the "0x" prefix. + result := sub(result, 2) // Move the pointer. + mstore(result, n) // Store the length. + } + } + + /// @dev Returns the hexadecimal representation of `value`, + /// left-padded to an input length of `byteCount` bytes. + /// The output is not prefixed with "0x" and is encoded using 2 hexadecimal digits per byte, + /// giving a total length of `byteCount * 2` bytes. + /// Reverts if `byteCount` is too small for the output to contain all the digits. + function toHexStringNoPrefix(uint256 value, uint256 byteCount) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + // We need 0x20 bytes for the trailing zeros padding, `byteCount * 2` bytes + // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. + // We add 0x20 to the total and round down to a multiple of 0x20. + // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. + result := add(mload(0x40), and(add(shl(1, byteCount), 0x42), not(0x1f))) + mstore(0x40, add(result, 0x20)) // Allocate memory. + mstore(result, 0) // Zeroize the slot after the string. + + let end := result // Cache the end to calculate the length later. + // Store "0123456789abcdef" in scratch space. + mstore(0x0f, 0x30313233343536373839616263646566) + + let start := sub(result, add(byteCount, byteCount)) + let w := not(1) // Tsk. + let temp := value + // We write the string from rightmost digit to leftmost digit. + // The following is essentially a do-while loop that also handles the zero case. + for { } 1 { } { + result := add(result, w) // `sub(result, 2)`. + mstore8(add(result, 1), mload(and(temp, 15))) + mstore8(result, mload(and(shr(4, temp), 15))) + temp := shr(8, temp) + if iszero(xor(result, start)) { break } + } + if temp { + mstore(0x00, 0x2194895a) // `HexLengthInsufficient()`. + revert(0x1c, 0x04) + } + let n := sub(end, result) + result := sub(result, 0x20) + mstore(result, n) // Store the length. + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. + /// As address are 20 bytes long, the output will left-padded to have + /// a length of `20 * 2 + 2` bytes. + function toHexString(uint256 value) internal pure returns (string memory result) { + result = toHexStringNoPrefix(value); + /// @solidity memory-safe-assembly + assembly { + let n := add(mload(result), 2) // Compute the length. + mstore(result, 0x3078) // Store the "0x" prefix. + result := sub(result, 2) // Move the pointer. + mstore(result, n) // Store the length. + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is prefixed with "0x". + /// The output excludes leading "0" from the `toHexString` output. + /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`. + function toMinimalHexString(uint256 value) internal pure returns (string memory result) { + result = toHexStringNoPrefix(value); + /// @solidity memory-safe-assembly + assembly { + let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present. + let n := add(mload(result), 2) // Compute the length. + mstore(add(result, o), 0x3078) // Store the "0x" prefix, accounting for leading zero. + result := sub(add(result, o), 2) // Move the pointer, accounting for leading zero. + mstore(result, sub(n, o)) // Store the length, accounting for leading zero. + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output excludes leading "0" from the `toHexStringNoPrefix` output. + /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`. + function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory result) { + result = toHexStringNoPrefix(value); + /// @solidity memory-safe-assembly + assembly { + let o := eq(byte(0, mload(add(result, 0x20))), 0x30) // Whether leading zero is present. + let n := mload(result) // Get the length. + result := add(result, o) // Move the pointer, accounting for leading zero. + mstore(result, sub(n, o)) // Store the length, accounting for leading zero. + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is encoded using 2 hexadecimal digits per byte. + /// As address are 20 bytes long, the output will left-padded to have + /// a length of `20 * 2` bytes. + function toHexStringNoPrefix(uint256 value) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, + // 0x02 bytes for the prefix, and 0x40 bytes for the digits. + // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. + result := add(mload(0x40), 0x80) + mstore(0x40, add(result, 0x20)) // Allocate memory. + mstore(result, 0) // Zeroize the slot after the string. + + let end := result // Cache the end to calculate the length later. + mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. + + let w := not(1) // Tsk. + // We write the string from rightmost digit to leftmost digit. + // The following is essentially a do-while loop that also handles the zero case. + for { let temp := value } 1 { } { + result := add(result, w) // `sub(result, 2)`. + mstore8(add(result, 1), mload(and(temp, 15))) + mstore8(result, mload(and(shr(4, temp), 15))) + temp := shr(8, temp) + if iszero(temp) { break } + } + let n := sub(end, result) + result := sub(result, 0x20) + mstore(result, n) // Store the length. + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, + /// and the alphabets are capitalized conditionally according to + /// https://eips.ethereum.org/EIPS/eip-55 + function toHexStringChecksummed(address value) internal pure returns (string memory result) { + result = toHexString(value); + /// @solidity memory-safe-assembly + assembly { + let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` + let o := add(result, 0x22) + let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` + let t := shl(240, 136) // `0b10001000 << 240` + for { let i := 0 } 1 { } { + mstore(add(i, i), mul(t, byte(i, hashed))) + i := add(i, 1) + if eq(i, 20) { break } + } + mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) + o := add(o, 0x20) + mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. + function toHexString(address value) internal pure returns (string memory result) { + result = toHexStringNoPrefix(value); + /// @solidity memory-safe-assembly + assembly { + let n := add(mload(result), 2) // Compute the length. + mstore(result, 0x3078) // Store the "0x" prefix. + result := sub(result, 2) // Move the pointer. + mstore(result, n) // Store the length. + } + } + + /// @dev Returns the hexadecimal representation of `value`. + /// The output is encoded using 2 hexadecimal digits per byte. + function toHexStringNoPrefix(address value) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + // Allocate memory. + // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, + // 0x02 bytes for the prefix, and 0x28 bytes for the digits. + // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. + mstore(0x40, add(result, 0x80)) + mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. + + result := add(result, 2) + mstore(result, 40) // Store the length. + let o := add(result, 0x20) + mstore(add(o, 40), 0) // Zeroize the slot after the string. + value := shl(96, value) + // We write the string from rightmost digit to leftmost digit. + // The following is essentially a do-while loop that also handles the zero case. + for { let i := 0 } 1 { } { + let p := add(o, add(i, i)) + let temp := byte(i, value) + mstore8(add(p, 1), mload(and(temp, 15))) + mstore8(p, mload(shr(4, temp))) + i := add(i, 1) + if eq(i, 20) { break } + } + } + } + + /// @dev Returns the hex encoded string from the raw bytes. + /// The output is encoded using 2 hexadecimal digits per byte. + function toHexString(bytes memory raw) internal pure returns (string memory result) { + result = toHexStringNoPrefix(raw); + /// @solidity memory-safe-assembly + assembly { + let n := add(mload(result), 2) // Compute the length. + mstore(result, 0x3078) // Store the "0x" prefix. + result := sub(result, 2) // Move the pointer. + mstore(result, n) // Store the length. + } + } + + /// @dev Returns the hex encoded string from the raw bytes. + /// The output is encoded using 2 hexadecimal digits per byte. + function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + let n := mload(raw) + result := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. + mstore(result, add(n, n)) // Store the length of the output. + + mstore(0x0f, 0x30313233343536373839616263646566) // Store the "0123456789abcdef" lookup. + let o := add(result, 0x20) + let end := add(raw, n) + for { } iszero(eq(raw, end)) { } { + raw := add(raw, 1) + mstore8(add(o, 1), mload(and(mload(raw), 15))) + mstore8(o, mload(and(shr(4, mload(raw)), 15))) + o := add(o, 2) + } + mstore(o, 0) // Zeroize the slot after the string. + mstore(0x40, add(o, 0x20)) // Allocate memory. + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* RUNE STRING OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + /// @dev Returns the number of UTF characters in the string. + function runeCount(string memory s) internal pure returns (uint256 result) { + /// @solidity memory-safe-assembly + assembly { + if mload(s) { + mstore(0x00, div(not(0), 255)) + mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) + let o := add(s, 0x20) + let end := add(o, mload(s)) + for { result := 1 } 1 { result := add(result, 1) } { + o := add(o, byte(0, mload(shr(250, mload(o))))) + if iszero(lt(o, end)) { break } + } + } + } + } + + /// @dev Returns if this string is a 7-bit ASCII string. + /// (i.e. all characters codes are in [0..127]) + function is7BitASCII(string memory s) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + result := 1 + let mask := shl(7, div(not(0), 255)) + let n := mload(s) + if n { + let o := add(s, 0x20) + let end := add(o, n) + let last := mload(end) + mstore(end, 0) + for { } 1 { } { + if and(mask, mload(o)) { + result := 0 + break + } + o := add(o, 0x20) + if iszero(lt(o, end)) { break } + } + mstore(end, last) + } + } + } + + /// @dev Returns if this string is a 7-bit ASCII string, + /// AND all characters are in the `allowed` lookup. + /// Note: If `s` is empty, returns true regardless of `allowed`. + function is7BitASCII(string memory s, uint128 allowed) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + result := 1 + if mload(s) { + let allowed_ := shr(128, shl(128, allowed)) + let o := add(s, 0x20) + for { let end := add(o, mload(s)) } 1 { } { + result := and(result, shr(byte(0, mload(o)), allowed_)) + o := add(o, 1) + if iszero(and(result, lt(o, end))) { break } + } + } + } + } + + /// @dev Converts the bytes in the 7-bit ASCII string `s` to + /// an allowed lookup for use in `is7BitASCII(s, allowed)`. + /// To save runtime gas, you can cache the result in an immutable variable. + function to7BitASCIIAllowedLookup(string memory s) internal pure returns (uint128 result) { + /// @solidity memory-safe-assembly + assembly { + if mload(s) { + let o := add(s, 0x20) + for { let end := add(o, mload(s)) } 1 { } { + result := or(result, shl(byte(0, mload(o)), 1)) + o := add(o, 1) + if iszero(lt(o, end)) { break } + } + if shr(128, result) { + mstore(0x00, 0xc9807e0d) // `StringNot7BitASCII()`. + revert(0x1c, 0x04) + } + } + } + } + + /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ + /* BYTE STRING OPERATIONS */ + /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ + + // For performance and bytecode compactness, byte string operations are restricted + // to 7-bit ASCII strings. All offsets are byte offsets, not UTF character offsets. + // Usage of byte string operations on charsets with runes spanning two or more bytes + // can lead to undefined behavior. + + /// @dev Returns `subject` all occurrences of `needle` replaced with `replacement`. + function replace(string memory subject, string memory needle, string memory replacement) + internal + pure + returns (string memory) + { + return string(LibBytes.replace(bytes(subject), bytes(needle), bytes(replacement))); + } + + /// @dev Returns the byte index of the first location of `needle` in `subject`, + /// needleing from left to right, starting from `from`. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. + function indexOf(string memory subject, string memory needle, uint256 from) internal pure returns (uint256) { + return LibBytes.indexOf(bytes(subject), bytes(needle), from); + } + + /// @dev Returns the byte index of the first location of `needle` in `subject`, + /// needleing from left to right. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. + function indexOf(string memory subject, string memory needle) internal pure returns (uint256) { + return LibBytes.indexOf(bytes(subject), bytes(needle), 0); + } + + /// @dev Returns the byte index of the first location of `needle` in `subject`, + /// needleing from right to left, starting from `from`. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. + function lastIndexOf(string memory subject, string memory needle, uint256 from) internal pure returns (uint256) { + return LibBytes.lastIndexOf(bytes(subject), bytes(needle), from); + } + + /// @dev Returns the byte index of the first location of `needle` in `subject`, + /// needleing from right to left. + /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `needle` is not found. + function lastIndexOf(string memory subject, string memory needle) internal pure returns (uint256) { + return LibBytes.lastIndexOf(bytes(subject), bytes(needle), type(uint256).max); + } + + /// @dev Returns true if `needle` is found in `subject`, false otherwise. + function contains(string memory subject, string memory needle) internal pure returns (bool) { + return LibBytes.contains(bytes(subject), bytes(needle)); + } + + /// @dev Returns whether `subject` starts with `needle`. + function startsWith(string memory subject, string memory needle) internal pure returns (bool) { + return LibBytes.startsWith(bytes(subject), bytes(needle)); + } + + /// @dev Returns whether `subject` ends with `needle`. + function endsWith(string memory subject, string memory needle) internal pure returns (bool) { + return LibBytes.endsWith(bytes(subject), bytes(needle)); + } + + /// @dev Returns `subject` repeated `times`. + function repeat(string memory subject, uint256 times) internal pure returns (string memory) { + return string(LibBytes.repeat(bytes(subject), times)); + } + + /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). + /// `start` and `end` are byte offsets. + function slice(string memory subject, uint256 start, uint256 end) internal pure returns (string memory) { + return string(LibBytes.slice(bytes(subject), start, end)); + } + + /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. + /// `start` is a byte offset. + function slice(string memory subject, uint256 start) internal pure returns (string memory) { + return string(LibBytes.slice(bytes(subject), start, type(uint256).max)); + } + + /// @dev Returns all the indices of `needle` in `subject`. + /// The indices are byte offsets. + function indicesOf(string memory subject, string memory needle) internal pure returns (uint256[] memory) { + return LibBytes.indicesOf(bytes(subject), bytes(needle)); + } + + /// @dev Returns an arrays of strings based on the `delimiter` inside of the `subject` string. + function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) { + bytes[] memory a = LibBytes.split(bytes(subject), bytes(delimiter)); + /// @solidity memory-safe-assembly + assembly { + result := a + } + } + + /// @dev Returns a concatenated string of `a` and `b`. + /// Cheaper than `string.concat()` and does not de-align the free memory pointer. + function concat(string memory a, string memory b) internal pure returns (string memory) { + return string(LibBytes.concat(bytes(a), bytes(b))); + } + + /// @dev Returns a copy of the string in either lowercase or UPPERCASE. + /// WARNING! This function is only compatible with 7-bit ASCII strings. + function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + let n := mload(subject) + if n { + result := mload(0x40) + let o := add(result, 0x20) + let d := sub(subject, result) + let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) + for { let end := add(o, n) } 1 { } { + let b := byte(0, mload(add(d, o))) + mstore8(o, xor(and(shr(b, flags), 0x20), b)) + o := add(o, 1) + if eq(o, end) { break } + } + mstore(result, n) // Store the length. + mstore(o, 0) // Zeroize the slot after the string. + mstore(0x40, add(o, 0x20)) // Allocate memory. + } + } + } + + /// @dev Returns a string from a small bytes32 string. + /// `s` must be null-terminated, or behavior will be undefined. + function fromSmallString(bytes32 s) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let n := 0 + for { } byte(n, s) { n := add(n, 1) } { } // Scan for '\0'. + mstore(result, n) // Store the length. + let o := add(result, 0x20) + mstore(o, s) // Store the bytes of the string. + mstore(add(o, n), 0) // Zeroize the slot after the string. + mstore(0x40, add(result, 0x40)) // Allocate memory. + } + } + + /// @dev Returns the small string, with all bytes after the first null byte zeroized. + function normalizeSmallString(bytes32 s) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + for { } byte(result, s) { result := add(result, 1) } { } // Scan for '\0'. + mstore(0x00, s) + mstore(result, 0x00) + result := mload(0x00) + } + } + + /// @dev Returns the string as a normalized null-terminated small string. + function toSmallString(string memory s) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(s) + if iszero(lt(result, 33)) { + mstore(0x00, 0xec92f9a3) // `TooBigForSmallString()`. + revert(0x1c, 0x04) + } + result := shl(shl(3, sub(32, result)), mload(add(s, result))) + } + } + + /// @dev Returns a lowercased copy of the string. + /// WARNING! This function is only compatible with 7-bit ASCII strings. + function lower(string memory subject) internal pure returns (string memory result) { + result = toCase(subject, false); + } + + /// @dev Returns an UPPERCASED copy of the string. + /// WARNING! This function is only compatible with 7-bit ASCII strings. + function upper(string memory subject) internal pure returns (string memory result) { + result = toCase(subject, true); + } + + /// @dev Escapes the string to be used within HTML tags. + function escapeHTML(string memory s) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let end := add(s, mload(s)) + let o := add(result, 0x20) + // Store the bytes of the packed offsets and strides into the scratch space. + // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. + mstore(0x1f, 0x900094) + mstore(0x08, 0xc0000000a6ab) + // Store ""&'<>" into the scratch space. + mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) + for { } iszero(eq(s, end)) { } { + s := add(s, 1) + let c := and(mload(s), 0xff) + // Not in `["\"","'","&","<",">"]`. + if iszero(and(shl(c, 1), 0x500000c400000000)) { + mstore8(o, c) + o := add(o, 1) + continue + } + let t := shr(248, mload(c)) + mstore(o, mload(and(t, 0x1f))) + o := add(o, shr(5, t)) + } + mstore(o, 0) // Zeroize the slot after the string. + mstore(result, sub(o, add(result, 0x20))) // Store the length. + mstore(0x40, add(o, 0x20)) // Allocate memory. + } + } + + /// @dev Escapes the string to be used within double-quotes in a JSON. + /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes. + function escapeJSON(string memory s, bool addDoubleQuotes) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + let o := add(result, 0x20) + if addDoubleQuotes { + mstore8(o, 34) + o := add(1, o) + } + // Store "\\u0000" in scratch space. + // Store "0123456789abcdef" in scratch space. + // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. + // into the scratch space. + mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) + // Bitmask for detecting `["\"","\\"]`. + let e := or(shl(0x22, 1), shl(0x5c, 1)) + for { let end := add(s, mload(s)) } iszero(eq(s, end)) { } { + s := add(s, 1) + let c := and(mload(s), 0xff) + if iszero(lt(c, 0x20)) { + if iszero(and(shl(c, 1), e)) { + // Not in `["\"","\\"]`. + mstore8(o, c) + o := add(o, 1) + continue + } + mstore8(o, 0x5c) // "\\". + mstore8(add(o, 1), c) + o := add(o, 2) + continue + } + if iszero(and(shl(c, 1), 0x3700)) { + // Not in `["\b","\t","\n","\f","\d"]`. + mstore8(0x1d, mload(shr(4, c))) // Hex value. + mstore8(0x1e, mload(and(c, 15))) // Hex value. + mstore(o, mload(0x19)) // "\\u00XX". + o := add(o, 6) + continue + } + mstore8(o, 0x5c) // "\\". + mstore8(add(o, 1), mload(add(c, 8))) + o := add(o, 2) + } + if addDoubleQuotes { + mstore8(o, 34) + o := add(1, o) + } + mstore(o, 0) // Zeroize the slot after the string. + mstore(result, sub(o, add(result, 0x20))) // Store the length. + mstore(0x40, add(o, 0x20)) // Allocate memory. + } + } + + /// @dev Escapes the string to be used within double-quotes in a JSON. + function escapeJSON(string memory s) internal pure returns (string memory result) { + result = escapeJSON(s, false); + } + + /// @dev Encodes `s` so that it can be safely used in a URI, + /// just like `encodeURIComponent` in JavaScript. + /// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent + /// See: https://datatracker.ietf.org/doc/html/rfc2396 + /// See: https://datatracker.ietf.org/doc/html/rfc3986 + function encodeURIComponent(string memory s) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) + // Store "0123456789ABCDEF" in scratch space. + // Uppercased to be consistent with JavaScript's implementation. + mstore(0x0f, 0x30313233343536373839414243444546) + let o := add(result, 0x20) + for { let end := add(s, mload(s)) } iszero(eq(s, end)) { } { + s := add(s, 1) + let c := and(mload(s), 0xff) + // If not in `[0-9A-Z-a-z-_.!~*'()]`. + if iszero(and(1, shr(c, 0x47fffffe87fffffe03ff678200000000))) { + mstore8(o, 0x25) // '%'. + mstore8(add(o, 1), mload(and(shr(4, c), 15))) + mstore8(add(o, 2), mload(and(c, 15))) + o := add(o, 3) + continue + } + mstore8(o, c) + o := add(o, 1) + } + mstore(result, sub(o, add(result, 0x20))) // Store the length. + mstore(o, 0) // Zeroize the slot after the string. + mstore(0x40, add(o, 0x20)) // Allocate memory. + } + } + + /// @dev Returns whether `a` equals `b`. + function eq(string memory a, string memory b) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) + } + } + + /// @dev Returns whether `a` equals `b`, where `b` is a null-terminated small string. + function eqs(string memory a, bytes32 b) internal pure returns (bool result) { + /// @solidity memory-safe-assembly + assembly { + // These should be evaluated on compile time, as far as possible. + let m := not(shl(7, div(not(iszero(b)), 255))) // `0x7f7f ...`. + let x := not(or(m, or(b, add(m, and(b, m))))) + let r := shl(7, iszero(iszero(shr(128, x)))) + r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) + r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) + r := or(r, shl(4, lt(0xffff, shr(r, x)))) + r := or(r, shl(3, lt(0xff, shr(r, x)))) + // forgefmt: disable-next-item + result := gt(eq(mload(a), add(iszero(x), xor(31, shr(3, r)))), + xor(shr(add(8, r), b), shr(add(8, r), mload(add(a, 0x20))))) + } + } + + /// @dev Returns 0 if `a == b`, -1 if `a < b`, +1 if `a > b`. + /// If `a` == b[:a.length]`, and `a.length < b.length`, returns -1. + function cmp(string memory a, string memory b) internal pure returns (int256) { + return LibBytes.cmp(bytes(a), bytes(b)); + } + + /// @dev Packs a single string with its length into a single word. + /// Returns `bytes32(0)` if the length is zero or greater than 31. + function packOne(string memory a) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + // We don't need to zero right pad the string, + // since this is our own custom non-standard packing scheme. + result := + mul( + // Load the length and the bytes. + mload(add(a, 0x1f)), + // `length != 0 && length < 32`. Abuses underflow. + // Assumes that the length is valid and within the block gas limit. + lt(sub(mload(a), 1), 0x1f) + ) + } + } + + /// @dev Unpacks a string packed using {packOne}. + /// Returns the empty string if `packed` is `bytes32(0)`. + /// If `packed` is not an output of {packOne}, the output behavior is undefined. + function unpackOne(bytes32 packed) internal pure returns (string memory result) { + /// @solidity memory-safe-assembly + assembly { + result := mload(0x40) // Grab the free memory pointer. + mstore(0x40, add(result, 0x40)) // Allocate 2 words (1 for the length, 1 for the bytes). + mstore(result, 0) // Zeroize the length slot. + mstore(add(result, 0x1f), packed) // Store the length and bytes. + mstore(add(add(result, 0x20), mload(result)), 0) // Right pad with zeroes. + } + } + + /// @dev Packs two strings with their lengths into a single word. + /// Returns `bytes32(0)` if combined length is zero or greater than 30. + function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { + /// @solidity memory-safe-assembly + assembly { + let aLen := mload(a) + // We don't need to zero right pad the strings, + // since this is our own custom non-standard packing scheme. + result := + mul( + or( // Load the length and the bytes of `a` and `b`. + shl(shl(3, sub(0x1f, aLen)), mload(add(a, aLen))), mload(sub(add(b, 0x1e), aLen))), + // `totalLen != 0 && totalLen < 31`. Abuses underflow. + // Assumes that the lengths are valid and within the block gas limit. + lt(sub(add(aLen, mload(b)), 1), 0x1e) + ) + } + } + + /// @dev Unpacks strings packed using {packTwo}. + /// Returns the empty strings if `packed` is `bytes32(0)`. + /// If `packed` is not an output of {packTwo}, the output behavior is undefined. + function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { + /// @solidity memory-safe-assembly + assembly { + resultA := mload(0x40) // Grab the free memory pointer. + resultB := add(resultA, 0x40) + // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. + mstore(0x40, add(resultB, 0x40)) + // Zeroize the length slots. + mstore(resultA, 0) + mstore(resultB, 0) + // Store the lengths and bytes. + mstore(add(resultA, 0x1f), packed) + mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) + // Right pad with zeroes. + mstore(add(add(resultA, 0x20), mload(resultA)), 0) + mstore(add(add(resultB, 0x20), mload(resultB)), 0) + } + } + + /// @dev Directly returns `a` without copying. + function directReturn(string memory a) internal pure { + /// @solidity memory-safe-assembly + assembly { + // Assumes that the string does not start from the scratch space. + let retStart := sub(a, 0x20) + let retUnpaddedSize := add(mload(a), 0x40) + // Right pad with zeroes. Just in case the string is produced + // by a method that doesn't zero right pad. + mstore(add(retStart, retUnpaddedSize), 0) + mstore(retStart, 0x20) // Store the return offset. + // End the transaction, returning the string. + return(retStart, and(not(0x1f), add(0x1f, retUnpaddedSize))) + } + } +} diff --git a/test/v1.0.1/OpenVmHalo2Verifer.t.sol b/test/v1.0.1/OpenVmHalo2Verifer.t.sol new file mode 100644 index 0000000..66ba4a6 --- /dev/null +++ b/test/v1.0.1/OpenVmHalo2Verifer.t.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import { OpenVmHalo2Verifier } from "../../src/v1.0.1/OpenVmHalo2Verifier.sol"; +import { LibString } from "../helpers/LibString.sol"; +import { Test, console2, safeconsole as console } from "forge-std/Test.sol"; + +contract OpenVmHalo2VerifierTest is Test { + bytes proofData; + bytes32 appExeCommit = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + bytes32 appVmCommit = 0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE; + bytes guestPvs; + + uint256 constant PUBLIC_VALUES_LENGTH = 32; + OpenVmHalo2Verifier verifier; + + function setUp() public { + proofData = new bytes(55 * 32); + for (uint256 i = 0; i < 55; i++) { + for (uint256 j = 0; j < 32; j++) { + proofData[i * 32 + j] = bytes1(uint8(i)); + } + } + + verifier = new OpenVmHalo2Verifier(); + } + + function test_ValidProofVerifies() public view { + string memory evmProofJson = vm.readFile("test/v1.0.1/evm.proof"); + bytes32 _appExeCommit = vm.parseJsonBytes32(evmProofJson, ".app_exe_commit"); + bytes32 _appVmCommit = vm.parseJsonBytes32(evmProofJson, ".app_vm_commit"); + bytes memory _guestPvs = vm.parseJsonBytes(evmProofJson, ".user_public_values"); + bytes memory accumulator = vm.parseJsonBytes(evmProofJson, ".proof_data.accumulator"); + bytes memory proof = vm.parseJsonBytes(evmProofJson, ".proof_data.proof"); + bytes memory _proofData = abi.encodePacked(accumulator, proof); + + verifier.verify(_guestPvs, _proofData, _appExeCommit, _appVmCommit); + } + + function test_ProofFormat() public { + guestPvs = new bytes(PUBLIC_VALUES_LENGTH); + for (uint256 i = 0; i < PUBLIC_VALUES_LENGTH; i++) { + guestPvs[i] = bytes1(uint8(i)); + } + + (bool success,) = address(verifier).delegatecall( + abi.encodeCall(OpenVmHalo2Verifier.verify, (guestPvs, proofData, appExeCommit, appVmCommit)) + ); + require(success, "Verification failed"); + } + + fallback(bytes calldata proof) external returns (bytes memory) { + bytes memory proofDataExpected = proofData; + + uint256 proofSuffixOffset = 0x1c0 + (32 * PUBLIC_VALUES_LENGTH); + + bytes memory kzgAccumulator = proof[0:0x180]; + bytes memory proofSuffix = proof[proofSuffixOffset:]; + bytes memory _proofData = abi.encodePacked(kzgAccumulator, proofSuffix); + + require(keccak256(_proofData) == keccak256(proofDataExpected), "Partial proof mismatch"); + + bytes memory _appExeCommit = proof[0x180:0x1a0]; + bytes memory _appVmCommit = proof[0x1a0:0x1c0]; + + require(bytes32(_appExeCommit) == appExeCommit, "App exe commit mismatch"); + require(bytes32(_appVmCommit) == appVmCommit, "App vm commit mismatch"); + + bytes calldata _guestPvs = proof[0x1c0:0x1c0 + 32 * PUBLIC_VALUES_LENGTH]; + for (uint256 i = 0; i < PUBLIC_VALUES_LENGTH; ++i) { + uint256 expected = uint256(uint8(guestPvs[i])); + uint256 actual = uint256(bytes32(_guestPvs[i * 32:(i + 1) * 32])); + require(expected == actual, "Guest PVs hash mismatch"); + } + + // Suppress return value warning + assembly { + return(0x00, 0x00) + } + } + + function test_RevertWhen_InvalidPublicValuesLength() public { + bytes memory invalidPvs = new bytes(0); + bytes4 sig = bytes4(keccak256("InvalidPublicValuesLength(uint256,uint256)")); + + vm.expectRevert(abi.encodeWithSelector(sig, 32, invalidPvs.length)); + verifier.verify(invalidPvs, hex"", bytes32(0), bytes32(0)); + } + + function test_RevertWhen_InvalidProofDataLength() public { + bytes memory invalidProofData = new bytes(0); + bytes4 sig = bytes4(keccak256("InvalidProofDataLength(uint256,uint256)")); + + bytes memory pvs = new bytes(PUBLIC_VALUES_LENGTH); + + vm.expectRevert(abi.encodeWithSelector(sig, 55 * 32, invalidProofData.length)); + verifier.verify(pvs, invalidProofData, appExeCommit, appVmCommit); + } + + function test_RevertWhen_ProofVerificationFailed() public { + bytes memory _proofData = new bytes(55 * 32); + bytes memory pvs = new bytes(PUBLIC_VALUES_LENGTH); + + bytes4 sig = bytes4(keccak256("ProofVerificationFailed()")); + + vm.expectRevert(abi.encodeWithSelector(sig)); + verifier.verify(pvs, _proofData, appExeCommit, appVmCommit); + } +} diff --git a/test/v1.0.1/evm.proof b/test/v1.0.1/evm.proof new file mode 100644 index 0000000..b39ce46 --- /dev/null +++ b/test/v1.0.1/evm.proof @@ -0,0 +1,9 @@ +{ + "app_exe_commit": "00303f848c4546ece61a1ad1732770e1c911aec4bdaa779b4a9ad97de58486ba", + "app_vm_commit": "00706079fa2dbd52e208154a90929c259c95b63a40d02cb1ceb4efc746011db5", + "user_public_values": "0000000000000000000000000000000000000000000000000000000000000000", + "proof_data": { + "accumulator": "0000000000000000000000000000000000000000005ac76df03787d3511e486d000000000000000000000000000000000000000000fed68872f2718456b3bff9000000000000000000000000000000000000000000000bae0013a4ee35394de00000000000000000000000000000000000000000008897a700820ac070db6ef60000000000000000000000000000000000000000004f05881a81153a01d008b0000000000000000000000000000000000000000000001df4dc4d3ea098837ba60000000000000000000000000000000000000000001082aeb50e030f8e0b23e9000000000000000000000000000000000000000000dfa8a1181b4df766ede2df0000000000000000000000000000000000000000000000e0fabcadb994fbd76f000000000000000000000000000000000000000000fcd115e1abcac231085342000000000000000000000000000000000000000000a64501938739672fdb04a20000000000000000000000000000000000000000000016417efaf8ee7458015d", + "proof": "14bcb42137535ded0fac649092ffc028e8fa0bf42f59313a57fcb499f6a663fe10d11afe94b79a56e4eac9eabc17111606e9c71d9ac269197c91c0a585422e2407ba6a1bfd7f192beb08332e7003c19547d670c1cdedbb7810fc6c43f142930d289206dbc2d6689ec4ba6f9585bea88fea56344ba2d6eac74d93d0a4cacbcc79210fbd74d80fba2e26207c12ac104e17ef047804fc3e93f7498ee1a1803f76fe1a395711b7608c24f6c487affe9f11e5a46499e2e7bd1f41005143c2ca0b006e166c8bb3c7351b0deabb70ebb86934794351570c672bb18a4e394ecd1a6e15ab1adfc1f1ec009ce7cc923ef7bc5e7f6d7d0ca328124f80f9eb821d6ec48c308a257f9cd698cea21fd0738049b105e848dc3d22faef26659359db4c1b7a42f0ec252138cee9f360d7921fa3fad485c48543cf9b0bf3fdc3baccd0d65ca289d97213b32b68397086286e62877868c907f034163fccbc22e27786968e12bdd93e82261ae437e1c146ab0e73a5ad68dfd36a5c8dacfa81148866e67f8e730168ce2e10de1dce95a6e1c87b0a4d5091c9894fd5338317a4eae7158c83c05861a73b37084c6c3aba9a21cd776a2fbceb19cc7f4597465c839c68ea67228a691ee9cd062a33c355ab039ab7b0ca6c314d77991271aa479b6a5e1e0b02a613ae072498eb1727575e4308aa74649fe935bfbb377aa1978ce0ba30ac7a5a091572aa03c958138c62fe2414cfefb821b072a90f3d9ce7954b7a6040ee3d1ba8a77d828e17212caa6b1c55ca92ed5f404d3d8fe1bcf4996e2cad3e56be0aa3a3b04a8d521f97213543a79e98baa7e4cc0b6be5ef6751c5ebe5cee508d10f21c1e3335de8bee11e5a44d3f854d60fc8209e9ae1d4fe91081b30783c2b8a6e0ac2eb2fc32a893a171dc6472fff4da48c7aacad8516051b4a4b98360419a11e5909c236ea1334ab0b3c7612184662aea1924a3209c13dc12c37c580933cd1fac2318636d78cd2c8017bd19a006ed698e22202109abe8e9c0afda9e60882aba0a866d7f3a75bcd630f4d461b787c7b3180144186320796b9141272780061a862a24d083db95a1db513a14115e571350d3114beb134851c0a5fb9361a36c1ffc88d2ce50b6f8597aa09360b81f8ede3820ea328b3844e5efe9649ddb4696e84fd45a78afd4c7063fa1fe23baf867e52475e3a66962ae16c7b2929f1947eb0b4db4bf9a27793cb71aa295a5bc0a3819d7fdfe8f135f049c06c2f9ce591e03c948d56911746c2ff097919e6e790f4c5c1804349017c0372cae6ec988fde5a853e319e84a1772ffb7a2d024b33f4d47d1d5c09b0dbf4c18a20fddbb44d119eb5a036a511b5efe17be69c0770c28bd2c411a82a083ca56cf0f53483e6eaf87f8fe1431ef0185e72a004cd22aeca87c28eab1966e894d83032ae2e7b1fdb0a7065747a7782f31443c333620657e528ec046b45fc84cbff90ede10a68b127b63e49871e5c1aff09469b101e055d71678c0a457861247f3d9097ed953fe970b84dac624793a5e5a85a1ed96522a6b03ed4970fe24b02de063fc420ebb1613d478d566b2104de1afdfeefc82e1d3c219655d4a824961d73c7701d2885cbf7d53a9d033273149623a7aa38eab90c3d09a92f9b760f0f9635517189d7cdad9a48afc92b6259c17992ad2ee443670d05dbcdcf7676695c57a87d56000c9cf930e848375707fe67b13f6fdff2be110315d6aff634cf9f5dbcecb4334583873b6e7b4a772b7635b6307bd2104f0e2c01348ccacb9bffac92356647270566426b435957b6bb0a929a1c10c45fd130ad1e0522d7efafd276fdebbce4c8fbd0d0bd9c591fb72a77a734cc82949e47975412f60616fdee9cf7cbb814e6d7966e7bbb1be1150f6eaa19418ee80525629d28000319eff4bb954d8bd74d24daedebb0827d60958fa40d0bd51413e1e13830fe" + } +} \ No newline at end of file From 1e9e2ba58eb843a60c0199a754ac72acb6f31f80 Mon Sep 17 00:00:00 2001 From: Hrik Bhowal <23041116+HrikB@users.noreply.github.com> Date: Mon, 5 May 2025 11:02:00 -0400 Subject: [PATCH 2/8] chore: update contracts for v1.1.1 --- foundry.toml | 2 +- src/{v1.0.1 => v1.1.1}/Halo2Verifier.sol | 0 src/{v1.0.1 => v1.1.1}/OpenVmHalo2Verifier.sol | 2 +- src/{v1.0.1 => v1.1.1}/interfaces/IOpenVmHalo2Verifier.sol | 0 test/{v1.0.1 => v1.1.1}/OpenVmHalo2Verifer.t.sol | 4 ++-- test/{v1.0.1 => v1.1.1}/evm.proof | 0 6 files changed, 4 insertions(+), 4 deletions(-) rename src/{v1.0.1 => v1.1.1}/Halo2Verifier.sol (100%) rename src/{v1.0.1 => v1.1.1}/OpenVmHalo2Verifier.sol (99%) rename src/{v1.0.1 => v1.1.1}/interfaces/IOpenVmHalo2Verifier.sol (100%) rename test/{v1.0.1 => v1.1.1}/OpenVmHalo2Verifer.t.sol (97%) rename test/{v1.0.1 => v1.1.1}/evm.proof (100%) diff --git a/foundry.toml b/foundry.toml index ebbf0b6..c073ccf 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,7 +8,7 @@ optimizer = true optimizer_runs = 100000 evm_version = "paris" show_progress = true -fs_permissions = [{ access = "read", path = "./test/v1.0.1/evm.proof"}] +fs_permissions = [{ access = "read", path = "./test/v1.1.1/evm.proof"}] [profile.default.optimizer_details] constantOptimizer = false diff --git a/src/v1.0.1/Halo2Verifier.sol b/src/v1.1.1/Halo2Verifier.sol similarity index 100% rename from src/v1.0.1/Halo2Verifier.sol rename to src/v1.1.1/Halo2Verifier.sol diff --git a/src/v1.0.1/OpenVmHalo2Verifier.sol b/src/v1.1.1/OpenVmHalo2Verifier.sol similarity index 99% rename from src/v1.0.1/OpenVmHalo2Verifier.sol rename to src/v1.1.1/OpenVmHalo2Verifier.sol index ef41b44..5969495 100644 --- a/src/v1.0.1/OpenVmHalo2Verifier.sol +++ b/src/v1.1.1/OpenVmHalo2Verifier.sol @@ -29,7 +29,7 @@ contract OpenVmHalo2Verifier is Halo2Verifier, IOpenVmHalo2Verifier { uint256 private constant FULL_PROOF_LENGTH = (12 + 2 + PUBLIC_VALUES_LENGTH + 43) * 32; /// @dev The version of OpenVM that generated this verifier. - string public constant OPENVM_VERSION = "1.0.1-rc.0"; + string public constant OPENVM_VERSION = "1.1.1"; /// @notice A wrapper that constructs the proof into the right format for /// use with the `snark-verifier` verification. diff --git a/src/v1.0.1/interfaces/IOpenVmHalo2Verifier.sol b/src/v1.1.1/interfaces/IOpenVmHalo2Verifier.sol similarity index 100% rename from src/v1.0.1/interfaces/IOpenVmHalo2Verifier.sol rename to src/v1.1.1/interfaces/IOpenVmHalo2Verifier.sol diff --git a/test/v1.0.1/OpenVmHalo2Verifer.t.sol b/test/v1.1.1/OpenVmHalo2Verifer.t.sol similarity index 97% rename from test/v1.0.1/OpenVmHalo2Verifer.t.sol rename to test/v1.1.1/OpenVmHalo2Verifer.t.sol index 66ba4a6..af2e1b4 100644 --- a/test/v1.0.1/OpenVmHalo2Verifer.t.sol +++ b/test/v1.1.1/OpenVmHalo2Verifer.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import { OpenVmHalo2Verifier } from "../../src/v1.0.1/OpenVmHalo2Verifier.sol"; +import { OpenVmHalo2Verifier } from "../../src/v1.1.1/OpenVmHalo2Verifier.sol"; import { LibString } from "../helpers/LibString.sol"; import { Test, console2, safeconsole as console } from "forge-std/Test.sol"; @@ -26,7 +26,7 @@ contract OpenVmHalo2VerifierTest is Test { } function test_ValidProofVerifies() public view { - string memory evmProofJson = vm.readFile("test/v1.0.1/evm.proof"); + string memory evmProofJson = vm.readFile("test/v1.1.1/evm.proof"); bytes32 _appExeCommit = vm.parseJsonBytes32(evmProofJson, ".app_exe_commit"); bytes32 _appVmCommit = vm.parseJsonBytes32(evmProofJson, ".app_vm_commit"); bytes memory _guestPvs = vm.parseJsonBytes(evmProofJson, ".user_public_values"); diff --git a/test/v1.0.1/evm.proof b/test/v1.1.1/evm.proof similarity index 100% rename from test/v1.0.1/evm.proof rename to test/v1.1.1/evm.proof From cb5c580c683059b031239a1baa069b8b339f2721 Mon Sep 17 00:00:00 2001 From: Hrik Bhowal <23041116+HrikB@users.noreply.github.com> Date: Mon, 5 May 2025 14:45:22 -0400 Subject: [PATCH 3/8] docs: add README --- README.md | 72 +++++++++++++++++++------------------------------------ 1 file changed, 24 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 9265b45..9aef445 100644 --- a/README.md +++ b/README.md @@ -1,66 +1,42 @@ -## Foundry +# OpenVM Solidity SDK -**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** +This repo houses OpenVM verifier contracts generated on official release commits of the [openvm](https://github.com/openvm-org/openvm) repo with the default VM config using the `cargo-openvm` cli tool. Advanced VM configurations may require their own generations. -Foundry consists of: +## Installation -- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). -- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. -- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. -- **Chisel**: Fast, utilitarian, and verbose solidity REPL. +To install `openvm-solidity-sdk` as a dependency in your forge project, run the following: -## Documentation - -https://book.getfoundry.sh/ - -## Usage - -### Build - -```shell -$ forge build -``` - -### Test - -```shell -$ forge test +```bash +forge install openvm-org/openvm-solidity-sdk ``` -### Format +## Usage -```shell -$ forge fmt -``` +If you are using a deployed instance of the verifier contract, then you can import the interfaces in your contract directly. -### Gas Snapshots +```solidity +import { IOpenVmHalo2Verifier } from "openvm-solidity-sdk/v1.1.1/interfaces/IOpenVmHalo2Verifier.sol"; -```shell -$ forge snapshot -``` +contract MyContract { + function myFunction() public view { + // ... snip ... -### Anvil + IOpenVmHalo2Verifier(verifierAddress) + .verify(publicValues, proofData, appExeCommit, appVmCommit); -```shell -$ anvil + // ... snip ... + } +} ``` -### Deploy +If you want to deploy your own instance of the verifier contract, you can use `forge create`: -```shell -$ forge script script/Counter.s.sol:CounterScript --rpc-url --private-key +```bash +forge create src/v1.1.1/OpenVmHalo2Verifier.sol:OpenVmHalo2Verifier --rpc-url $RPC --private-key $PRIVATE_KEY --broadcast ``` -### Cast +If you want to import the verifier contract into your own repository for testing purposes, note that it is locked to Solidity version `0.8.19`. If your project uses a different version, the import may not compile. As a workaround, you can compile the contract separately and use `vm.etch()` to inject the raw bytecode into your tests. -```shell -$ cast -``` - -### Help +## Audits -```shell -$ forge --help -$ anvil --help -$ cast --help -``` +You can find the audit reports for these contracts in the [OpenVM repo](https://github.com/openvm-org/openvm/tree/main/audits). From a9dd4dd2b871825dc222643daa1da64fc2220ac5 Mon Sep 17 00:00:00 2001 From: Hrik Bhowal <23041116+HrikB@users.noreply.github.com> Date: Tue, 6 May 2025 14:00:50 -0400 Subject: [PATCH 4/8] chore: add comment in README about only building across minor releases --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9aef445..479d861 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # OpenVM Solidity SDK -This repo houses OpenVM verifier contracts generated on official release commits of the [openvm](https://github.com/openvm-org/openvm) repo with the default VM config using the `cargo-openvm` cli tool. Advanced VM configurations may require their own generations. +This repository contains OpenVM verifier contracts generated from official release commits of the [openvm](https://github.com/openvm-org/openvm) repository using the default VM configuration via the cargo-openvm CLI tool. If you're using an advanced or custom VM configuration, you may need to generate and maintain your own verifier contracts separately. + +The contracts are built on every _minor_ release as OpenVM guarantees verifier backward compatibility across patch releases. ## Installation From b657231c751e785e4089a35d6dba5ea83fc07990 Mon Sep 17 00:00:00 2001 From: Hrik Bhowal <23041116+HrikB@users.noreply.github.com> Date: Tue, 6 May 2025 17:30:13 -0400 Subject: [PATCH 5/8] chore: rename to v1.1 --- foundry.toml | 2 +- src/{v1.1.1 => v1.1}/Halo2Verifier.sol | 0 src/{v1.1.1 => v1.1}/OpenVmHalo2Verifier.sol | 0 src/{v1.1.1 => v1.1}/interfaces/IOpenVmHalo2Verifier.sol | 0 test/{v1.1.1 => v1.1}/OpenVmHalo2Verifer.t.sol | 4 ++-- test/{v1.1.1 => v1.1}/evm.proof | 0 6 files changed, 3 insertions(+), 3 deletions(-) rename src/{v1.1.1 => v1.1}/Halo2Verifier.sol (100%) rename src/{v1.1.1 => v1.1}/OpenVmHalo2Verifier.sol (100%) rename src/{v1.1.1 => v1.1}/interfaces/IOpenVmHalo2Verifier.sol (100%) rename test/{v1.1.1 => v1.1}/OpenVmHalo2Verifer.t.sol (96%) rename test/{v1.1.1 => v1.1}/evm.proof (100%) diff --git a/foundry.toml b/foundry.toml index c073ccf..fddcd84 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,7 +8,7 @@ optimizer = true optimizer_runs = 100000 evm_version = "paris" show_progress = true -fs_permissions = [{ access = "read", path = "./test/v1.1.1/evm.proof"}] +fs_permissions = [{ access = "read", path = "./test/v1.1/evm.proof"}] [profile.default.optimizer_details] constantOptimizer = false diff --git a/src/v1.1.1/Halo2Verifier.sol b/src/v1.1/Halo2Verifier.sol similarity index 100% rename from src/v1.1.1/Halo2Verifier.sol rename to src/v1.1/Halo2Verifier.sol diff --git a/src/v1.1.1/OpenVmHalo2Verifier.sol b/src/v1.1/OpenVmHalo2Verifier.sol similarity index 100% rename from src/v1.1.1/OpenVmHalo2Verifier.sol rename to src/v1.1/OpenVmHalo2Verifier.sol diff --git a/src/v1.1.1/interfaces/IOpenVmHalo2Verifier.sol b/src/v1.1/interfaces/IOpenVmHalo2Verifier.sol similarity index 100% rename from src/v1.1.1/interfaces/IOpenVmHalo2Verifier.sol rename to src/v1.1/interfaces/IOpenVmHalo2Verifier.sol diff --git a/test/v1.1.1/OpenVmHalo2Verifer.t.sol b/test/v1.1/OpenVmHalo2Verifer.t.sol similarity index 96% rename from test/v1.1.1/OpenVmHalo2Verifer.t.sol rename to test/v1.1/OpenVmHalo2Verifer.t.sol index af2e1b4..c4071d9 100644 --- a/test/v1.1.1/OpenVmHalo2Verifer.t.sol +++ b/test/v1.1/OpenVmHalo2Verifer.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.19; -import { OpenVmHalo2Verifier } from "../../src/v1.1.1/OpenVmHalo2Verifier.sol"; +import { OpenVmHalo2Verifier } from "../../src/v1.1/OpenVmHalo2Verifier.sol"; import { LibString } from "../helpers/LibString.sol"; import { Test, console2, safeconsole as console } from "forge-std/Test.sol"; @@ -26,7 +26,7 @@ contract OpenVmHalo2VerifierTest is Test { } function test_ValidProofVerifies() public view { - string memory evmProofJson = vm.readFile("test/v1.1.1/evm.proof"); + string memory evmProofJson = vm.readFile("test/v1.1/evm.proof"); bytes32 _appExeCommit = vm.parseJsonBytes32(evmProofJson, ".app_exe_commit"); bytes32 _appVmCommit = vm.parseJsonBytes32(evmProofJson, ".app_vm_commit"); bytes memory _guestPvs = vm.parseJsonBytes(evmProofJson, ".user_public_values"); diff --git a/test/v1.1.1/evm.proof b/test/v1.1/evm.proof similarity index 100% rename from test/v1.1.1/evm.proof rename to test/v1.1/evm.proof From 05d7d05cb754e97869eb630681c71ba85deaff05 Mon Sep 17 00:00:00 2001 From: Hrik Bhowal <23041116+HrikB@users.noreply.github.com> Date: Tue, 6 May 2025 18:11:56 -0400 Subject: [PATCH 6/8] chore: update OpenVM version --- src/v1.1/OpenVmHalo2Verifier.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/v1.1/OpenVmHalo2Verifier.sol b/src/v1.1/OpenVmHalo2Verifier.sol index 5969495..ce26153 100644 --- a/src/v1.1/OpenVmHalo2Verifier.sol +++ b/src/v1.1/OpenVmHalo2Verifier.sol @@ -29,7 +29,7 @@ contract OpenVmHalo2Verifier is Halo2Verifier, IOpenVmHalo2Verifier { uint256 private constant FULL_PROOF_LENGTH = (12 + 2 + PUBLIC_VALUES_LENGTH + 43) * 32; /// @dev The version of OpenVM that generated this verifier. - string public constant OPENVM_VERSION = "1.1.1"; + string public constant OPENVM_VERSION = "1.1"; /// @notice A wrapper that constructs the proof into the right format for /// use with the `snark-verifier` verification. From 368d5d8abfdce828de1adbc1869462b1934cf500 Mon Sep 17 00:00:00 2001 From: Hrik Bhowal <23041116+HrikB@users.noreply.github.com> Date: Tue, 6 May 2025 18:12:20 -0400 Subject: [PATCH 7/8] docs: update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 479d861..d8f5bf2 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ forge install openvm-org/openvm-solidity-sdk If you are using a deployed instance of the verifier contract, then you can import the interfaces in your contract directly. ```solidity -import { IOpenVmHalo2Verifier } from "openvm-solidity-sdk/v1.1.1/interfaces/IOpenVmHalo2Verifier.sol"; +import { IOpenVmHalo2Verifier } from "openvm-solidity-sdk/v1.1/interfaces/IOpenVmHalo2Verifier.sol"; contract MyContract { function myFunction() public view { @@ -34,7 +34,7 @@ contract MyContract { If you want to deploy your own instance of the verifier contract, you can use `forge create`: ```bash -forge create src/v1.1.1/OpenVmHalo2Verifier.sol:OpenVmHalo2Verifier --rpc-url $RPC --private-key $PRIVATE_KEY --broadcast +forge create src/v1.1/OpenVmHalo2Verifier.sol:OpenVmHalo2Verifier --rpc-url $RPC --private-key $PRIVATE_KEY --broadcast ``` If you want to import the verifier contract into your own repository for testing purposes, note that it is locked to Solidity version `0.8.19`. If your project uses a different version, the import may not compile. As a workaround, you can compile the contract separately and use `vm.etch()` to inject the raw bytecode into your tests. From b5048dbe445d5a4b7ee3cf1a076034ff674d3943 Mon Sep 17 00:00:00 2001 From: Hrik Bhowal <23041116+HrikB@users.noreply.github.com> Date: Wed, 7 May 2025 20:57:54 -0400 Subject: [PATCH 8/8] chore: update consistency check --- .github/workflows/consistency.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/consistency.yml b/.github/workflows/consistency.yml index 619f455..f83d206 100644 --- a/.github/workflows/consistency.yml +++ b/.github/workflows/consistency.yml @@ -37,8 +37,8 @@ jobs: - name: Check TRUNC_VERSION run: | expected="${VERSION%.*}" # bash: strip last “.patch” - if [ "${{ steps.extract.outputs.TRUNC_VERSION }}" != "$expected" ]; then - echo "::error::TRUNC_VERSION mismatch: got '${{ steps.extract.outputs.TRUNC_VERSION }}' but expected '$expected'" + if [ "$TRUNC_VERSION" != "$expected" ]; then + echo "::error::TRUNC_VERSION mismatch: got '$TRUNC_VERSION' but expected '$expected'" exit 1 fi