diff --git a/README.md b/README.md index 97f1b44..e735d99 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,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.3/interfaces/IOpenVmHalo2Verifier.sol"; +import { IOpenVmHalo2Verifier } from "openvm-solidity-sdk/v1.4/interfaces/IOpenVmHalo2Verifier.sol"; contract MyContract { function myFunction() public view { @@ -36,7 +36,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.3/OpenVmHalo2Verifier.sol:OpenVmHalo2Verifier --rpc-url $RPC --private-key $PRIVATE_KEY --broadcast +forge create src/v1.4/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. diff --git a/foundry.toml b/foundry.toml index 7c56697..4f794d6 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.2/evm.proof"}, { access = "read", path = "./test/v1.3/evm.proof"}] +fs_permissions = [{ access = "read", path = "./test/v1.2/evm.proof"}, { access = "read", path = "./test/v1.3/evm.proof"}, { access = "read", path = "./test/v1.4/evm.proof"}] [profile.default.optimizer_details] constantOptimizer = false diff --git a/src/v1.4/Halo2Verifier.sol b/src/v1.4/Halo2Verifier.sol new file mode 100644 index 0000000..c0f4623 --- /dev/null +++ b/src/v1.4/Halo2Verifier.sol @@ -0,0 +1,2036 @@ +// 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, 390330814115173750526380320564184081440196244988022584520359727231485894627) + + { + 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, 0x04633090806662534335356654f3ee6430f215f4f010ae32ae068a900596d598) + mstore(0x5560, 0x2559e379146d41a440431d5bcb5ebdf2f978e08999ccf6cfff2586402050780f) + 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, 0x10ff9174af9a7540d6335a6dad3dd0fe0c3943b24b706ba111c73534218c8d99) + mstore(0x58e0, 0x07769bf2a7e819b8b069c49481451ca34b9cfe9cf4ab6c03709a3b33aa9f25f9) + 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, 0x2347cc725aa8ab0c70557e7effada9a02cba0277f8aca1785f986430d8b63ad4) + mstore(0x59c0, 0x2f7f8f66561183dd0a7c6cf12a1fa248b80509334c8e87dbdcf192301a3c6a4d) + 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.4/OpenVmHalo2Verifier.sol b/src/v1.4/OpenVmHalo2Verifier.sol new file mode 100644 index 0000000..65408b1 --- /dev/null +++ b/src/v1.4/OpenVmHalo2Verifier.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import { Halo2Verifier } from "./Halo2Verifier.sol"; +import { IOpenVmHalo2Verifier } from "./interfaces/IOpenVmHalo2Verifier.sol"; + +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 public values length + 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. + uint256 private constant PROOF_DATA_LENGTH = (12 + 43) * 32; + + /// @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 version of OpenVM that generated this verifier. + string public constant OPENVM_VERSION = "1.4"; + + /// @notice A wrapper that constructs the proof into the right format for + /// use with the `snark-verifier` verification. + /// + /// @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]: app vm commit + /// proof[14 * 32..(14 + PUBLIC_VALUES_LENGTH) * 32]: publicValues[0..PUBLIC_VALUES_LENGTH] + /// 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 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. + /// @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, appVmCommit); + + uint256 fullProofLength = FULL_PROOF_LENGTH; + + /// @solidity memory-safe-assembly + assembly { + // Self-call using the proof as calldata + if iszero(staticcall(gas(), address(), proofPtr, fullProofLength, 0, 0)) { + mstore(0x00, 0xd611c318) // ProofVerificationFailed() + revert(0x1c, 0x04) + } + } + } + + /// @dev The assembly code should perform the same function as the following + /// solidity code: + // + /// ```solidity + /// bytes memory proof = + /// 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 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. 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; + + // The expected proof format using hex offsets: + // + // proof[..0x180]: KZG accumulator + // proof[0x180..0x1a0]: app 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)..]: Proof Suffix + + /// @solidity memory-safe-assembly + assembly { + proofPtr := mload(0x40) + // Allocate the memory as a safety measure. + mstore(0x40, add(proofPtr, fullProofLength)) + + // Copy the KZG accumulator (length 0x180) into the beginning of + // the memory buffer + calldatacopy(proofPtr, proofData.offset, 0x180) + + // Copy the App Exe Commit and App Vm Commit into the memory buffer + mstore(add(proofPtr, 0x180), appExeCommit) + mstore(add(proofPtr, 0x1a0), appVmCommit) + + // 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 accumulator in the + // calldata buffer (0x180) + 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. + let publicValuesMemOffset := add(add(proofPtr, 0x1c0), 0x1f) + for { let i := 0 } iszero(eq(i, PUBLIC_VALUES_LENGTH)) { i := add(i, 1) } { + calldatacopy(add(publicValuesMemOffset, shl(5, i)), add(publicValues.offset, i), 0x01) + } + } + } +} diff --git a/src/v1.4/interfaces/IOpenVmHalo2Verifier.sol b/src/v1.4/interfaces/IOpenVmHalo2Verifier.sol new file mode 100644 index 0000000..ac8292c --- /dev/null +++ b/src/v1.4/interfaces/IOpenVmHalo2Verifier.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +interface IOpenVmHalo2Verifier { + function verify(bytes calldata publicValues, bytes calldata proofData, bytes32 appExeCommit, bytes32 appVmCommit) + external + view; +} diff --git a/test/helpers/LibString.sol b/test/helpers/LibString.sol deleted file mode 100644 index f046fa4..0000000 --- a/test/helpers/LibString.sol +++ /dev/null @@ -1,1628 +0,0 @@ -// 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.2/OpenVmHalo2Verifer.t.sol b/test/v1.2/OpenVmHalo2Verifer.t.sol index ce87f8f..7cceed1 100644 --- a/test/v1.2/OpenVmHalo2Verifer.t.sol +++ b/test/v1.2/OpenVmHalo2Verifer.t.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.19; import { OpenVmHalo2Verifier } from "../../src/v1.2/OpenVmHalo2Verifier.sol"; -import { LibString } from "../helpers/LibString.sol"; import { Test, console2, safeconsole as console } from "forge-std/Test.sol"; contract OpenVmHalo2VerifierTest is Test { diff --git a/test/v1.3/OpenVmHalo2Verifer.t.sol b/test/v1.3/OpenVmHalo2Verifer.t.sol index da82f14..5009953 100644 --- a/test/v1.3/OpenVmHalo2Verifer.t.sol +++ b/test/v1.3/OpenVmHalo2Verifer.t.sol @@ -2,7 +2,6 @@ pragma solidity 0.8.19; import { OpenVmHalo2Verifier } from "../../src/v1.3/OpenVmHalo2Verifier.sol"; -import { LibString } from "../helpers/LibString.sol"; import { Test, console2, safeconsole as console } from "forge-std/Test.sol"; contract OpenVmHalo2VerifierTest is Test { diff --git a/test/v1.4/OpenVmHalo2Verifer.t.sol b/test/v1.4/OpenVmHalo2Verifer.t.sol new file mode 100644 index 0000000..639705e --- /dev/null +++ b/test/v1.4/OpenVmHalo2Verifer.t.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import { OpenVmHalo2Verifier } from "../../src/v1.4/OpenVmHalo2Verifier.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.4/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.4/evm.proof b/test/v1.4/evm.proof new file mode 100644 index 0000000..8e65c8c --- /dev/null +++ b/test/v1.4/evm.proof @@ -0,0 +1,10 @@ +{ + "version": "v1.4", + "app_exe_commit": "006a52a4763a5ec11067b1eef48f752a474f8c3363cf106c8aedce8b8b5d8268", + "app_vm_commit": "0053b850b281802e42a58b63fe114a0797f8092777f9bbf01df5800fba3c761c", + "user_public_values": "3d39740655edfc46fb8afd539e23070dfb35e0d6d1cf67eed7a1c25442e01696", + "proof_data": { + "accumulator": "0000000000000000000000000000000000000000008bd71a69f5415c58085188000000000000000000000000000000000000000000557b6e65edf910e88902d6000000000000000000000000000000000000000000000d2ae29d694c41ca523c000000000000000000000000000000000000000000ba94b20f906f386199383e000000000000000000000000000000000000000000d0ba03db70a0881c1f4023000000000000000000000000000000000000000000001eeff5612fb64348fbe30000000000000000000000000000000000000000008f62cc30864ec4d6b6374800000000000000000000000000000000000000000085bcc2d13420766823ac67000000000000000000000000000000000000000000002fdb379a11b23a8deda1000000000000000000000000000000000000000000055ba0e90b3a17943b6d0c000000000000000000000000000000000000000000872fd4bd16c7b92b494e9f0000000000000000000000000000000000000000000013e02e4e69594346e550", + "proof": "170b9021e87448c497035fbeb1ad5a6e73c24b621525bd3d26d0196efe6c70081ef2c0edade65c6c6c04ddfcee336fc23e085823a39e38239a356bfc4fe052792bde9957d66c442924bc34b2312c943a2c16ab54318cd5285303579d7d5ac9cc02a6f74a859bc1be1456ebf72c3f2d1d36252e9085ea1efb84f372c0943e95ea0f9c976ca8d38167b0cbbddb848833f27e25326922978874acdcf3f22a1292f925f95e8a627d9d88ae46d5369eaaf20b2943b4d4a56b4ac1243407e367f11e682060e110c6b64d33ef00137aa60e35dc4de63fd2866a5fbd9ece6cdf5476976805f3c4ede9d358183468b5d3652eb0a0916c4148bb1425ac0d3ff516ffe8869c1e7ae63f0d7fef908a5d1ba88bf4396d15d985d4661029c36402e8262e9986922411ab15344d0c090cfc5de48094969b20f91691cb049ab2b5f5f01a0755911f1d6669489d567feded2f33dc512a56786b755cf7c1f43e73fd3ab8d1c55919640953a2ca3e3119c826fcead8491393321daff8f9dbe5010490658004cf67de8e2629f96fd805a89656a4a39bfc41e1ffc23bbd7995b0c955f07a94d7e85c961119b7a45c96ca87aa70d2051d90bf673c677e3a9c1470ffe69a3d5fcef169f2cd1c52974c18d4a00336d61be053ca00b3c76d28ec3d8e373a0d4e41e6fe22909c2c5cfbd12365e5c296b7951cb35da9aea11000d4efecf6a4b2bd61ba774ba402225b63158ef8d6da0ab9731b69fd7f55801c0750d4bc4cd5d8569389813822be05ba80ab2f1ab3c6c8a2ddd8a1a8ee98d081d0c455b1edce2b2cea4f3d09bced1d61d934a25e0721b913188fcd213e31a5300168182d5482bede2ccffe2e3b6819edeb2c660cff868a884e3baef99dd0aeeca02e936a7d0c0aebeb5c98098b2b0cc4cc1ece5daae223e2399a35e890f04a4142f49739098a1dbc85dafb497e39226ea6720d51967d3a41ab6d460b0b7162b4ae072300c03959151dab3a89f1810a745e447d2dd914d7397c18e4075b1918400f335c9ecf5bfe247c67318cbf2701812ab98b9a8505e6bb4dee68facee3ff5c74d9f0d8d8ca917df81e13bc29a7229a590c2cbb6ee41d2ba9d46dc25739af19dd69a596c25517e6f49032d8b88316d4db63ad87a739702600821cd3fe2fe3fd1bb4653034e2e883ee5b97fbb7a02a5497b6ac7fefcd7fee9bd3394912b38d4d4a08bb2ffb115a8e058d8fccaa992ae8999962be11d39b13d64ad53323ca1661bb4b928cc7d1d13f6d185144524d241a472d17b5bf7f3c25e4d5a54432bb5b1efb998beac8239e3a9199c955f2fd0415dddf8790e3c47232deab0d3ef47190e7cf34a46a04374a8b68389779490e0296fb1df68dead22252f2696de38cdd2f1bbb3c5fa4e12a72698b5614b978970b2bf374aecce0f8abbfac33c136062f42f51f51504f8f1936eb540e19bb90f429a27b7ce0ec7cf4e852872acd9d759a22bb9ea746df2a9545ec7879b88e20671161281955a7084408a0fa49d8db048f5a7208b1a6351aa95c57e975615188a210c29ff44fca018c2ab4b8700336378f674d0f8802e037e1549613cbe1059fd304ee281f1bc948bf3154362031e26063fef208cc2fb8cd47283819b77969ac62183cfdbe2ea8c56e7edfe650d37dd87138b538ab934fd6c6350875f79eda6c4e1bd2002e399373b64251d81a5977cc82ae5925806ee6c056a8b6767a5e4bbef6132a3cc33c79ec0d0f292188cc54e54008d315c66a54412874f5079745b5eee80b25419832f6e75622bd9ca28f2e6ebb023ce29456202c803ff82adb9fb8ac8706abb09051c32f07ce5bc5f4ea025efbf148ab6aef041d15b81437ac36473b350fba5604dcc1f162a2db2e0454d35e08c9ea0de5f4cd294228e9dd10933f550111e38b74ce85445deeb23718e09f1d8b6b10c4d772a811e4d31740271bcf4751" + } +} \ No newline at end of file