diff --git a/system-contracts/SystemContractsHashes.json b/system-contracts/SystemContractsHashes.json index e39d8c95b..eeb4a80f3 100644 --- a/system-contracts/SystemContractsHashes.json +++ b/system-contracts/SystemContractsHashes.json @@ -3,49 +3,49 @@ "contractName": "AccountCodeStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/AccountCodeStorage.sol/AccountCodeStorage.json", "sourceCodePath": "contracts-preprocessed/AccountCodeStorage.sol", - "bytecodeHash": "0x01000075e8397f3d02eddfe166a8324a2ac31a6da0db8eeebfa8739d3f31aad6", + "bytecodeHash": "0x0100007549287362e4263ea5b204f01fc3c7f2ac09d71e6eb21029698220f01a", "sourceCodeHash": "0xfbf66e830201c4b7fda14f0ddf28a53beb7fbb48a8406392bcfd0ef7ea9265c8" }, { "contractName": "BootloaderUtilities", "bytecodePath": "artifacts-zk/contracts-preprocessed/BootloaderUtilities.sol/BootloaderUtilities.json", "sourceCodePath": "contracts-preprocessed/BootloaderUtilities.sol", - "bytecodeHash": "0x010007d1a4fc820cffd43ee10a7cec7ddba49e3e4bffb93f490c56ad76029187", + "bytecodeHash": "0x010007d1e53f2dca05f7e27ae5b7062291ed3a1470ca511140b8e786aae7eb77", "sourceCodeHash": "0x9ff5a2da00acfa145ee4575381ad386587d96b6a0309d05015974f4726881132" }, { "contractName": "ComplexUpgrader", "bytecodePath": "artifacts-zk/contracts-preprocessed/ComplexUpgrader.sol/ComplexUpgrader.json", "sourceCodePath": "contracts-preprocessed/ComplexUpgrader.sol", - "bytecodeHash": "0x010000559e18a99ccbb6b1d3460baa9fbc44c68254335c4476d9dc74ff1828cf", + "bytecodeHash": "0x01000055c1f27b8316ba61bf07959b11cf3b2a418aa357ccc5531c0914a2da27", "sourceCodeHash": "0x0aa5d7ed159e783acde47856b13801b7f2268ba39b2fa50807fe3d705c506e96" }, { "contractName": "Compressor", "bytecodePath": "artifacts-zk/contracts-preprocessed/Compressor.sol/Compressor.json", "sourceCodePath": "contracts-preprocessed/Compressor.sol", - "bytecodeHash": "0x01000179af9c195ca2588ae3034175586cec77ba5d95ead095851c042873d785", + "bytecodeHash": "0x01000179842b5aa1c76036f5b90652fe614dacb28438a89649d6ca48131bd402", "sourceCodeHash": "0xd43ac120a50398e0d6bdcfcf807154bfeece0c231509a0eb2e00bcad744e60cd" }, { "contractName": "ContractDeployer", "bytecodePath": "artifacts-zk/contracts-preprocessed/ContractDeployer.sol/ContractDeployer.json", "sourceCodePath": "contracts-preprocessed/ContractDeployer.sol", - "bytecodeHash": "0x010005212b10d9ee47071bcd4ad45f5c8c773faec7505bc27d82a2109001e9f2", + "bytecodeHash": "0x010005215fda00bfbf95847a13078bd16cdcb1b875534261c1dda9940c7754fe", "sourceCodeHash": "0x635301b824f927b4d17b3d9974cf6abbf979dda49e610805637db7c677d5f522" }, { "contractName": "Create2Factory", "bytecodePath": "artifacts-zk/contracts-preprocessed/Create2Factory.sol/Create2Factory.json", "sourceCodePath": "contracts-preprocessed/Create2Factory.sol", - "bytecodeHash": "0x0100004b1ac12a1a5fa649f97aea9257952e07fcc27f86a5ce537013eaf726c5", + "bytecodeHash": "0x0100004bc85f45ebf0f0bf004752bcbff1bb99792d6cc6494227970ec77fe53b", "sourceCodeHash": "0x217e65f55c8add77982171da65e0db8cc10141ba75159af582973b332a4e098a" }, { "contractName": "DefaultAccount", "bytecodePath": "artifacts-zk/contracts-preprocessed/DefaultAccount.sol/DefaultAccount.json", "sourceCodePath": "contracts-preprocessed/DefaultAccount.sol", - "bytecodeHash": "0x01000563dc93ec6220498801ccef18c8e667fe26b7fdd9fb9a8d8e01796144ff", + "bytecodeHash": "0x01000563374c277a2c1e34659a2a1e87371bb6d852ce142022d497bfb50b9e32", "sourceCodeHash": "0xa42423712ddaa8f357d26e46825fda80a9a870d0ac7ff52c98884355f1173ec7" }, { @@ -59,63 +59,63 @@ "contractName": "GasBoundCaller", "bytecodePath": "artifacts-zk/contracts-preprocessed/GasBoundCaller.sol/GasBoundCaller.json", "sourceCodePath": "contracts-preprocessed/GasBoundCaller.sol", - "bytecodeHash": "0x010000b50991299ed474c996afdd521b9ba50ff8da1ae58d0ded14747f6bd441", + "bytecodeHash": "0x010000b5e930829f22bd5df4fac3cb37b599cf9733554124bfb7e717fa4a726b", "sourceCodeHash": "0x68db837d79ab575450f9123d97c7e566f311fb2e8d91c0d43dc9769ca895ccd3" }, { "contractName": "ImmutableSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/ImmutableSimulator.sol/ImmutableSimulator.json", "sourceCodePath": "contracts-preprocessed/ImmutableSimulator.sol", - "bytecodeHash": "0x0100003d95fede326bfa4034abb2ad310b88549cca833123f14a9d2b42e4625b", + "bytecodeHash": "0x0100003de00c5ceaa3fdf4566a9822ce94abe676f68b17a6ae11c453e14455fd", "sourceCodeHash": "0x30df621c72cb35b8820b902b91057f72d0214a0e4a6b7ad4c0847e674e8b9df8" }, { "contractName": "KnownCodesStorage", "bytecodePath": "artifacts-zk/contracts-preprocessed/KnownCodesStorage.sol/KnownCodesStorage.json", "sourceCodePath": "contracts-preprocessed/KnownCodesStorage.sol", - "bytecodeHash": "0x0100007dc7756ea428ae2f0d238b6015adbcb47a25ac433ca8275b0e2a2a20c8", + "bytecodeHash": "0x0100007d82d4a2eb62e539e3c89cc641f507132b247022ba05ef1ddfed2b0073", "sourceCodeHash": "0x51d388adc58f67ef975a94a7978caa60ed8a0df9d3bd9ac723dfcfc540286c70" }, { "contractName": "L1Messenger", "bytecodePath": "artifacts-zk/contracts-preprocessed/L1Messenger.sol/L1Messenger.json", "sourceCodePath": "contracts-preprocessed/L1Messenger.sol", - "bytecodeHash": "0x010002b90c71cc691211147a9c28f9325083785d0b4bda935234deb5b9744e77", + "bytecodeHash": "0x010002b97ebf3c481ead775617590ffca139bee428e443aa49eb38b6a5b83657", "sourceCodeHash": "0x35c189f3babf5c7a9ce2590bed9eb62b59766e358b7733fdb1bc33f4c232f765" }, { "contractName": "L2BaseToken", "bytecodePath": "artifacts-zk/contracts-preprocessed/L2BaseToken.sol/L2BaseToken.json", "sourceCodePath": "contracts-preprocessed/L2BaseToken.sol", - "bytecodeHash": "0x010001035ba7b7620a3fa7e2d4443131a29bf9dcbcaf1bc4d44951f8044acf71", + "bytecodeHash": "0x010001039329e4bb55b24531c7e7d27ed40d2c82ad145033fdd5ed5b8ea86cf3", "sourceCodeHash": "0x76ac95c12820d9a02cd1f177eab59092d99463816f2616e1e0f44637bf791a43" }, { "contractName": "MsgValueSimulator", "bytecodePath": "artifacts-zk/contracts-preprocessed/MsgValueSimulator.sol/MsgValueSimulator.json", "sourceCodePath": "contracts-preprocessed/MsgValueSimulator.sol", - "bytecodeHash": "0x010000694b6afab8a82dad8330a0489a7fb7021429168dd0fe21e5167ff19cf4", + "bytecodeHash": "0x010000695a1e821b6d5fcb25e25793b81de0bdca3ff8277e3ac93a38e729e0a1", "sourceCodeHash": "0x3f9e0af527875bebcdc20ca4ecb6822305877fd6038e4c4c58854d000b9ac115" }, { "contractName": "NonceHolder", "bytecodePath": "artifacts-zk/contracts-preprocessed/NonceHolder.sol/NonceHolder.json", "sourceCodePath": "contracts-preprocessed/NonceHolder.sol", - "bytecodeHash": "0x010000e55a75bd4aa370257d02878df356132e99d206932f2c86c71afe06fe0a", + "bytecodeHash": "0x010000e563d4ad7b4822cc19d8f74f2c41ee3d3153379be4b02b27d4498d52b6", "sourceCodeHash": "0x91847512344ac5026e9fd396189c23ad9e253f22cb6e2fe65805c20c915797d4" }, { "contractName": "PubdataChunkPublisher", "bytecodePath": "artifacts-zk/contracts-preprocessed/PubdataChunkPublisher.sol/PubdataChunkPublisher.json", "sourceCodePath": "contracts-preprocessed/PubdataChunkPublisher.sol", - "bytecodeHash": "0x01000049f4e028a9434cf0293f01080534b4dc2cb030696e82aaa5ee9ac57c99", + "bytecodeHash": "0x01000049eb6d79244e74e5286ed4d3f6eef2b5eb746b67d98691dbc28fa16984", "sourceCodeHash": "0xbc62d673c2cf9ba2d2148e5e2f99ea577cd357c6fd3ad7d248f670c750050faa" }, { "contractName": "SystemContext", "bytecodePath": "artifacts-zk/contracts-preprocessed/SystemContext.sol/SystemContext.json", "sourceCodePath": "contracts-preprocessed/SystemContext.sol", - "bytecodeHash": "0x010001b3e077b7fdc663b698442e88daa71e51115aa801dd0829faf1ec44f59e", + "bytecodeHash": "0x010001b3f2c3a6bdd5ad00ae29a7cbbb32dca3c31fb608b5cd52f8f3056a3847", "sourceCodeHash": "0xb90284d78f48a958d082c4c877fc91ec292d05f0e388c6c78e6cce6d3b069a63" }, { @@ -146,6 +146,13 @@ "bytecodeHash": "0x010000bd8bd7ab008f76e359dc296ff5fe0e8a95fedce1d570943e90143acdfd", "sourceCodeHash": "0xb142465167a02139087fda7640ff859489b33081dcc7c2a8089da5b480bcb58c" }, + { + "contractName": "EcPairing", + "bytecodePath": "contracts-preprocessed/precompiles/artifacts/EcPairing.yul.zbin", + "sourceCodePath": "contracts-preprocessed/precompiles/EcPairing.yul", + "bytecodeHash": "0x01000f1b3432a32f9fba2115f5dd3b0ee8127e7bf2c609d57d3e231f19119c43", + "sourceCodeHash": "0x149f025b222369ab65b9995a6d61df8b557b23f8b52a05f21dc2164839befb18" + }, { "contractName": "Ecrecover", "bytecodePath": "contracts-preprocessed/precompiles/artifacts/Ecrecover.yul.zbin", @@ -178,35 +185,35 @@ "contractName": "bootloader_test", "bytecodePath": "bootloader/build/artifacts/bootloader_test.yul.zbin", "sourceCodePath": "bootloader/build/bootloader_test.yul", - "bytecodeHash": "0x010003cb170488b0d4f5a250d546364414eb07572fc0ea9fc4ee3ea5d3126487", - "sourceCodeHash": "0x87e09808464eec5c25e958743ab8ead8cdb81b0c3948341c1e8c312d28b66121" + "bytecodeHash": "0x010003cb722f6b3ac4928fadcb3ad05bb76a7e2497a5635efffb7bbc40f23d29", + "sourceCodeHash": "0x818032f314539edf7b5569c70099f27e2336199ce24313a3f1968e27082c18ae" }, { "contractName": "fee_estimate", "bytecodePath": "bootloader/build/artifacts/fee_estimate.yul.zbin", "sourceCodePath": "bootloader/build/fee_estimate.yul", - "bytecodeHash": "0x010009517d943eabe605fba79edc8997a57ceeccc00e84d38b9a14f6995c4e9b", - "sourceCodeHash": "0x0f486544f7211215c60f1dc28576fa6c8771d05b8233894f0519b5d0d2c9bc48" + "bytecodeHash": "0x01000951a10ba35cd1fd7ea05039e53a06037213a162e6a3cfddf81ff6e54ad5", + "sourceCodeHash": "0x2cadacf92b4db89ecd699426b769526e5482e565d6d86ed287c9cc36cfe2cc2f" }, { "contractName": "gas_test", "bytecodePath": "bootloader/build/artifacts/gas_test.yul.zbin", "sourceCodePath": "bootloader/build/gas_test.yul", - "bytecodeHash": "0x010008d765df2e281933e54276469a1bb780df7be2d77d766e8fd3f916368090", - "sourceCodeHash": "0xc25205451d83055646e4106e1915228969a6872f07f2edbec819c0cc3bd6bf49" + "bytecodeHash": "0x010008d750d8f3fa01d99a747a5113d133594b72f26f0dce3243b225f5b91f9a", + "sourceCodeHash": "0x9a3aead2fe745861da43b8f43028821f7d592ffd16da70f38af19d3b24ae5ef7" }, { "contractName": "playground_batch", "bytecodePath": "bootloader/build/artifacts/playground_batch.yul.zbin", "sourceCodePath": "bootloader/build/playground_batch.yul", - "bytecodeHash": "0x01000957f5258bb8028a4f7843221c4efd6a1d9ff414c318a5cdadd60d1cf060", - "sourceCodeHash": "0x2549576b59baa5a9dd9e25455cc8e66c7f9a7bb6020f63f22e2f0a1cb79e89ee" + "bytecodeHash": "0x010009579942207c3a46a48ad0e66d9f73b6141bb5a2435a8fdce6ee8dfdd17d", + "sourceCodeHash": "0x2302636e39803befa5438f557bfc24b7e1587cfd8edead02dc41a0bb4002f15f" }, { "contractName": "proved_batch", "bytecodePath": "bootloader/build/artifacts/proved_batch.yul.zbin", "sourceCodePath": "bootloader/build/proved_batch.yul", - "bytecodeHash": "0x010008e7e76ac18ea5e49634399ec7062152c224259c85f4fa88690e5447d2e7", - "sourceCodeHash": "0xcb7bffd9535fdad708cbb3fab9a566fb8852fa3edf3ae7dbe2b47d43f9bfb52f" + "bytecodeHash": "0x010008e7f0f15ed191392960117f88fe371348982b28a033c7207ed2c09bc0f4", + "sourceCodeHash": "0x55ce91a28b1d143ecba38dfe1b64d4877ad8f510256f47e5155fd4fd138840ea" } ] diff --git a/system-contracts/contracts/Constants.sol b/system-contracts/contracts/Constants.sol index d166127e5..0f8e2307f 100644 --- a/system-contracts/contracts/Constants.sol +++ b/system-contracts/contracts/Constants.sol @@ -32,6 +32,7 @@ address constant ECRECOVER_SYSTEM_CONTRACT = address(0x01); address constant SHA256_SYSTEM_CONTRACT = address(0x02); address constant ECADD_SYSTEM_CONTRACT = address(0x06); address constant ECMUL_SYSTEM_CONTRACT = address(0x07); +address constant ECPAIRING_SYSTEM_CONTRACT = address(0x08); /// @dev The number of ergs that need to be spent for a single byte of pubdata regardless of the pubdata price. diff --git a/system-contracts/contracts/precompiles/EcPairing.yul b/system-contracts/contracts/precompiles/EcPairing.yul new file mode 100644 index 000000000..6ea6e92de --- /dev/null +++ b/system-contracts/contracts/precompiles/EcPairing.yul @@ -0,0 +1,1745 @@ +object "EcPairing" { + code { + return(0, 0) + } + object "EcPairing_deployed" { + code { + // CONSTANTS + + /// @notice Constant function for value one in Montgomery form. + /// @dev This value was precomputed using Python. + /// @return m_one The value one in Montgomery form. + function MONTGOMERY_ONE() -> m_one { + m_one := 6350874878119819312338956282401532409788428879151445726012394534686998597021 + } + + /// @notice Constant function for value three in Montgomery form. + /// @dev This value was precomputed using Python. + /// @return m_three The value three in Montgomery form. + function MONTGOMERY_THREE() -> m_three { + m_three := 19052624634359457937016868847204597229365286637454337178037183604060995791063 + } + + /// @notice Constant function for the inverse of two on the alt_bn128 group in Montgomery form. + /// @dev This value was precomputed using Python. + /// @return two_inv The value of the inverse of two on the alt_bn128 group in Montgomery form. + function MONTGOMERY_TWO_INV() -> two_inv { + two_inv := 14119558874979547267292681013829403749242370018224634694350716214666112402802 + } + /// @notice constant function for the coeffitients of the sextic twist of the BN256 curve. + /// @dev E': y' ** 2 = x' ** 3 + 3 / (9 + u) + /// @dev the curve E' is defined over Fp2 elements. + /// @dev See https://hackmd.io/@jpw/bn254#Twists for further details. + /// @return coefficients of the sextic twist of the BN256 curve + function MONTGOMERY_TWISTED_CURVE_COEFFS() -> z0, z1 { + z0 := 16772280239760917788496391897731603718812008455956943122563801666366297604776 + z1 := 568440292453150825972223760836185707764922522371208948902804025364325400423 + } + + /// @notice Constant function for the alt_bn128 group order. + /// @dev See https://eips.ethereum.org/EIPS/eip-196 for further details. + /// @return ret The alt_bn128 group order. + function P() -> ret { + ret := 21888242871839275222246405745257275088696311157297823662689037894645226208583 + } + + /// @notice Constant function for the twisted curve subgroup order. + /// @dev See https://hackmd.io/@jpw/bn254#Parameter-for-BN254 for further details. + /// @return ret The twisted curve subgroup orde. + function TWISTED_SUBGROUP_ORDER() -> ret { + ret := 21888242871839275222246405745257275088548364400416034343698204186575808495617 + } + + /// @notice Constant function for the pre-computation of R^2 % N for the Montgomery REDC algorithm. + /// @dev R^2 is the Montgomery residue of the value 2^512. + /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm for further details. + /// @dev This value was precomputed using Python. + /// @return ret The value R^2 modulus the curve group order. + function R2_MOD_P() -> ret { + ret := 3096616502983703923843567936837374451735540968419076528771170197431451843209 + } + + /// @notice Constant function for the pre-computation of N' for the Montgomery REDC algorithm. + /// @dev N' is a value such that NN' = -1 mod R, with N being the curve group order. + /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm for further details. + /// @dev This value was precomputed using Python. + /// @return ret The value N'. + function N_PRIME() -> ret { + ret := 111032442853175714102588374283752698368366046808579839647964533820976443843465 + } + + /// @notice Constant function for the alt_bn128 curve seed (parameter `x`). + /// @dev See https://eips.ethereum.org/EIPS/eip-196 for further details. + /// @return ret The alt_bn128 curve seed. + function X() -> ret { + ret := 4965661367192848881 + } + + /// @notice Constant function for decimal representation of the NAF for the Millers Loop. + /// @dev Millers loop uses to iterate the NAF representation of the value t = 6x^2. Where x = 4965661367192848881 is a parameter of the BN 256 curve. + /// @dev For details of the x parameter: https://hackmd.io/@jpw/bn254#Barreto-Naehrig-curves. + /// @dev A NAF representation uses values: -1, 0 and 1. https://en.wikipedia.org/wiki/Non-adjacent_form. + /// @dev For iterating between this values we represent the 0 as 00, the 1 as 01 and the -1 as 10. + /// @dev Then we concatenate all and represent the result as a decimal. E.g. [0,-1,0,1] -> 00 10 00 01 -> 33 + /// @dev In each step of the iteration we just need to compute the operation AND between the number and 1 and 2 to check the original value. + /// @dev Finally we shift 2 bits to the right to get the next value. + /// @dev For this implementation, the first two iterations of the Miller loop are skipped, so the last two digits of the NAF representation of t are not used. + /// @dev This value was precomputed using Python. + /// @return ret The value of the decimal representation of the NAF. + function NAF_REPRESENTATIVE() -> ret { + // NAF rep in binary form + // 000000010001001000001000000001000010001000000001001000000000100000010010000001000000000010000010000100100000001000100000000100 + ret := 355712981487968141245753120442583044 + } + + /// @notice Constant function for the zero element in Fp6 representation. + /// @return z00, z01, z10, z11, z20, z21 The values of zero in Fp6. + function FP6_ZERO() -> z00, z01, z10, z11, z20, z21 { + z00 := 0 + z01 := 0 + z10 := 0 + z11 := 0 + z20 := 0 + z21 := 0 + } + + /// @notice Constant function for the zero element in the twisted curve on affine representation. + /// @return z00, z01, z10, z11, z20, z21 The values of infinity point on affine representation. + function G2_INFINITY() -> z00, z01, z02, z10, z11, z12 { + z00 := 0 + z01 := 0 + z02 := 0 + z10 := 0 + z11 := 0 + z12 := 0 + } + + /// @notice Constant function for element one in Fp12 representation. + /// @return the values of one in Fp12. + function FP12_ONE() -> z000, z001, z010, z011, z100, z101, z110, z111, z200, z201, z210, z211 { + z000 := MONTGOMERY_ONE() + z001 := 0 + z010 := 0 + z011 := 0 + z100 := 0 + z101 := 0 + z110 := 0 + z111 := 0 + z200 := 0 + z201 := 0 + z210 := 0 + z211 := 0 + } + + /// @notice Constant function for the length of the input of a single pair of points to compute the pairing. + /// @return ret The length of a pair of points input. + function PAIR_LENGTH() -> ret { + ret := 0xc0 + } + + // HELPER FUNCTIONS + + /// @dev Executes the `precompileCall` opcode. + function precompileCall(precompileParams, gasToBurn) -> ret { + // Compiler simulation for calling `precompileCall` opcode + ret := verbatim_2i_1o("precompile", precompileParams, gasToBurn) + } + + /// @notice Burns remaining gas until revert. + /// @dev This function is used to burn gas in the case of a failed precompile call. + function burnGas() { + // Precompiles that do not have a circuit counterpart + // will burn the provided gas by calling this function. + precompileCall(0, gas()) + } + + /// @notice Calculate the bit length of a number. + /// @param x The number to calculate the bit length of. + /// @return ret The bit length of the number. + function bitLen(x) -> ret { + ret := 0 + for {} x {} { + ret := add(ret, 1) + x := shr(1, x) + } + } + + /// @notice Checks if the bit of a number at a given index is 1. + /// @dev The index is counted from the right, starting at 0. + /// @param index The index of the bit to check. + /// @param n The number to check the bit of. + /// @return ret The value of the bit at the given index. + function checkBit(index, n) -> ret { + ret := and(shr(index, n), 1) + } + + // MONTGOMERY + + /// @notice Computes the inverse in Montgomery Form of a number in Montgomery Form. + /// @dev Reference: https://github.com/lambdaclass/lambdaworks/blob/main/math/src/field/fields/montgomery_backed_prime_fields.rs#L169 + /// @dev Let `base` be a number in Montgomery Form, then base = a*R mod P() being `a` the base number (not in Montgomery Form) + /// @dev Let `inv` be the inverse of a number `a` in Montgomery Form, then inv = a^(-1)*R mod P() + /// @dev The original binary extended euclidean algorithms takes a number a and returns a^(-1) mod N + /// @dev In our case N is P(), and we'd like the input and output to be in Montgomery Form (a*R mod P() + /// @dev and a^(-1)*R mod P() respectively). + /// @dev If we just pass the input as a number in Montgomery Form the result would be a^(-1)*R^(-1) mod P(), + /// @dev but we want it to be a^(-1)*R mod P(). + /// @dev For that, we take advantage of the algorithm's linearity and multiply the result by R^2 mod P() + /// @dev to get R^2*a^(-1)*R^(-1) mod P() = a^(-1)*R mod P() as the desired result in Montgomery Form. + /// @dev `inv` takes the value of `b` or `c` being the result sometimes `b` and sometimes `c`. In paper + /// @dev multiplying `b` or `c` by R^2 mod P() results on starting their values as b = R2_MOD_P() and c = 0. + /// @param base A number `a` in Montgomery Form, then base = a*R mod P(). + /// @return inv The inverse of a number `a` in Montgomery Form, then inv = a^(-1)*R mod P(). + function binaryExtendedEuclideanAlgorithm(base) -> inv { + let modulus := P() + let u := base + let v := modulus + // Avoids unnecessary reduction step. + let b := R2_MOD_P() + let c := 0 + + for {} and(iszero(eq(u, 1)), iszero(eq(v, 1))) {} { + for {} iszero(and(u, 1)) {} { + u := shr(1, u) + let current := b + switch and(current, 1) + case 0 { + b := shr(1, b) + } + case 1 { + b := shr(1, add(b, modulus)) + } + } + + for {} iszero(and(v, 1)) {} { + v := shr(1, v) + let current := c + switch and(current, 1) + case 0 { + c := shr(1, c) + } + case 1 { + c := shr(1, add(c, modulus)) + } + } + + switch gt(v, u) + case 0 { + u := sub(u, v) + if lt(b, c) { + b := add(b, modulus) + } + b := sub(b, c) + } + case 1 { + v := sub(v, u) + if lt(c, b) { + c := add(c, modulus) + } + c := sub(c, b) + } + } + + switch eq(u, 1) + case 0 { + inv := c + } + case 1 { + inv := b + } + } + + /// @notice Computes an addition and checks for overflow. + /// @param augend The value to add to. + /// @param addend The value to add. + /// @return sum The sum of the two values. + /// @return overflowed True if the addition overflowed, false otherwise. + function overflowingAdd(augend, addend) -> sum, overflowed { + sum := add(augend, addend) + overflowed := lt(sum, augend) + } + + /// @notice Retrieves the highest half of the multiplication result. + /// @param multiplicand The value to multiply. + /// @param multiplier The multiplier. + /// @return ret The highest half of the multiplication result. + function getHighestHalfOfMultiplication(multiplicand, multiplier) -> ret { + ret := verbatim_2i_1o("mul_high", multiplicand, multiplier) + } + + /// @notice Implementation of the Montgomery reduction algorithm (a.k.a. REDC). + /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm + /// @param lowestHalfOfT The lowest half of the value T. + /// @param higherHalfOfT The higher half of the value T. + /// @return S The result of the Montgomery reduction. + function REDC(lowest_half_of_T, higher_half_of_T) -> S { + let q := mul(lowest_half_of_T, N_PRIME()) + let a_high := add(higher_half_of_T, getHighestHalfOfMultiplication(q, P())) + let a_low, overflowed := overflowingAdd(lowest_half_of_T, mul(q, P())) + if overflowed { + a_high := add(a_high, 1) + } + S := a_high + if iszero(lt(a_high, P())) { + S := sub(a_high, P()) + } + } + + /// @notice Encodes a field element into the Montgomery form using the Montgomery reduction algorithm (REDC). + /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm for further details on transforming a field element into the Montgomery form. + /// @param a The field element to encode. + /// @return ret The field element in Montgomery form. + function intoMontgomeryForm(a) -> ret { + let hi := getHighestHalfOfMultiplication(a, R2_MOD_P()) + let lo := mul(a, R2_MOD_P()) + ret := REDC(lo, hi) + } + + /// @notice Decodes a field element out of the Montgomery form using the Montgomery reduction algorithm (REDC). + /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm for further details on transforming a field element out of the Montgomery form. + /// @param m The field element in Montgomery form to decode. + /// @return ret The decoded field element. + function outOfMontgomeryForm(m) -> ret { + let higher_half_of_m := 0 + let lowest_half_of_m := m + ret := REDC(lowest_half_of_m, higher_half_of_m) + } + + /// @notice Computes the Montgomery addition. + /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm for further details on the Montgomery multiplication. + /// @param augend The augend in Montgomery form. + /// @param addend The addend in Montgomery form. + /// @return ret The result of the Montgomery addition. + function montgomeryAdd(augend, addend) -> ret { + ret := add(augend, addend) + if iszero(lt(ret, P())) { + ret := sub(ret, P()) + } + } + + /// @notice Computes the Montgomery subtraction. + /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm for further details on the Montgomery multiplication. + /// @param minuend The minuend in Montgomery form. + /// @param subtrahend The subtrahend in Montgomery form. + /// @return ret The result of the Montgomery addition. + function montgomerySub(minuend, subtrahend) -> ret { + ret := sub(minuend, subtrahend) + if lt(minuend, subtrahend) { + ret := add(ret, P()) + } + } + + /// @notice Computes the Montgomery multiplication using the Montgomery reduction algorithm (REDC). + /// @dev See https://en.wikipedia.org/wiki/Montgomery_modular_multiplication#The_REDC_algorithm for further details on the Montgomery multiplication. + /// @param multiplicand The multiplicand in Montgomery form. + /// @param multiplier The multiplier in Montgomery form. + /// @return ret The result of the Montgomery multiplication. + function montgomeryMul(multiplicand, multiplier) -> ret { + let higher_half_of_product := getHighestHalfOfMultiplication(multiplicand, multiplier) + let lowest_half_of_product := mul(multiplicand, multiplier) + ret := REDC(lowest_half_of_product, higher_half_of_product) + } + + /// @notice Computes the Montgomery modular inverse skipping the Montgomery reduction step. + /// @dev The Montgomery reduction step is skipped because a modification in the binary extended Euclidean algorithm is used to compute the modular inverse. + /// @dev See the function `binaryExtendedEuclideanAlgorithm` for further details. + /// @param a The field element in Montgomery form to compute the modular inverse of. + /// @return invmod The result of the Montgomery modular inverse (in Montgomery form). + function montgomeryModularInverse(a) -> invmod { + invmod := binaryExtendedEuclideanAlgorithm(a) + } + + // CURVE ARITHMETIC + + /// @notice Checks if a coordinate is on the curve group order. + /// @dev A coordinate is on the curve group order if it is on the range [0, curveFieldOrder). + /// @param coordinate The coordinate to check. + /// @return ret True if the coordinate is in the range, false otherwise. + function coordinateIsOnFieldOrder(coordinate) -> ret { + ret := lt(coordinate, P()) + } + + // G1 + + /// @notice Checks if a point of the G1 curve is infinity. + /// @dev In affine coordinates the infinity is represented by the point (0,0). + /// @param x The x coordinate to check. + /// @param y The y coordinate to check. + /// @return ret True if the point is infinity, false otherwise. + function g1AffinePointIsInfinity(x, y) -> ret { + ret := iszero(or(x, y)) + } + + /// @notice Checks if a point in affine coordinates in Montgomery form is on the curve. + /// @dev The curve in question is the alt_bn128 curve. + /// @dev The Short Weierstrass equation of the curve is y^2 = x^3 + 3. + /// @param x The x coordinate of the point in Montgomery form. + /// @param y The y coordinate of the point in Montgomery form. + /// @return ret True if the point is on the curve, false otherwise. + function g1AffinePointIsOnCurve(x, y) -> ret { + let ySquared := montgomeryMul(y, y) + let xSquared := montgomeryMul(x, x) + let xQubed := montgomeryMul(xSquared, x) + let xQubedPlusThree := montgomeryAdd(xQubed, MONTGOMERY_THREE()) + + ret := eq(ySquared, xQubedPlusThree) + } + + // G2 + + /// @notice Converts a G2 point in affine coordinates to projective coordinates. + /// @dev Both input and output coordinates are encoded in Montgomery form. + /// @dev If x or y differ from 0, just add z = (1,0). + /// @dev If x and y are equal to 0, then P is the infinity point, and z = (0,0). + /// @param xp0, xp1 The x coordinate to transform. + /// @param yp0, yp1 The y coordinate to transform. + /// @return xr0, xr1, yr0, yr1, zr0, zr1 The projective coordinates of the given G2 point. + function g2ProjectiveFromAffine(xp0, xp1, yp0, yp1) -> xr0, xr1, yr0, yr1, zr0, zr1 { + xr0 := xp0 + xr1 := xp1 + yr0 := yp0 + yr1 := yp1 + zr0 := MONTGOMERY_ONE() + zr1 := 0 + } + + /// @notice Checks if a G2 point in affine coordinates is the point at infinity. + /// @dev The coordinates are encoded in Montgomery form. + /// @dev in Affine coordinates the point represents the infinity if both coordinates are 0. + /// @param x0, x1 The x coordinate to check. + /// @param y0, y1 The y coordinate to check. + /// @return ret True if the point is the point at infinity, false otherwise. + function g2AffinePointIsInfinity(x0, x1, y0, y1) -> ret { + ret := iszero(or(or(x0, x1), or(y0, y1))) + } + + /// @notice Checks if a G2 point in affine coordinates belongs to the twisted curve. + /// @dev The coordinates are encoded in Montgomery form. + /// @dev in Affine coordinates the point belongs to the curve if it satisfies the equation: y^2 = x^3 + 3. + /// @dev See https://hackmd.io/@jpw/bn254#Twists for further details. + /// @param x0, x1 The x coordinate to check. + /// @param y0, y1 The y coordinate to check. + /// @return ret True if the point is in the curve, false otherwise. + function g2AffinePointIsOnCurve(x0, x1, y0, y1) -> ret { + let a0, a1 := MONTGOMERY_TWISTED_CURVE_COEFFS() + let b0, b1 := fp2Mul(x0, x1, x0, x1) + b0, b1 := fp2Mul(b0, b1, x0, x1) + b0, b1 := fp2Add(b0, b1, a0, a1) + let c0, c1 := fp2Mul(y0, y1, y0, y1) + ret := and(eq(b0, c0), eq(b1, c1)) + } + + /// @notice Checks if a G2 point in projective coordinates is the point at infinity. + /// @dev The coordinates are encoded in Montgomery form. + /// @dev A projective point is at infinity if the z coordinate is (0, 0). + /// @param x0, x1 The x coordinate of the point. + /// @param y0, y1 The y coordinate of the point. + /// @param z0, z1 The z coordinate of the point. + /// @return ret True if the point is the point at infinity, false otherwise. + function g2ProjectivePointIsInfinity(x0, x1, y0, y1, z0, z1) -> ret { + ret := iszero(or(z0, z1)) + } + + /// @notice Negates a G2 point in affine coordinates. + /// @dev The coordinates are encoded in Montgomery form. + /// @dev The negation of a point (x, y) is (x, -y). + /// @param x0, x1 The x coordinate of the point. + /// @param y0, y1 The y coordinate of the point. + /// @return nx0, nx1, ny0, ny1 The coordinates of the negated point. + function g2AffineNeg(x0, x1, y0, y1) -> nx0, nx1, ny0, ny1 { + nx0 := x0 + nx1 := x1 + ny0, ny1 := fp2Neg(y0, y1) + } + + /// @notice Constant function for the alt_bn128 returning `(xi)^ ((N - 1) // 2)`. Where `xi` is D-type twist param. + /// @dev See https://eprint.iacr.org/2022/352.pdf (2 Preliminaries) for further details. + /// @return ret Twisted curve `xi2 = (xi)^ ((N - 1) // 2)` value in Montgomery form. + function xi2() -> xi0, xi1 { + xi0 := intoMontgomeryForm(2821565182194536844548159561693502659359617185244120367078079554186484126554) + xi1 := intoMontgomeryForm(3505843767911556378687030309984248845540243509899259641013678093033130930403) + } + + /// @notice Constant function for the alt_bn128 returning `(xi)^ ((N - 1) // 2)`. Where `xi` is D-type twist param. + /// @dev See https://eprint.iacr.org/2022/352.pdf (2 Preliminaries) for further details. + /// @return ret Twisted curve `xi2 = (xi)^ ((N - 1) // 2)` value in Montgomery form. + function xi3() -> xi0, xi1 { + xi0 := intoMontgomeryForm(21575463638280843010398324269430826099269044274347216827212613867836435027261) + xi1 := intoMontgomeryForm(10307601595873709700152284273816112264069230130616436755625194854815875713954) + } + + /// @notice Frobenius endomophism used to G2 sub group check for twisted curve. + /// @dev For more datail see https://eprint.iacr.org/2022/348.pdf + /// @param xp0, xp1 The x coordinate of the point on twisted curve. + /// @param yp0, yp1 The y coordinate of the point on twisted curve. + /// @param zp0, zp1 The z coordinate of the point on twisted curve. + /// @return Point on twisted curve transformed by the phi endomorphism + function endomorphism(xp0, xp1, yp0, yp1, zp0, zp1) -> xr0, xr1, yr0, yr1, zr0, zr1 { + let xp0_c, xp1_c := fp2Conjugate(xp0, xp1) + let yp0_c, yp1_c := fp2Conjugate(yp0, yp1) + + let xi2_0, xi2_1 := xi2() + let xi3_0, xi3_1 := xi3() + + xr0, xr1 := fp2Mul(xp0_c, xp1_c, xi3_0, xi3_1) + yr0, yr1 := fp2Mul(yp0_c, yp1_c, xi2_0, xi2_1) + zr0, zr1 := fp2Conjugate(zp0, zp1) + } + /// @notice Check if a G2 point in jacobian coordinates is in the subgroup of the twisted curve. + /// @dev The coordinates are encoded in Montgomery form. + /// @param xp0, xp1 The x coordinate of the point. + /// @param yp0, yp1 The y coordinate of the point. + /// @param zp0, zp1 The z coordinate of the point. + /// @return ret True if the point is in the subgroup, false otherwise. + function g2IsInSubGroup(xp0, xp1, yp0, yp1, zp0, zp1) -> ret { + // P * X + let px_xp0, px_xp1, px_yp0, px_yp1, px_zp0, px_zp1 := g2ScalarMul(xp0, xp1, yp0, yp1, zp0, zp1, X()) + // P * (X + 1) + let px1_xp0, px1_xp1, px1_yp0, px1_yp1, px1_zp0, px1_zp1 := g2JacobianAdd(px_xp0, px_xp1, px_yp0, px_yp1, px_zp0, px_zp1, xp0, xp1, yp0, yp1, zp0, zp1) + // P * 2X + let p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1 := g2JacobianDouble(px_xp0, px_xp1, px_yp0, px_yp1, px_zp0, px_zp1) + + // phi(P * X) + let e_px_xp0, e_px_xp1, e_px_yp0, e_px_yp1, e_px_zp0, e_px_zp1 := endomorphism(px_xp0, px_xp1, px_yp0, px_yp1, px_zp0, px_zp1) + // phi(phi(P * X)) + let e2_px_xp0, e2_px_xp1, e2_px_yp0, e2_px_yp1, e2_px_zp0, e2_px_zp1 := endomorphism(e_px_xp0, e_px_xp1, e_px_yp0, e_px_yp1, e_px_zp0, e_px_zp1) + + // phi(phi(phi(P * 2X))) + p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1 := endomorphism(p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1) + p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1 := endomorphism(p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1) + p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1 := endomorphism(p2x_xp0, p2x_xp1, p2x_yp0, p2x_yp1, p2x_zp0, p2x_zp1) + + let l1x0, l1x2, l1y0, l1y2, l1z0, l1z2 := g2JacobianAdd(px1_xp0, px1_xp1, px1_yp0, px1_yp1, px1_zp0, px1_zp1, e_px_xp0, e_px_xp1, e_px_yp0, e_px_yp1, e_px_zp0, e_px_zp1) + l1x0, l1x2, l1y0, l1y2, l1z0, l1z2 := g2JacobianAdd(l1x0, l1x2, l1y0, l1y2, l1z0, l1z2, e2_px_xp0, e2_px_xp1, e2_px_yp0, e2_px_yp1, e2_px_zp0, e2_px_zp1) + + let l1z0_square, l1z2_square := fp2Mul(l1z0, l1z2, l1z0, l1z2) + let p2x_zp0_square, p2x_zp1_square := fp2Mul(p2x_zp0, p2x_zp1, p2x_zp0, p2x_zp1) + + let r00, r01 := fp2Mul(p2x_xp0, p2x_xp1, l1z0_square, l1z2_square) + let r10, r11 := fp2Mul(l1x0, l1x2, p2x_zp0_square, p2x_zp1_square) + + let l1z0_cube, l1z2_cube := fp2Mul(l1z0_square, l1z2_square, l1z0, l1z2) + let p2x_zp0_cube, p2x_zp1_cube := fp2Mul(p2x_zp0_square, p2x_zp1_square, p2x_zp0, p2x_zp1) + + let l00, l01 := fp2Mul(p2x_yp0, p2x_yp1, l1z0_cube, l1z2_cube) + let l10, l11 := fp2Mul(l1y0, l1y2, p2x_zp0_cube, p2x_zp1_cube) + + let r1 := and(eq(r00, r10), eq(r01, r11)) + let r2 := and(eq(l00, l10), eq(l01, l11)) + ret := and(r1, r2) + } + + + /// @notice Check if a G2 point in jacobian coordinates is in the subgroup of the twisted curve. + /// @dev The coordinates are encoded in Montgomery form. + /// @param xp0, xp1 The x coordinate of the point. + /// @param yp0, yp1 The y coordinate of the point. + /// @param zp0, zp1 The z coordinate of the point. + /// @return ret True if the point is in the subgroup, false otherwise. + function g2IsInSubGroupNaive(xp0, xp1, yp0, yp1, zp0, zp1) -> ret { + let xr0, xr1, yr0, yr1, zr0, zr1 := g2ScalarMul(xp0, xp1, yp0, yp1, zp0, zp1, TWISTED_SUBGROUP_ORDER()) + ret := and(iszero(zr0), iszero(zr1)) + } + + /// @notice Double a g2 point represented in jacobian coordinates. + /// @dev The coordinates must be encoded in Montgomery form. + /// @param xp0, xp1 The x coordinate of the point. + /// @param yp0, yp1 The y coordinate of the point. + /// @param zp0, zp1 The z coordinate of the point. + /// @return xr0, xr1, yr0, yr1, zr0, zr1 The coordinates of the doubled point. + function g2JacobianDouble(xp0, xp1, yp0, yp1, zp0, zp1) -> xr0, xr1, yr0, yr1, zr0, zr1 { + let a00, a01 := fp2Mul(xp0, xp1, xp0, xp1) // A = X1^2 + let b00, b01 := fp2Mul(yp0, yp1, yp0, yp1) // B = Y1^2 + let c00, c01 := fp2Mul(b00, b01, b00, b01) // C = B^2 + let t00, t01 := fp2Add(xp0, xp1, b00, b01) // t0 = X1+B + let t10, t11 := fp2Mul(t00, t01, t00, t01) // t1 = t0^2 + let t20, t21 := fp2Sub(t10, t11, a00, a01) // t2 = t1-A + let t30, t31 := fp2Sub(t20, t21, c00, c01) // t3 = t2-C + let d00, d01 := fp2Add(t30, t31, t30, t31) // D = 2*t3 + let e00, e01 := fp2Add(a00, a01, a00, a01) // E = 3*A + e00, e01 := fp2Add(e00, e01, a00, a01) + let f00, f01 := fp2Mul(e00, e01, e00, e01) // F = E^2 + let t40, t41 := fp2Add(d00, d01, d00, d01) // t4 = 2*D + xr0, xr1 := fp2Sub(f00, f01, t40, t41) // X3 = F-t4 + let t50, t51 := fp2Sub(d00, d01, xr0, xr1) // t5 = D-X3 + let t60, t61 := fp2Add(c00, c01, c00, c01) // t6 = 8*C + t60, t61 := fp2Add(t60, t61, t60, t61) + t60, t61 := fp2Add(t60, t61, t60, t61) + let t70, t71 := fp2Mul(e00, e01, t50, t51) // t7 = E*t5 + yr0, yr1 := fp2Sub(t70, t71, t60, t61) // Y3 = t7-t6 + let t80, t81 := fp2Mul(yp0, yp1, zp0, zp1) // t8 = Y1*Z1 + zr0, zr1 := fp2Add(t80, t81, t80, t81) // Z3 = 2*t8 + } + + /// @notice Add two g2 points represented in jacobian coordinates. + /// @dev The coordinates must be encoded in Montgomery form. + /// @dev The points to be added must be different, if not the function will return infinity. The function `g2JacobianDouble` should be used in that case. + /// @param xq0, xq1 The x coordinate of the first point. + /// @param yq0, yq1 The y coordinate of the first point. + /// @param zq0, zq1 The z coordinate of the first point. + /// @param xr0, xr1 The x coordinate of the second point. + /// @param yr0, yr1 The y coordinate of the second point. + /// @param zr0, zr1 The z coordinate of the second point. + /// @return c00, c01, c10, c11, c20, c21 The coordinates of the added points. + function g2JacobianAdd(xq0, xq1, yq0, yq1, zq0, zq1, xr0, xr1, yr0, yr1, zr0, zr1) -> c00, c01, c10, c11, c20, c21 { + // Check for infinity in projective coordinates is the same as jacobian + let qIsInfinity := g2ProjectivePointIsInfinity(xq0, xq1, yq0, yq1, zq0, zq1) + let rIsInfinity := g2ProjectivePointIsInfinity(xr0, xr1, yr0, yr1, zr0, zr1) + if rIsInfinity { + // Infinity + P = P + c00 := xq0 + c01 := xq1 + c10 := yq0 + c11 := yq1 + c20 := zq0 + c21 := zq1 + leave + } + if qIsInfinity { + // P + Infinity = P + c00 := xr0 + c01 := xr1 + c10 := yr0 + c11 := yr1 + c20 := zr0 + c21 := zr1 + leave + } + + // Z1Z1 = Z1^2 + let zqzq0, zqzq1 := fp2Mul(zq0, zq1, zq0, zq1) + // Z2Z2 = Z2^2 + let zrzr0, zrzr1 := fp2Mul(zr0, zr1, zr0, zr1) + // U1 = X1*Z2Z2 + let u0, u1 := fp2Mul(xq0, xq1, zrzr0, zrzr1) + // U2 = X2*Z1Z1 + let u2, u3 := fp2Mul(xr0, xr1, zqzq0, zqzq1) + // t0 = Z2*Z2Z2 + let t0, t1 := fp2Mul(zr0, zr1, zrzr0, zrzr1) + // S1 = Y1*t0 + let s0, s1 := fp2Mul(yq0, yq1, t0, t1) + // t1 = Z1*Z1Z1 + let t2, t3 := fp2Mul(zq0, zq1, zqzq0, zqzq1) + // S2 = Y2*t1 + let s2, s3 := fp2Mul(yr0, yr1, t2, t3) + // H = U2-U1 + let h0, h1 := fp2Sub(u2, u3, u0, u1) + // t2 = 2*H + let t4, t5 := fp2Add(h0, h1, h0, h1) + // I = t2^2 + let i0, i1 := fp2Mul(t4, t5, t4, t5) + // J = H*I + let j0, j1 := fp2Mul(h0, h1, i0, i1) + // t3 = S2-S1 + let t6, t7 := fp2Sub(s2, s3, s0, s1) + // r = 2*t3 + let r0, r1 := fp2Add(t6, t7, t6, t7) + // V = U1*I + let v0, v1 := fp2Mul(u0, u1, i0, i1) + // t4 = r^2 + let t8, t9 := fp2Mul(r0, r1, r0, r1) + // t5 = 2*V + let t10, t11 := fp2Add(v0, v1, v0, v1) + // t6 = t4-J + let t12, t13 := fp2Sub(t8, t9, j0, j1) + // X3 = t6-t5 + c00, c01 := fp2Sub(t12, t13, t10, t11) + // t7 = V-X3 + let t14, t15 := fp2Sub(v0, v1, c00, c01) + // t8 = S1*J + let t16, t17 := fp2Mul(s0, s1, j0, j1) + // t9 = 2*t8 + let t18, t19 := fp2Add(t16, t17, t16, t17) + // t10 = r*t7 + let t20, t21 := fp2Mul(r0, r1, t14, t15) + // Y3 = t10-t9 + c10, c11 := fp2Sub(t20, t21, t18, t19) + // t11 = Z1+Z2 + let t22, t23 := fp2Add(zq0, zq1, zr0, zr1) + // t12 = t11^2 + let t24, t25 := fp2Mul(t22, t23, t22, t23) + // t13 = t12-Z1Z1 + let t26, t27 := fp2Sub(t24, t25, zqzq0, zqzq1) + // t14 = t13-Z2Z2 + let t28, t29 := fp2Sub(t26, t27, zrzr0, zrzr1) + // Z3 = t14*H + c20, c21 := fp2Mul(t28, t29, h0, h1) + } + + /// @notice Multiplies a G2 point represented in jacobian coordinates by a scalar. + /// @dev The coordinates must be encoded in Montgomery form. + /// @dev The scalar must not be encoded in Montgomery form. + /// @param xp0, xp1 The x coordinate of the point. + /// @param yp0, yp1 The y coordinate of the point. + /// @param zp0, zp1 The z coordinate of the point. + /// @param scalar The scalar to multiply the point by. + /// @return xr0, xr1, yr0, yr1, zr0, zr1 The coordinates of the multiplied point. + function g2ScalarMul(xp0, xp1, yp0, yp1, zp0, zp1, scalar) -> xr0, xr1, yr0, yr1, zr0, zr1 { + let scalarBitIndex := bitLen(scalar) + switch scalar + case 0x02 { + xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianDouble(xp0, xp1, yp0, yp1, zp0, yp1) + } + default { + xr0 := 0 + xr1 := 0 + yr0 := MONTGOMERY_ONE() + yr1 := 0 + zr0 := 0 + zr1 := 0 + for {} scalarBitIndex {} { + scalarBitIndex := sub(scalarBitIndex, 1) + xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianDouble(xr0, xr1, yr0, yr1, zr0, zr1) + let bitindex := checkBit(scalarBitIndex, scalar) + if bitindex { + xr0, xr1, yr0, yr1, zr0, zr1 := g2JacobianAdd(xp0, xp1, yp0, yp1, zp0, zp1, xr0, xr1, yr0, yr1, zr0, zr1) + } + + } + } + } + + // FP2 ARITHMETHICS + + /// @notice Computes the sum of two Fp2 elements. + /// @dev Algorithm 5 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01 The coefficients of the A element to sum. + /// @param b00, b01 The coefficients of the B element to sum. + /// @return c00, c01 The coefficients of the element C = A + B. + function fp2Add(a00, a01, b00, b01) -> c00, c01 { + c00 := montgomeryAdd(a00, b00) + c01 := montgomeryAdd(a01, b01) + } + + /// @notice Computes the subtraction of two Fp2 elements. + /// @dev Algorithm 6 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01 The coefficients of the minuend A. + /// @param b00, b01 The coefficients of the subtrahend B. + /// @return c00, c01 The coefficients of the element C = A - B. + function fp2Sub(a00, a01, b00, b01) -> c00, c01 { + c00 := montgomerySub(a00, b00) + c01 := montgomerySub(a01, b01) + } + + /// @notice Computes the multiplication between a Fp2 element a Fp element. + /// @dev Algorithm 7 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01 The coefficients of the Fp2 element A. + /// @param scalar The value of the Fp element k. + /// @return c00, c01 The coefficients of the element C = k * A. + function fp2ScalarMul(a00, a01, scalar) -> c00, c01 { + c00 := montgomeryMul(a00, scalar) + c01 := montgomeryMul(a01, scalar) + } + + /// @notice Computes the multiplication between two Fp2 elements. + /// @dev Algorithm 7 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01 The coefficients of the Fp2 element A. + /// @param a00, a01 The coefficients of the Fp2 element B. + /// @return c00, c01 The coefficients of the element C = A * B. + function fp2Mul(a00, a01, b00, b01) -> c00, c01 { + c00 := montgomerySub(montgomeryMul(a00, b00), montgomeryMul(a01, b01)) + c01 := montgomeryAdd(montgomeryMul(a00, b01), montgomeryMul(a01, b00)) + } + + /// @notice Computes the negative of a Fp2 elements. + /// @param a00, a01 The coefficients of the Fp2 element A. + /// @return c00, c01 The coefficients of the element C = -A. + function fp2Neg(a00, a01) -> c00, c01 { + c00, c01 := fp2Sub(0, 0, a00, a01) + } + + /// @notice Computes the inverse of a Fp2 element. + /// @dev Algorithm 8 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01 The coefficients of the Fp2 element A. + /// @return c00, c01 The coefficients of the element C = A^(-1). + function fp2Inv(a00, a01) -> c00, c01 { + let t0 := montgomeryMul(a00, a00) + let t1 := montgomeryMul(a01, a01) + t0 := montgomeryAdd(t0, t1) + t1 := montgomeryModularInverse(t0) + + c00 := montgomeryMul(a00, t1) + c01 := montgomerySub(0, montgomeryMul(a01, t1)) + } + + /// @notice Computes the multiplication of a Fp2 element with xi. + /// @dev Where xi = u in Fp + /// @dev See https://hackmd.io/@jpw/bn254#Field-extension-towers for further details. + /// @param a00, a01 The coefficients of the Fp2 element A. + /// @return c00, c01 The coefficients of the element C = A * xi. + function mulByXi(a00, a01) -> c00, c01 { + let t0, t1 := fp2ScalarMul(a00, a01, intoMontgomeryForm(8)) + c00 := montgomerySub(montgomeryAdd(t0, a00), a01) + c01 := montgomeryAdd(montgomeryAdd(t1, a00), a01) + } + + /// @notice Computes the conjugation of a Fp2 element. + /// @param a00, a01 The coefficients of the Fp2 element A. + /// @return c00, c01 The coefficients of the element C = A'. + function fp2Conjugate(a00, a01) -> c00, c01 { + c00 := a00 + c01 := montgomerySub(0, a01) + } + + // FP6 ARITHMETHICS + + /// @notice Computes the sum of two Fp6 elements. + /// @dev Algorithm 10 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01, a10, a11, a20, a21 The coefficients of the A element to sum. + /// @param b00, b01, b10, b11, b20, b21 The coefficients of the B element to sum. + /// @return c00, c01, c10, c11, c20, c21 The coefficients of the element C = A + B. + function fp6Add(a00, a01, a10, a11, a20, a21, b00, b01, b10, b11, b20, b21) -> c00, c01, c10, c11, c20, c21 { + c00, c01 := fp2Add(a00, a01, b00, b01) + c10, c11 := fp2Add(a10, a11, b10, b11) + c20, c21 := fp2Add(a20, a21, b20, b21) + } + + /// @notice Computes the subtraction of two Fp6 elements. + /// @dev Algorithm 11 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01, a10, a11, a20, a21 The coefficients of the minuend A. + /// @param b00, b01, b10, b11, b20, b21 The coefficients of the subtrahend B. + /// @return c00, c01, c10, c11, c20, c21 The coefficients of the element C = A - B. + function fp6Sub(a00, a01, a10, a11, a20, a21, b00, b01, b10, b11, b20, b21) -> c00, c01, c10, c11, c20, c21 { + c00, c01 := fp2Sub(a00, a01, b00, b01) + c10, c11 := fp2Sub(a10, a11, b10, b11) + c20, c21 := fp2Sub(a20, a21, b20, b21) + } + + /// @notice Computes the multiplication of a Fp6 element with g. + /// @dev Algorithm 12 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01, a10, a11, a20, a21 The coefficients of the Fp6 element A. + /// @return c00, c01, c10, c11, c20, c21 The coefficients of the element C = A * g. + function mulByGamma(a00, a01, a10, a11, a20, a21) -> c00, c01, c10, c11, c20, c21 { + c00, c01 := mulByXi(a20, a21) + c10 := a00 + c11 := a01 + c20 := a10 + c21 := a11 + } + + /// @notice Computes the multiplication between two Fp6 elements. + /// @dev Algorithm 13 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01, a10, a11, a20, a21 The coefficients of the Fp6 element A. + /// @param b00, b01, b10, b11, b20, b21 The coefficients of the Fp6 element B. + /// @return c00, c01, c10, c11, c20, c21 The coefficients of the element C = A * B. + function fp6Mul(a00, a01, a10, a11, a20, a21, b00, b01, b10, b11, b20, b21) -> c00, c01, c10, c11, c20, c21 { + let t00, t01 := fp2Mul(a00, a01, b00, b01) + let t10, t11 := fp2Mul(a10, a11, b10, b11) + let t20, t21 := fp2Mul(a20, a21, b20, b21) + + let tmp0, temp1 := fp2Add(a10, a11, a20, a21) + let tmp2, tmp3 := fp2Add(b10, b11, b20, b21) + let tmp4, tmp5 := fp2Mul(tmp0, temp1, tmp2, tmp3) + let tmp6, tmp7 := fp2Sub(tmp4, tmp5, t10, t11) + let tmp8, tmp9 := fp2Sub(tmp6, tmp7, t20, t21) + let tmp10, tmp11 := mulByXi(tmp8, tmp9) + c00, c01 := fp2Add(tmp10, tmp11, t00, t01) + + tmp0, temp1 := fp2Add(a00, a01, a10, a11) + tmp2, tmp3 := fp2Add(b00, b01, b10, b11) + tmp4, tmp5 := fp2Mul(tmp0, temp1, tmp2, tmp3) + tmp6, tmp7 := fp2Sub(tmp4, tmp5, t00, t01) + tmp8, tmp9 := fp2Sub(tmp6, tmp7, t10, t11) + tmp10, tmp11 := mulByXi(t20, t21) + c10, c11 := fp2Add(tmp8, tmp9, tmp10, tmp11) + + tmp0, temp1 := fp2Add(a00, a01, a20, a21) + tmp2, tmp3 := fp2Add(b00, b01, b20, b21) + tmp4, tmp5 := fp2Mul(tmp0, temp1, tmp2, tmp3) + tmp6, tmp7 := fp2Sub(tmp4, tmp5, t00, t01) + tmp8, tmp9 := fp2Sub(tmp6, tmp7, t20, t21) + c20, c21 := fp2Add(tmp8, tmp9, t10, t11) + } + + /// @notice Computes the negative of a Fp6 element. + /// @param a00, a01, a10, a11, a20, a21 The coefficients of the Fp2 element A. + /// @return c00, c01, c10, c11, c20, c21 The coefficients of the element C = -A. + function fp6Neg(a00, a01, a10, a11, a20, a21) -> c00, c01, c10, c11, c20, c21 { + c00, c01 := fp2Neg(a00, a01) + c10, c11 := fp2Neg(a10, a11) + c20, c21 := fp2Neg(a20, a21) + } + + /// @notice Computes the square of a Fp6 element. + /// @dev Algorithm 16 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01, a10, a11, a20, a21 The coefficients of the Fp6 element A. + /// @return c00, c01, c10, c11, c20, c21 The coefficients of the element C = A^2. + function fp6Square(a00, a01, a10, a11, a20, a21) -> c00, c01, c10, c11, c20, c21 { + let tmp0, tmp1 := fp2Mul(a00, a01, a10, a11) + tmp0, tmp1 := fp2Add(tmp0, tmp1, tmp0, tmp1) + + let tmp2, tmp3 := fp2Mul(a20, a21, a20, a21) + let tmp4, tmp5 := mulByXi(tmp2, tmp3) + c10, c11 := fp2Add(tmp4, tmp5, tmp0, tmp1) + + c20, c21 := fp2Sub(tmp0, tmp1, tmp2, tmp3) + + let tmp6, tmp7 := fp2Mul(a00, a01, a00, a01) + let tmp8, tmp9 := fp2Sub(a00, a01, a10, a11) + tmp0, tmp1 := fp2Add(tmp8, tmp9, a20, a21) + + let tmp10, tmp11 := fp2Mul(a10, a11, a20, a21) + tmp2, tmp3 := fp2Add(tmp10, tmp11, tmp10, tmp11) + tmp0, tmp1 := fp2Mul(tmp0, tmp1, tmp0, tmp1) + + let tmp12, tmp13 := mulByXi(tmp2, tmp3) + c00, c01 := fp2Add(tmp12, tmp13, tmp6, tmp7) + + let tmp14, tmp15 := fp2Add(c20, c21, tmp0, tmp1) + tmp14, tmp15 := fp2Add(tmp14, tmp15, tmp2, tmp3) + c20, c21 := fp2Sub(tmp14, tmp15, tmp6, tmp7) + + } + + /// @notice Computes the inverse of a Fp6 element. + /// @dev Algorithm 17 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a00, a01, a10, a11, a20, a21 The coefficients of the Fp6 element A. + /// @return c00, c01, c10, c11, c20, c21 The coefficients of the element C = A^(-1). + function fp6Inv(a00, a01, a10, a11, a20, a21) -> c00, c01, c10, c11, c20, c21 { + let t00, t01 := fp2Mul(a00, a01, a00, a01) + let t10, t11 := fp2Mul(a10, a11, a10, a11) + let t20, t21 := fp2Mul(a20, a21, a20, a21) + let t30, t31 := fp2Mul(a00, a01, a10, a11) + let t40, t41 := fp2Mul(a00, a01, a20, a21) + let t50, t51 := fp2Mul(a20, a21, a10, a11) + let t50Xi, t51Xi := mulByXi(t50, t51) + c00, c01 := fp2Sub(t00, t01, t50Xi, t51Xi) + let t20Xi, t21Xi := mulByXi(t20, t21) + c10, c11 := fp2Sub(t20Xi, t21Xi, t30, t31) + c20, c21 := fp2Sub(t10, t11, t40, t41) + let t60, t61 := fp2Mul(a00, a01, c00, c01) + let a20Xi, a21Xi := mulByXi(a20, a21) + let a20XiC10, a21XiC11 := fp2Mul(a20Xi, a21Xi, c10, c11) + t60, t61 := fp2Add(t60, t61, a20XiC10, a21XiC11) + let a10Xi, a11Xi := mulByXi(a10, a11) + let a10XiC20, a11XiC21 := fp2Mul(a10Xi, a11Xi, c20, c21) + t60, t61 := fp2Add(t60, t61, a10XiC20, a11XiC21) + t60, t61 := fp2Inv(t60, t61) + c00, c01 := fp2Mul(c00, c01, t60, t61) + c10, c11 := fp2Mul(c10, c11, t60, t61) + c20, c21 := fp2Mul(c20, c21, t60, t61) + } + + // FP12 ARITHMETHICS + + /// @notice Computes the sum of two Fp12 elements. + /// @dev Algorithm 18 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the A element to sum. + /// @param b000, b001, b010, b011, b020, b021, b100, b101, b110, b111, b120, b121 The coefficients of the B element to sum. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A + B. + function fp12Add(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121, b000, b001, b010, b011, b020, b021, b100, b101, b110, b111, b120, b121) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + c000, c001, c010, c011, c020, c021 := fp6Add(a000, a001, a010, a011, a020, a021, b000, b001, b010, b011, b020, b021) + c100, c101, c110, c111, c120, c121 := fp6Add(a100, a101, a110, a111, a120, a121, b100, b101, b110, b111, b120, b121) + } + + /// @notice Computes the subtraction of two Fp12 elements. + /// @dev Algorithm 19 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the minuend A. + /// @param b000, b001, b010, b011, b020, b021, b100, b101, b110, b111, b120, b121 The coefficients of the subtrahend B. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A - B. + function fp12Sub(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121, b000, b001, b010, b011, b020, b021, b100, b101, b110, b111, b120, b121) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + c000, c001, c010, c011, c020, c021 := fp6Sub(a000, a001, a010, a011, a020, a021, b000, b001, b010, b011, b020, b021) + c100, c101, c110, c111, c120, c121 := fp6Sub(a100, a101, a110, a111, a120, a121, b100, b101, b110, b111, b120, b121) + } + + /// @notice Computes the multiplication between two Fp12 elements. + /// @dev Algorithm 20 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @param b000, b001, b010, b011, b020, b021, b100, b101, b110, b111, b120, b121 The coefficients of the Fp12 element B. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A * B. + function fp12Mul(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121, b000, b001, b010, b011, b020, b021, b100, b101, b110, b111, b120, b121) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + let t000, t001, t010, t011, t020, t021 := fp6Mul(a000, a001, a010, a011, a020, a021, b000, b001, b010, b011, b020, b021) + let t100, t101, t110, t111, t120, t121 := fp6Mul(a100, a101, a110, a111, a120, a121, b100, b101, b110, b111, b120, b121) + let t200, t201, t210, t211, t220, t221 := mulByGamma(t100, t101, t110, t111, t120, t121) + c000, c001, c010, c011, c020, c021 := fp6Add(t000, t001, t010, t011, t020, t021, t200, t201, t210, t211, t220, t221) + let t300, t301, t310, t311, t320, t321 := fp6Add(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) + let t400, t401, t410, t411, t420, t421 := fp6Add(b000, b001, b010, b011, b020, b021, b100, b101, b110, b111, b120, b121) + c100, c101, c110, c111, c120, c121 := fp6Mul(t300, t301, t310, t311, t320, t321, t400, t401, t410, t411, t420, t421) + c100, c101, c110, c111, c120, c121 := fp6Sub(c100, c101, c110, c111, c120, c121, t000, t001, t010, t011, t020, t021) + c100, c101, c110, c111, c120, c121 := fp6Sub(c100, c101, c110, c111, c120, c121, t100, t101, t110, t111, t120, t121) + } + + /// @notice Computes the square of a Fp12 element. + /// @dev Algorithm 22 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A^2. + function fp12Square(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + let t100, t101, t110, t111, t120, t121 := fp6Sub(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) + let t200, t201, t210, t211, t220, t221 := mulByGamma(a100, a101, a110, a111, a120, a121) + let t300, t301, t310, t311, t320, t321 := fp6Sub(a000, a001, a010, a011, a020, a021, t200, t201, t210, t211, t220, t221) + let t400, t401, t410, t411, t420, t421 := fp6Mul(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) + let t500, t501, t510, t511, t520, t521 := fp6Mul(t100, t101, t110, t111, t120, t121, t300, t301, t310, t311, t320, t321) + let t600, t601, t610, t611, t620, t621 := fp6Add(t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521) + c100, c101, c110, c111, c120, c121 := fp6Add(t400, t401, t410, t411, t420, t421, t400, t401, t410, t411, t420, t421) + let t700, t701, t710, t711, t720, t721 := mulByGamma(t400, t401, t410, t411, t420, t421) + c000, c001, c010, c011, c020, c021 := fp6Add(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721) + } + + /// @notice Computes the inverse of a Fp12 element. + /// @dev Algorithm 23 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A^(-1). + function fp12Inv(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + let t000, t001, t010, t011, t020, t021 := fp6Square(a000, a001, a010, a011, a020, a021) + let t100, t101, t110, t111, t120, t121 := fp6Square(a100, a101, a110, a111, a120, a121) + let t200, t201, t210, t211, t220, t221 := mulByGamma(t100, t101, t110, t111, t120, t121) + t000, t001, t010, t011, t020, t021 := fp6Sub(t000, t001, t010, t011, t020, t021, t200, t201, t210, t211, t220, t221) + t100, t101, t110, t111, t120, t121 := fp6Inv(t000, t001, t010, t011, t020, t021) + c000, c001, c010, c011, c020, c021 := fp6Mul(a000, a001, a010, a011, a020, a021, t100, t101, t110, t111, t120, t121) + let z00, z01, z10, z11, z20, z21 := FP6_ZERO() + c100, c101, c110, c111, c120, c121 := fp6Mul(a100, a101, a110, a111, a120, a121,t100, t101, t110, t111, t120, t121) + c100, c101, c110, c111, c120, c121 := fp6Sub(z00, z01, z10, z11, z20, z21, c100, c101, c110, c111, c120, c121) + } + + /// @notice Computes the exponentiation of a Fp12 element in the cyclotomic subgroup to t = 4965661367192848881. + /// @dev We make use of an addition chain to optimize the operation. + /// @dev See https://eprint.iacr.org/2015/192.pdf for further details. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A^t. + function fp12Expt(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + let t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121 := fp12CyclotomicSquare(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) + let t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321 := fp12CyclotomicSquare(t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121) + c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 := fp12CyclotomicSquare(t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321) + let t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521 := fp12CyclotomicSquare(c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121) + + let t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721 := fp12Mul(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121, t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521) + t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521 := fp12Mul(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721, t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121) + let t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921 := fp12Mul(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121, t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521) + let t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121 := fp12Mul(c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121, t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721) + let t1200, t1201, t1210, t1211, t1220, t1221, t1300, t1301, t1310, t1311, t1320, t1321 := fp12CyclotomicSquare(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721) + t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921 := fp12Mul(t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921, t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521) + t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521 := fp12Mul(t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921, t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121) + t1200, t1201, t1210, t1211, t1220, t1221, t1300, t1301, t1310, t1311, t1320, t1321 := nSquare(t1200, t1201, t1210, t1211, t1220, t1221, t1300, t1301, t1310, t1311, t1320, t1321, 6) + t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321 := fp12Mul(t1200, t1201, t1210, t1211, t1220, t1221, t1300, t1301, t1310, t1311, t1320, t1321, t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321) + t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321 := fp12Mul(t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321, t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121) + t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321 := nSquare(t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321, 7) + t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121 := fp12Mul(t200, t201, t210, t211, t220, t221, t300, t301, t310, t311, t320, t321, t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121) + t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121 := nSquare(t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121, 8) + t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121 := fp12Mul(t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121, t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521) + t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121 := fp12Mul(t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121, t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121) + t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121 := nSquare(t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121, 6) + t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721 := fp12Mul(t000, t001, t010, t011, t020, t021, t100, t101, t110, t111, t120, t121, t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721) + t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721 := nSquare(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721, 8) + t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721 := fp12Mul(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721, t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521) + t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721 := nSquare(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721, 6) + t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721 := fp12Mul(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721, t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521) + t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721 := nSquare(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721, 10) + t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921 := fp12Mul(t600, t601, t610, t611, t620, t621, t700, t701, t710, t711, t720, t721, t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921) + t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921 := nSquare(t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921, 6) + t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521 := fp12Mul(t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521, t800, t801, t810, t811, t820, t821, t900, t901, t910, t911, t920, t921) + c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 := fp12Mul(t400, t401, t410, t411, t420, t421, t500, t501, t510, t511, t520, t521, c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121) + } + + /// @notice Computes the conjugation of a Fp12 element. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A'. + function fp12Conjugate(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + c000 := a000 + c001 := a001 + c010 := a010 + c011 := a011 + c020 := a020 + c021 := a021 + c100, c101, c110, c111, c120, c121 := fp6Neg(a100, a101, a110, a111, a120, a121) + } + + /// @notice Computes the square of a Fp12 element in the cyclotomic subgroup. + /// @dev See https://eprint.iacr.org/2010/354.pdf for further details. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A^2. + function fp12CyclotomicSquare(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + let t00, t01 := fp2Mul(a110, a111, a110, a111) + let t10, t11 := fp2Mul(a000, a001, a000, a001) + let t20, t21 := fp2Add(a110, a111, a000, a001) + t20, t21 := fp2Mul(t20, t21, t20, t21) + t20, t21 := fp2Sub(t20, t21, t00, t01) + t20, t21 := fp2Sub(t20, t21, t10, t11) + let t30, t31 := fp2Mul(a020, a021, a020, a021) + let t40, t41 := fp2Mul(a100, a101, a100, a101) + let t50, t51 := fp2Add(a020, a021, a100, a101) + t50, t51 := fp2Mul(t50, t51, t50, t51) + t50, t51 := fp2Sub(t50, t51, t30, t31) + t50, t51 := fp2Sub(t50, t51, t40, t41) + let t60, t61 := fp2Mul(a120, a121, a120, a121) + let t70, t71 := fp2Mul(a010, a011, a010, a011) + let t80, t81 := fp2Add(a120, a121, a010, a011) + t80, t81 := fp2Mul(t80, t81, t80, t81) + t80, t81 := fp2Sub(t80, t81, t60, t61) + t80, t81 := fp2Sub(t80, t81, t70, t71) + t80, t81 := mulByXi(t80, t81) + t00, t01 := mulByXi(t00, t01) + t00, t01 := fp2Add(t00, t01, t10, t11) + t30, t31 := mulByXi(t30, t31) + t30, t31 := fp2Add(t30, t31, t40, t41) + t60, t61 := mulByXi(t60, t61) + t60, t61 := fp2Add(t60, t61, t70, t71) + + c000, c001 := fp2Sub(t00, t01, a000, a001) + c000, c001 := fp2Add(c000, c001, c000, c001) + c000, c001 := fp2Add(c000, c001, t00, t01) + + c010, c011 := fp2Sub(t30, t31, a010, a011) + c010, c011 := fp2Add(c010, c011, c010, c011) + c010, c011 := fp2Add(c010, c011, t30, t31) + + c020, c021 := fp2Sub(t60, t61, a020, a021) + c020, c021 := fp2Add(c020, c021, c020, c021) + c020, c021 := fp2Add(c020, c021, t60, t61) + + c100, c101 := fp2Add(t80, t81, a100, a101) + c100, c101 := fp2Add(c100, c101, c100, c101) + c100, c101 := fp2Add(c100, c101, t80, t81) + + c110, c111 := fp2Add(t20, t21, a110, a111) + c110, c111 := fp2Add(c110, c111, c110, c111) + c110, c111 := fp2Add(c110, c111, t20, t21) + + c120, c121 := fp2Add(t50, t51, a120, a121) + c120, c121 := fp2Add(c120, c121, c120, c121) + c120, c121 := fp2Add(c120, c121, t50, t51) + } + + /// @notice Computes the exponentiation of a Fp12 element in the cyclotomic subgroup to 2n. + /// @dev We compute A^2n as n cyclotomic squares. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A^2n. + function nSquare(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121, n) -> c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 { + c000 := a000 + c001 := a001 + c010 := a010 + c011 := a011 + c020 := a020 + c021 := a021 + c100 := a100 + c101 := a101 + c110 := a110 + c111 := a111 + c120 := a120 + c121 := a121 + for { let i := 0 } lt(i, n) { i := add(i, 1) } { + c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 := fp12CyclotomicSquare(c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121) + } + } + + // FROBENIUS + + + /// @notice Computes the exponentiation of a Fp12 element to p. + /// @dev Algorithm 28 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A^p. + function frobenius(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> c00, c01, c10, c11, c20, c21, c30, c31, c40, c41, c50, c51 { + let t10, t11 := fp2Conjugate(a000, a001) + let t20, t21 := fp2Conjugate(a100, a101) + let t30, t31 := fp2Conjugate(a010, a011) + let t40, t41 := fp2Conjugate(a110, a111) + let t50, t51 := fp2Conjugate(a020, a021) + let t60, t61 := fp2Conjugate(a120, a121) + + t20, t21 := mulByGamma11(t20, t21) + t30, t31 := mulByGamma12(t30, t31) + t40, t41 := mulByGamma13(t40, t41) + t50, t51 := mulByGamma14(t50, t51) + t60, t61 := mulByGamma15(t60, t61) + + c00 := t10 + c01 := t11 + c10 := t30 + c11 := t31 + c20 := t50 + c21 := t51 + c30 := t20 + c31 := t21 + c40 := t40 + c41 := t41 + c50 := t60 + c51 := t61 + } + + /// @notice Computes the exponentiation of a Fp12 element to p^2. + /// @dev Algorithm 29 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A^(p^2). + function frobeniusSquare(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> c00, c01, c10, c11, c20, c21, c30, c31, c40, c41, c50, c51 { + let t10 := a000 + let t11 := a001 + let t20, t21 := mulByGamma21(a100, a101) + let t30, t31 := mulByGamma22(a010, a011) + let t40, t41 := mulByGamma23(a110, a111) + let t50, t51 := mulByGamma24(a020, a021) + let t60, t61 := mulByGamma25(a120, a121) + + c00 := t10 + c01 := t11 + c10 := t30 + c11 := t31 + c20 := t50 + c21 := t51 + c30 := t20 + c31 := t21 + c40 := t40 + c41 := t41 + c50 := t60 + c51 := t61 + } + + /// @notice Computes the exponentiation of a Fp12 element to p^3. + /// @dev @dev Algorithm 29 in: https://eprint.iacr.org/2010/354.pdf. + /// @param a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return c000, c001, c010, c011, c020, c021, c100, c101, c110, c111, c120, c121 The coefficients of the element C = A^(p^3). + function frobeniusCube(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> c00, c01, c10, c11, c20, c21, c30, c31, c40, c41, c50, c51 { + let t10, t11 := fp2Conjugate(a000, a001) + let t20, t21 := fp2Conjugate(a100, a101) + let t30, t31 := fp2Conjugate(a010, a011) + let t40, t41 := fp2Conjugate(a110, a111) + let t50, t51 := fp2Conjugate(a020, a021) + let t60, t61 := fp2Conjugate(a120, a121) + + t20, t21 := mulByGamma31(t20, t21) + t30, t31 := mulByGamma32(t30, t31) + t40, t41 := mulByGamma33(t40, t41) + t50, t51 := mulByGamma34(t50, t51) + t60, t61 := mulByGamma35(t60, t61) + + c00 := t10 + c01 := t11 + c10 := t30 + c11 := t31 + c20 := t50 + c21 := t51 + c30 := t20 + c31 := t21 + c40 := t40 + c41 := t41 + c50 := t60 + c51 := t61 + } + + // GAMMA_1_i + /// @notice Computes the multiplication between a fp2 element by the constants g_1,i. + /// @dev Where g_1,i = u^(i(p-1)/6) + /// @dev This value was precomputed using Python. Already in montgomery form. + /// @dev See https://eprint.iacr.org/2010/354.pdf for further details. + /// @params a00, a01 The coefficients of the Fp2 element A. + /// @return c00, c01 The coefficients of the element C = A*g_1,i. + + function mulByGamma11(a00, a01) -> c00, c01 { + let g00 := 1334504125441109323775816677333762124980877086439557453392802825656291576071 + let g01 := 7532670101108748540749979597679923402841328813027773483599019704565791010162 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + function mulByGamma12(a00, a01) -> c00, c01 { + let g00 := 11461073415658098971834280704587444395456423268720245247603935854280982113072 + let g01 := 17373957475705492831721812124331982823197004514106338927670775596783233550167 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + function mulByGamma13(a00, a01) -> c00, c01 { + let g00 := 16829996427371746075450799880956928810557034522864196246648550205375670302249 + let g01 := 20140510615310063345578764457068708762835443761990824243702724480509675468743 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + function mulByGamma14(a00, a01) -> c00, c01 { + let g00 := 9893659366031634526915473325149983243417508801286144596494093251884139331218 + let g01 := 16514792769865828027011044701859348114858257981779976519405133026725453154633 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + function mulByGamma15(a00, a01) -> c00, c01 { + let g00 := 8443299194457421137480282511969901974227997168695360756777672575877693116391 + let g01 := 21318636632361225103955470331868462398471880609949088574192481281746934874025 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + // GAMMA_2_i + /// @notice Computes the multiplication between a fp2 element by the constants g_2,i. + /// @dev Where g_2,i = g_1,i * g'_1,i + /// @dev This value was precomputed using Python. Already in montgomery form. + /// @dev See https://eprint.iacr.org/2010/354.pdf for further details. + /// @params a00, a01 The coefficients of the Fp2 element A. + /// @return c00, c01 The coefficients of the element C = A*g_2,i. + + function mulByGamma21(a00, a01) -> c00, c01 { + let g0 := 1881798392815877688876180778159931906057091683336018750908411925848733129714 + c00, c01 := fp2ScalarMul(a00, a01, g0) + } + + function mulByGamma22(a00, a01) -> c00, c01 { + let g0 := 17419166386535333598783630241015674584964973961482396687585055285806960741276 + c00, c01 := fp2ScalarMul(a00, a01, g0) + } + + function mulByGamma23(a00, a01) -> c00, c01 { + let g0 := 15537367993719455909907449462855742678907882278146377936676643359958227611562 + c00, c01 := fp2ScalarMul(a00, a01, g0) + } + + function mulByGamma24(a00, a01) -> c00, c01 { + let g0 := 20006444479023397533370224967097343182639219473961804911780625968796493078869 + c00, c01 := fp2ScalarMul(a00, a01, g0) + } + + function mulByGamma25(a00, a01) -> c00, c01 { + let g0 := 4469076485303941623462775504241600503731337195815426975103982608838265467307 + c00, c01 := fp2ScalarMul(a00, a01, g0) + } + + // GAMMA_3_i + /// @notice Computes the multiplication between a fp2 element by the constants g_3,i. + /// @dev Where g_3,i = g_1,i * g_2,i + /// @dev This value was precomputed using Python. Already in montgomery form. + /// @dev See https://eprint.iacr.org/2010/354.pdf for further details. + /// @params a00, a01 The coefficients of the Fp2 element A. + /// @return c00, c01 The coefficients of the element C = A*g_3,i. + + function mulByGamma31(a00, a01) -> c00, c01 { + let g00 := 3649295186494431467217240962842301358951278585756714214031945394966344685949 + let g01 := 17372117152826387298350653207345606612066102743297871578090761045572893546809 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + function mulByGamma32(a00, a01) -> c00, c01 { + let g00 := 14543349330631744552586812320441124107441202078168618766450326117520897829805 + let g01 := 4646831431411403714092965637071058625728899792817054432901795759277546050476 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + function mulByGamma33(a00, a01) -> c00, c01 { + let g00 := 5058246444467529146795605864300346278139276634433627416040487689269555906334 + let g01 := 1747732256529211876667641288188566325860867395306999418986313414135550739840 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + function mulByGamma34(a00, a01) -> c00, c01 { + let g00 := 3025265262868802913511075437173590487338001780554453930995247874855578067679 + let g01 := 10425289180741305073643362413949631488281652900778689227251281048515799234257 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + function mulByGamma35(a00, a01) -> c00, c01 { + let g00 := 9862576063628467829192720579684130652367741026604221989510773554027227469215 + let g01 := 16681752610922605480353377694363181135019829138759259603037557916788351015335 + c00, c01 := fp2Mul(a00, a01, g00, g01) + } + + // PAIRING FUNCTIONS + + /// @notice Computes the double of a G2 point and its tangent line. + /// @dev The point is in projective coordinates. + /// @dev See https://eprint.iacr.org/2013/722.pdf for further details. + /// @params xq0, xq1 The coefficients of the Fp2 X coordinate of the Q point. + /// @params yq0, yq1 The coefficients of the Fp2 X coordinate of the Q point. + /// @params zq0, zq1 The coefficients of the Fp2 X coordinate of the Q point. + /// @return xt0, xt1 The coefficients of the Fp2 X coordinate of T = 2Q. + /// @return yt0, yt1 The coefficients of the Fp2 X coordinate of T = 2Q. + /// @return zt0, zt1 The coefficients of the Fp2 X coordinate of T = 2Q. + /// @return l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51 The coefficients of the tangent line to Q. + function doubleStep(xq0, xq1, yq0, yq1, zq0, zq1) -> l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51, xt0, xt1, yt0, yt1, zt0, zt1 { + let zero := 0 + let twoInv := MONTGOMERY_TWO_INV() + let t00, t01 := fp2Mul(xq0, xq1, yq0, yq1) + let t10, t11 := fp2ScalarMul(t00, t01, twoInv) + let t20, t21 := fp2Mul(yq0, yq1, yq0, yq1) + let t30, t31 := fp2Mul(zq0, zq1, zq0, zq1) + let t40, t41 := fp2Add(t30, t31, t30, t31) + t40, t41 := fp2Add(t40, t41, t30, t31) + let t50, t51 := MONTGOMERY_TWISTED_CURVE_COEFFS() + t50, t51 := fp2Mul(t40, t41, t50, t51) + let t60, t61 :=fp2Add(t50, t51, t50, t51) + t60, t61 := fp2Add(t60, t61, t50, t51) + let t70, t71 := fp2Add(t20, t21, t60, t61) + t70, t71 := fp2ScalarMul(t70, t71, twoInv) + let t80, t81 := fp2Add(yq0, yq1, zq0, zq1) + t80, t81 := fp2Mul(t80, t81, t80, t81) + let t90, t91 := fp2Add(t30, t31, t20, t21) + t80, t81 := fp2Sub(t80, t81, t90, t91) + let t100, t101 := fp2Sub(t50, t51, t20, t21) + let t110, t111 := fp2Mul(xq0, xq1, xq0, xq1) + let t120, t121 := fp2Mul(t50, t51, t50, t51) + let t130, t131 := fp2Add(t120, t121, t120, t121) + t130, t131 := fp2Add(t130, t131, t120, t121) + + // l0 + l00 := t80 + l01 := t81 + l10 := zero + l11 := zero + l20 := zero + l21 := zero + + // l1 + l30, l31 := fp2Add(t110, t111, t110, t111) + l30, l31 := fp2Add(l30, l31, t110, t111) + + // l2 + l40 := t100 + l41 := t101 + + l50 := zero + l51 := zero + + // Tx + xt0, xt1 := fp2Sub(t20, t21, t60, t61) + xt0, xt1 := fp2Mul(xt0, xt1, t10, t11) + + // Ty + yt0, yt1 := fp2Mul(t70, t71, t70, t71) + yt0, yt1 := fp2Sub(yt0, yt1, t130, t131) + + // Tz + zt0, zt1 := fp2Mul(t20, t21, t80, t81) + } + + /// @notice Computes the addition of two G2 points and the line through them. + /// @dev It's called mixed addition because Q is in affine coordinates and T in projective coordinates. + /// @dev The two points must be different, in this Q, which is G2 group generator of an order of 21888242871839275222246405745257275088548364400416034343698204186575808495617, is doubled and added. So will never reach Q. + /// @dev See https://eprint.iacr.org/2013/722.pdf for further details. + /// @dev Disclaimer: The algorithm described in the paper is has a typo, the (`l00`,`l01`) coefficients should not be negated. + /// @params xq0, xq1 The coefficients of the Fp2 X coordinate of the Q point. + /// @params yq0, yq1 The coefficients of the Fp2 Y coordinate of the Q point. + /// @params xt0, xt1 The coefficients of the Fp2 X coordinate of the T point. + /// @params yt0, yt1 The coefficients of the Fp2 Y coordinate of the T point. + /// @params zt0, zt1 The coefficients of the Fp2 Z coordinate of the T point. + /// @return xc0, xc1 The coefficients of the Fp2 X coordinate of C = Q + T. + /// @return yc0, yc1 The coefficients of the Fp2 X coordinate of C = Q + T. + /// @return zc0, zc1 The coefficients of the Fp2 X coordinate of C = Q + T. + /// @return l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51 The coefficients of the line through T and Q. + function mixedAdditionStep(xq0, xq1, yq0, yq1, xt0, xt1, yt0, yt1, zt0, zt1) -> l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51, xc0, xc1, yc0, yc1, zc0, zc1 { + let zero := 0 + let t00, t01 := fp2Mul(yq0,yq1,zt0,zt1) + let t10, t11 := fp2Sub(yt0, yt1, t00, t01) + t00, t01 := fp2Mul(xq0, xq1, zt0, zt1) + let t20, t21 := fp2Sub(xt0, xt1, t00, t01) + let t30, t31 := fp2Mul(t10, t11, t10, t11) + let t40, t41 := fp2Mul(t20, t21, t20, t21) + let t50, t51 := fp2Mul(t20, t21, t40, t41) + let t60, t61 := fp2Mul(zt0, zt1, t30, t31) + let t70, t71 := fp2Mul(xt0, xt1, t40, t41) + t00, t01 := fp2Add(t70, t71, t70, t71) + let t80, t81 := fp2Add(t50, t51, t60, t61) + t80, t81 := fp2Sub(t80, t81, t00, t01) + t00, t01 := fp2Mul(yt0, yt1, t50, t51) + + // Xc0 + xc0, xc1 := fp2Mul(t20, t21, t80, t81) + + // Yc0 + yc0, yc1 := fp2Sub(t70, t71, t80, t81) + yc0, yc1 := fp2Mul(yc0, yc1, t10, t11) + yc0, yc1 := fp2Sub(yc0, yc1, t00, t01) + + // Zc0 + zc0, zc1 := fp2Mul(t50, t51, zt0, zt1) + t00, t01 := fp2Mul(t20, t21, yq0, yq1) + let t90, t91 := fp2Mul(xq0, xq1, t10, t11) + t90, t91 := fp2Sub(t90, t91, t00, t01) + + // l0 + l00 := t20 + l01 := t21 + l10 := zero + l11 := zero + l20 := zero + l21 := zero + + // l1 + l30 := t10 + l31 := t11 + + // l2 + l40 := t90 + l41 := t91 + l50 := zero + l51 := zero + } + + /// @notice Computes the line through two G2 points. + /// @dev Like in the mixedAdditionStep, Q is in affine coordinates and T in projective coordinates. + /// @dev The two points must be different, in this Q, which is G2 group generator of an order of 21888242871839275222246405745257275088548364400416034343698204186575808495617, is doubled and added. So will never reach Q. + /// @params xq0, xq1 The coefficients of the Fp2 X coordinate of the Q point. + /// @params yq0, yq1 The coefficients of the Fp2 Y coordinate of the Q point. + /// @params xt0, xt1 The coefficients of the Fp2 X coordinate of the T point. + /// @params yt0, yt1 The coefficients of the Fp2 Y coordinate of the T point. + /// @params zt0, zt1 The coefficients of the Fp2 Z coordinate of the T point. + /// @return l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51 The coefficients of the line through T and Q. + function computeLine(xq0, xq1, yq0, yq1, xt0, xt1, yt0, yt1, zt0, zt1) -> l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51 { + let zero := 0 + let t00, t01 := fp2Mul(yq0,yq1,zt0,zt1) + let t10, t11 := fp2Sub(yt0, yt1, t00, t01) + t00, t01 := fp2Mul(xq0, xq1, zt0, zt1) + let t20, t21 := fp2Sub(xt0, xt1, t00, t01) + let t30, t31 := fp2Mul(t20, t21, yq0, yq1) + let t40, t41 := fp2Mul(xq0, xq1, t10, t11) + t40, t41 := fp2Sub(t40, t41, t30, t31) + + // l0 + l00 := t20 + l01 := t21 + l10 := zero + l11 := zero + l20 := zero + l21 := zero + + // l1 + l30, l31 := fp2Neg(t10, t11) + + // l2 + l40 := t40 + l41 := t41 + l50 := zero + l51 := zero + } + + /// @notice Computes the final exponentiation to the result given by the Millers Loop. + /// @dev It computes the exponentiation of a Fp12 elemento to e, with e = (p^12 -1)/r + /// @dev We can split this exponentitation in three parts: e = (p^6 - 1)(p^2 + 1)((p^4 - p^2 + 1)/r) + /// @dev The first 2 parts are easy to compute using the Frobenius operator. + /// @dev To calculate this we use the first 5 lines of Algorithm 31 in: https://eprint.iacr.org/2010/354.pdf + /// @dev For the hard part we use the Fuentes et al. method. Algorithm 6 in: https://eprint.iacr.org/2015/192.pdf + /// @params a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121 The coefficients of the Fp12 element A. + /// @return f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 The coefficients of A^(s*((p^12 -1)/r)) where s is not divisible by r. + function finalExponentiation(a000, a001, a010, a011, a020, a021, a100, a101, a110, a111, a120, a121) -> f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 { + f000 := a000 + f001 := a001 + f010 := a010 + f011 := a011 + f020 := a020 + f021 := a021 + f100 := a100 + f101 := a101 + f110 := a110 + f111 := a111 + f120 := a120 + f121 := a121 + + // Easy Part + let t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12Conjugate(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Inv(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12Mul(t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121, f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + let t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121 := frobeniusSquare(t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121, t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + + // Hard Part + t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12Expt(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12Conjugate(t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12CyclotomicSquare(t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121 := fp12CyclotomicSquare(t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121 := fp12Mul(t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121, t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121) + let t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121 := fp12Expt(t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121) + t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121 := fp12Conjugate(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121) + let t3000, t3001, t3010, t3011, t3020, t3021, t3100, t3101, t3110, t3111, t3120, t3121 := fp12Conjugate(t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121) + t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121 := fp12Mul(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121, t3000, t3001, t3010, t3011, t3020, t3021, t3100, t3101, t3110, t3111, t3120, t3121) + t3000, t3001, t3010, t3011, t3020, t3021, t3100, t3101, t3110, t3111, t3120, t3121 := fp12CyclotomicSquare(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121) + let t4000, t4001, t4010, t4011, t4020, t4021, t4100, t4101, t4110, t4111, t4120, t4121 := fp12Expt(t3000, t3001, t3010, t3011, t3020, t3021, t3100, t3101, t3110, t3111, t3120, t3121) + t4000, t4001, t4010, t4011, t4020, t4021, t4100, t4101, t4110, t4111, t4120, t4121 := fp12Mul(t4000, t4001, t4010, t4011, t4020, t4021, t4100, t4101, t4110, t4111, t4120, t4121, t1000, t1001, t1010, t1011, t1020, t1021, t1100, t1101, t1110, t1111, t1120, t1121) + t3000, t3001, t3010, t3011, t3020, t3021, t3100, t3101, t3110, t3111, t3120, t3121 := fp12Mul(t4000, t4001, t4010, t4011, t4020, t4021, t4100, t4101, t4110, t4111, t4120, t4121, t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12Mul(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121, t4000, t4001, t4010, t4011, t4020, t4021, t4100, t4101, t4110, t4111, t4120, t4121) + t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12Mul(t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121, f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121 := frobenius(t3000, t3001, t3010, t3011, t3020, t3021, t3100, t3101, t3110, t3111, t3120, t3121) + t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12Mul(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121, t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121 := frobeniusSquare(t4000, t4001, t4010, t4011, t4020, t4021, t4100, t4101, t4110, t4111, t4120, t4121) + t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121 := fp12Mul(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121, t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121 := fp12Conjugate(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121 := fp12Mul(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121, t3000, t3001, t3010, t3011, t3020, t3021, t3100, t3101, t3110, t3111, t3120, t3121) + t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121 := frobeniusCube(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(t2000, t2001, t2010, t2011, t2020, t2021, t2100, t2101, t2110, t2111, t2120, t2121, t0000, t0001, t0010, t0011, t0020, t0021, t0100, t0101, t0110, t0111, t0120, t0121) + } + + /// @notice Computes the Millers Loop for the optimal ate pairing. + /// @dev Algorithm 1 in: https://eprint.iacr.org/2010/354.pdf + /// @dev It takes two points: P that belongs to the curve G1, in affine coordinates (Fp elements) + /// @dev Point Q belongs to the twisted G2 curve, in affine coordinates (Fp2 elements) + /// @params xp, yp The coordinates of the point P. + /// @params xq0, xq1 The coefficients of the X coordinate of point Q. + /// @params yq0, yq1 The coefficients of the Y coordinate of point Q. + /// @return f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 The Fp12 element result of the Miller Loop + function millerLoop(xq0, xq1, yq0, yq1, xp, yp) -> f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 { + let t00, t01, t10, t11, t20, t21 := g2ProjectiveFromAffine(xq0, xq1, yq0, yq1) + let mq00, mq01, mq10, mq11 := g2AffineNeg(xq0, xq1, yq0, yq1) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := FP12_ONE() + let naf := NAF_REPRESENTATIVE() + let n_iter := 63 + let l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51 + let myp := montgomerySub(0, yp) + let mxp := montgomerySub(0, xp) + + // Computes the first iteration of Millers loop outside to avoid unecesariy square + // NAF[64] == 0 + l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51, t00, t01, t10, t11, t20, t21 := doubleStep(t00, t01, t10, t11, t20, t21) + l00, l01 := fp2ScalarMul(l00, l01, myp) + l30, l31 := fp2ScalarMul(l30, l31, xp) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121, l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51) + + // Computes the second iteration of Millers loop outside + // NAF[63] == -1. + // Here T = 2Q, so doing a double step and a mixed addition step with -Q looks like: (2(2Q)-Q) = 3Q. + // This is equivalent to a mixed addition step with Q: (2Q + Q) = 3Q + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121,f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51 := computeLine(mq00, mq01, mq10, mq11, t00, t01, t10, t11, t20, t21) + l00, l01 := fp2ScalarMul(l00, l01, yp) + l30, l31 := fp2ScalarMul(l30, l31, xp) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121, l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51) + l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51, t00, t01, t10, t11, t20, t21 := mixedAdditionStep(xq0, xq1, yq0, yq1, t00, t01, t10, t11, t20, t21) + l00, l01 := fp2ScalarMul(l00, l01, yp) + l30, l31 := fp2ScalarMul(l30, l31, mxp) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121, l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51) + + for {let i := 0} lt(i, n_iter) { i := add(i, 1) } { + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Square(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + + l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51, t00, t01, t10, t11, t20, t21 := doubleStep(t00, t01, t10, t11, t20, t21) + l00, l01 := fp2ScalarMul(l00, l01, myp) + l30, l31 := fp2ScalarMul(l30, l31, xp) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121, l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51) + + // naf digit = 1 + if and(naf, 1) { + l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51, t00, t01, t10, t11, t20, t21 := mixedAdditionStep(xq0, xq1, yq0, yq1, t00, t01, t10, t11, t20, t21) + l00, l01 := fp2ScalarMul(l00, l01, yp) + l30, l31 := fp2ScalarMul(l30, l31, mxp) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121, l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51) + } + + // naf digit = -1 + if and(naf, 2) { + l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51, t00, t01, t10, t11, t20, t21 := mixedAdditionStep(mq00, mq01, mq10, mq11, t00, t01, t10, t11, t20, t21) + l00, l01 := fp2ScalarMul(l00, l01, yp) + l30, l31 := fp2ScalarMul(l30, l31, mxp) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121, l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51) + } + + naf := shr(2, naf) + } + + let r00, r01 := fp2Conjugate(xq0, xq1) + let r10, r11 := fp2Conjugate(yq0, yq1) + r00, r01 := mulByGamma12(r00, r01) + r10, r11 := mulByGamma13(r10, r11) + + let r20, r21 := mulByGamma22(xq0, xq1) + let r30, r31 := mulByGamma23(yq0, yq1) + r30, r31 := fp2Neg(r30, r31) + + l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51, t00, t01, t10, t11, t20, t21 := mixedAdditionStep(r00, r01, r10, r11, t00, t01, t10, t11, t20, t21) + l00, l01 := fp2ScalarMul(l00, l01, yp) + l30, l31 := fp2ScalarMul(l30, l31, mxp) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121, l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51) + + l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51 := computeLine(r20, r21, r30, r31, t00, t01, t10, t11, t20, t21) + l00, l01 := fp2ScalarMul(l00, l01, yp) + l30, l31 := fp2ScalarMul(l30, l31, xp) + f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := fp12Mul(f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121, l00, l01, l10, l11, l20, l21, l30, l31, l40, l41, l50, l51) + } + + // FALLBACK + + let inputSize := calldatasize() + + // Empty input is valid and results in returning one. + if eq(inputSize, 0) { + mstore(0, 1) + return(0, 32) + } + + // If the input length is not a multiple of 192, the call fails. + if mod(inputSize, PAIR_LENGTH()) { + // Bad pairing input + burnGas() + } + + let r000, r001, r010, r011, r020, r021, r100, r101, r110, r111, r120, r121 := FP12_ONE() + + // Calldata "parsing" + for { let i := 0 } lt(i, inputSize) { i := add(i, PAIR_LENGTH()) } { + /* G1 */ + calldatacopy(i, i, 32) // x + calldatacopy(add(i, 32), add(i, 32), 32) // y + + let g1_x := mload(i) + let g1_y := mload(add(i, 32)) + + if iszero(and(coordinateIsOnFieldOrder(g1_x), coordinateIsOnFieldOrder(g1_y))) { + burnGas() + } + + g1_x := intoMontgomeryForm(g1_x) + g1_y := intoMontgomeryForm(g1_y) + + let g1IsInfinity := g1AffinePointIsInfinity(g1_x, g1_y) + + if and(iszero(g1IsInfinity), iszero(g1AffinePointIsOnCurve(g1_x, g1_y))) { + burnGas() + } + + /* G2 */ + let g2_x1_offset := add(i, 64) + let g2_x0_offset := add(i, 96) + let g2_y1_offset := add(i, 128) + let g2_y0_offset := add(i, 160) + + calldatacopy(g2_x1_offset, g2_x1_offset, 32) + calldatacopy(g2_x0_offset, g2_x0_offset, 32) + calldatacopy(g2_y1_offset, g2_y1_offset, 32) + calldatacopy(g2_y0_offset, g2_y0_offset, 32) + + let g2_x1 := mload(g2_x1_offset) + let g2_x0 := mload(g2_x0_offset) + let g2_y1 := mload(g2_y1_offset) + let g2_y0 := mload(g2_y0_offset) + + if iszero(and(coordinateIsOnFieldOrder(g2_x0), coordinateIsOnFieldOrder(g2_x1))) { + burnGas() + } + + if iszero(and(coordinateIsOnFieldOrder(g2_y0), coordinateIsOnFieldOrder(g2_y1))) { + burnGas() + } + + if g2AffinePointIsInfinity(g2_x0, g2_x1, g2_y0, g2_y1) { + continue + } + + g2_x0 := intoMontgomeryForm(g2_x0) + g2_x1 := intoMontgomeryForm(g2_x1) + g2_y0 := intoMontgomeryForm(g2_y0) + g2_y1 := intoMontgomeryForm(g2_y1) + + if iszero(g2IsInSubGroup(g2_x0,g2_x1, g2_y0, g2_y1, MONTGOMERY_ONE(), 0)) { + burnGas() + } + + if iszero(g2AffinePointIsOnCurve(g2_x0, g2_x1, g2_y0, g2_y1)) { + burnGas() + } + + // We must continue if g1 is the point at infinity after validating both g1 and g2 + // That's why although knowing this before parsing and validating g2 we check it later. + if g1IsInfinity { + continue + } + + + let f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121 := millerLoop(g2_x0, g2_x1, g2_y0, g2_y1, g1_x, g1_y) + + r000, r001, r010, r011, r020, r021, r100, r101, r110, r111, r120, r121 := fp12Mul(r000, r001, r010, r011, r020, r021, r100, r101, r110, r111, r120, r121, f000, f001, f010, f011, f020, f021, f100, f101, f110, f111, f120, f121) + } + + r000, r001, r010, r011, r020, r021, r100, r101, r110, r111, r120, r121 := finalExponentiation(r000, r001, r010, r011, r020, r021, r100, r101, r110, r111, r120, r121) + + // Pair check + if and(eq(r000, MONTGOMERY_ONE()), iszero(or(r001, or(r010, r011)))) { + if iszero(or(or(r020, r021), or(r100, r101))) { + if iszero(or(or(r110, r111), or(r120, r121))) { + mstore(0, 1) + return(0, 32) + } + } + } + + mstore(0, 0) + return(0, 32) + } + } +} diff --git a/system-contracts/scripts/constants.ts b/system-contracts/scripts/constants.ts index 88f8e0dd7..406b4cb6e 100644 --- a/system-contracts/scripts/constants.ts +++ b/system-contracts/scripts/constants.ts @@ -65,6 +65,12 @@ export const SYSTEM_CONTRACTS: ISystemContracts = { lang: Language.Yul, path: "precompiles", }, + ecPairing: { + address: "0x0000000000000000000000000000000000000008", + codeName: "EcPairing", + lang: Language.Yul, + path: "precompiles", + }, bootloader: { // Bootloader has EmptyContract code address: "0x0000000000000000000000000000000000008001", diff --git a/system-contracts/test/precompiles/EcPairing.spec.ts b/system-contracts/test/precompiles/EcPairing.spec.ts new file mode 100644 index 000000000..f938a14b9 --- /dev/null +++ b/system-contracts/test/precompiles/EcPairing.spec.ts @@ -0,0 +1,415 @@ +import { expect } from "chai"; +import type { Contract } from "zksync-ethers"; +import { callFallback, deployContractYul } from "../shared/utils"; + +describe("EcPairing tests", function () { + let ecPairing: Contract; + + before(async () => { + ecPairing = await deployContractYul("EcPairing", "precompiles"); + }); + + describe("Ethereum tests", function () { + it("ecpairing_empty_data_insufficient_gas", async () => { + const returnData = await callFallback(ecPairing, ""); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_perturb_g2_by_curve_order", async () => { + const call = callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c248652d61f350be9ffaba461cdfdd9cd68f770b1d71184b6e8ac0b2f0c992f6ee090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_bad_length_191", async () => { + const call = callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7d00" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_perturb_zeropoint_by_one", async () => { + const call = callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_one_point_with_g1_zero", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_perturb_g2_by_field_modulus", async () => { + const call = callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed396ad8433991909fa4eedf63ea8d8bf353cc9bc4d925598091cd66f3a99f94a212c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_one_point_with_g2_zero_and_g1_invalid", async () => { + const call = callFallback( + ecPairing, + "0x000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_two_point_match_1", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_two_point_fail_1", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000000"); + }); + + it("ecpairing_two_point_oog", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_three_point_fail_1", async () => { + const returnData = await callFallback( + ecPairing, + "0x105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f00cacf3523caf879d7d05e30549f1e6fdce364cbb8724b0329c6c2a39d4f018e0692e55db067300e6e3fe56218fa2f940054e57e7ef92bf7d475a9d8a8502fd200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000000"); + }); + + it("ecpairing_one_point_insufficient_gas", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000000"); + }); + + it("ecpairing_two_point_match_4", async () => { + const returnData = await callFallback( + ecPairing, + "0x105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_perturb_g2_by_one", async () => { + const call = callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c31800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_three_point_match_1", async () => { + const returnData = await callFallback( + ecPairing, + "0x105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd030427000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000021a2c3013d2ea92e13c800cde68ef56a294b883f6ac35d25f587c09b1b3c635f7290158a80cd3d66530f74dc94c94adb88f5cdb481acca997b6e60071f08a115f2f997f3dbd66a7afe07fe7862ce239edba9e05c5afff7f8a1259c9733b2dfbb929d1691530ca701b4a106054688728c9972c8512e9789e9567aae23e302ccd75000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_two_point_match_5", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_two_point_match_2", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_two_point_fail_2", async () => { + const returnData = await callFallback( + ecPairing, + "0x105456a333e6d636854f987ea7bb713dfd0ae8371a72aea313ae0c32c0bf10160cf031d41b41557f3e7e3ba0c51bebe5da8e6ecd855ec50fc87efcdeac168bcc0476be093a6d2b4bbf907172049874af11e1b6267606e00804d3ff0037ec57fd3010c68cb50161b7d1d96bb71edfec9880171954e56871abf3d93cc94d745fa114c059d74e5b6c4ec14ae5864ebe23a71781d86c29fb8fb6cce94f70d3de7a2101b33461f39d9e887dbb100f170a2345dde3c07e256d1dfa2b657ba5cd03042700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002105384b6dd6c48634b9fe89cb3e19667c1fe6736c69df070d674c95a42b3b8242c0d8e67f0f2c14c43734b430d8be4265af8c4f7a67deb0b029fd2dff99cc6b9015eaec465d922580c7de5d4a5c26de75eaf2af6841b7412ef2eebd1e051076f1b4c21849e48de12d1bae2bad3299717aa8664ade430e19dec72a6e10a39b0ab" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000000"); + }); + + it("ecpairing_two_points_with_one_g2_zero", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000000"); + }); + + it("ecpairing_perturb_g2_by_field_modulus_again", async () => { + const call = callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b432cad18bcbe0e1502fbb7370f4c98ed7b5351fa74b59e08890758183f777af1" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_perturb_zeropoint_by_field_modulus", async () => { + const call = callFallback( + ecPairing, + "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd470000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_perturb_zeropoint_by_curve_order", async () => { + const call = callFallback( + ecPairing, + "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_one_point_with_g2_zero", async () => { + const returnData = await callFallback( + ecPairing, + "0x000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_one_point_not_in_subgroup", async () => { + const call = callFallback( + ecPairing, + "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800d3270b7da683f988d3889abcdad9776ecd45abaca689f1118c3fd33404b4392588360d269af2cd3e0803839ea274c2b8f062a6308e8da85fd774c26f1bcb87" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_bad_length_193", async () => { + const call = callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa0000000000000000000000000000000000000000000000000000000000000000" + ); + await expect(call).to.be.reverted; + }); + + it("ecpairing_two_point_match_3", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd31a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + + it("ecpairing_one_point_fail", async () => { + const returnData = await callFallback( + ecPairing, + "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa" + ); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000000"); + }); + + it("ecpairing_fuzz_positive", async () => { + const positive_inputs = [ + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c153931579d3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e104316c97997c17267a1bb67365523b4388e1306d66ea6e4d8f4a4a4b65f5c7d06e286b49c56f6293b2cea30764f0d5eabe5817905468a41f09b77588f692e8b081070efe3d4913dde35bba2513c426d065dee815c478700cef07180fb6146182432428b1490a4f25053d4c20c8723a73de6f0681bd3a8fca41008a6c3c288252d50f18403272e96c10135f96db0f8d0aec25033ebdffb88d2e7956c9bb198ec072462211ebc0a2f042f993d5bd76caf4adb5e99610dcf7c1d992595e6976aa3", + "0084c3136563609ce6d7218719af3024e2163ea8124130cfb61c2521379b00672e775227ccd46bb5bd8b9f9714ecae9e037f8e91246b2a7e171337cf1332e3411fee9eea81af0f92485cb60cec6fdd90385b3f390c67d0885520bea38a07bb081242a8a318ba046cd7f4b87b4ede31c0c19f823ce0ab3192f36acc7683a9170411d50fa9a8a15815baf103030117065601aff6b54f4242d2a5a14e3147e89e25133ca084be363f41cb3886eed01fa8d896a609c22e099c4c9f5bb5a4363a57ad2f9b2caf0345e3ec3eccbc16af5713bbf15eaf2a17cd9f7a02966bd5e7ccd6fe185a77dfe45c4c1c9042c67fdb65d25bbd8b6f79dfdde27458a35792653443f3198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "00898cc97b5a1f95221e7272c6ea8ceb56e702d678ac6c24275a4f6147d2b27916f336255e3f8970fd79a12281e6a6bee510e7d2ce5117214f4dd8c764e00ef312580b380ddf94a3370e6842e68b260a12012ab02c883678abf3f0f37606f55c268f02e60dfc36a40bb1fa3dba22bba44953358031876c21748d5d57dfb39ef407c890d2b747cb1e0456c9c1b30c03badffb8cde540704104b016976d0a374472a3e2918076d66622d7d6014299b77850a74b2691dda1dadcd0232fbbfe2a9e02781b3ace921b11d98193720d1493609c4d47f607a2266d608185c6382b5d23519797c215563c8b73cc7f0041d1f5fe1ce017b36afbeffe56683e3ffccb9380b198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "082a5b0eb620c491dc7cfbf269ea3e9db35fdaf6c7c4baa25a829127a56f63c41c49e6c57c3c0ddca035138e5aadb79ac638f67a918e6a4377200ebfa80ed1880bf4734681e3030e126c2a595de85f80b46bd327bae8a860a3ed5f5be7318037116c5e07de19e858eb720604f6e2935bf0f5e40d9194cfba05bfea0ab69f1d82263ee7293c68743b04034a5b63e1d2b65261f7c6758d8084a50419cc65d02ad214189e61084d61cf2ad9b6140b951ca35ae18a008cb3c1c4904e59f462e66b0428ec2c3d544a901ad85af62e1ec02aac643e707a3a45983ef168877b6901a19d108e7a47061fd472bd715c35b71b842fc1914a8972e472a2a4faa4880e0d2787198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "09148ba3625b1d4862ecfed71970e395d22bc4dafaa4a662546e4fb39141d8ba0a8f563782b2b31347c9e7ced5a21d959a38f4ae4bbc7cd37b1180a0d491cad617f32b902d2096a48b25f94ad6c692a99c68e937a4bae3c820f25093cf2786ab155c57d2b7f4aefc0e1a44a78c7d59d2cac917546717c665dc001fa43cf5775317b81fe685dd2e24a14899ef74821d1d147b88fe0c63d0921eb45c191146e7e41c9ae775eba85ea225258c9bafdf5cbad6106cd79194f4b0f8e7814068370daf302ac8191cd6d55b858891d05b919ad95f5e2f16b78c5b0ad2ecc72c3fe238421a924fa7e779ad503830c25838c2489d9bbc21fd519ceb3f4a10f4dc3e3b72be198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0ee7f0252e8210eaa3ee452c8731ad6864d8506ba61836f48c99a16a03d59b0d2ba1ddc868f452ef0cfdcac410e9d38d726557b05edc84f8d4f62203acc1f9f029aa08ecade2c1d537c14baf6cf62758c02e6f3c4eeb0b621ad3ab941b1559de11eac12aaf058f799ee8146f36a0bbbf8e67a0aa0f9e2c192bafd05ff8da45d303b7bd661becfa6ff5092b36f1768c815434f3b7f4254fb4b8abf68e36086cd3196caa297895d4a1d58a5fe388416fbac2a74fb9beb835dbbbaa8f6b63bf9cab29a00816ed140fc36e515f43fb054c891b4b07f013c9e6d5c3f284c8528c71ae2f060699e8f54b1028b78e5f016d8142563947adcac5a5857137b6958ddc995e141dd828af529924148912360fd71ef6a365a707173aa2c06c8290d54fae458e1cf61d5e9ceced0c662c6e2df96d63adc7d84e182fa0f62e08b9afdd5c90545e01f3e8049b7934995dee28faa0e401289b92ccd1a2f408c85383f5312f528c9e2c6aaa06b56e9d6c98a701b4a9cf2f8cfddbfa93d7104d5eafbad9a84b5174e5", + "0f740d88de760df099674c96dd2d476b42673d0f2972e6d5d1f9dba95a29d26a04a44d72c2a82088da663ff162fe2d3ddd4a139e1ef6c0bbb1124d8de75f7c68196346d9774f017c351dbbdd99960e834b188b3347b92fac7f83cc6453617fe11e3ba24a0848096a0d6484133af97b7487ca9a4309cb5574d8bb96cfd7f22678235603772d0e9cf9503d05742962b07cd0af71a7c7b757960ced6fcfa74d3ded1ceed193f30fbbe5e9a9438e82bac927a0977b49f6e476aee20923e1a230f2c4201af9371c7cea66800f961d6a7219da9c27fbe61b5df43fccb275d8c034bf8e1f0e7c5a1143b0f8e850d9516ea2c836b0331521829dc2b71dbf775078c770f4198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "107b8a2cb6318aabd95763c0cafc3b24d8a9acdf8dd967176f05c400951b3e0613a1c9847bf87c8cc79ef1120ed6e3e98412f0dd4e9c8c8c421057a2353279e6198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa2fb5b7e464a0a76d9aca8698e73802782da01fce50384f459be1427855c0eb502e6c7af07418cd0203fad6a1abde95e745c41a78c6ad1ae7b1b2ada2e643fd37260e01b251f6f1c7e7ff4e580791dee8ea51d87a358e038b4efe30fac09383c10118c4d5b837bcc2bc89b5b398b5974e9f5944073b32078b7e231fec938883b004fc6369f7110fe3d25156c1bb9a72859cf2a04641f99ba4ee413c80da6a5fe422febda3c0c0632a56475b4214e5615e11e6dd3f96e6cea2854a87d4dacc5e55", + "117196c7ea11e6362bca58533a55de3deadc441b18b0a7ca68557f374dfed6a629b1b63f1d649480aa9655406c454b55cbbf8d29f3aedc30de3b0745bed3be52226ad93c0a164e73d8d9161f9d0206ab232fa5a08cb349f7df3633096cd04e920de4a56223ee43aa3c4a18bf4c84be1879dd9182fd4a03318a4bebd642627d1e142a0ed74ba11936e27101a43db8e16f6a603953c3ea4b14eaebbe117970268a2abced69ffbce2e34440530111050a4d72282dd5f4faa7703d5762d04327175619eedb06c5d1f53510f7f7464b39730659eccf0e9dfeec2cc131fd0d27c4f8e3043aaea131d0e69b79a816b675ac0dd21b4796046c149ca16df5884d615025c5238f967690e26443e6f5207b5c2af7fedb837d534282c7db1ca926a9bc06a7390ec4c043a606711a021205ef7d97785d9ae246ba06b2912e24ff9e669fb700b42714a2e42c4bd73a79cd7b2243d1b784cadb22a58e6360be1af873424efd420c12f54bdcf07ff30854629a0531065973197e685193051f2fa11a7761b0cc39f6", + "13b076f9e2ddf66bd1e9ec7509945ec37289d9ad65f210ed35e78ba2b02e7e3d2c6d9689097e79c4227b7b8519e4850be2d795f9496a70ae8607a3172208371a0774089b1be2375b269ac0cd913803af1e8e967c7a12ded9e38bbfe816cb6b2b25e481bf589a76e531e3cbaee9b47a07fb832b052a71029665a5ca89f1eb6d6b0f10223646f0d0f8f79189c456f40295fa02708a2629b8a88d7ae36caab470552b4bf562f83ed28edf524edc1646b4c4cd538bec5d544d8071f0c73406339b2e2043a8194e50b9ff3b93d13873035fee0ccc3b4a0fda33d34ac7a4771c9b884d291b24054553ecab7eb9a6807281a895bdcf29e95d8d3a2552749b7fc0932637198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "15d6eaaa18c5737e7bd229c2900f8deddf6da9c0e20e7e905e7b7491d041eaba2ab7d1b2a259b85054253da8947c2fd38b04a4ede3bfa6e258e22f668dcb5a63092a62b029973fcd9ec18db33eb4c7b6b649a2e6196561761789e39bc84f11ac0a59f2672462be814a277f495d53244691c40da85d39b210ed3e099b397a4cf92ad603022931e8c20c927fa114866ca26b305156336511a9224d6bd88e5ba7fb2b44dd02df7f7a846f546c77f3330cc171abeea7747ec03607c4b754a07101421f4b96b82bd3631447045f1bb66198fa6a904e48092750762efd419fb5ff52b51fac61c1a7265d0e1a6433e9767cb51c71e9ac5a119be9894f509bf92b0fb1b4198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "16f5d1473d8a9012df4efc624ae3b5fc3b1c1742bb73b9b62aa50c0dec9848ad1b2381a593f0a661db10b659329907fb62b4f5fd196092ef235f0775463b11f3054f9c54b1560a307e173c70fce534a2e4c7b248ec342f968a9db905fb31ba362513859b9c3a196e357d5d4f34e17f5cb2d78f4160103ecae86cb57a3e48ef7715e96b3ad7bfbccc491029f30be0ced0654c6c2600b49bfafc70af802b305a09154bb828c71576e1809723e3bbb5d459ece5bdccb9bcdff733761fe908e1e1d52f91cec7b5d03d4c5930875239f825c55849b352fa27b4e20581fc4a68950c752ee478820a0dc3f22866e7c5111d6fd1f057c18b7c9c1568173916ce67555c47198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "18dd52daaa11ff5dbb97c8776924d95b1bb86bf16481ba52519674873e0279ea0b32f4758cc18142794358e62b9c29951d3cb7e705d97e4cefd8422fa340ed5804cbac0707b92f59b87024017aae6941a3d8f42c6b93c619fa85cd54a3f0596325ef128bd051c44f95f7aa6122a390666691c2ec8a328f5302605f0aaae670db14a3194db0c978125b0212d2dbcf3639650e40f8acaeff5a5c20ba700de3966f004d3f0a629eb1456685db5a1b94d4b2f8dc0a9cdc5d29cccc5b596d88ba29fe0bcf53d38a1b0732fd90b73149559e0ee767f525875ebdb26f7f123136282afa28e440620ea4064d1f0190c75e2a36003f18643507a927926130eb54ecc1004d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "1b6e4577cc71df5e856ed88d2d14a464343f140b07693e3b08308570b28fd55b24198aa6ee0f5bfec020ad2ff15729434439e4af7554fa0f7395ee20cb926346246b8e8c771c3db7226a8066537632923d7d5a542f8e0d600e7f0195240f1ec513cbe706f9ba436dd4a781fab85fa2e9d82854446cf91182dcfa66eb68c4b7e72533a60b837f9cf4838c4c38f4f9c8988fee10c9895753e7925a86330e925db702f47f10f7da957cfcc613361ab6aaeb67f14d22c06eec14e47e36988c4ee06705a596bd22bbb13dc898acfdd420c88893dd09f7fd4875e8b3fb65b54ad9643f2847ab3c7d853e89cfdf520de28e1092c1955b7e17d9cba5808f047a3d6898fd2f64f057deda8bbb646d5b9864d9789a696abf2a42218f7af28baae517f5e45723bd3952d332068086b2079260b285896cb84c73ece3647094fac90d8b1374c21eebb3f8ea3c3d9147fa09e4506bcff1c222a02ea8b4904fc6df3bca1cc0505e133d9a4794eb099e9bdf82a6fecdb2e2e29b0867bf0fe557475dc758d796714e", + "1ee2af29808bc7baad8eb3e87bf55588ef59763ae59585453aa57222a60409141756d11b0679f2e3c44a95592d270f55f76670c2d248fe2c0b391a11aa90d0ec0ea16e33157b6d0f197ed197ce3ad5b93ac91458632464dd5e4aa23ec628e6cb03c160eb0d2ab47566d495f1a53b7b1cdb659cb64e26c848c74267bc74ffeec81d5ee181514337f685e60ba02daccded24ea3122fb04d9da37790f3dce54587803087c55d223005ff8ad78f6f417b19caff564e8b8cb820d0eadb6bec43f0cf902b88f6e24920bd60dbbf082df1cd200abf141b6c01e7fcb262525364c14c20502958d26f4e6ad2d9b1db8a6b23e7fa5dc4d0e4c6673b0f840c000eeac001988198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "1f357db2cd961c374f81a4ae374f5306787361c3aea270815962003c0915b5d2044c12e4c6321f0a73b09480c1d7a84ce30ee9219b3b649ff36ec7553150428f1b789c458e1e033db933f65230c01f173dc581494d23407494818cbc70c5e4eb05685001b0f48e5191bc50797f1330beb69d7dabe58b9b7d0c9c86c61c8d2a3301c8dc500074bc4207038e94f028d954405d8b6ff8b3f1b52a7ce67c97382c0b0a9c7d7844ca3f0a571b04211c021dc96a29628119b067364a06fea760698a060452283eefd9b336e591f37119aa473f03932c9c6d7cb0256c93e2bb7ebc214024a87b7afbb4143434a5aecbec2c38e02c7e01e8b056b812c80deab761bd942906b4a2cadf0c0d14e9015f1dd1833e1a71bdecfcc8d400f0c96c309dd8f3f30e243fddb630078af3593c50273c6fbc2c33f2fc6e208ad1e6dd537079f569cd5f25e6c61eb8d52bfe15205fb66a6e90da703a7cb8b441023f90ae987e35c72f800e8e708e896b7ba90163f4dd87bb6660359ca5558473831dea4aac4d222fe443", + "21b1479be2a88d421d6aa893a59fe4966322436bcd047e610f90929a5823188a2feb0baed066808be65012e37e364243877a4d47b75cd9ffe4a5df3011a68f90198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa1aa2ba0e64e80047d931df5931420de2d4dc0b7b6fc83121a9f155ddf9d6577520108e265338e3f2bdcc276bf69d955d60c73419d7ba24c25a40e9efd4cb24b80dbbd035ab913dc40917c815e66941eef6529d94ecbaaee8f77efe9aac45aab22ff4691bd3aa5f5bbb6067c1a4392dc87bb7a9997f7a660dd960df462203c18f2d4e9ee9f56967d24826884a2473b374d24b5afef1cb8d11555a2a62249b44a907a93f02c17da88bbef2e8b41a34ee1137aa7edb0459e06be0bd88ef68a876b0", + "2371e7d92e9fc444d0e11526f0752b520318c80be68bf0131704b36b7976572e2dca8f05ed5d58e0f2e13c49ae40480c0f99dfcd9268521eea6c81c6387b66c4051a93d697db02afd3dcf8414ecb906a114a2bfdb6b06c95d41798d1801b3cbd2e275fef7a0bdb0a2aea77d8ec5817e66e199b3d55bc0fa308dcdda74e85060b1c7e33c2a72d6e12a31eababad3dbc388525135628102bb64742d9e325f43410115dc41fa10b2dbf99036f252ad6f00e8876b22f02cb4738dc4413b22ea9b2df09a760ea8f9bd87dc258a949395a03f7d2500c6e72c61f570986328a096b610a148027063c072345298117eb2cb980ad79601db31cc69bba6bcbe4937ada6720198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "23e6648726d779a5e9943a9cb2c3d89cc966f0ca965bbe7bbdc1bd93df6429302a1e299a0d2c7fb74af9e086c19b07b357c55e76d2c65fba3b122c8158ee2b76071a0694c6a785ae2ff2553f4c06e2d24d031b86a4023a0d41a095b9d35ec8d30c1ab42b9a9d852e0901c708a5a0dfa9920e0ba08828620a5ebbdf1ffb8f9d851741332dcd8798b695edf6be41e0e58803ab926516d0d600514c4572575b29241a64993ddd9be2bab46a7190024de9e051862f46ac3130db54ede8c322e604a22e198a9651f4d48dc87f0ebc9fba1061d49962668790edc37948681da728a79f1649dc12a9101ad7e6c1631c1290bec29720bf5617ee6b5afa720733748bc3fe2ae647da926e44cc8e5476a15ffc62f11eafc89cb7d1d9ea1107403e1773c49a2408eaaabd36aa0a8dc6db3fb091f3c8b4df202714f74ae8d8cca11a216a301725ef6bf83c607781e9c1696e31cb5baa60b9e2deb0856479b342737592c0961211d363f2f2fc286d903a1e94c6673714464530bdb734ded5608b4654466d3f35", + "24ab69f46f3e3333027d67d51af71571141bd5652b9829157a3c5d12684619840f0e1495665bccf97d627b714e8a49e9c77c21e8d5b383ad7dde7e50040d0f622cab595b9d579f8b82e433249b83ae1d7b62d7073a4f67cb3aeb9b316988907f1326d1905ffde0c77e8ebd98257aa239b05ae76c8ec7723ec19bbc8282b0debe130502106676b537e01cc356765e91c005d6c4bd1a75f5f6d41d2556c73e56ac2dc4cb08068b4aa5f14b7f1096ab35d5c13d78319ec7e66e9f67a1ff20cbbf031459f4140b271cbc8746de9dfcb477d5b72d50ef95bec5fef4a68dd69ddfdb2e2c589584551d16a9723b5d356d1ee2066d10381555cdc739e39efca2612fc544229ab0abdb0a7d1a5f0d93fb36ce41e12a31ba52fd9e3c27bebce524ab6c4e9b00f8756832b244377d06e2d00eeb95ec8096dcfd81f4e4931b50fea23c04a2fe29605352ce973ec48d1ab2c8355643c999b70ff771946078b519c556058c3d56059a65ae6e0189d4e04a966140aa40f781a1345824a90a91bb035e12ad29af1d", + "2b5154892605b4b57c6f309a1f39d91a89d985264f9dc47342470b5605532939153954f4ac012e3d5860731081c59b0b5d9df76eeeca47c1ede99d0a73a8d1351b7962c4f91943b75bfa71d26698f33807c0a82c4aff43116e6b0e97e087d64e0337b7f3f182a6241093041180e874062c10467e0147e1abd93857dda7e6aa34153cac48663d8de0c9f23d1a2cecf0bd14f6d7cf877ef833751f8a524d0f4c1c1ff902721cd414e4c564c569143a0356d3b6cb7b1970067365c0a0f8ecb4976b0da26ca39baccc3b276b50cbd06db50b1c544055c0c636256fa35afdafee49bf2238e1b69bc5e674c9351b702c26021f8eb254e0f890595f5f22ca04b205748003d0a15ac5814b86f5463c48d5f7d398da010569d67f979b2af9381802b485fd2d3de1b5338ac2222d3d1bfca2e20f7c1a280780ba9abd54b46d42380fda599c243c33dbb5c54e9566fdda8567e059b596fba1e9afef57deb1703f27cd93ce7419779141bb51eaa0ea4f1fd39efc61aee4e6b40316248277599c599ec30c8dd4", + "2cc6c9acabc199e21be075d6d7885acfa1f38fec18999d78105c87e55a5d8eb7238e3f5edea71116b4905645df360589eed6e69ff4145a28c6ae29599ccb590e0e8248a6ccd8770c3cdbbd6d7f247c76f189978508b6f3514877833b00a7c593261d44028c3119ad2707a456739918ad10b51e7257b539a34e7f479f2e76707a2b359c070959c19eb7bb2bf1dbbb2cc060558f6dd682965535cba0f2392d13b818a023e2b4d60c38c7c0ab6af9ff0887dd192ac88b5070851f4d0e7495a1144d0ed1531303f89b56ebe6e9a39e7b7c4b5f2385d774ab00ab0b3c32c9b110552a0794c8d242f2aa6f638a597e53d8f236c780fcac4a7e2cabcb688896a84046c713532c71aeac024eea1bbccb01b45ceb4ad91e106200b7e25b0e30bc588d783520692d41373a18914e163949fa765d49dbaa5999b1d606300a469555da182b591fe664fc1dc73f67d3261d81477fd5b43e9b7d9a376a09e4a2b82f935ce0ef730abd60b3b4ca1e15eae3dd484e5e22f0f8f50dfbc756b40cb2406e6a1d86c07c", + "2e565cd418929c3795adf3ea378f2616af24a1d7a93962c5be375e7fbdfe3d731197465669e70fd0793c00fb922d25dd53c8c35d4d686985e4e04741569497d1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003a781e0964591a816d07c8d6aa23d23b2ad50ae3acc529f877abf4d13e3f35e0fc25d443ba88ff661c914498d09f6c47bd3db7f8768c13ac2602e02250c34d10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "2e5e8d67b60e1d06acd7a490f8394be4aab0eca89eac6738fff6664263500f2b23a1b85312f58351b078bfabd3ab8517e383900cc23a4d9a8fdb0a5ae691bd0023751d707cd5ababe78100ade4bcc1691182ef8a6086be7af4a0a62b52d7a5940dea3332f3c63c37814cba5afb4380c5c3ee3920ef5de5484c1961a98430c2820c10e66f5bd143c35562d6b5f1bd6d0a256f4afb69641a3d8c871f5caa98fa6a1c8f31e252726f5fb9f5f444f60b32fcf94ba5e077c9724d36246ec666d767cc1f0befe534aa028faf6181644d6774e4e14f600a5657b3e2f6287115317f678c13357844009932b0b873101c362a9ee9fea643412da278256377c345e3f409dc1db60e215fc52c5af9d1b5045473f2f06bb20399de3cde3ce877ba545b994f3327c359cd987e9f2d05f015974e1b18b98b8f9684a24b0a50441244333bf5a35a023036aeb27f556ca1e93dd351651594c723239a7c0cb15b8ab7a0446ba789ff17e4fcbe1d017009100203f39e446d7b686e30fce3323ef61d46bc956143947c", + "30618669fa4f387a7e9fcbf763bc0dc9908cf927d1f92b585ca17d46b7e97fd902acc05526844e174394b69b1e0b78d24f7b5bd0cfadcb6a33aa6dc090d9028f160c5af4494850ee7590972d0efda781e2879e76c807a16dd60ede657e0be6a8230f495774d31e8b73bf7ce73e6f87826d7a59e1c47b508d5f953a61c2b4939f0492560c98f9feab356c744c09f1ec80f5fc9cc579f9929a3c7d9046585431b7273a1175635e2ab1f1b63838c13bd9d1daf4dad2620a0e72beae5f66ab88acab0f0887388fbcfeed9d128b7324b47676549d378c55d6d28b80cc179258a2801711423de6d1555acffe946eebaee182f8d007d5bafea10c7e00236552de1c68b32b74c50fe814021736df034cfe502673b483baa8f12825b9c7f74c59943ca53516196e1e69cee3dc1a0ceebcd6163c57f0155afb41d78aac57966a2681fccd0229b85e084727db2843d66cc73e163875ae0cdf5ce9386bd6b2be7018476d460625bb91869e1c2453109ba45cda0fa3f4ba9b8186df72098e81ca6f131140d8b0", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000103aa4b2aeff1bc7afc5cd39952c872c8f29d64188e8d18c1b65e526d6277a33209854e0ac153a98eff0fd08a0c73d46dbbe4209f8a6a4f2aad914e40686f4720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ef4aac9b7954d5fc6eafae7f4f4c2a732ab05b45f8d50d102cee4973f36eb2c23db7d30c99e0a2a7f3bb5cd1f04635aaea58732b58887df93d9239c28230d282bd99d31a5054f2556d226f2e5ef0e075423d8604178b2e2c08006311caee54f0f11afb0c6073d12d21b13f4f78210e8ca9a66729206d3fcc2c1b04824c425f200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "06236320cf783a01e2b8be7816a84671de9e8bebda0e8f957809ee1253565bae0a4ce1ac405b9974e4eaf9342c2a43bd1fdc4edc2bd18235249bf51ec43287440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "24ab69f46f3e3333027d67d51af71571141bd5652b9829157a3c5d12684619840f0e1495665bccf97d627b714e8a49e9c77c21e8d5b383ad7dde7e50040d0f622cab595b9d579f8b82e433249b83ae1d7b62d7073a4f67cb3aeb9b316988907f1326d1905ffde0c77e8ebd98257aa239b05ae76c8ec7723ec19bbc8282b0debe130502106676b537e01cc356765e91c005d6c4bd1a75f5f6d41d2556c73e56ac2dc4cb08068b4aa5f14b7f1096ab35d5c13d78319ec7e66e9f67a1ff20cbbf031459f4140b271cbc8746de9dfcb477d5b72d50ef95bec5fef4a68dd69ddfdb2e2c589584551d16a9723b5d356d1ee2066d10381555cdc739e39efca2612fc544229ab0abdb0a7d1a5f0d93fb36ce41e12a31ba52fd9e3c27bebce524ab6c4e9b00f8756832b244377d06e2d00eeb95ec8096dcfd81f4e4931b50fea23c04a2fe29605352ce973ec48d1ab2c8355643c999b70ff771946078b519c556058c3d56059a65ae6e0189d4e04a966140aa40f781a1345824a90a91bb035e12ad29af1d1459f4140b271cbc8746de9dfcb477d5b72d50ef95bec5fef4a68dd69ddfdb2e2c589584551d16a9723b5d356d1ee2066d10381555cdc739e39efca2612fc544229ab0abdb0a7d1a5f0d93fb36ce41e12a31ba52fd9e3c27bebce524ab6c4e9b00f8756832b244377d06e2d00eeb95ec8096dcfd81f4e4931b50fea23c04a2fe29605352ce973ec48d1ab2c8355643c999b70ff771946078b519c556058c3d56059a65ae6e0189d4e04a966140aa40f781a1345824a90a91bb035e12ad29af1d24ab69f46f3e3333027d67d51af71571141bd5652b9829157a3c5d12684619840f0e1495665bccf97d627b714e8a49e9c77c21e8d5b383ad7dde7e50040d0f622cab595b9d579f8b82e433249b83ae1d7b62d7073a4f67cb3aeb9b316988907f1326d1905ffde0c77e8ebd98257aa239b05ae76c8ec7723ec19bbc8282b0debe130502106676b537e01cc356765e91c005d6c4bd1a75f5f6d41d2556c73e56ac2dc4cb08068b4aa5f14b7f1096ab35d5c13d78319ec7e66e9f67a1ff20cbbf03", + "1147057b17237df94a3186435acf66924e1d382b8c935fdd493ceb38c38def7303cd046286139915160357ce5b29b9ea28bfb781b71734455d20ef1a64be76ca0daa7cc4983cf74c94607519df747f61e317307c449bafb6923f6d6a65299a7e1d48db8f275830859fd61370addbc5d5ef3f0ce7491d16918e065f7e3727439d1ca8ac2f4a0f540e5505edbe1d15d13899a2a0dfccb012d068134ac66edec6252162c315417d1d12c9d7028c5619015391003a9006d4d8979784c7af2c4537a30d221a19ca86dafa8cb804daff78fd3d1bed30aa32e7d4029b1aa69afda2d750018628c766a98de1d0cca887a6d90303e68a7729490f25f937b76b57624ba0be14550ccf7139312da6fa9eb1259c6365b0bd688a27473ccb42bc5cd6f14c8abd165f8721ee9f614382c8c7edb103c941d3a55c1849c9787f34317777d5d9365b0d19da7439edb573a1b3e357faade63d5d68b6031771fd911459b7ab0bda9d3f25a50a44d10c99c5f107e3b3874f717873cb2d4674699a468204df27c0c50a9a0d7136c59b907615e1b45cf730fbfd6cf38b7e126e85e52be804620a23ace4fb03e80c29d24ed5cc407329ae093bb1be00f9e3c9332f532bc3658937110d76072129813bd7247065ac58eac42c81e874044e199f48c12aa749a9fe6bb6e4bddc1b72b9ab4579283e62445555d5b2921424213d09a776152361c46988b82be8a7111bc8198f932e379b8f9825f01af0f5e5cacbf8bfe274bf674f6eaa6e338e04259f58d438fd6391e158c991e155966218e6a432703a84068a325439657498571ba47a91d487cce77aa78390a295df54d9351637d67810c400415fb374278e3f24318bbc05a4e4d779b9498075841c360c6973c1c51dea254281829bbc9aef33198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa1e219772c16eee72450bbf43e9cadae7bf6b2e6ae6637cfeb1d1e8965287acfb0347e7bf4245debd3d00b6f51d2d50fd718e6769352f4fe1db0efe492fed2fc324fdcc7d4ed0953e3dad500c7ef9836fc61ded44ba454ec76f0a6d0687f4c1b4282b18f7e59c1db4852e622919b2ce9aa5980ca883eac312049c19a3deb79f6d0c9d6ce303b7811dd7ea506c8fa124837405bd209b8731bda79a66eb7206277b1ac5dac62d2332faa8069faca3b0d27fcdf95d8c8bafc9074ee72b5c1f33aa70", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + ]; + positive_inputs.forEach(async (input) => { + const returnData = callFallback(ecPairing, input); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000001"); + }); + }); + + it("ecpairing_fuzz_negative", async () => { + const negative_inputs = [ + "142c9123c08a0d7f66d95f3ad637a06b95700bc525073b75610884ef45416e1610104c796f40bfeef3588e996c040d2a88c0b4b85afd2578327b99413c6fe820198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed275dc4a288d1afb3cbb1ac09187524c7db36395df7be3b99e673b13a075a65ec1d9befcd05a5323e6da4d435f3b617cdb3af83285c2df711ef39c01571827f9d", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000092a62b029973fcd9ec18db33eb4c7b6b649a2e6196561761789e39bc84f11ac0a59f2672462be814a277f495d53244691c40da85d39b210ed3e099b397a4cf92ad603022931e8c20c927fa114866ca26b305156336511a9224d6bd88e5ba7fb2b44dd02df7f7a846f546c77f3330cc171abeea7747ec03607c4b754a07101421f4b96b82bd3631447045f1bb66198fa6a904e48092750762efd419fb5ff52b51fac61c1a7265d0e1a6433e9767cb51c71e9ac5a119be9894f509bf92b0fb1b4198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "104f6d8507d6a112e8fd516d70bfe3d2474539948276d36eee987be127a9e3ab199eef24146007e80386f391edc886f5ddba22b5cf131efa3b6ae71a673fc88e198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa2642f046ad222f094c9282816131317b8d52fc0fee64f26898232432d34fdae605a5f2a2e8f15ba27e90f54faff78391b307b402a0ae995d1c12f92c0b574593198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "1f0ab114f50077d71a195869dd8c07e2a031a55a8dac07c522ee7ac74136c50c229c9d498f5ca70a27612a244743773fe22668370a91410b645fe22f2e6eb78c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000284869d750e478e4b09637f7ae55bcce23bd622f04e986e1cba0f64b59b023200a88bc2898a2930aee2b92055ca2f97360a79c598f5e8998fed7add43a692d1021f4f3ddd5250962baaefc6ef587f464c8fdad1bb7812b292dce48d5462039df14044ca96015f3c6666f98d756fbf7bd0e12c3e884db4100391da8a47deff16e00b30c250f71db58eb20e14d67cd98ebb47790a7e7d45d30a90ce707175f295b1e45eca08667a7cfe06158fae4a38cac01a37853e730eaa28b2f7167fd74c22d", + "0624d2c76767981fadc8f74f29c034f7ae0522585e1e0201308109c40d1d5420251ba3c545339bbe22da0fe90692a099384c435ec0902c99be7e7a84d62bb49c0c0b524584214b17414a25236e4e2b6f4bf398ec62e9abeb7ea1f158d89df05b0e05f600bf1408ab25c0f3dbb98ff26c4d85a6ce6a63bebc3b2e360de77cf6062488eb90497d64621124420825abd353268fe00bd90726991d0ce623e59da9c62340b1fb006017382cb76606a873f48de8f31434cc243c8f465cb908d8cc734618cde0d7bde7e1da88fb8192ce94bea754f24a53f1f000f15b4c5c151c12df4f0066b16bf4229edf5375b78a5c7469ddf68f7d5c7577642ed2c90d5d42c231ed198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa2948734db775bb41a29257214b19a6356e6640894c2a48f201addce62de7b41e1bb87bddbe46d75809751232405928c3314934082052131a64574ff5a59c7db7198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa09c91a687e6c44f2917682dc5d0a34be7c77fff361aa36610effab6f6d2d87ac13849277ed76b58020ab993f1847ebcc0f1bcf711fba15f4a7a80ab72b4fe9642f0c63d0c53b3dfbca27b6b43ae7fbf55a38d78a21470996485b03128accc20800556502356e37ed150db2e36531b0f275fd6835c0fc1945922e270b48c48a8602644c27b5dbd793592a70b735e22c798a5e309fa17a992a7dc2a050e01b298f194776b6a53439d7336f389d2a8f6651e40885f5ca2538b0dc9cb534fb23f7fa1e379e19dbffba1edf0a04505f67229e707a4b82f19285d3c186964e83e1553608ee58baa16641b1513f007da7d3cf58b503b80f73c7d8e792db63def167e0d82b1f95d6aa6fd6f5e6978d0135c206079770d5c3ba001967c5d94c5902f95fe9264ad64952cb1d30a7230bca35b3907b2457abdcd6052e14e2a132d76822222514f79ce35164acfdb802551e02a9aebdb20bf4248c0ccf5c6cf5e576908778410f8dfb99aaa272bbaa49012da81dfafa1a3c07eba89355602143c58f63df2939117a8203a67ab052af18032434b61fe6b33e0a6ad5ab70d57aef4444478b42ba123a80e91f77c135f657e2dbf5af0d07e9a4fb63585b88e5439ddab7741c0961198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + ]; + negative_inputs.forEach(async (input) => { + const returnData = callFallback(ecPairing, input); + await expect(returnData).to.be.equal("0x0000000000000000000000000000000000000000000000000000000000000000"); + }); + }); + + it("ecpairing_fuzz_invalid_g1_point", async () => { + const invalid_inputs = [ + "00000000000000000000000000000000000000000000000000000000000000000000000000be00be00bebebebebebe00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000ffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca71ca8d3c16d87cfd450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000ffff7d7d7d7d7d7d7d7d7d7d7d7d30644e72e131a0297d7d7d7dffffffffff00000000000000000000000000000000000000000000000000000000ff7d7d7d7d7d817d7d7d7d7dffffffffffa100000000000000ffffffffffffffffffffffffffff7d7d7d7d7d7d7d7d7d7d7d7d30644e72e131a0297d7d7d7dffffffffff00000000000000000000000000000000000000000000000000000000ff7d7d7d7d7d817d7d7d7d7d827d7d7d7d7d7d", + "0000000000002900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000fffffffffdfffffe2e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "30644e72e131a029b85045ac81ec585dffffffffffffffffffffffffffffffffffff7d7dffff7d817f827d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d767d7d7d7d7d7d797d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7f7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d8d7d7d7d7d7d7d7dffffffffffffffffffffff01ffffffffffffffffffffff747d7d7d7d7d7d7d7d7dfd7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7d7dffffffffffffffffffffffff29ffffff0affffff0a", + "30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f00000010000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47000000000000000000000000000000000000000000000000000000000000000000000000ffffff000060bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad0c693395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "25a78fa05de3e5f7c69f35ab209d6595697e8664c3572a57ea0c971fe33532ed0bc38b0a2d9961cf8d392de63be18471ffaaa192111cd8adccc98b7d790b61140000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008013e823575500fffffffffffffffa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "2dec711c75595613e8f7e4723c19f6e69be2ebafe07e965a001f4fa00a41eecc10246180d145035dfe0e334a8e1f4274a189b8dde0b2cc683cddfd9cae9b634b198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff0f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "30644e72e131a029b85045b68181585d97816a916871ca8d3c208d16d87cfd4700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "0a12d3fb2743836bbbb51414a351e5e70429a5de70c0fe7cec084e47d6027709006a8c414196abf21da0b3f6944846c77a1032b519baa1abf125f4f84010c47a250f9cf43675bc1077753c607600f3e51b627a10f3aa68a7e462d89a6bd2a21312ae5d695c4f9792cf70228a1ba07e5e0c2cb47d7aecbae923a84a3734a94ff10bdcd3d0b8e47a925f98bad0184dfe81967aaff8db8f0dfae31afccbcb8c4bd6148dff646f2764243ba9100a930eb7cc8c766b58e0d9953256698da5dbe66cc31f372b78747db898121455853a5672e71977957f134615fd0dd1fab4938b65e7201458c7d8ec49141bd3289f8cc4d19bb52041d51187432579e2e67cab27c847198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa2110e6d9f2378c1c1cd070a3f1507c3aa924a60f67259abe487621b0d3c5c38f0c26130b8aaa54109a5d82fbb2782b9ed461a4b8faa69341ccf652d2f73e188722f1acbb03c4508760c2430af35865e7cdf9f3eb1224504fdcc3708ddb954a482a344fad01c2ed0ed73142ae1752429eaea515c6f3f6b941103cc21c2308e1cb159f15b842ba9c8449aa3268f981010d4c7142e5193473d80b464e964845c3f80efd30ac7b6f8d0d3ccbc2207587c2acbad1532dc0293f0d034cf8258cd428b300710c68e1b8b73a72a289422d2b6f841cc56fe8c51105021c56ae30c3ae1aca0b2ff392a2fc535427ec9b7e1ae1c35a7961986788cf648349190dd92e182f05198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daabebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebefdbebebebeabc689bebebebe43be92be5fbebebebebebebebebebebebebebebebebebebebebebebe9ebebebe2abebebebebebebebebebebebebebebebebebe", + ]; + invalid_inputs.forEach(async (input) => { + const call = callFallback(ecPairing, input); + await expect(call).to.be.reverted; + }); + }); + + it("ecpairing_fuzz_invalid_g2_point", async () => { + const invalid_inputs = [ + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffff0000000000000000ffffffffffffffffffff", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d00e9ff0000000000000000ffffffffffffffffbfffffffffffffffffffa120000000000000fffffffffff7ffffffffffff000000000000000000000000000000000000000000000000000000002d0002ff0000000000000000ffffffffffffffffbfffffffffffffffffffa120000000000000ff007d7d7d7d7d7d7d7d7d7d", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a0294fafba497e1ec6d18c48814a69000000010000000030000000cd71ec682d26313681ea8a1a9a410c862cc44a5d0000000000000000000001158d600a2d8411f2e9bd1a1b51eac64e43b0c511f2e9bd1a1b51eac64e43b0c511fc9ba8b80b727a2c28ee454fc286fd659262c510a3e7f11a4b0e4b74bebafc", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d00e9ff0000000000000000ffffffffffffffffbfb2ffffffffffffffffa120000000000000fffffffffff7ffffffffffff000000000000000000000000000000000000000000000000000000002d0002ff0000000000000000ffffffffffffffffbfffffffffffffffffffa120000000000000ff007d7d7d7d7d7d7d7d7d7d", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f8ffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffa10000000000ffffffffffffffffffa100000000000000000000000000000000000000000000000000000000000000000016c46ebe0077418d002f28e20236919ad92313729f18578ba8547626478ea52c2b5b688b4d8078d1e1acd7acc7be7f9e0e30812ce2925b35559213646c93237f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000fb55bf7df894a746fbe20b6f8c54d0dc0d9fd4ed005174a42fc3c45e6e27f912a20d63d446eb175733853b88dd36708eb7a81f5c79e7659c3a6e2b2c470077005ed60ba723a2dd3a5fc35520f982963de61e3b563636fe6996cc2c3008035720ad16f835a46faca48c6d39ae00a10d3514e93ae3b946ee2f009ea2dccff97e198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed09068950585ff0759e99ecad6903000000000000000000000000000000002c000000000000000000000085b7aef328c21800deef5e0000aa426a00665e0000aa", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a83000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8329a0d6d5e7d14a774c3abff1435361da2ee5d8b4f3ee62085ce779f248b41d4a2fd37ae5468f6a17b7f9a0bcca02ee128bdced61402a566e4eee2d0fa825f03d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47000000000000004500000000000000000000000000000000000000000000000107b55de1642362d16b8fc4dccfec9e794d24968511cf8e252ed27afc4d72ca1e0301cb4d7e0b7b52a6a8f78613c403ead1543fedb0d3f28c77685fdb9eaa27420cdc335fa81dff303b71cffb0256d7097c2b4c6715ca7b1589ea9386a41c2f851223d6bb1f3e68ddfb08f4c95f590f99227fb681a3b0abdf685cf12bde37d8ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000001f1cd7f247f2ae1ba9a1aeb4b32ed4c8c13c70c8861b6ab5340e276c58e1046b13d7de2b557e0ae2b53380ad596ba79f07c037c9d9aa17cf407e9ed86201436e", + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed95bc4b313370b38ef355acdadcd122975b4b313370b38ef355acdadcd122975b120000c8db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1", + "2a4f1dbc9fe6d2882462fb11afeae7b7f2a0cf213b1f19c45cb222336283f3801141763c897c9a90e387ea80f68eb88c2d79aab680196d9538cd2ca632a5e81b0e540f5b5be91f82ed05349750761224f543068ce30d2d5e838bf66866f345200fa5ac8c46f725e54505019bda3356ef6e35b0a89b9b4f79bb0c62211235c9312e6d97a1f7e0428fc0be6be02c811095f5166710dcbe869c36a8ef89cac63e012657bbaf3bcfd106dd677eef03172f693a6c919776f441dc0fd47a4bd91d0487225fdab8fe6cd876363d27075cdf0d01c209da61b1634b574a5d811cfae407001216b7c3e2adc07c3bef31771c7bb9e1d02f07ff3a5b74953c4fd5bf9a7a6dff25f63fcc543337b8f6275f97d6479633b921541a96ac1bc2aff2e0905db7407c206776f9480168741eca625c06e5526b4664a02ce664bc656f39664d96278b6e1e40e8084fd648ba315f691e8367be1d4c13844421c87223d84829c31a0d7afe24b8042d4cb604ae66c0dc97ca8a9c2d22c743335d92bc401700f6b00d5cdc5b", + "2eca0c7238bf16e83e7a1e6c5d49540685ff51380f309842a98561558019fc0203d3260361bb8451de5ff5ecd17f010ff22f5c31cdf184e9020b06fa5997db841213d2149b006137fcfb23036606f848d638d576a120ca981b5b1a5f9300b3ee2276cf730cf493cd95d64677bbb75fc42db72513a4c1e387b476d056f80aa75f21ee6226d31426322afcda621464d0611d226783262e21bb3bc86b537e986237096df1f82dff337dd5972e32a8ad43e28a78a96a823ef1cd4debe12b6552ea5f06967a1237ebfeca9aaae0d6d0bab8e28c198c5a339ef8a2407e31cdac516db922160fa257a5fd5b280642ff47b65eca77e626cb685c84fa6d3b6882a283ddd1198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c2e8ff2110ede0e189426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa0f25929bcb43d5a57391564615c9e70a992b10eafa4db109709649cf48c50dd216da2f5cb6be7a0aa72c440c53c9bbdfec6c36c7d515536431b3a865468acbba2e89718ad33c8bed92e210e81d1853435399a271913a6520736a4729cf0d51eb01a9e2ffa2e92599b68e44de5bcf354fa2642bd4f26b259daa6f7ce3ed57aeb314a9a87b789a58af499b314e13c3d65bede56c07ea2d418d6874857b70763713178fb49a2d6cd347dc58973ff49613a20757d0fcc22079f9abd10c3baee2459000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000b4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + ]; + invalid_inputs.forEach(async (input) => { + const call = callFallback(ecPairing, input); + await expect(call).to.be.reverted; + }); + }); + + it("ecpairing_fuzz_invalid_g2_subgroup", async () => { + const invalid_inputs = [ + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000070a77c19a07df2e666ea36f7879462c0a78ebbdf5c70b3dd35d438dc58f0d9d0a2dd5b476a606a8243e7e879bddaa8086ba658087aacc4d986a10c74dd9e7742d46618f9d516e0f07a59d3c97f5e167a1b49ebe9fb30dd05bded8185a545420", + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb43", + "0a96696a78e6818da746c504a8b83f7a3faea0dfee0a2c52751f3cfae5fe979b017a319dce2d1976671eddd5b909781c102e5eed5c40db00bdbd19a14397b889198e9393920daef312c20b9f1099ecefa8b45575d349b0a6f04c16d0d58af9001800deef121f1e76426a00665e5c4479674322d4f75edaddde46bd5cd992f6ed05a7a5759338c23ca603c1c4adf979e004c2f3e3c5bad6f07693c59a85d600a922376289c558493c1d6cc413a5f07dcb54526a964e4e687b65a881aa9752faa2", + "111f95e1632a3624dd29bbc012e6462b7836eb9c80e281b9381e103aebe632372b38b76d492b3af692eb99d03cd8dcfd8a8c3a6e4a161037c42f542af5564c41198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b924933879", + "17956b549321b92c524d191a62b90c81f08e694af4dceb00a80094e53120d39e2fd15d78815388959caa54affa2d7b2c8d52c528e6a1f6c0a50fcf0498fac7ee30644e72e131a0290000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000016b9e28a32ee61a7ad5bd4bad29d64fc35a8fb46b510be2067907069497ae1f0c3c07c40f41a731fe27d953f1154b4e9a8acf3ef44608817150de0df916503a", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800104e75a20b641566a0c71c9069a5256391aa31e22021d36c037c108dfb79c66200bf257ae3d66a589214f980a2ae34f9544be2fcbcc13b21f4c1642f31aa4d200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800104e75a20b641566a0c71c9069a5256391aa31e22021d36c037c108dfb79c66200bf257ae3d66a589214f980a2ae34f9544be2fcbcc13b21f4c1642f31aa4d20", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008000000000000000000012ab9bb0c853fb1d884197cdefdf654c01a289b677094fe609c835d2b249dcc51460243cc357281001b8b257c6396865c729761ac575a85b2f8f0e58af439c84000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000fb1b0e8c8b85ec99ebedae0880006b0809f5151890cca524eea2c0b3ad87f3229d3e6bc7f0886b75e186b10564dc990258855e0311e3e2e72f9d9c0dfab4f0d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000000000000000000000fb1b0e8c8b85ec99ebedae0880006b0809f5151890cca524eea2c0b3ad87f3229d3e6bc7f0886b75e186b10564dc990258855e0311e3e2e72f9d9c0dfab4f0d", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000114cc34c5044c87540b942287d555ce935499ce155202081ca5ce495c356896009fd9490a6f214b7729f9574d53fce82a2d46b3ccf7499ab5616b2c4f36582d7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000192b7e3a0ca8b63592989fe8b2589465703315272bc730644e72e131a029b85045b68181585d00001b86b77538000000000100000000128a694e7017ae1db6a312c9ef648b1a4910a41e684cb554302044a2065f04680df2d76a91278279cf401d431c31876ee9c8ad35070694552ccbd368755413830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000192b7e3a0ca8b63592989fe8b2589465703315272bc730644e72e131a029b85045b68181585d00001b86b77538000000000100000000128a694e7017ae1db6a312c9ef648b1a4910a41e684cb554302044a2065f04680df2d76a91278279cf401d431c31876ee9c8ad35070694552ccbd36875541383", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005b000000000045000000002a00000000008000000000000000000130644e72e131a029b65045b68181585d2b0d3025c6abdbaa75ef4d163a7cfd4017c66e1806ece8d631d792f8cbd8bdf7514a9058c183a3b2fcda7e86c44b7752301a6a722cdc812165b619f3cd2b21250d7f7083d0b984fdaf8636202e4ae0470000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d2b0d3025c6ab2eb4fbef4d16d87cfd4000000000005b004500000000000000002a000000000000000000000000000001109beddbaa84026c8f71d34a485af27fe418028129b55b0a0df51e3dba2310021aea54403ce01876467152aa79ca1f227b6b1e0f95b56cb0537583190252556d", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000fb55bf7df894a746fbe20b6f8c54d5dc0d9fd4eda4742fc35100c45e6e27f912a20d63d446eb175733853b88dd36708eb7a81f5c79e7659c3a6e2b2c470077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00000000000000000000000000000000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000fb55bf7df894a746fbe20b6f8c549feddc0dd0d4005174a42fc3c45e6e27f912a20d63d446eb175733853b88dd36708eb7a81f5c79e7659c3a6e2b2c470077", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ea1146af1a0c4df0000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015d42e5a1c18703100bef65a743007bead1fc389b7d8f0a8ccda34b7d30620cb1c4156d243211b733af4f4fff11b9c0928308bf3c3102415e3105b986166d0cd00000000000000000000000000000000000000000000f8ffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000249409822e0000000000000000000000000000000000000001154baf30644e72e131a029b85045b66ac4175696816a916871ca8d3c208c161a46c432126f44fab27034ba2832173059ebf31a96607ada20f006b4f12d4a8bad410d8f02eee77897359cfc599b6ca4e8c2a47f641fc0cd6d4146d8eca263a85c7aa36500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b92493387900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b924933879", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb43000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb43", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb431a3cf0c503b2dbc6e1b0f8aea605de20d8612fe14627c6f4d3706f8272a1cf842a0bb098018ca4392a1ddc08b9605602aed954308944ac729e2f99bfdd0166dd0000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000024b364d16400886c82900af060fd117cb2f4e0b93590cbf6ec34ed97f4c391ec26a10e3233aeff776d578de9178a9b7bb919e6eb6cf2cf924eb16ec8dc659af9", + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b05040ff00000000000000000000000000000000000000002e00d573b028b22b9d347da8044a53c94b6c3aee7d9bfb00ab2c0a10eb4775e619e1a75be1c5d39a105504a0e5d16c949524742dde6bbb6e831668fef921a1430000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b050ffffffff585d97816a86b77537dafbef4d16d87cfd4500000000005b00450000f400000000002a00647d903b504c1a020000000000012db3d49da56012cf4f73cbf19fdb59ef41356cebb4f4b6f692f9a38fb282a9ff2d96db5d24dcea418a44feae6052fbc7788e65e521b17232666818386252176a", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000107b55de1642362d16b8f25dccf30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d800000009eae594f627b7ccf496d680f0266e7364f51d2d99ccd69801f29137d889e1e006ea1409d3f9bb38bd40e8da0701d122e059a02fc3c3c4a99a8cbf6e4290bdc0000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd450000000000000000000000000000000000000107b55de1642362d16b8f25dccf30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d800000009eae594f627b7ccf496d680f0266e7364f51d2d99ccd69801f29137d889e1e006ea1409d3f9bb38bd40e8da0701d122e059a02fc3c3c4a99a8cbf6e4290bdc0", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000005b00450000000000da76b36b83c72830000000000000000000000830644e72e131a029b85045b68181585d97816a916871ca8d104d5b2baf5734631b8765e7f5163862fcd3bc29f751af42d869a0c8a329f5a53472d3c977038bb7068cd6bc298bd1ec695a49e1db0620332f26e03a4a0ed9076973c1fea43099f2000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000005b00450000fc00000000002a00647d903b504c1a0200000000000130644e72e131a029b85045b68181585d97816a86b77537dafbef4d16d87cfd450125b8edede9e4f320e4dbfd7ee46d23a82508c3b44dbb99cb29724daaa5c5602d663cc48c6d7469cc9df0e861f3a9b93239af8001f8ecf28b4572f201ef1369000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a86b77538dafbef4d16d87cfd4500000000005b004500000000000000002a00647d903b504c1a02000000000001247f1f6fbf2fe1fff7e28d816724080774979f57692c2fa8569ea1a3ce52dfc51f5c96f77d84ae24b701a32ff1b8d95b982c76bf5ab02e605c9ba4fa9f45b958", + "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002012cd187faae3648a35e12cc197ddb26d9d3a44a625d97816a86b77537dafbef30644e72e131a029b050ffffffff5802086325c1129ad4a34ebb48ee9634016008aef33d9388c8f993f0af340e047c1c9519d222062186f7b025291b6f6f4ef2293f1064b0e3345895dc4968bc8bb52fcbe960621c1f15cd7c693c7347078e32000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45012cd187faae3648a35e12cc197ddb26d9d3a44a625d97816a86b77537dafbef30644e72e131a029b050ffffffff5802086325c1129ad4a34ebb48ee9634016008aef33d9388c8f993f0af340e047c1c9519d222062186f7b025291b6f6f4ef2293f1064b0e3345895dc4968bc8bb52fcbe960621c1f15cd7c693c7347078e32", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b05045b68181585d97816a86b77537dafbef4d16d87cfd4500000000005b00450000fc00000000002a00647d903b504c1a020000000000012c32d7465b0607cfaebe0b13577b25e42fe6d9e49eeb9d5c0ca78cc17e114cbc2cd71c8288ebbd272a784c5b4921128cb68e9e2ddaaec0595b266126c4e526bd000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b05045b68181585d97816a86b77537dafbef4d16d87cfd4500000000005b00450000fc00000000002a00647d903b504c1a020000000000012c32d7465b0607cfaebe0b13577b25e42fe6d9e49eeb9d5c0ca78cc17e114cbc2cd71c8288ebbd272a784c5b4921128cb68e9e2ddaaec0595b266126c4e526bd", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d97816a68917120ca8d3c8c16d87cfd4500000000005b004500000000000000000000000000000000000000004000000111ee2be185fb562c979eeb452df376f91c7852bfb8a6d5727024444a2fc58c152825d78e66be7952503e9e1bf2d8aad4c39f4ce4095349043106508bab20d6e5000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a68917120ca8d3c8c16d87cfd4500000000005b004500000000000000000000000000000000000000004000000111ee2be185fb562c979eeb452df376f91c7852bfb8a6d5727024444a2fc58c152825d78e66be7952503e9e1bf2d8aad4c39f4ce4095349043106508bab20d6e5", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d97816a86b77538dafbef4d16d87cfd4500000000005b004500000000000000002a000000000000000000000000000001124ca5dc43ed1e22d1c616b50a7d4249a012c1d65330ae8e4cad70567c8231bb23f9415fa570e58e0ac61e4e6e7a6bd02a564f6b206a9d4645401b1d677bf3ab000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a86b77538dafbef4d16d87cfd4500000000005b004500000000000000002a000000000000000000000000000001124ca5dc43ed1e22d1c616b50a7d4249a012c1d65330ae8e4cad70567c8231bb23f9415fa570e58e0ac61e4e6e7a6bd02a564f6b206a9d4645401b1d677bf3ab", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d97816a86b77538dafbef4d16d87cfd4500000000005b004500000000000000002a00647d903b504c1a02000000000001247f1f6fbf2fe1fff7e28d816724080774979f57692c2fa8569ea1a3ce52dfc51f5c96f77d84ae24b701a32ff1b8d95b982c76bf5ab02e605c9ba4fa9f45b958000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a86b77538dafbef4d16d87cfd4500000000005b004500000000000000002a00647d903b504c1a02000000000001247f1f6fbf2fe1fff7e28d816724080774979f57692c2fa8569ea1a3ce52dfc51f5c96f77d84ae24b701a32ff1b8d95b982c76bf5ab02e605c9ba4fa9f45b958", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d97816a916871ca8d30140b193f92396c00000000005b0045000000000000000000000000000000080000000000000001301a8b8043356b83c4341d5e9cfc67071c61c0b053ec979b59ad8c0455b71c9815f88e8c709168c1ab898ca5288423b585331497f16b2c074dcbf76c2480e8c9000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d30140b193f92396c00000000005b0045000000000000000000000000000000080000000000000001301a8b8043356b83c4341d5e9cfc67071c61c0b053ec979b59ad8c0455b71c9815f88e8c709168c1ab898ca5288423b585331497f16b2c074dcbf76c2480e8c9", + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd452b0d3f819fd0046aee4d0e54cc759ad714c473bd5f86a0d68df977f7450390f730644e72e131a029b85045ae0000000000000107b55de1642362d16b8fc4dccf2a5c3a27c4a417ea34f627b9481b9004e3b0ef59619daa16e846d8a152d6bf771301a8bb9c71966a4b557a21eea89447294e8a9cc1308f0c8eecb16dfbf5d53b000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45000000000000004500000000000000000000000000000000000000000000000107b55de1642362d16b8fc4dccfec9e794d24968511cf8e252ed27afc4d72ca1e0301cb4d7e0b7b52a6a8f78613c403ead1543fedb0d3f28c77685fdb9eaa2742", + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd0000000000005b0045000000000000000000000000000000000000000000000001112006831f3f0e121585c21cdf0aaf63f09728bcfee141bb4cd407caae7715e319df1e217ad7b1f874726c6683ab87b423c1737f46a54dc5356a9b617c6b9e28000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1", + "003db26a0b244fb3fd47f7f5f0f8a289e088fd932d340f0592696503fc62124824c9b9423ef04336563adc35604ba729a4c9e6104bfcb55517e3198b8edbdf981800deef121f1e76426a00665e5c4479674322d4f75edaddde46bd5cd992f6ed198e9393920d483a7260bfb731dd5d25f1aa493335a9e71297e485b7aef312c20b9f1099ecefa8b45575d349b0a6f04c16d0d58af9007f2c6d8bd7aa763a3b0e0b7c77862fe8d10d489a493a1a5c5d0f282c7d4e8148f340653c4b6297a1088f003db26a0b244fb3fd47f7f5f0f8a289e088fd932d340f0592696503fc62124824c9b9423ef04336563adc35604ba729a4c9e6104bfcb55517e3198b8edbdf98198e9393920d483a7260bfb731dd5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed2ba3e0cab22efbfef96e027f1a29ba7ec11a0f824024a3787d3cb8370b9eed71103ce48dac2e3ff691b61c7e4466adf35aa183aade7f15386bf7c789628b1b0e", + "0d1e4322f03fa0515f8ed6cb02b15e5a024b5092003dd7792d2ac055f14ac67d155c53a1daa56e3a5e9bd097cebfcd44038f2c606c8951310aae0ea01a44725300000000000000279573fbfe8e6c2300000000000030640472e131a029b85045255495ffba02a0ed6877a0a6ed4684235053c04cc3aaa05934e35bbb953673af0e1cec4c9d6d3921992e4c5c71523f5d097cde90f25b3e6c52c9c63e764a67870fe0cfe24e0b2f78d4f909c9131e3794fa911b1109df4a567b1423475cf409c1000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4700000000000000000000000000000000000000fcff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000212a3b4059e59d125dd17f662945170e1e13024fae97a401895690acac63cf681aecb67409d1d9c142ffd13375bd79fd0457deb278dfa91b5756378fb837fd60", + "0e474956b0fbc11ba65f3c7632afb3c66a98340871cee7c974db4461d8791b8d25410e545457af7bb0e9d2f1f8126180f11dec9bfcf69cd2934e6814741c9aa2231e75fddf7e2fa8b38b7dd6fd13eb4b7046000000000000000000000200650000000000000000000000000000000000000000000000002546ac8393e836a2970d312fc0ac642c207b5b36e17990b4f7d0ef39cfe6f0d46b9c556a0b7935714b1e4cd9fd726048138dcdea456203d4b7414f3ba5ef7b37817aa98874d71c191f2e5efcb149c6a4914b8d85be71e79254f9fdf19f62de2b2f6cc0822e578a420f07fd874051cb395ca999e596047a25fc6bc12cba97dab578f9f903612c1c757f198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "102c9afaa6ac9d2553d0f47e2a945816f56b10014ddcc00ef63f5cda3d1431020db583622c614b32310911623e2f71f7871413d13dc372ace97f1813d2622d590a0e124cd6005b33c8a16492b1b451897b1679f44f3d288585ec8f82b49f6f501579cd1aa3fb1bf108c6b7b5d1f4ae1fae44099a8bf5fddc74d87665e29812ba0d5db514ca1c318cc409bcbf9a75055b1a8e7b783127734d902b6118b518ee962f24a2abda945e843c6952a5f0632365146cfd4e2034b70e41a78772fc404bea102c9afaa6ac9d2553d0f47e2a945816f56b10014ddcc00ef63f5cda3d14310222aecb10b4d054f7874734544351e666106d56c02aae57e052a17403061acfee0a0e124cd6005b33c8a16492b1b451897b1679f44f3d288585ec8f82b49f6f501579cd1aa3fb1bf108c6b7b5d1f4ae1fae44099a8bf5fddc74d87665e29812ba0d5db514ca1c318cc409bcbf9a75055b1a8e7b783127734d902b6118b518ee962f24a2abda945e843c6952a5f0632365146cfd4e2034b70e41a78772fc404bea", + "11c8f0ea735e099781f852fb93256540c7c7611ba324f67680f3dbfc00bbdda12d7fb8d83e13afa4ea8a9d8817cd5b7c5b1804aa1006af2a8ab7e9c7757b300c040000002db3759aaff5357156bc2e8d52eacd11efb7d108d3b4034162839a9300000000003000000000000000000000000000000000000000000031000000000649ebc27a3909e089c6023c7ed952098a3e5547ce56e79953929ba4d718f1311c2c7e752e7947b506c14a8300a0d15d9022bd6fba67461cc8a0783dfeba77fb11c8f0ea735e099781f852fb93256540c7c7611ba324f67680f3dbfc00bbdda102e4959aa31df084cdc5a82e69b3fce13c6965e7586b1b62b168a24f6301cd3b040000002db3759aaff5357156bc2e8d52eacd11efb7d108d3b4034162839a9300000000003000000000000000000000000000000000000000000031000000000649ebc27a3909e089c6023c7ed952098a3e5547ce56e79953929ba4d718f1311c2c7e752e7947b506c14a8300a0d15d9022bd6fba67461cc8a0783dfeba77fb", + "12ca97e893996e5bb392c57abba4578276654d2856b336b640eafd84de31140e055370544625ea3c3e85a42831dcaf47353695b158149bc2d5bf4061326f089e0e80426227ce5fd647afba497e7ea7a2687e956e978e3572c3df73e9278302b9000000000000000000000000000000000000000000000000000000c451f8749a05602fde384569be9950c3466d0fb40a66a63f202c3a685856cd21c76cea2be70ed4f367e657bc59c785bf6bf6ac33fb4b653e8bd68d1d838e0416e8c47b5f5312ca97e893996e5bb392c57abba4578276654d2856b336b640eafd84de31140e2b10de1e9b0bb5ed79caa18e4fa4a916624ad4e0105d2eca66614bb5a60df4a90e80426227ce5fd647afba497e7ea7a2687e956e978e3572c3df73e9278302b9000000000000000000000000000000000000000000000000000000c451f8749a05602fde384569be9950c3466d0fb40a66a63f202c3a685856cd21c76cea2be70ed4f367e657bc59c785bf6bf6ac33fb4b653e8bd68d1d838e0416e8c47b5f53", + "1425b11b4fe47a394dbfc0ad3e99dc93c3e1c0980b9bcb68529ba9de33dbf585168b8cdff7ae7d084fd111608fa03e018b415fd4f0755f7e8f039a2d852bda0e0000000000000000000000000000000000000000000000000000000000005b00149bb18d1ece5fd647afba497e7ea7a2687e956e978e3072c4fc9ec579b809462061ebaffaedc532d8bb542f9d93cae5dc6c4b431833ee0a7be4d053e2e0d60d03c12e282e4442c88f5235d004e8edba3080754ca41c976d03cd332a9b6fa42d1425b11b4fe47a394dbfc0ad3e99dc93c3e1c0980b9bcb68529ba9de33dbf58519d8c192e9832321687f3455f1e11a5c0c400abc77fc6b0ead1cf1e953512339149bb18d1ece5fd647afba497e7ea7a2687e956e978e3572c4fc9ec579b809460000000000000000000000000000000000000000000000000000000000005b0029e864b516b661105d0a2708f21beaae1bf1636608ac53864c4d8f0ea2e7dc3805c3b957caa8f4c5f248e3e6e754304e9ba7e5b6dd3a4ece125f47811a525e65", + "1565586940bbb0e082639a1e2c9ecfe96bff6cbda25a93c4d462b8360230067219767d96dd90432f459bf8f6181c9fea507bf7bcd4063fd070fe8e72093e8bf900000000000000000000000000000000000000000000000000000000000000e8000000000000000000000000000000000000000000000000000000000000000023812d70e97cfd777c5cf0b64b18400f3ea88afb56146bfc1f34071eebb0599827352c710f0e1711b5d18e672b5cab3c369cd8d84b70ed159bb712c7d929c8e51565586940bbb0e082639a1e2c9ecfe96bff6cbda25a93c4d462b8360230067216edd0dc03a15cfa72b44cc06964b873470572d4946b8abccb21fda4cf3e714e252b5ec0e7df7df26d0cc46dd811ace847a4174aa634c8d9269ccebb52845d7829a0fe67466554bc0a6c97089dec1ebac672344bc3a69b8fc6904f9b83438cbb24c159cda85a7f7e04f7e128101e8fbe502fee784946ffce0a9174424f0254b22b0c8ac28082e97eae5ee175ae2c1a6e466c7dd693238a386ec80f7743ffc517", + "17ac723d48c98fce706845b61c254630acb6da96a9620fc6cd07276a4ef8936d1a515b7e403d2a46bb5387138f17d33c2b12e3d5051f9d22042921f35031035e000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002d8ad25dc006eea79b9c052a6a367b209d5c9215c8a4c325555a7e1e608abed022dd54ed23382326a8df674c7be35d7ac181c9625a10f508dd7fbd109760d76217ac723d48c98fce706845b61c254630acb6da96a9620fc6cd07276a4ef8936d1612f2f4a0f475e2fcfcbea2f26985216c6e86bc63522d6b37f76a23884bf9e9062ca96b10a86be7bc151a74e01170193f2ad046c2f5eb3a25a95592cc3a17752043c18fe0a47868239575b1c3700be7308890370eb6982f4e3e36916ebc28f52b7d22180be2bdca69028c5bf32181d203b2350781d675098b320766f5dcd9b02a21aa3010f604fea531e9264671d546cc94d8a0ddab42b7146573f5c1ed26fb", + "1c4189bbfe590521da71e6834b73a622528fd923e9421f18df9a48e9123b16aa15c8e4948927f2e089accb33c8d51b4c83ca69505f4a7801399573273d7439910000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000800023ad956e40f22f18bf59310f6d23055f2a3fdc2713f05cf1b099a18041cd498220b15bb64c3c6223098439e4fc7e9b2bd956ed94b2bf1f066c281d41c8560ee51c4189bbfe590521da71e6834b73a622528fd923e9421f18df9a48e9123b16aa1a9b69de5809ad492ea37a82b8ac3d1113b701410927528c028b18ef9b08c3b60000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000800023ad956e40f22f18bf59310f6d23055f2a3fdc2713f05cf1b099a18041cd498220b15bb64c3c6223098439e4fc7e9b2bd956ed94b2bf1f066c281d41c8560ee5", + "1c934d642c245e76eb0c016b17e8eced3a1cf56ef6d787eace40bc0cb933dea902567d6e1141f67673e6f17a705f3d9764c6d5673f1f93e2ed5a21fd38e9a572000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007700000000000000000000001f3953d0d836a44e15aebef16dd187ccf9493693379fe7d1c5a2cf2deb87f89c0dd737c063475fc587c1574a8ff4e9e4f933118d67bb7ba4a70c79c1ccb0d3441c934d642c245e76eb0c016b17e8eced3a1cf56ef6d787eace40bc0cb933dea92e0dd104cfefa9b34469543c11221ac632ba952a295236aa4ec66a199f9357d5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007700000000000000000000001f3953d0d836a44e15aebef16dd187ccf9493693379fe7d1c5a2cf2deb87f89c0dd737c063475fc587c1574a8ff4e9e4f933118d67bb7ba4a70c79c1ccb0d344", + "1ced814234531c29aa14f95e3a3f59657aacea69fa4c35dfa5b8e03c87e8075725c3327a2c4f61437d53abbb2bb75dc1dc4f2427c3e9d7714d5d83dae80697690ec85b4f8a9224b27145b3757b7bf6e7397bc04c8634404d924689e27bb8a261000000000000000000000000000000000000000000000000000000000000bf890e717255b589c7286f7bde23fe6ac9d838262d9e7ba5bc603e410abf27aada5b1760bbdb5183ae4e28f049cfe75fb9600b8fd03e63d9aa1a9bd709d23afbc12f1ced814234531c29aa14f95e3a3f59657aacea69fa4c35dfa5b8e03c87e807570aa11bf8b4e23ee63afc99fb55c9fa9bbb324669a487f31beec3083bf07665de0ec85b4f8a9224b27145b3757b7bf6e7397bc04c8634404d924689e27bb8a261000000000000000000000000000000000000000000000000000000000000bf890e717255b589c7286f7bde23fe6ac9d838262d9e7ba5bc603e410abf27aada5b1760bbdb5183ae4e28f049cfe75fb9600b8fd03e63d9aa1a9bd709d23afbc12f", + "1f9d02fa71d0ae244edc79709af68216d99a978a5e8fd92ebe793fff8317aad42cd6a493d88a0a87a6f6818c23fa87ff12fd84e5641e1081e849f6e88a488a4304bb53b8977e5f92a0bc372742c48309445cd3cfa9a62aee49f8130962b4b3b9203e205db4f19b37b60121b83a7333706db86431c6d835849957edf0509de1521f544b5e4ab6d13dcb7e89582aaee12a8410a7c6b2d8a5de7bd83e8cdb1fb1a82baef43e095caaf7c2aca272aee277d1da98d74656f2ca01b833c67dcc08ca5d000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1", + "225fdab8fe6cd876363d27075cdf0d01c209da61b1634b574a5d811cfae407001216b7c3e2adc07c3bef31771c7bb9e1d02f07ff3a5b74953c4fd5bf9a7a6dff25f63fcc543337b8f6275f97d6479633b921541a96e0f2acaf901bc25db7407c206776f9480168741eca625c06e5526b4664a02ce664bc656f39664d96278b6e1e40e8084fd648ba315f691e8367be1d4c13844421c87223d84829c31a0d7afe24b8042d4cb604ae66c0dc97ca8a9c2d22c743335d92bc401700f6b00d5cdc5b2a4f1dbc9fe6d2882462fb11afeae7b7f2a0cf213b1f19c45cb222336283f3801141763c897c9a90e387ea80f68eb88c2d79aab680196d9538cd2ca632a5e81b0e540f5b5be91f82ed05349750761224f543068ce30d2d5e838bf66866f345200fa5ac8c46f725e54505019bda3356ef6e35b0a89b9b4f79bb0c62211235c9312e6d97a1f7e0428fc0be6be02c811095f5166710dcbe869c36a8ef89cac63e012657bbaf3bcfd106dd677eef03172f693a6c919776f441dc0fd47a4bd91d0487", + "22847a3cd1cb28b60a51b54991d6dd86d206aa12b3873942c45a120b2e63a49226c3889c7b08d9288f13f64e6e3f166c59f57b5c014cbecf815a4aba4ebabbfb2e91f2b0f4f7a42c870fe8f6236ec1042bdeb2586a641808a5e09019514e83a02b078a1f12cb5426b6bcfeb9548566afb4bdf7454190eaf97f63e210f3a94ce1008a9ede8dde2c849793027433d90c626bc326bdc248dc223f537f7152955dac00b319b7060ad49091d9b52015b4ae8085bf6739b7f5a2c098c2f113ff33806a22847a3cd1cb28b60a51b54991d6dd86d206aa12b3873942c45a120b2e63a49226c3889c7b08d9288f13f64e6e3f166c59f57b5c014cbecf815a4aba4ebabbfb2e91f2b0f4f7a42c870fe8f6236ec1042bdeb2586a641808a5e09019514e83a02b078a1f12cb5426b6bcfeb9548566afb4bdf7454190eaf97f63e210f3a94ce1008a9ede8dde2c849793027433d90c626bc326bdc248dc223f537f7152955dac00b319b7060ad49091d9b52015b4ae8085bf6739b7f5a2c098c2f113ff33806a", + "26581613e6c802822867d2b3a0867fd7fd8cea503764ae1a9d01ec0d5364d1be0530ae3d4fe87ac76da4382835135ac0aec9b1ecd0705cf84c21b48327eade5407a13e1be6f9072893c0e5bb528c5cc22dd6370308dda1613068e064c6e5d15a067239c795d2ea27b85d93e54fc221d81e9de3ebc8e43c20d63608e7078fab6822fa7ea234e21aec74f4db0b365ae1e7cc6e7f4aac89bc460d0308b83399be8627705b19388ebeddbd35a5ce72830ec07e3f1bca3f4e9fdc0aebaab5a1fe91b126581613e6c802822867d2b3a0867fd7fd8cea503764ae1a9d01ec0d5364d1be2b33a035914925624aac0d8e4c6dfd9ce8b7b8a498016d94effed793b0921ef307a13e1be6f9072893c0e5bb528c5cc22dd6370308dda1613068e064c6e5d15a067239c795d2ea27b85d93e54fc221d81e9de3ebc8e43c20d63608e7078fab6822fa7ea234e21aec74f4db0b365ae1e7cc6e7f4aac89bc460d0308b83399be8627705b19388ebeddbd35a5ce72830ec07e3f1bca3f4e9fdc0aebaab5a1fe91b1", + "28100e22df4dc1c6bf8801f63ffdef2af6bfb80147994395be6dc792479ca4f517414e05ac69d56dec03e0af314645b463414a7d831aff54459b6d409351a5a400002851fd14fbaa7fd586e3039a2d9fa136c42685cb2b5904bf0591b95b52fc000000000000000000000000000000000000000000000000000000000000000024d852a54e2d52ffc43b2e4f1993fd159ae7ec19af2f3ad548f6daa33bd0de310b0fabbc2f33ea444e5f3c92269f11fc30c048463973a914a6787dbd616e703e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "299a5917adb7b45c30cea045edaa623f5036280df176f4a2c751ca728c2c9e3a2bedf30ba7d8bd0814e9d091913ea5bc6a4b60ee993e949507832df60190651900000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000213d689af09b7f52ac4a04c34bcdb2f90521b1cd213edcfe48fa3063520b6be22ef0f3110ea3051248e907221493e6c2943ebe8d70794188a515ec23df5d9b2b299a5917adb7b45c30cea045edaa623f5036280df176f4a2c751ca728c2c9e3a04765b673958e321a3667524f042b2a12d3609a2cf3335f8349d5e20d6ec982e00000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000213d689af09b7f52ac4a04c34bcdb2f90521b1cd213edcfe48fa3063520b6be22ef0f3110ea3051248e907221493e6c2943ebe8d70794188a515ec23df5d9b2b", + "2deb529e45720f20be41088dfd7489c315e9e18fe23dd1c8a8183295dc770dee1090b3fcd1898780c332b7d73a602cadd1b26029124db9da19941250eda890b500000000000000000000000000ff0a69a42c284614e3d7de26b3f9b2f3c71ff600000000000000000000000000000000000000000000000000000000000000001e2c7f9d6a525b6af5dad683c5e6408661da29780860c3209b2dddcad5a88795033123665850406fc2428227bbb3509fc71c911cf41623de1605cd3e0a281b902deb529e45720f20be41088dfd7489c315e9e18fe23dd1c8a8183295dc770dee1090b3fcd1898780c332b7d73a602cadd1b26029124db9da19941250eda890b5000000000000000000000000000000000000000000275680008ded35ba000000000000000000000000000000000000000000000000000000000000000000000019367fe97b92d85d7c29090c2313de1a85f030239cfa71f939a197249cb17b890465a8498153883aca20e4de66ac02b1b6957fab9b4bce71df334cd6bee5ff95", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005908648c919b2bd65122716e5e4aa74fb342e22eeb59635c84bb4e20e34da7230644e72e131a0296427b6542e3a6a00ccfa1d43577c305499096bbd8414f16c0df67cf39cb400a898e9aa727937ca9e07d92185f3fba6aa42be45ddad4fdadb145da376a6ec6d77185df39edde6e9037e58065a054cda2796f4e669bcdcbf1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005908648c919b2bd65122716e5e4aa74fb342e22eeb59635c84bb4e20e34da7230644e72e131a0296427b6542e3a6a00ccfa1d43577c305499096bbd8414f16c0df67cf39cb400a898e9aa727937ca9e07d92185f3fba6aa42be45ddad4fdadb145da376a6ec6d77185df39edde6e9037e58065a054cda2796f4e669bcdcbf1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005908648c919b2bd65122716e5e4aa74fb342e22eeb59635c84bb4e20e34da7230644e72e131a0296427b6542e3a6a00ccfa1d43577c305499096bbd8414f16c0df67cf39cb400a898e9aa727937ca9e07d92185f3fba6aa42be45ddad4fdadb145da376a6ec6d77185df39edde6e9037e58065a054cda2796f4e669bcdcbf1f", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b92493387900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b92493387900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b924933879", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000005b004500000000000000000000000000000000000000000000000830644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d81702c919c023ce224a0c0b121dd4f578b39e0ddf00a55580b03265cb32fcb623bbf778240f6bf16b9a25500a02b09fbe8abfa933f9ee09effb104d712baf573463ef8c000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000830644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d81702c919c023ce224a0c0b121dd4f578b39e0ddf00a55580b03265cb32fcb623bbf778240f6bf16b9a25500a02b09fbe8abfa933f9ee09effb104d712baf573463ef8c000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd450d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000230644e72e131a029b85045b68181585d97816a68917120ca8d3c8c16d87cfd4500000000005b004500000000000000000000000000000000000000004000000111ee2be185fb562c979eeb452df376f91c7852bfb8a6d5727024444a2fc58c152825d78e66be7952503e9e1bf2d8aad4c39f4ce4095349043106508bab20d6e5000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a68917120ca8d3c8c16d87cfd4500000000005b004500000000000000000000000000000000000000004000000111ee2be185fb562c979eeb452df376f91c7852bfb8a6d5727024444a2fc58c152825d78e66be7952503e9e1bf2d8aad4c39f4ce4095349043106508bab20d6e5000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a68917120ca8d3c8c16d87cfd4500000000005b004500000000000000000000000000000000000000004000000111ee2be185fb562c979eeb452df376f91c7852bfb8a6d5727024444a2fc58c152825d78e66be7952503e9e1bf2d8aad4c39f4ce4095349043106508bab20d6e5", + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45000000000000004500000000000000000000000000000000000000000000000107b55de1642362d16b8fc4dccfec9e794d24968511cf8e252ed27afc4d72ca1e0301cb4d7e0b7b52a6a8f78613c403ead1543fedb0d3f28c77685fdb9eaa2742000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45000000000000004500000000000000000000000000000000000000000000000107b55de1642362d16b8fc4dccfec9e794d24968511cf8e252ed27afc4d72ca1e0301cb4d7e0b7b52a6a8f78613c403ead1543fedb0d3f28c77685fdb9eaa2742000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45000000000000004500000000000000000000000000000000000000000000000107b55de1642362d16b8fc4dccfec9e794d24968511cf8e252ed27afc4d72ca1e0301cb4d7e0b7b52a6a8f78613c403ead1543fedb0d3f28c77685fdb9eaa2742", + "0272af94b7aeb772c1666db65ff3987fd8ec43b8454aad395232b5eef18ca2c51d5fc6b50ef577096ec2489356e8ecccd5ed5a144928c5338af5549701dbb40d255495ffba02a0ed6877a0a6ed658c93a1487a3319aaa05934e35bbb953673af00000000000000279573fbfe8e6c2300000000000030640472e131a029b85045090d17c2eaeda0d61dd0442997d161e176fb98a27091fe8b6b5746fe2f344a0f26f1f16556d24506af1fd1c8cb4b3b6dfa73e2ce958c50e3b937fd59a1df623f0272af94b7aeb772c1666db65ff3987fd8ec43b8454aad395232b5eef18ca2c5130487bdd23c2920498dfd232a986b90c194107d1f490559b12b377fd6a1493a255495ffba02a0ed6877a0a6ed658c93a1487a3319aaa05934e35bbb953673af00000000000000279573fbfe8e6c2300000000000030640472e131a029b85045090d17c2eaeda0d61dd0442997d161e176fb98a27091fe8b6b5746fe2f344a0f26f1f16556d24506af1fd1c8cb4b3b6dfa73e2ce958c50e3b937fd59a1df623f000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4700000000000000000000000000000000000000fcff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000212a3b4059e59d125dd17f662945170e1e13024fae97a401895690acac63cf681aecb67409d1d9c142ffd13375bd79fd0457deb278dfa91b5756378fb837fd60", + "17956b549321b92c524d191a62b90c81f08e694af4dceb00a80094e53120d39e2fd15d78815388959caa54affa2d7b2c8d52c528e6a1f6c0a50fcf0498fac7ee30644e72e131a0290000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000016b9e28a32ee61a7ad5bd4bad29d64fc35a8fb46b510be2067907069497ae1f0c3c07c40f41a731fe27d953f1154b4e9a8acf3ef44608817150de0df916503a17956b549321b92c524d191a62b90c81f08e694af4dceb00a80094e53120d39e2fd15d78815388959caa54affa2d7b2c8d52c528e6a1f6c0a50fcf0498fac7ee30644e72e131a0290000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000016b9e28a32ee61a7ad5bd4bad29d64fc35a8fb46b510be2067907069497ae1f0c3c07c40f41a731fe27d953f1154b4e9a8acf3ef44608817150de0df916503a17956b549321b92c524d191a62b90c81f08e694af4dceb00a80094e53120d39e2fd15d78815388959caa54affa2d7b2c8d52c528e6a1f6c0a50fcf0498fac7ee30644e72e131a0290000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000016b9e28a32ee61a7ad5bd4bad29d64fc35a8fb46b510be2067907069497ae1f0c3c07c40f41a731fe27d953f1154b4e9a8acf3ef44608817150de0df916503a", + "1bbefdb0536c0996d663a908adc2cae17180c53d971e73fe234a6927cd10da332481152785517c274b2dc4ab7b6cacbbdc7d7b8e4dd396faa05a119761130e5c039a49879c785fd647afba497e7ea7a2687e956e978e3572c3df73e9278302b9194b9757129492217d64c4b267e5e935137ae3859ec8b99cf6e6dc61ca90bdfd1c67b6b637c528053a25079e25734a67ffc5388209d11839b3ff1f92591c81ee09750d9bc6fdb71f16e6a69eea50e7288aac1c25a5cd674ca9c57d8ac06fac691bbefdb0536c0996d663a908adc2cae17180c53d971e73fe234a6927cd10da330be3394b5be024026d22810b0614aba1bb03ef031a9e33929bc67a7f7769eeeb039a49879c785fd647afba497e7ea7a2687e956e978e3572c3df73e9278302b9194b9757129492217d64c4b267e5e935137ae3859ec8b99cf6e6dc61ca90bdfd1c67b6b637c528053a25079e25734a67ffc5388209d11839b3ff1f92591c81ee09750d9bc6fdb71f16e6a69eea50e7288aac1c25a5cd674ca9c57d8ac06fac6929a0d6d5e7d14a774c3abff1435361da2ee5d8b4f3ee62085ce779f248b41d4a2fd37ae5468f6a17b7f9a0bcca02ee128bdced61402a566e4eee2d0fa825f03d198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa", + "299a5917adb7b45c30cea045edaa623f5036280df176f4a2c751ca728c2c9e3a2bedf30ba7d8bd0814e9d091913ea5bc6a4b60ee993e949507832df60190651900000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000213d689af09b7f52ac4a04c34bcdb2f90521b1cd213edcfe48fa3063520b6be22ef0f3110ea3051248e907221493e6c2943ebe8d70794188a515ec23df5d9b2b299a5917adb7b45c30cea045edaa623f5036280df176f4a2c751ca728c2c9e3a04765b673958e321a3667524f042b2a12d3609a2cf3335f8349d5e20d6ec982e00000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000213d689af09b7f52ac4a04c34bcdb2f90521b1cd213edcfe48fa3063520b6be22ef0f3110ea3051248e907221493e6c2943ebe8d70794188a515ec23df5d9b2b299a5917adb7b45c30cea045edaa623f5036280df176f4a2c751ca728c2c9e3a2bedf30ba7d8bd0814e9d091913ea5bc6a4b60ee993e949507832df60190651900000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000213d689af09b7f52ac4a04c34bcdb2f90521b1cd213edcfe48fa3063520b6be22ef0f3110ea3051248e907221493e6c2943ebe8d70794188a515ec23df5d9b2b", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080a77c09a07dfaf666ea36f7879462c0a78eb28f5c70b5ed35d438dc58f0d9d058bcabf53ccb1b6f2ad14e6bc531485dba856600b7941c8c5a4968940fba978210dc371bfd9736eb879d25be8a799eff45bea91130b9b2768689786235accd9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800104e7500000000000000000000000000000000000000000000000000000000000000000a535bc14c0f581a039d0bfb15e730b6bbb3d6e33ecda8c74ebd901deeb4ef8423e99eadcc72ced9221df2db154b36f6931270c3f21f4ec8c4206b9211924bb203a99966a8db46602ac05b8f0d7669c3bc066fb7b9a188cc79e5239e27e3580517e54f1fff192038a907711c8123997f785d2c2c9a214355466b60652ee53e4d00c662006274fb346e22eeb59635c82cb4e20e34da7230644e72e131c82cb4e2000000104e75a20b64000000000000000000150000000000000000006629c731270ef2ea6f8c3dad3832f95f309cb6a1591ad53af025c6e0809992f15234e5fe1b37771ce3bb5a62017c56c496e57673e2617634e900c587724d21f24e603a6303a99966a8db46602ac05b8f0d7669c3bc066fb7b9a188cc79e5239e27e35805187eff52e2187ff10f48d49a005dbede1f243e64ce508737f5b52bb1a997befa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf0000000000000000001284bdd8e69832b232222d3493a6abb523575be2a5af6e88f5501cef408aa5bb0ca4d5b3c58e0bc7be2c8885e53c673b54f3057cff97cb1020cb2f5a9f61affb000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005908648c919b2bd65122716e5e4aa74fb342e22eeb59635c84bb4e20e34da7230644e72e131a0296427b6542e3a6a00ccfa1d43577c305499096bbd8414f16c0df67cf39cb400a898e9aa727937ca9e07d92185f3fba6aa42be45ddad4fdadb145da376a6ec6d77185df39edde6e9037e58065a054cda2796f4e669bcdcbf1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005908648c919b2bd65122716e5e4aa74fb342e22eeb59635c84bb4e20e34da7230644e72e131a0296427b6542e3a6a00ccfa1d43577c305499096bbd8414f16c0df67cf39cb400a898e9aa727937ca9e07d92185f3fba6aa42be45ddad4fdadb145da376a6ec6d77185df39edde6e9037e58065a054cda2796f4e669bcdcbf1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005908648c919b2bd65122716e5e4aa74fb342e22eeb59635c84bb4e20e34da7230644e72e131a0296427b6542e3a6a00ccfa1d43577c305499096bbd8414f16c0df67cf39cb400a898e9aa727937ca9e07d92185f3fba6aa42be45ddad4fdadb145da376a6ec6d77185df39edde6e9037e58065a054cda2796f4e669bcdcbf1f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005908648c919b2bd65122716e5e4aa74fb342e22eeb59635c84bb4e20e34da7230644e72e131a0296427b6542e3a6a00ccfa1d43577c305499096bbd8414f16c0df67cf39cb400a898e9aa727937ca9e07d92185f3fba6aa42be45ddad4fdadb145da376a6ec6d77185df39edde6e9037e58065a054cda2796f4e669bcdcbf1f", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b92493387900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b92493387900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b92493387900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21a76dae6d3272396d0cbe61fced2bc532edac647851e3ac53ce1cc9c7e645a8305b993046905746641a19b500ebbbd30cf0068a845bfbee9de55b8fe57d1dee8243ef33537f73ef4ace4279d86344d93a5dc8c20c69045865c0fa3b924933879", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a0290000000000000000000000000000000040000000000000000001000000000000000000000000000000000000000017f66b9111253405000006f2a18a982e6e9436f171f8bcdb4f60b9ea1dafaf115f9228e11ddd98ca9680304502b9c299712d0cf851e73adc8ce68c549a5b2ae712a44811db357d3bdb9717956b549321b92c524d191a62b90c81f08e694af4dceb00a80094e53120d39e2fd15d78815388959caa54affa2d7b2c8d52c528e6a1f6c0a50fcf0498fac7ee30644e72e131a0290000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000016b9e28a32ee61a7ad5bd4bad29d64fc35a8fb46b510be2067907069497ae1f0c3c07c40f41a731fe27d953f1154b4e9a8acf3ef44608817150de0df916503a17956b549321b92c524d191a62b90c81f08e694af4dceb00a80094e53120d39e2fd15d78815388959caa54affa2d7b2c8d52c528e6a1f6c0a50fcf0498fac7ee30644e72e131a0290000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000016b9e28a32ee61a7ad5bd4bad29d64fc35a8fb46b510be2067907069497ae1f0c3c07c40f41a731fe27d953f1154b4e9a8acf3ef44608817150de0df916503a17956b549321b92c524d191a62b90c81f08e694af4dceb00a80094e53120d39e2fd15d78815388959caa54affa2d7b2c8d52c528e6a1f6c0a50fcf0498fac7ee30644e72e131a0290000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000016b9e28a32ee61a7ad5bd4bad29d64fc35a8fb46b510be2067907069497ae1f0c3c07c40f41a731fe27d953f1154b4e9a8acf3ef44608817150de0df916503a", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d977f9597358c206e72c88c16e622b8b2000000000000004500000000000000000000000107b55de1642362d16b8fc4dc2a314eca6adb97bf3a3872a406ecf97bf2bdd210b6cba667eba043f71c1ffc200ba9caa5888ae7c0e6d2b54181905e66769d9ed9badc43da3d9f3e56b46cb43b0acfd59a153bdc736907cc4a640a2fb675cdaa8066a58616537ce4cd7145aa061b1d6bf901bda3c5e3df60e3741d6f5d6660f4034e2f99545e0dc71e9b53af2c30644e72e131a029b85045b68181585d977f956e978c3572c8208c166622b8b2000000000000004500000000000000000000000107b55de1642362d16b8fc4dc2ad9bb009c15bdc11d5ed75a805d5f24ba0e10e5b7953e281e241f72d208ce880d6f072450fd6ef76dd73ff2924238cbee09ea4c6281b867a897d85d8d091b330acfd59a153bdc736907cc4a640a2fb675cdaa8066a58616537ce4cd7145aa061b1d6bf901bda3c5e3df60e3741d6f5d6660f4034e2f99545e0dc71e9b53af2c30644e72e131a029b85045b68181585d977f956e978c3572c8208c166622b8b2000000000000004500000000000000000000000107b55de1642362d16b8fc4dc2ad9bb009c15bdc11d5ed75a805d5f24ba0e10e5b7953e281e241f72d208ce880d6f072450fd6ef76dd73ff2924238cbee09ea4c6281b867a897d85d8d091b330acfd59a153bdc736907cc4a640a2fb675cdaa8066a58616537ce4cd7145aa061546e279df73fc63d470e4d30d63e9003120768e1a423138de12c4f83d294e1b30644e72e131a029b85045b68181585d977f956e978c3572c8208c166622b8b2000000000000004500000000000000000000000107b55de1642362d16b8fc4dc2ad9bb009c15bdc11d5ed75a805d5f24ba0e10e5b7953e281e241f72d208ce880d6f072450fd6ef76dd73ff2924238cbee09ea4c6281b867a897d85d8d091b33", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb431a5d389ca17607c4b12d7f1c8463b960754dbbb74ee00f645ca559f5ee759ad5231fdb414002b25ebeed48fd799214f4db09a7dae25ac92c15b7e429e1800a70297861ced3673fa135f178751f203f13d8a4c9f15ad9fdbb4bb7468154af97f71698710af2efbde1687bc1530e7da2ca42c8e85baf594682890d36c8fb8d08f9106f1d083223bd63d1630baf008588ac9c0392c289c68afee37b013030a9f7d10128036f1a405f40bce08bc346f0f83cb8ca2fe81d18fdfffa984c97cef373580000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb431c251d57a884434484d488b3fa5bb0b0d8566a251bfa57cdcfa452ee114a19601bcd6dec5e0b17fb9bb77b1145793cd6a99534de85911f41c37934fc6f1f508e203e205db4f19b37b60121b83a7333706db86431c6d835849957ed8c3928ad7927dc7234fd11d3e8c36c59277c3e6f149d5cd3cfa9a62aee49f8130962b4b3b9195e8aa5b7827463722b8c15393157ad3505566b4edf48d498e185f0509de15204bb53b8977e5f92a0bc372742c4830944a59b4fe6b1c0466e2a6dad122b5d2e", + "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000005b004500000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45218c0cef2a606613357bfaa3c880e71ff8490195337fa26205a21ccd9a6949a313fed2c79add9d85b9949c5852ef8cccf02b69ea6fc4db0d1660ac7b50df39e1", + "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6", + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21cc9a9eb823f8d97adee0206828c9f19eaf8f536c23207d68ae3d056a1480bb21990ddb90388b94f31089d3b4ff594d5449c04fcdf2a3681017b5d7749367ba1000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21cc9a9eb823f8d97adee0206828c9f19eaf8f536c23207d68ae3d056a1480bb21990ddb90388b94f31089d3b4ff594d5449c04fcdf2a3681017b5d7749367ba1000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21cc9a9eb823f8d97adee0206828c9f19eaf8f536c23207d68ae3d056a1480bb21990ddb90388b94f31089d3b4ff594d5449c04fcdf2a3681017b5d7749367ba1000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21cc9a9eb823f8d97adee0206828c9f19eaf8f536c23207d68ae3d056a1480bb21990ddb90388b94f31089d3b4ff594d5449c04fcdf2a3681017b5d7749367ba1", + "000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb43000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4500000000000000000000000000000000000000000000000000000000000000020833e47a2eaa8bbe12d33b2da1a4fa8d763f5c567fe0da6c5c9da2e246f2096f28dc125bf7443bc1826c69fe4c7bf30c26ec60882350e784c4848c822726eb43", + "01ea6e2eae2a2501d6830a3f5ea1353a5f920719aaeeea537dde0df31bec7a802b40433b22aaaec8f6d6d21dc04994ab99ba4a4b16545f63430a6ecf01881e090000000004000000000000000000000000000000000000000000000000000000196ec634c6397f591ebee925f9fa9e89a1fa55ba5e38d5cb0f7dcfa49e0c0ae4260377b5b8fed1c2f2a2f848038464ffc8c3bd1d71844fcc85f64b478248b21d0990a17d0f06d3bb026b17967cc55b2160d7570fde6a448502bd28e8192db67e01ea6e2eae2a2501d6830a3f5ea1353a5f920719aaeeea537dde0df31bec7a8005240b37be86f160c1797398c137c3b1fdc72046521d6b29f9161d47d6f4df3e0000000004000000000000000000000000000000000000000000000000000000196ec634c6397f591ebee925f9fa9e89a1fa55ba5e38d5cb0f7dcfa49e0c0ae4260377b5b8fed1c2f2a2f848038464ffc8c3bd1d71844fcc85f64b478248b21d0990a17d0f06d3bb026b17967cc55b2160d7570fde6a448502bd28e8192db67e000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020d7d3be95c6b1fc7d70f2edc7b7bf6e7397bc04bc6aaa0584b9e5bbc064de4fa30644e72e131a029b85045b68181585d97816a9168543fedb0d3f28c77685fdb10fb4e584f10053cba1117d920db188f54d1ab64e66e10b6177401a44c71e25020f50c6423205b18d96cdf7a832041d89076cf36dcde6700e62833186acd60b6", + "03a99966a8db46602ac05b8f0d7669c3bc066fb7b9a188cc79e5239e27e35805187eff52e2187ff10f48d49a005dbede1f243e64ce508737f5b52bb1a997befa000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bf0000000000000000001284bdd8e69832b232222d3493a6abb523575be2a5af6e88f5501cef408aa5bb0ca4d5b3c58e0bc7be2c8885e53c673b54f3057cff97cb1020cb2f5a9f61affb03a99966a8db46602ac05b8f0d7669c3bc066fb7b9a188cc79e5239e27e3580517e54f1fff192038a907711c8123997f785d2c2c9a214355466b60652ee53e4d00c662006274fb346e22eeb59635c82cb4e20e34da7230644e72e131c82cb4e2000000104e75a20b64000000000000000000150000000000000000006629c731270ef2ea6f8c3dad3832f95f309cb6a1591ad53af025c6e0809992f15234e5fe1b37771ce3bb5a62017c56c496e57673e2617634e900c587724d21f24e603a630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800104e75a20b641566a0c71c9069a5256391aa31e22021d36c037c108dfb79c66200bf257ae3d66a589214f980a2ae34f9544be2fcbcc13b21f4c1642f31aa4d20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000", + "0cdc335fa81dff303b71cffb0256d7097c2b4c6715ca7b1589ea9386a41c2f851e4077b7c1f3374bbd4750ed222848c47501b40fc4c11eadd3c39aeafa452459000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000001f1cd7f247f2ae1ba9a1aeb4b32ed4c8c13c70c8861b6ab5340e276c58e1046b13d7de2b557e0ae2b53380ad596ba79f07c037c9d9aa17cf407e9ed86201436e0cdc335fa81dff303b71cffb0256d7097c2b4c6715ca7b1589ea9386a41c2f851223d6bb1f3e68ddfb08f4c95f590f99227fb681a3b0abdf685cf12bde37d8ee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000001f1cd7f247f2ae1ba9a1aeb4b32ed4c8c13c70c8861b6ab5340e276c58e1046b13d7de2b557e0ae2b53380ad596ba79f07c037c9d9aa17cf407e9ed86201436e000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd45000000000000004500000000000000000000000000000000000000000000000107b55de1642362d16b8fc4dccfec9e794d24968511cf8e252ed27afc4d72ca1e0301cb4d7e0b7b52a6a8f78613c403ead1543fedb0d3f28c77685fdb9eaa274212ee4836f908e5e39090a4124b75f3ec5b665a729997f55cdea80f76ab940c5902a12ba6400116479dcf5179e4d06471a65e72229fb444b34d8f07458a4a81570000000000ffffffff00000000000000000000000000000000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000fb55bf7df894a746fbe20b6f8c54d5dc0d9fd4eda4742fc35100c45e6e27f912a20d63d446eb175733853b88dd36708eb7a81f5c79e7659c3a6e2b2c470077", + "1838ccf8d820349a8c38a57dc0a5495577898e422509e4c9636e9737fe0dcd9116f566a30b7d2579fd6109eb09d66c005fdccdc0d91b28f43be34c5668f34f812a9a64f273fa329bc9c98ff2083c627609e2219177b9c5df7286af31a3b27e8120d85101e1e6af0126c00a95c645b192b3ea69ed72297deac1fbb0e6bd1440c600ec155ab3cd58005b488308578aaeeaeae9c65939f785bf693fa5763c174dd624d3b2adb35ad17933d0317a00436d10f2d6908faf109e7fe3db77a39d5c1711175bcec7c4c77c612df588174dd44362722f7b8e588a0ef599129cf123f63b8d2e1ff6c569e5cb0d20fb28504057d4f7ed248900ac99bd0f31a853af5c10a806198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daa000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21cc9a9eb823f8d97adee0206828c9f19eaf8f536c23207d68ae3d056a1480bb21990ddb90388b94f31089d3b4ff594d5449c04fcdf2a3681017b5d7749367ba1000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd451800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21cc9a9eb823f8d97adee0206828c9f19eaf8f536c23207d68ae3d056a1480bb21990ddb90388b94f31089d3b4ff594d5449c04fcdf2a3681017b5d7749367ba1", + "1bbf3321bfdf76e22ca0e95089338a772a4a2982dbede2571c14a258bd9874ff2b5aa74efa3429cd77acc68bd2e487960f34074d3a725444b06a552c1be0ca442471fa6c0785556dacbe4d9570fa9e89a2fa55ba5e38d5cb0f7dcfa49e0c0ae4181b42bbb36f2bb465708c62b0bd12ae5d6922cf5c4f928a7097f8db8f0dfae3103e85ddefbe5900ded1384badbc32c907c50baa4282172d02562116005aecdf2cce8d04251b64ced66cc7b66a1c54000e17872b10b4793f5bd81052e5ef3e5c1bbf3321bfdf76e22ca0e95089338a772a4a2982dbede2571c14a258bd9874ff0509a723e6fd765c40a37f2aae9cd0c7884d63442dff76488bb636eabc9c33032471fa6c0785556dacbe4d9570fa9e89a2fa55ba5e38d5cb0f7dcfa49e0c0ae4181b42bbb36f2bb465708c62b0bd12ae5d6922cf5c4f928a7097f8db8f0dfae3103e85ddefbe5900ded1384badbc32c907c50baa4282172d02562116005aecdf2cce8d04251b64ced66cc7b66a1c54000e17872b10b4793f5bd81052e5ef3e5c00710c68e1b8b73a72a289422d2b6f841cc56fe8c51105021c56ae30c3ae1aca0b2ff392a2fc535427ec9b7e1ae1c35a7961986788cf648349190dd92e182f05198e9393920d483a7260bfb731fb5d25f1aa493335a9e71297e485b7aef312c21800deef121f1e76426a00665e5c4479674322d4f75edadd46debd5cd992f6ed090689d0585ff075ec9e99ad690c3395bc4b313370b38ef355acdadcd122975b12c85ea5db8c6deb4aab71808dcb408fe3d1e7690c43d37b4ce6cc0166fa7daabebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebebefdbebebebeabc689bebebebe43be92be5fbebebebebebebebebebebebebebebebebebebebebebebe9ebebebe2abebebebebebebebebebebebebebebebebebe", + "29c19b11665cbeb0fd496542e6115ceef9b628ab4bc7419802e7016cf1b2c931057cdbec3f7c2ad0f4da33b3cfd3234ea29f71c6c46b8b90eb2d4a5851adf74b00bb00b95e1b59fca2159a5f33f27f36b15fe6bcda0b1ee0381b0ef7dd804bd625ed3c1a0120b6531e39cca6f67f35f9a849cfbfe958cc1840d1c20cc6b3e1ad08cbba5815581dcea62fa0426dbff39c8f0e9fdc451b1fb127a4e7a4c058799619068e06466a9dd7543ac89d53a17a8beb6c11c59c3af32ea0c1bbc55e0fae7b2c52ab9e7e9296183b9695387e3ae3d06c9d11a98676e4e046914b36b636ddc4050abc3606826f8aeaac5aa7be9f44d15a55805037eb2b260c32d43bd32adef10763b017bc2777ac81ff30fff09f65381c24912bfc9d1490a23765b4830f0aae2b71c998abc367ad761da7e9900eb7e579989f82eee3338c17b8dc17e53e052b21e242369877673070a6b8b0307da6e84e32bdfb3eb09d514b25e8704bd55d431782c07d2d471af10469a13476a49ec51d1bc538d49bbaf85a63fdb6b412fb4a1bc85b155eee2e33bf5322810c7c0150158f373e998316edf821ae9804a2ac191899a5e18c57f4e6b5f736c2f21e95f4147a4235ed2c1f6e6615eb0e00979f0920568d8f3b2f2d0a6a596ea20b3e62191baf420de10125557f9a665900a82a5925a43a697ceaa9deba01a97d8722815e8185973a402e11fdca62fa908da409c4054081d5b9d0e3aa18a31cc5f9d0fa925205fd50a71edf4ca157a607587e24571c9cacf3b3d821146083f9e5fe6be23546643e3ad4540a1f02c02728010a4b9f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030644e72e131a029b85045b68181585d97816a916871ca8d3c208c166622b860000000000000004500000000000000000000000107b55de1642362d16b8fc4dc2215436159d2bdb4e084a6c63bd04f6b84d69bfad793da72c21b421f3f89b22f22b811a1a4fa51df281657f0b0a1add382fab768ac1ed8886bf979e2bf11402d", + "2deb529e45720f20be41088dfd7489c315e9e18fe23dd1c8a8183295dc770dee1090b3fcd1898780c332b7d73a602cadd1b26029124db9da19941250eda890b500000000000000000000000000ff0a69a42c284614e3d7de26b3f9b2f3c71ff600000000000000000000000000000000000000000000000000000000000000001e2c7f9d6a525b6af5dad683c5e6408661da29780860c3209b2dddcad5a88795033123665850406fc2428227bbb3509fc71c911cf41623de1605cd3e0a281b902deb529e45720f20be41088dfd7489c315e9e18fe23dd1c8a8183295dc770dee1090b3fcd1898780c332b7d73a602cadd1b26029124db9da19941250eda890b5000000000000000000000000000000000000000000275680008ded35ba000000000000000000000000000000000000000000000000000000000000000000000019367fe97b92d85d7c29090c2313de1a85f030239cfa71f939a197249cb17b890465a8498153883aca20e4de66ac02b1b6957fab9b4bce71df334cd6bee5ff952deb529e45720f20be41088dfd7489c315e9e18fe23dd1c8a8183295dc770dee1090b3fcd1898780c332b7d73a602cadd1b26029124db9da19941250eda890b5000000000000000000000000000000000000000000275680008ded35ba000000000000000000000000000000000000000000000000000000000000000000000019367fe97b92d85d7c29090c2313de1a85f030239cfa71f939a197249cb17b890465a8498153883aca20e4de66ac02b1b6957fab9b4bce71df334cd6bee5ff952deb529e45720f20be41088dfd7489c315e9e18fe23dd1c8a8183295dc770dee1090b3fcd1898780c332b7d73a602cadd1b26029124db9da19941250eda890b5000000000000000000000000000000000000000000275680008ded35ba000000000000000000000000000000000000000000000000000000000000000000000019367fe97b92d85d7c29090c2313de1a85f030239cfa71f939a197249cb17b890465a8498153883aca20e4de66ac02b1b6957fab9b4bce71df334cd6bee5ff95", + "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a36c98d1ece5fd647afba497e7ea7a2687e956e978e3572c3df75e9278302b9000000000000000000000000000000000000000000000000000000991b498795099a3faf27255b9542daf48d9588fc4d6927c7fcd88c5784a4245345474e1e4509a44ea53f191ddef8a32ec03a7e1a24e06588f8de364ba0024b3b8a062ca91a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a36c98d1ece5fd647afba497e7ea7a2687e956e978e3572c3df75e9278302b9000000000000000000000000000000000000000000000000000000991b498795099a3faf27255b9542daf48d9588fc4d6927c7fcd88c5784a4245345474e1e4509a44ea53f191ddef8a32ec03a7e1a24e06588f8de364ba0024b3b8a062ca91a000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a68917120ca8d3c8c16d87cfd4500000000005b004500000000000000000000000000000000000000004000000111ee2be185fb562c979eeb452df376f91c7852bfb8a6d5727024444a2fc58c152825d78e66be7952503e9e1bf2d8aad4c39f4ce4095349043106508bab20d6e5000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a68917120ca8d3c8c16d87cfd4500000000005b004500000000000000000000000000000000000000004000000111ee2be185fb562c979eeb452df376f91c7852bfb8a6d5727024444a2fc58c152825d78e66be7952503e9e1bf2d8aad4c39f4ce4095349043106508bab20d6e5000000000000000000000000000000000000000000000000000000000000000130644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd4530644e72e131a029b85045b68181585d97816a68917120ca8d3c8c16d87cfd4500000000005b004500000000000000000000000000000000000000004000000111ee2be185fb562c979eeb452df376f91c7852bfb8a6d5727024444a2fc58c152825d78e66be7952503e9e1bf2d8aad4c39f4ce4095349043106508bab20d6e5", + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000310000000000000000000000000ccc369050590000000000000000000000000000000000000000000000000000000000000000034e16015e4aeadb3c1c93fd54f496a6a75271f9e514b6c51782d145a1771a9827910e860d8264c87a784c364d78f117ca598153fc5947c915eb6e2e7e0ad6d5000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "06e42bc7eb32a979b39a77435202c2a062c8bef977f46500b040fa35b8a45b2315ca114b0522bdd5e928f9629ed2a283d36141f3307071cad74fb768721cbb0e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006e42bc7eb32a979b39a77435202c2a062c8bef977f46500b040fa35b8a45b231a9a3d27dc0ee253cf274c53e2aeb5d9c420289e380158c264d0d4ae6660423900000000000000000000000000000000000000000000010000000000000000001611179006befd3749602129813bd7247045ac58ea0000000100000000000000202a170a4a4385bdcefbe36ade6cc7c0e9834b78fc6ca233e8de345a2d5782dc1699f57deaacb74e8b98bd8532c3f152570db884fc1755fbc0c8cb3ef54a537f26c6e104e6c30ed077379b1762d05ac66a6d8e1bb0699ff4f8e3fa52568f0f7709179c965e45c4f28e2ee2affb9b86744b8f29df08357dc80776ecede39adc7f221276dfff5b3a2b6a46ec6c0e0891aefd121170d8361187b73b46e1c6b1218f29255d7955576bba25241ec1edfdfc08c9b8d29379d4213025fa95b0dbdd16460ff9fdfcccda82fb0dd8f17931e05823321073a1a4e484699acdc9d0f14ea6f60c25b531fed0c7e8899296888cc0b27fbb6dbfc2041d482e21d908e240772a2326c6e104e6c30ed077379b1762d05ac66a6d8e1bb0699ff4f8e3fa52568f0f7709179c965e45c4f28e2ee2affb9b86744b8f29df08357dc80776ecede39adc7f221276dfff5b3a2b6a46ec6c0e0891aefd121170d8361187b73b46e1c6b1218f29255d7955576bba25241ec1edfdfc08c9b8d29379d4213025fa95b0dbdd16460ff9fdfcccda82fb0dd8f17931e05823321073a1a4e484699acdc9d0f14ea6f60c25b531fed0c7e8899296888cc0b27fbb6dbfc2041d482e21d908e240772a2326c6e104e6c30ed077379b1762d05ac66a6d8e1bb0699ff4f8e3fa52568f0f7709179c965e45c4f28e2ee2affb9b86744b8f29df08357dc80776ecede39adc7f221276dfff5b3a2b6a46ec6c0e0891aefd121170d8361187b73b46e1c6b1218f29255d7955576bba25241ec1edfdfc08c9b8d29379d4213025fa95b0dbdd16460ff9fdfcccda82fb0dd8f17931e05823321073a1a4e484699acdc9d0f14ea6f60c25b531fed0c7e8899296888cc0b27fbb6dbfc2041d482e21d908e240772a2326c6e104e6c30ed077379b1762d05ac66a6d8e1bb0699ff4f8e3fa52568f0f7709179c965e45c4f28e2ee2affb9b86744b8f29df08357dc80776ecede39adc7f221276dfff5b3a2b6a46ec6c0e0891aefd121170d8361187b73b46e1c6b1218f29255d7955576bba25241ec1edfdfc08c9b8d29379d4213025fa95b0dbdd16460ff9fdfcccda82fb0dd8f17931e05823321073a1a4e484699acdc9d0f14ea6f60c25b531fed0c7e8899296888cc0b27fbb6dbfc2041d482e21d908e240772a23", + ]; + invalid_inputs.forEach(async (input) => { + const call = callFallback(ecPairing, input); + await expect(call).to.be.reverted; + }); + }); + }); +});