diff --git a/contracts/contracts/recipientRegistry/PermissionedRecipientRegistry.sol b/contracts/contracts/recipientRegistry/PermissionedRecipientRegistry.sol new file mode 100644 index 000000000..02265f2d5 --- /dev/null +++ b/contracts/contracts/recipientRegistry/PermissionedRecipientRegistry.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.6.12; + +import '@openzeppelin/contracts/access/Ownable.sol'; + +import './BaseRecipientRegistry.sol'; + +/** + * @dev Recipient registry with permissioned execution of registrations and removals. + */ +contract PermissionedRecipientRegistry is Ownable, BaseRecipientRegistry { + + // Enums + enum RequestType { + Registration, + Removal + } + + // Structs + struct Request { + RequestType requestType; + address payable requester; + uint256 submissionTime; + uint256 deposit; + address recipientAddress; // Only for registration requests + string recipientMetadata; // Only for registration requests + } + + // State + uint256 public baseDeposit; + uint256 public challengePeriodDuration; + mapping(bytes32 => Request) private requests; + + // Events + event RequestSubmitted( + bytes32 indexed _recipientId, + RequestType indexed _type, + address _recipient, + string _metadata, + uint256 _timestamp + ); + event RequestResolved( + bytes32 indexed _recipientId, + RequestType indexed _type, + bool indexed _rejected, + uint256 _recipientIndex, // Only for accepted registration requests + uint256 _timestamp + ); + + /** + * @dev Deploy the registry. + * @param _baseDeposit Deposit required to add or remove item. + * @param _challengePeriodDuration The time owner has to challenge a request (seconds). + * @param _controller Controller address. Normally it's a funding round factory contract. + */ + constructor( + uint256 _baseDeposit, + uint256 _challengePeriodDuration, + address _controller + ) + public + { + baseDeposit = _baseDeposit; + challengePeriodDuration = _challengePeriodDuration; + controller = _controller; + } + + /** + * @dev Change base deposit. + */ + function setBaseDeposit(uint256 _baseDeposit) + external + onlyOwner + { + baseDeposit = _baseDeposit; + } + + /** + * @dev Change challenge period duration. + */ + function setChallengePeriodDuration(uint256 _challengePeriodDuration) + external + onlyOwner + { + challengePeriodDuration = _challengePeriodDuration; + } + + /** + * @dev Submit recipient registration request. + * @param _recipient The address that receives funds. + * @param _metadata The metadata info of the recipient. + */ + function addRecipient(address _recipient, string calldata _metadata) + external + payable + { + require(_recipient != address(0), 'RecipientRegistry: Recipient address is zero'); + require(bytes(_metadata).length != 0, 'RecipientRegistry: Metadata info is empty string'); + bytes32 recipientId = keccak256(abi.encodePacked(_recipient, _metadata)); + require(recipients[recipientId].index == 0, 'RecipientRegistry: Recipient already registered'); + require(requests[recipientId].submissionTime == 0, 'RecipientRegistry: Request already submitted'); + require(msg.value == baseDeposit, 'RecipientRegistry: Incorrect deposit amount'); + requests[recipientId] = Request( + RequestType.Registration, + msg.sender, + block.timestamp, + msg.value, + _recipient, + _metadata + ); + emit RequestSubmitted( + recipientId, + RequestType.Registration, + _recipient, + _metadata, + block.timestamp + ); + } + + /** + * @dev Submit recipient removal request. + * @param _recipientId The ID of recipient. + */ + function removeRecipient(bytes32 _recipientId) + external + payable + { + require(recipients[_recipientId].index != 0, 'RecipientRegistry: Recipient is not in the registry'); + require(recipients[_recipientId].removedAt == 0, 'RecipientRegistry: Recipient already removed'); + require(requests[_recipientId].submissionTime == 0, 'RecipientRegistry: Request already submitted'); + require(msg.value == baseDeposit, 'RecipientRegistry: Incorrect deposit amount'); + requests[_recipientId] = Request( + RequestType.Removal, + msg.sender, + block.timestamp, + msg.value, + address(0), + '' + ); + emit RequestSubmitted( + _recipientId, + RequestType.Removal, + address(0), + '', + block.timestamp + ); + } + + /** + * @dev Reject request. + * @param _recipientId The ID of recipient. + * @param _beneficiary Address to which the deposit should be transferred. + */ + function challengeRequest(bytes32 _recipientId, address payable _beneficiary) + external + onlyOwner + returns (bool) + { + Request memory request = requests[_recipientId]; + require(request.submissionTime != 0, 'RecipientRegistry: Request does not exist'); + delete requests[_recipientId]; + bool isSent = _beneficiary.send(request.deposit); + emit RequestResolved( + _recipientId, + request.requestType, + true, + 0, + block.timestamp + ); + return isSent; + } + + /** + * @dev Execute request. + * @param _recipientId The ID of recipient. + */ + function executeRequest(bytes32 _recipientId) + external + onlyOwner + returns (bool) + { + Request memory request = requests[_recipientId]; + require(request.submissionTime != 0, 'RecipientRegistry: Request does not exist'); + uint256 recipientIndex = 0; + if (request.requestType == RequestType.Removal) { + _removeRecipient(_recipientId); + } else { + // WARNING: Could revert if no slots are available or if recipient limit is not set + recipientIndex = _addRecipient(_recipientId, request.recipientAddress); + } + delete requests[_recipientId]; + bool isSent = request.requester.send(request.deposit); + emit RequestResolved( + _recipientId, + request.requestType, + false, + recipientIndex, + block.timestamp + ); + return isSent; + } +} diff --git a/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifier32Batch16.sol b/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifier32Batch16.sol new file mode 100644 index 000000000..7450710a4 --- /dev/null +++ b/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifier32Batch16.sol @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: MIT + +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// 2019 OKIMS + +pragma solidity ^0.6.12; + +library Pairing { + + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct G1Point { + uint256 X; + uint256 Y; + } + + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + + /* + * @return The negation of p, i.e. p.plus(p.negate()) should be zero. + */ + function negate(G1Point memory p) internal pure returns (G1Point memory) { + + // The prime q in the base field F_q for G1 + if (p.X == 0 && p.Y == 0) { + return G1Point(0, 0); + } else { + return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); + } + } + + /* + * @return The sum of two points of G1 + */ + function plus( + G1Point memory p1, + G1Point memory p2 + ) internal view returns (G1Point memory r) { + + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-add-failed"); + } + + /* + * @return The product of a point on G1 and a scalar, i.e. + * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all + * points p. + */ + function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { + + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + require (success,"pairing-mul-failed"); + } + + /* @return The result of computing the pairing check + * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + * For example, + * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. + */ + function pairing( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2, + G1Point memory d1, + G2Point memory d2 + ) internal view returns (bool) { + + G1Point[4] memory p1 = [a1, b1, c1, d1]; + G2Point[4] memory p2 = [a2, b2, c2, d2]; + + uint256 inputSize = 24; + uint256[] memory input = new uint256[](inputSize); + + for (uint256 i = 0; i < 4; i++) { + uint256 j = i * 6; + input[j + 0] = p1[i].X; + input[j + 1] = p1[i].Y; + input[j + 2] = p2[i].X[0]; + input[j + 3] = p2[i].X[1]; + input[j + 4] = p2[i].Y[0]; + input[j + 5] = p2[i].Y[1]; + } + + uint256[1] memory out; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-opcode-failed"); + + return out[0] != 0; + } +} + +contract BatchUpdateStateTreeVerifier32Batch16 { + + using Pairing for *; + + uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct VerifyingKey { + Pairing.G1Point alpha1; + Pairing.G2Point beta2; + Pairing.G2Point gamma2; + Pairing.G2Point delta2; + Pairing.G1Point[41] IC; + } + + struct Proof { + Pairing.G1Point A; + Pairing.G2Point B; + Pairing.G1Point C; + } + + function verifyingKey() internal pure returns (VerifyingKey memory vk) { + vk.alpha1 = Pairing.G1Point(uint256(17564466972987208178905070198019717960990774560424996549632340406571921270135),uint256(6212564911154525198736769572109918484573926992228075175561121911305420747892)); + vk.beta2 = Pairing.G2Point([uint256(19862806046850266517918019458699375042492414007810580051130360096409146797114),uint256(18968182867766566527308258731979440721408347503223170931357675093063900018998)], [uint256(15869963506744343382954688098759615756353669448820636651988694593208220260541),uint256(17415631041899920918759548249552784090262638044632769170520738845364398275366)]); + vk.gamma2 = Pairing.G2Point([uint256(11257253160245273080869306676658832074325489738113181602057846648381137371076),uint256(397473356291690562654977459050659929392435262209846402221339197403868900174)], [uint256(15964627439216663086041856051177544908720747472786343019771626700991693030486),uint256(6022076246583820501204467975945998225634462297953032723555607903078152261782)]); + vk.delta2 = Pairing.G2Point([uint256(8960304480954470322423292821010723559000712087743155784344677845709031842356),uint256(4733214216315583653992596764777849980658452927149554703439675897776902590030)], [uint256(5883145061333084001583838147325844327393273269643138829803996465957380959561),uint256(6556416943678009124760019244975816690522642072024061675141905579498114037881)]); + vk.IC[0] = Pairing.G1Point(uint256(20573688474783321846811030145444435978946625455817040976400110171429717762313),uint256(14219795410675122323711179893760742300342532333476317858787056167262160908937)); + vk.IC[1] = Pairing.G1Point(uint256(16042051692957428703650515029444172293895511179110788253133560310150814316357),uint256(17413331071024005080031335036071091790041478145914042688818908877361537279793)); + vk.IC[2] = Pairing.G1Point(uint256(4469309738798192251990686173604159186219995479238540395764540658446930749145),uint256(11574488575927551819587207552625323277821402181373682396813667332531657122576)); + vk.IC[3] = Pairing.G1Point(uint256(13663215469207902744973802514353956563905823199937005483772920223690579405487),uint256(14871215385058895610722549990927147904343728800816746147041918066710584226713)); + vk.IC[4] = Pairing.G1Point(uint256(3489292745098913386172335587317749246980343597777688754173176571645664598569),uint256(16900666770697929965250780969454193347188841711403509395696012994572983674828)); + vk.IC[5] = Pairing.G1Point(uint256(2945290910588501179672392707111211440991551718469541150674976815500282424963),uint256(14598879565524130705374188049646292346600280808118113951050422637050501514200)); + vk.IC[6] = Pairing.G1Point(uint256(1743907228984471502641556290643648078257956018892523942117108222472161514139),uint256(13546457107687734244831126558572113644917121420356142865411127965095244393366)); + vk.IC[7] = Pairing.G1Point(uint256(16239926623460462478853653011219290924924349984217262572113572728526722183948),uint256(6389572798636712055906110008459033979858910590912048163453532204275624832591)); + vk.IC[8] = Pairing.G1Point(uint256(4530655587139374397780646343900207609123290875354646335164468398273552121272),uint256(14128028354444670871720348761393076419788117867224991521747385291768930733900)); + vk.IC[9] = Pairing.G1Point(uint256(2084130186412291868004808742213302933245412886080440295285131766970403857381),uint256(13804644315777078738867680048258647864123013819623348345307968440653417669189)); + vk.IC[10] = Pairing.G1Point(uint256(18709486436861021941485824601602744556543964540298118932107637038300041246452),uint256(6227388549172304522473912467840752177496736905518667301640567961284075418436)); + vk.IC[11] = Pairing.G1Point(uint256(19122006749333320301608897562376816558619150023714623953742504350126075003788),uint256(1363782165044296870545293244929009318232323707960615856401281654383175898692)); + vk.IC[12] = Pairing.G1Point(uint256(5364413641687847338151532060627376981772485264434472923538886778626702553325),uint256(15629906454889243991306438607174696870420819153196924767318221349043732920137)); + vk.IC[13] = Pairing.G1Point(uint256(359121925809342098662374393566383044710271325511783297443508988005252057893),uint256(10811152193314929697376755563378270957694626213124806939092423550685049711334)); + vk.IC[14] = Pairing.G1Point(uint256(17981051144466785906880479913215952997275376146299543204378412051670626574122),uint256(9186797238560565226322431760068041435141486516483393586811312602762508934600)); + vk.IC[15] = Pairing.G1Point(uint256(6365374263154884818000869700803640946567044106549610402459455159852388744718),uint256(11797546156554134645751011447880999714231910885220716843380510034965755651040)); + vk.IC[16] = Pairing.G1Point(uint256(10032451025857463031715146956483534885580387223837889532345041361533387806339),uint256(3387493870558732227389076524589538808644245211636504330892809177455235677785)); + vk.IC[17] = Pairing.G1Point(uint256(2410612158467426620067995137390208949985066150538147153449069937898731808572),uint256(18521287304374984854388998234185412497698792859563570797864631992955274102818)); + vk.IC[18] = Pairing.G1Point(uint256(1813841176463493988104135613769097659002362073379920872551589566744485729425),uint256(6358940015006101246169889831008910886341927609866160536081376191976735620689)); + vk.IC[19] = Pairing.G1Point(uint256(19847081327301917802071509303149389020431564544248328496360668988278832485136),uint256(5211553418968503163058788485400452950515983724722138311061753467468280712633)); + vk.IC[20] = Pairing.G1Point(uint256(3426067802106052552538239688068111098552336356878690064196584551340276343362),uint256(16164134703700162701548772292563817322744059513730556911486018167068452826168)); + vk.IC[21] = Pairing.G1Point(uint256(8668312818069908501493412489204108759565349640063306306478085197244829974013),uint256(13810982784687518301385299461854703917878448472895644674481777667257255651320)); + vk.IC[22] = Pairing.G1Point(uint256(981077468077602291838374553840063429865380290334026656328300299263444718358),uint256(9119368190311983873098621061906337470424090352798797307268722991397740756160)); + vk.IC[23] = Pairing.G1Point(uint256(12527168323265871598354886691498363541846438222537709625367385945449773211746),uint256(2917118573022600221023372381853434922615408196327449576428539574935065403243)); + vk.IC[24] = Pairing.G1Point(uint256(10930278986094694298133371790221234279985134460531943692403668317610232742941),uint256(5862695788973409172672967320590899199162083486531717036727412479032235988065)); + vk.IC[25] = Pairing.G1Point(uint256(19143840440293111384177720404724681785293925298123626495518125552351038037165),uint256(7856520125014358900158196027676468930009418746941329024543565879388665656381)); + vk.IC[26] = Pairing.G1Point(uint256(6775239578044551661567862976069748513078619995670391427056172891956633265071),uint256(9284560981899345091778250628921565052467457991819815198504467006412459015751)); + vk.IC[27] = Pairing.G1Point(uint256(5437988353950279901669028370215493448328043979559724253975191086761262229143),uint256(8176067762582906031719418628593932629618591280530549530843556225763407224284)); + vk.IC[28] = Pairing.G1Point(uint256(14419631426965159557658781212186643724248390035688706768554749807493869120240),uint256(20829049296989859000251091454357109695901711580992440150395839575453171214015)); + vk.IC[29] = Pairing.G1Point(uint256(19608141634472932940433918240261639952962553934341785939122543569039402409141),uint256(13202356373118919099990439264234752617330284072473003408798579312466654945225)); + vk.IC[30] = Pairing.G1Point(uint256(6721446556587383910935961059312054388432394977522853801119708229959202737436),uint256(18874416278004109801516351556023467746164986339155152231450545308976800785800)); + vk.IC[31] = Pairing.G1Point(uint256(1121297910398623972014321595193328729838277331998467353747864490201136608978),uint256(5479532420089283113354607976800616803141032716983446523858749471234109786252)); + vk.IC[32] = Pairing.G1Point(uint256(467086760777282035652878041333579903278580939332416688170241256808993547575),uint256(13756152367683309448429375410787943224591772034678008033485038211776826923105)); + vk.IC[33] = Pairing.G1Point(uint256(544588907891071733780736623354853251736286846455666159919529736335009172121),uint256(19814174389629121597516552220557392671724772880943104553900974211505364673700)); + vk.IC[34] = Pairing.G1Point(uint256(9718043170262637281912309305033913756397788551277481886761502969350964449209),uint256(9928286376606476192337438359582609228172024954515187888460390683064495863566)); + vk.IC[35] = Pairing.G1Point(uint256(8833241059924366760602716054754193491983227264165402284693696886993482375792),uint256(20217437076969561492960066126914621838511190476542843609181381577334240082262)); + vk.IC[36] = Pairing.G1Point(uint256(21691301249544308137330827386088690246733997944828733953042133363071032775561),uint256(8416731669909969552797133391600423980357220150298256151928192993549593014572)); + vk.IC[37] = Pairing.G1Point(uint256(18591239855619987960480421772799014085639022706413599497355173036346631326229),uint256(19557170727169515145077122571593152129100237734331890620470088472162864118761)); + vk.IC[38] = Pairing.G1Point(uint256(3862906872967117807500943404548237031139970973065025611287515004824373974308),uint256(14763824391416574387650349848092354763656539119581187921054485482268328744531)); + vk.IC[39] = Pairing.G1Point(uint256(4581382328791187996308811405974001991040756459205750759173122039179987836596),uint256(14577738635095065315453864617647694757772478953718921634232796382928686688380)); + vk.IC[40] = Pairing.G1Point(uint256(17187231740479352852864198251137151477227537702159385928600612471898233918627),uint256(835160958364646681144544132626099284400020971025799680904837349622297335830)); + + } + + /* + * @returns Whether the proof is valid given the hardcoded verifying key + * above and the public inputs + */ + function verifyProof( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[] memory input + ) public view returns (bool) { + + Proof memory proof; + proof.A = Pairing.G1Point(a[0], a[1]); + proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); + proof.C = Pairing.G1Point(c[0], c[1]); + + VerifyingKey memory vk = verifyingKey(); + + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + + // Make sure that proof.A, B, and C are each less than the prime q + require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); + require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); + + require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); + require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); + + require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); + require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); + + require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); + require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); + + // Make sure that every input is less than the snark scalar field + //for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < 40; i++) { + require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field"); + vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i])); + } + + vk_x = Pairing.plus(vk_x, vk.IC[0]); + + return Pairing.pairing( + Pairing.negate(proof.A), + proof.B, + vk.alpha1, + vk.beta2, + vk_x, + vk.gamma2, + proof.C, + vk.delta2 + ); + } +} diff --git a/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifierCustom.sol b/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifierCustom.sol new file mode 100644 index 000000000..48ce34bcd --- /dev/null +++ b/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifierCustom.sol @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: MIT + +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// 2019 OKIMS + +pragma solidity ^0.6.12; + +library Pairing { + + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct G1Point { + uint256 X; + uint256 Y; + } + + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + + /* + * @return The negation of p, i.e. p.plus(p.negate()) should be zero. + */ + function negate(G1Point memory p) internal pure returns (G1Point memory) { + + // The prime q in the base field F_q for G1 + if (p.X == 0 && p.Y == 0) { + return G1Point(0, 0); + } else { + return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); + } + } + + /* + * @return The sum of two points of G1 + */ + function plus( + G1Point memory p1, + G1Point memory p2 + ) internal view returns (G1Point memory r) { + + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-add-failed"); + } + + /* + * @return The product of a point on G1 and a scalar, i.e. + * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all + * points p. + */ + function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { + + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + require (success,"pairing-mul-failed"); + } + + /* @return The result of computing the pairing check + * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + * For example, + * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. + */ + function pairing( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2, + G1Point memory d1, + G2Point memory d2 + ) internal view returns (bool) { + + G1Point[4] memory p1 = [a1, b1, c1, d1]; + G2Point[4] memory p2 = [a2, b2, c2, d2]; + + uint256 inputSize = 24; + uint256[] memory input = new uint256[](inputSize); + + for (uint256 i = 0; i < 4; i++) { + uint256 j = i * 6; + input[j + 0] = p1[i].X; + input[j + 1] = p1[i].Y; + input[j + 2] = p2[i].X[0]; + input[j + 3] = p2[i].X[1]; + input[j + 4] = p2[i].Y[0]; + input[j + 5] = p2[i].Y[1]; + } + + uint256[1] memory out; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-opcode-failed"); + + return out[0] != 0; + } +} + +contract BatchUpdateStateTreeVerifierCustom { + + using Pairing for *; + + uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct VerifyingKey { + Pairing.G1Point alpha1; + Pairing.G2Point beta2; + Pairing.G2Point gamma2; + Pairing.G2Point delta2; + Pairing.G1Point[137] IC; + } + + struct Proof { + Pairing.G1Point A; + Pairing.G2Point B; + Pairing.G1Point C; + } + + function verifyingKey() internal pure returns (VerifyingKey memory vk) { + vk.alpha1 = Pairing.G1Point(uint256(20087353828077202850794044320950857268374503478528972609823484324215331801636),uint256(11476883565453070321867378546975856970885584116909418759035533502511538352926)); + vk.beta2 = Pairing.G2Point([uint256(21661869609286011667004284886876061748883471906320898511156819152590982503460),uint256(1411267331255101216687187759308799712318293061108820897500456502826062760161)], [uint256(242338193632610253406780460721162660970322868717712034766855444702801043068),uint256(11205102304711535815941921385002492265801636744630227197506747841473030702040)]); + vk.gamma2 = Pairing.G2Point([uint256(10703132643546140356186378204895065022814221219458116741589121895614548783664),uint256(8728787808915888886518516188706571827044921488715782720858292899421614548992)], [uint256(21855932176432612007184523640521010134533271824713890942038051024979667517411),uint256(14463000202053145707614344130583620641279820564069360237424059003065520412766)]); + vk.delta2 = Pairing.G2Point([uint256(15511409780586937596469386375448730565918438087848214063975685304482412679403),uint256(2871962587045834446520485474136952467328346328772359835190464961287391630197)], [uint256(15098923887127735110115098383257212634964365431441844211165982132092604642119),uint256(19716506293446006761611333969798166840619850480902019859898831749671195591534)]); + vk.IC[0] = Pairing.G1Point(uint256(13449409323553982807626717659206995893486616914721488975771211075709370315026),uint256(19819167563363500342420294247298414043779583800301883591559879913778994928970)); + vk.IC[1] = Pairing.G1Point(uint256(18288751040873995405829352561880517511670216090222253370470629005274973230224),uint256(3340939551764112714060652679366038538227707806197811714382712640842923887338)); + vk.IC[2] = Pairing.G1Point(uint256(9034324435110195600984520035015153412337260951213866638651602731777208517827),uint256(14546595006162979541631014330847388063705106451104883570324018472333580218369)); + vk.IC[3] = Pairing.G1Point(uint256(18959526910556928462007511646743694022357186454775805231806268761848703787052),uint256(21548913956199741505617644115252315331247918262133746388493422156797029529793)); + vk.IC[4] = Pairing.G1Point(uint256(14667130442384977541051000098091277853515714184455121995211368752877562919286),uint256(1571213305279482731892557325220575745236783105648521604666877211311213326751)); + vk.IC[5] = Pairing.G1Point(uint256(19913405290485444662222555136914754224161963225418121384899196830085445637499),uint256(9353516643529373068293908681832909830620075327436046394651358446880200530845)); + vk.IC[6] = Pairing.G1Point(uint256(4392121493858814884850445257811530682565304094158379534072964024456308711140),uint256(14425403533600244626342298100875789752762767255327078376159416785560008923705)); + vk.IC[7] = Pairing.G1Point(uint256(15817729744441652723145528920073644919288747927823346517846772502038661326087),uint256(6339493756631971029867733380652623802663224827384426871973039193434998926674)); + vk.IC[8] = Pairing.G1Point(uint256(480637294199047307667161992553993337214654072373808050110284394523079810339),uint256(8832148377128939012209474718545762695603645681727887570297879424473451535544)); + vk.IC[9] = Pairing.G1Point(uint256(18744271653088608732764725475608044605973633651736355180626060057128665597247),uint256(10029431604036329130096548676181936710270515833881358240468741367688995476231)); + vk.IC[10] = Pairing.G1Point(uint256(2887974560189403494889743306897414514537070863016749530233722584820661287284),uint256(19531090389757911465455371999342290893522171780307155242713966624430836080666)); + vk.IC[11] = Pairing.G1Point(uint256(14831622056182946369342012331708153541419644376854897007623505162522515905155),uint256(17207180946963880497939168446060614092109855520510825916157704836216098405189)); + vk.IC[12] = Pairing.G1Point(uint256(15506398342704908970868595954855105998758099219541375937953499095105173507550),uint256(1509358924009691430397237655658579260967617755952022251213522582773276442679)); + vk.IC[13] = Pairing.G1Point(uint256(13081356290354186063074749971306070814496882977524291959736906760563684568695),uint256(9884406427032162441347033456153235609148950546399906330857509941718552620268)); + vk.IC[14] = Pairing.G1Point(uint256(719373316591795302151839273680068838979384878504494828773494046765201947280),uint256(4285574473052728017508050004433562406388738477725957133910140811836712357969)); + vk.IC[15] = Pairing.G1Point(uint256(1161961279887123633351369246418294359429428925248242696699854294267840126266),uint256(2753838659155669461065600495917012924384518686391121187033697795542200403972)); + vk.IC[16] = Pairing.G1Point(uint256(13682879487790288634920243025369860399504234851888478701512257545221680936810),uint256(2153246008085528874532592135250171671811203616520867548078143863278493007749)); + vk.IC[17] = Pairing.G1Point(uint256(3516369807590261163059894146447014910302140628998802112065686961872586702551),uint256(5370787160830393410214210351964623688718912410066120987032733452259286528086)); + vk.IC[18] = Pairing.G1Point(uint256(1988524361751778706932724684329605660441921048206095037586339797069472862182),uint256(21114285963513640070787641747881253082556602533623795027313886115253996080647)); + vk.IC[19] = Pairing.G1Point(uint256(2485955165890974183236816009331112192305108639988665043518792297482584311923),uint256(11880640146304852444592379682415385509408598783155502899174478577569880083056)); + vk.IC[20] = Pairing.G1Point(uint256(10219331172860533280779041276433310836289975824303193871202179089572278703569),uint256(7370861950873412157131303975669615391088774540400276783484707074149416805294)); + vk.IC[21] = Pairing.G1Point(uint256(14167728148516477840256196286607935101668320057446161671806500834090072725785),uint256(14874328203966708937121090833723158080118646495824532613915964279405802897922)); + vk.IC[22] = Pairing.G1Point(uint256(6889309751089080036157423235545680142690650792398279314062387575899228869494),uint256(17678911668088463543175270505444996571219698085940587035964935219043301776169)); + vk.IC[23] = Pairing.G1Point(uint256(9673323848791621411824117667951494409448803912571572035911987567276800873692),uint256(8010318233717184439361450385728075741524318837946509278121601177634948395076)); + vk.IC[24] = Pairing.G1Point(uint256(19001216591939800685596329025823328559725214996604900719736845720656499624867),uint256(16800790914194952657872094131443784987321340590188967934648441664397455856225)); + vk.IC[25] = Pairing.G1Point(uint256(14229772670511491842377063841403695319183750623861101302835638264008211544676),uint256(16700086453597824634366081416911323903395382119942186242938984470600767077018)); + vk.IC[26] = Pairing.G1Point(uint256(6190110473128199039318674139275953731627477080237305570664048170694216558607),uint256(9156069687286703875886126396409243836856376303654173871625378309678962742301)); + vk.IC[27] = Pairing.G1Point(uint256(19041950742766772503703484046141610117011636465603587128037213418839536735126),uint256(1741521843922631538075137437516945946494447419662725791349055244115174732397)); + vk.IC[28] = Pairing.G1Point(uint256(20572595967381616358855167208674217372017018727251574275804447670907816537950),uint256(15530155742998922859635289650887433052284249839254842342598193039162339193385)); + vk.IC[29] = Pairing.G1Point(uint256(18894959902692739855443380454453511822218459028123989847552010175424931175589),uint256(21720555445949680264216624728183544115705279456243597689157013642155036265415)); + vk.IC[30] = Pairing.G1Point(uint256(11420223426220804446523491080946075698396582996335464285431689162267487484067),uint256(3181697461117579761185097953588782487765626082815159306282849737563745424506)); + vk.IC[31] = Pairing.G1Point(uint256(2921370814594030903899051421374417440461203550081209203567416200073023145049),uint256(5347812108419343983369408836373704300702310310540619421686782928991729409164)); + vk.IC[32] = Pairing.G1Point(uint256(16741476685225855934358725975984964877988445638736565287964542186206107152293),uint256(4729669602346204655607884191853032759553867998868447356742304646390812496415)); + vk.IC[33] = Pairing.G1Point(uint256(9756675234809793791892705465682087248632694984652554680972716007491924825951),uint256(5988354804897075683722895724359335032053854461455265017672782021744588344814)); + vk.IC[34] = Pairing.G1Point(uint256(19814683865678732851894888194537462976081202780523678223106719506629549258897),uint256(6080471180403505923559123931327801567873683689169241241662583679341260897736)); + vk.IC[35] = Pairing.G1Point(uint256(1438239620527399948189462788892238398578982906275271867592155948061939566626),uint256(2199055174486710563451423451952849431624087101967400585017518019108165853190)); + vk.IC[36] = Pairing.G1Point(uint256(967676148418361677737873476276863318059309477609707671643977589336859176127),uint256(15797276132011461989185803015523111093330042939894033038100054846133562563324)); + vk.IC[37] = Pairing.G1Point(uint256(20201801978563503818789599624676158315679823610998322710056742798817954976063),uint256(11598358783925779109653102359075130468763033066887386333859407419035615301007)); + vk.IC[38] = Pairing.G1Point(uint256(7747526982755731888665585361416626948929515076673700820729567631840982182127),uint256(8266477541281971795125742686488473665271470129792619047143237743745554013710)); + vk.IC[39] = Pairing.G1Point(uint256(19630347857068381357762752321517383973545380130254869958667818061574406509936),uint256(15186347777687718013099982394931225548070830348604080143466772297210051562836)); + vk.IC[40] = Pairing.G1Point(uint256(7557975808969352606285335263414157381982093159126770512582919553699648350483),uint256(6331359472500035804643208799188536087855091113155475777082984912192250759374)); + vk.IC[41] = Pairing.G1Point(uint256(14845008071231916162530646819506161170789752619959011105785671398905933762955),uint256(17510852870482242342381039816675219266744505657530214435696082802116282488658)); + vk.IC[42] = Pairing.G1Point(uint256(13028483425255096939536859978320334835707092501368542039049983266498742408332),uint256(9680955997542002092545710879095660094124123676658363044411252537219803810269)); + vk.IC[43] = Pairing.G1Point(uint256(18570485684385256456511558721236129814674664211827057233856298536476007823563),uint256(6786587425550032533190651930990699335249545132866171624112725170443784236607)); + vk.IC[44] = Pairing.G1Point(uint256(6229010105044576579707704062880618330142301356182755458537022491776084974500),uint256(2958446025628338713087051414787827196521720086407187883901740977668009458474)); + vk.IC[45] = Pairing.G1Point(uint256(19711064707259131885284329850510953278564434575422258680339661582446519996893),uint256(6447334502627514228819588471839200327796435951160054171955070846382297766993)); + vk.IC[46] = Pairing.G1Point(uint256(13654512333629104147605163426403071872951885435411994083936410963051621892949),uint256(17650265592485694925363566185316073499680014776202819473217500634597804211217)); + vk.IC[47] = Pairing.G1Point(uint256(5632109105332974207832397468511779966406880188637740594191372151186045229561),uint256(13651675148237626614923296165038029228539866352571194577844266938252974170084)); + vk.IC[48] = Pairing.G1Point(uint256(8527345386665720309165783434253008921795266063983738469923301602966779024366),uint256(16149187991861855926722833165905235574832072162827813978624809869628415187689)); + vk.IC[49] = Pairing.G1Point(uint256(6646163805935695669626521901182643404621755887577997925506901386112844229851),uint256(11361301962986100960081361654544753510182204509642964951112554856701961156187)); + vk.IC[50] = Pairing.G1Point(uint256(10921559406917075466554221464922756249180248754762588574619694367252428227945),uint256(15987818311916597433092891016570609774371036003985987971567882858356951694123)); + vk.IC[51] = Pairing.G1Point(uint256(19319321803549704705343605501384818081154665651565507251713174153621472515462),uint256(2737644346677906343234185972413750255380030320584877572350390603749165517091)); + vk.IC[52] = Pairing.G1Point(uint256(14097924791420990626067662965994354649057100100900878471771528723878979063670),uint256(3446659685548421144804125954538853863956916787759744530684127569073823331713)); + vk.IC[53] = Pairing.G1Point(uint256(9983862632251067005815597520616536972155239828533016448370610241448628423219),uint256(9425718421842054710526654962601883705369930663657776686776262667638786657422)); + vk.IC[54] = Pairing.G1Point(uint256(2688984403049401342903683721913746062136910340901612180883159738614628664638),uint256(6537385568788079956897582609732708399792014147299594434121004163987996923797)); + vk.IC[55] = Pairing.G1Point(uint256(18265540920707633157018592793425156625083767097873832410185928416315911651112),uint256(614830982358373399190790570742951602645537091079871616861069500149456469620)); + vk.IC[56] = Pairing.G1Point(uint256(12125321699285743073611607029813382470546481051048160495748291470225531864512),uint256(3716293670309368968675908852623314593073639774424731454150351335135845445749)); + vk.IC[57] = Pairing.G1Point(uint256(7853056802721517963036421484032072942693765378562098368306104805678418585162),uint256(6424789894907881712069802715529228349918886636331654615971484908566086243872)); + vk.IC[58] = Pairing.G1Point(uint256(19115005503431091096721184080895329804587690694447705536547788962171345376377),uint256(21684667845604031973485165382669985845242661934129735060595710490022281580308)); + vk.IC[59] = Pairing.G1Point(uint256(5934951296346946320689083498211342144043216739159202457194772865605223848817),uint256(16531730612519172995954542776277328233589925446255179981248179286637232098743)); + vk.IC[60] = Pairing.G1Point(uint256(548179290994039790062273715118266322711869818074110355798782872494554119127),uint256(21277541640969431282210948877991685569792198011788537435034440443744017806814)); + vk.IC[61] = Pairing.G1Point(uint256(5698932685323749446566832552112536072262214364822836541442194088625134869756),uint256(18173025416893411048966167980710924405089325259853820754338009550096212580109)); + vk.IC[62] = Pairing.G1Point(uint256(5407139961162687782789268353002587250953566955217132246179178517076765432715),uint256(15088228606773832436431560223153028924260952727854826128522128552157315934324)); + vk.IC[63] = Pairing.G1Point(uint256(3297833865321076345999149557999739794735986140950154026339694076355675796967),uint256(3411471118249712872884387591128740913549716748840876207700900440280171428377)); + vk.IC[64] = Pairing.G1Point(uint256(7842388686335992312801332510612951894847538866301083390230994724615251547549),uint256(9506065469948872795705994936031211750874514789756601843559917376763183093918)); + vk.IC[65] = Pairing.G1Point(uint256(4095852829188395404575969005812446864304840367703021566476256455754460675693),uint256(8491316200241330102237645830695567828271170242496389953775586915469855840662)); + vk.IC[66] = Pairing.G1Point(uint256(13025537576077929051149126228765064213045713215302857727492279859380396356016),uint256(2692834756506679874200086947427348829159380570066044209560542269051062640881)); + vk.IC[67] = Pairing.G1Point(uint256(8213689088249141668841132843593219869582692686267381807826045996178718589976),uint256(5934750740375389011242980889807579582140412795439731691760487737537146384870)); + vk.IC[68] = Pairing.G1Point(uint256(5064286005858996906539521173567342723294768227186533518332524072316626813796),uint256(18194357467986683563844190921029033308662124754563987268423540451940150494386)); + vk.IC[69] = Pairing.G1Point(uint256(513524361877395586890580626735501312175734658618400473937221160065153077430),uint256(21066430682077818283600250344847770187662959287016517114730950504105800523459)); + vk.IC[70] = Pairing.G1Point(uint256(16131973789317585551032586924024478720306017209112230164516773197156811660827),uint256(2629437023298887657704157265354419011007844685935531259234622748649925287960)); + vk.IC[71] = Pairing.G1Point(uint256(14333443795131676087362902053591471457648753635869483711366294938960766965855),uint256(12375065056354337574541874851953853772434526791527859885521192098306924954605)); + vk.IC[72] = Pairing.G1Point(uint256(8650965935327444683681899304659642514103760946243255832533343127837713901260),uint256(17350766954455027422821468769776856954427397105499750970125107357315321891443)); + vk.IC[73] = Pairing.G1Point(uint256(19127940891316463106484748416155199078020628471209896451231437541817085077431),uint256(9663583369451454043509657828849429547454302647650088620902697913423546564526)); + vk.IC[74] = Pairing.G1Point(uint256(4024045629953114025834675015518977207041945104823465323803797652513143739853),uint256(7099031579319409562306045437060668113626384467426238808017971940237526720957)); + vk.IC[75] = Pairing.G1Point(uint256(19777520349508781241579877240909708384019577017734082523373047015766886330187),uint256(20543687412232979508991763895812193752676648814819172150316512715838932781811)); + vk.IC[76] = Pairing.G1Point(uint256(13381716905414454291256465188057975528662534279036129120834023444787120778828),uint256(83068903289307625321440883120020242942419200390426643332494275740989439527)); + vk.IC[77] = Pairing.G1Point(uint256(7761960949208896426802556459429974793308266350208241353184947107970422491129),uint256(2089026195731535072085584097366292321990391592844991889095562893901021029045)); + vk.IC[78] = Pairing.G1Point(uint256(5197970819517869977641836587074894393807023630746693731371708152450489969498),uint256(15030173127846951102748719502126507555120149267996232824889459685032460633760)); + vk.IC[79] = Pairing.G1Point(uint256(10738029463630555541033436864764967361429379579447728903906800161601920881914),uint256(8890051727148365024606043733688100310356137564792142189316987818514656702210)); + vk.IC[80] = Pairing.G1Point(uint256(12792936448350863485798336539770498877619679941787502943063936842797879294217),uint256(14379669594142278338427457116016736020516627571642516277280265863257375782458)); + vk.IC[81] = Pairing.G1Point(uint256(13559434756703389257582627200705563231522482733893623255451993723994545049976),uint256(14892650996406162560002958956428094083109122401519142177808416014717662579626)); + vk.IC[82] = Pairing.G1Point(uint256(14252276184535708042709518130518888874496340661124769270302165933313034236450),uint256(15273924344508848264258477250341486434993268879902111993744999413719610729096)); + vk.IC[83] = Pairing.G1Point(uint256(9809205799202468521806578646546780450146808562401906118296006228293627684369),uint256(11307415536498866034675798915908732740209657309834816970198638577514763250721)); + vk.IC[84] = Pairing.G1Point(uint256(21705327260612726721607027572593333828131899563073285225389452443571564581741),uint256(21018596752445529907566552196456728940837044358621700846827205193330981345608)); + vk.IC[85] = Pairing.G1Point(uint256(8726253405089330847191862720082852744524984989277533298149093584335630612059),uint256(18486186530998050985018181700908491808421033831981323727450435858762932718129)); + vk.IC[86] = Pairing.G1Point(uint256(6503536483724878233870656193473281418950363845795000055683485451008630495010),uint256(20373536533546802032215725505482963077326337719927421078346183177571358001238)); + vk.IC[87] = Pairing.G1Point(uint256(21362345493034032487313388146136193327479786529066051395403594100453963769111),uint256(5659302265151057957108067254997946560345397781011169944959956043052574665674)); + vk.IC[88] = Pairing.G1Point(uint256(7449550964742803340675630812325796218565644913828914465913881384357756681548),uint256(17292064125777099006635793940096466365126227952794001913174478930939581123256)); + vk.IC[89] = Pairing.G1Point(uint256(6125497125689284067831318181472617987031052184276342829492439288589510595502),uint256(12585026108513932953515017993201613768584424358528209508664951085853306523754)); + vk.IC[90] = Pairing.G1Point(uint256(12762245270309389117737641685449675404740403315746754385981092875802564011552),uint256(4776105253784521029665216303967098003880083781647370925393247521792397407888)); + vk.IC[91] = Pairing.G1Point(uint256(13807938458651671227739057402325558662935793724423758150238166950827759630565),uint256(8407443299360885769442648551918604539455371547608849811398829544820103779159)); + vk.IC[92] = Pairing.G1Point(uint256(19354271469665911556037183311554687370650863636736009934227267454117678598867),uint256(2889581474166896849215445080012200130279831332202980472677608450870018824419)); + vk.IC[93] = Pairing.G1Point(uint256(6226227238242660704404188961830279826158507659691896952725574118929530402933),uint256(16450115825870066758814214991430148198082508764837404422948743285414888884178)); + vk.IC[94] = Pairing.G1Point(uint256(4780964847774203341666353527075414079062026230513049950866837627817139518944),uint256(16859068361144747142625578004213384508540191114869687793312011678232224372225)); + vk.IC[95] = Pairing.G1Point(uint256(8062525302997605008090891400414607999817592945480209521332494130391040549486),uint256(10042482661997039621263546535097387401245876819894143240347776832509613454998)); + vk.IC[96] = Pairing.G1Point(uint256(4156112324011039404668548707912082198272430690748058804543223692230235584010),uint256(6467835632275616211076158221632969554420082266873705242098900605173284788072)); + vk.IC[97] = Pairing.G1Point(uint256(16978493603420177201284890469513822737364101412764684161147950679395900370490),uint256(4804013104215913645504042647931299604664370881965529654982744609336873815572)); + vk.IC[98] = Pairing.G1Point(uint256(20063224436134296487145192369456053667043930173386619477170690006672425660230),uint256(8018762550302361989416661307519944480135763273042015031096306898128032672769)); + vk.IC[99] = Pairing.G1Point(uint256(9381136972738249930310747728613628512869836321932371408132641447585727108675),uint256(13792274675162440241090306118979937533255670775110959353619238438903042099483)); + vk.IC[100] = Pairing.G1Point(uint256(17249799750153971053356997293380265081163205379000759347308627353711859528438),uint256(11860582928346589952937516820758334143823517059789983241580798089780599031873)); + vk.IC[101] = Pairing.G1Point(uint256(2209125375494676829996593878418698944200062459109379104033878964709019186550),uint256(9044941556620400719209087348338656682667735114334848800591128202753917262364)); + vk.IC[102] = Pairing.G1Point(uint256(7905831048152572475855614559007206580981737455003199081628859335938538130184),uint256(3219362210026178835574532431902129534204421524894508591333411433674437426662)); + vk.IC[103] = Pairing.G1Point(uint256(10034645824488683370676189228201474691737684065016222057492787707541855843562),uint256(10962875262870711754286714288576688333775621455833092480063924261333137338082)); + vk.IC[104] = Pairing.G1Point(uint256(16681217492767809220626946625462893148742282263278770454356009745085326859036),uint256(8609765386746653380889813599059419164123726125803365736957612935297948115084)); + vk.IC[105] = Pairing.G1Point(uint256(10409861984682249634638249483107543711654774759190644297788218352203797832971),uint256(10963947759662935212884888830847146879486435953717902604694788526569408050896)); + vk.IC[106] = Pairing.G1Point(uint256(19610116640561738573121735873270623412842636275783845690930764829525483185517),uint256(19999697268734757669286329323254853862322933620601846163862344221876819142639)); + vk.IC[107] = Pairing.G1Point(uint256(20796538276246959844610503898053705465834091935725306697900595861453314277534),uint256(8463040972818515190474433150965544533570162872588257416128796378472361625254)); + vk.IC[108] = Pairing.G1Point(uint256(16242734927529699680977092850391685525876857116431871118405705879284460279923),uint256(233800030226637912442083358100093812963916941762884764432747765761205593722)); + vk.IC[109] = Pairing.G1Point(uint256(575299231355200429649645736454601450683561619418476466277518167882449241582),uint256(16441392148980496339439815763952122005106502317757024622981193995288053243753)); + vk.IC[110] = Pairing.G1Point(uint256(1603299324276303191667584100848645563589905602358091967172803717926108919833),uint256(2923207606433658024984614511442599369631740064463368057365647551116862475865)); + vk.IC[111] = Pairing.G1Point(uint256(15612397606496480900845188862617925195166889100242281072974188587056786460685),uint256(6067268371068187386960150210701543773298636595460683602669641801140055222693)); + vk.IC[112] = Pairing.G1Point(uint256(11716278844761725707707926991887023640732693999845301976833122906763630000470),uint256(4785089586353316211662706302146360930361614633336109347707536468281339428146)); + vk.IC[113] = Pairing.G1Point(uint256(13583970090332558444106411973400436007441644550158519399118705791407995280322),uint256(19644166569147988073283886636967542007943692023233562457318835255034799889940)); + vk.IC[114] = Pairing.G1Point(uint256(1686045902562150190863786039249272152595323263993869676824656256416347524970),uint256(17748495582430458497854085558648682169890368554794356546738567734485590533503)); + vk.IC[115] = Pairing.G1Point(uint256(14018407513023200909083059584310902264885930292989350049846251819450636644515),uint256(15282365300853626656506664284946919761222329988945904085162429142333058688432)); + vk.IC[116] = Pairing.G1Point(uint256(16724562362615523120167011908918353532981098883153390523539421723425152477307),uint256(843622462409191805919999628441033590397683131077427864110140693629784330140)); + vk.IC[117] = Pairing.G1Point(uint256(9317229908502429358451115076979763649356526034355777152382574378803867793407),uint256(2617787134893809524818335754594505537496659392456978184732584754140270601161)); + vk.IC[118] = Pairing.G1Point(uint256(12643564839060300175073382088429811648928756114554035624702422497673075822811),uint256(15646981702412923801232315502751915516527154519138252804608886597168220251051)); + vk.IC[119] = Pairing.G1Point(uint256(16136179725000153445080279536165216574525067823359334408100828869622561025204),uint256(710304109108463984900640046876452138580862272055110902990476351162646910693)); + vk.IC[120] = Pairing.G1Point(uint256(692942860249784705898329710403028922082659734627553976311078555856071631087),uint256(11843201175511394674224882314719744090005823775711198123908970674468441340626)); + vk.IC[121] = Pairing.G1Point(uint256(11779866285886477058233262617181102875810432515236086165301634888647796468285),uint256(20304904017079364293528985902914652711056094838577523831107251186430000376103)); + vk.IC[122] = Pairing.G1Point(uint256(16741048327094051449850380442322997638089566456733427175028993526920079216552),uint256(469168782820627354614016869964037684089616570710843167108196420227532543358)); + vk.IC[123] = Pairing.G1Point(uint256(8192970453232272547440654687523742209402660277066973787541850715511374651622),uint256(15378234133052651769174717524827877745034818735188283718621316832075692924406)); + vk.IC[124] = Pairing.G1Point(uint256(10874114257144317851900054481205527956050976138359009857985393174723062892676),uint256(12717308684825512964345073222442623220963599686472628674634895201106020201890)); + vk.IC[125] = Pairing.G1Point(uint256(5267065588819698865144857558764204428263327949346016006240918993613635535317),uint256(9504308288810723489809880319665125296646318783448726885811570230750183852840)); + vk.IC[126] = Pairing.G1Point(uint256(19589750306950764739097899155765349671907282871881276201801496319992047650746),uint256(19355465427953889761240746637309186677085883165092990254569077723585487580684)); + vk.IC[127] = Pairing.G1Point(uint256(17157506086704891040661746569169794341719382632415432679454534292409879487085),uint256(19897635624356405012232528284119658467695828553967677022062327424627204803193)); + vk.IC[128] = Pairing.G1Point(uint256(20238858483410373364817397805629956894825450075652394120977179046203337104373),uint256(6874509773395989359964200584287462074246787158135629964693169157409514901534)); + vk.IC[129] = Pairing.G1Point(uint256(12042579617996728748165735987255778752470955736200812791100866788721737191632),uint256(11956625767696871244461941300661106065113240619864391737851713805635374157021)); + vk.IC[130] = Pairing.G1Point(uint256(16548366919176947814040435329595118023993324878565761882418197223671907573357),uint256(10620821967136310262692802689974809502198526374547943939269158284384653114350)); + vk.IC[131] = Pairing.G1Point(uint256(8175466437668934308709975762665321770552084750706095349920295311638284090340),uint256(17425364680901154207661740679315488758276320872402168607111940050845887976471)); + vk.IC[132] = Pairing.G1Point(uint256(9198147470468043329255739956904344209746369324919711050881466969196801966276),uint256(20298732777222909816627151550361882672333769232236128952325518941075042836010)); + vk.IC[133] = Pairing.G1Point(uint256(15904946907253788347044186320302300871858274132480685493537494288950980594193),uint256(15409483562413553293784442699758192278775213388937158234684578475424442082364)); + vk.IC[134] = Pairing.G1Point(uint256(14220065024533152210243662627649996829439850016253669280094507063796589016407),uint256(2782695826772336437168593281979032224437557674189704274938530289022757554551)); + vk.IC[135] = Pairing.G1Point(uint256(3439664554786124907619188183455389534759238310861135614302099160907607051269),uint256(20990451399839802618416331354962424116625631351552047454336086879944775872991)); + vk.IC[136] = Pairing.G1Point(uint256(17322663660000121892857220907135345561983364271033777284327298998169328112152),uint256(8518935329747198938862986634975284512581273824227035851902576752634386127933)); + + } + + /* + * @returns Whether the proof is valid given the hardcoded verifying key + * above and the public inputs + */ + function verifyProof( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[] memory input + ) public view returns (bool) { + + Proof memory proof; + proof.A = Pairing.G1Point(a[0], a[1]); + proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); + proof.C = Pairing.G1Point(c[0], c[1]); + + VerifyingKey memory vk = verifyingKey(); + + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + + // Make sure that proof.A, B, and C are each less than the prime q + require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); + require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); + + require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); + require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); + + require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); + require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); + + require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); + require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); + + // Make sure that every input is less than the snark scalar field + //for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < 136; i++) { + require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field"); + vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i])); + } + + vk_x = Pairing.plus(vk_x, vk.IC[0]); + + return Pairing.pairing( + Pairing.negate(proof.A), + proof.B, + vk.alpha1, + vk.beta2, + vk_x, + vk.gamma2, + proof.C, + vk.delta2 + ); + } +} diff --git a/contracts/contracts/snarkVerifiers/QuadVoteTallyVerifier32Batch16.sol b/contracts/contracts/snarkVerifiers/QuadVoteTallyVerifier32Batch16.sol new file mode 100644 index 000000000..4c0cb2f1f --- /dev/null +++ b/contracts/contracts/snarkVerifiers/QuadVoteTallyVerifier32Batch16.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: MIT + +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// 2019 OKIMS + +pragma solidity ^0.6.12; + +library Pairing { + + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct G1Point { + uint256 X; + uint256 Y; + } + + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + + /* + * @return The negation of p, i.e. p.plus(p.negate()) should be zero. + */ + function negate(G1Point memory p) internal pure returns (G1Point memory) { + + // The prime q in the base field F_q for G1 + if (p.X == 0 && p.Y == 0) { + return G1Point(0, 0); + } else { + return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); + } + } + + /* + * @return The sum of two points of G1 + */ + function plus( + G1Point memory p1, + G1Point memory p2 + ) internal view returns (G1Point memory r) { + + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-add-failed"); + } + + /* + * @return The product of a point on G1 and a scalar, i.e. + * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all + * points p. + */ + function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { + + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + require (success,"pairing-mul-failed"); + } + + /* @return The result of computing the pairing check + * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + * For example, + * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. + */ + function pairing( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2, + G1Point memory d1, + G2Point memory d2 + ) internal view returns (bool) { + + G1Point[4] memory p1 = [a1, b1, c1, d1]; + G2Point[4] memory p2 = [a2, b2, c2, d2]; + + uint256 inputSize = 24; + uint256[] memory input = new uint256[](inputSize); + + for (uint256 i = 0; i < 4; i++) { + uint256 j = i * 6; + input[j + 0] = p1[i].X; + input[j + 1] = p1[i].Y; + input[j + 2] = p2[i].X[0]; + input[j + 3] = p2[i].X[1]; + input[j + 4] = p2[i].Y[0]; + input[j + 5] = p2[i].Y[1]; + } + + uint256[1] memory out; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-opcode-failed"); + + return out[0] != 0; + } +} + +contract QuadVoteTallyVerifier32Batch16 { + + using Pairing for *; + + uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct VerifyingKey { + Pairing.G1Point alpha1; + Pairing.G2Point beta2; + Pairing.G2Point gamma2; + Pairing.G2Point delta2; + Pairing.G1Point[11] IC; + } + + struct Proof { + Pairing.G1Point A; + Pairing.G2Point B; + Pairing.G1Point C; + } + + function verifyingKey() internal pure returns (VerifyingKey memory vk) { + vk.alpha1 = Pairing.G1Point(uint256(3931802286032916768722587566330261370902955393767942929056531203487688137529),uint256(10293105233586296031473050653492052327805448683376110761534611791713915549265)); + vk.beta2 = Pairing.G2Point([uint256(21113959492300078886023582393043413128535975125428858805958839308822991748856),uint256(12222282698476210310273536080661281164688722800089362655366747391082242682958)], [uint256(20739573447354048976161197946493569928714465565589532971602923073536082697608),uint256(12941541312444627642958656716514029404685754754869818026526533196090365546374)]); + vk.gamma2 = Pairing.G2Point([uint256(14670836137271604202540255380769830849745744579684969689183516705496317922507),uint256(12178657156513808651243577987886528335149661869282225179912079606061386989744)], [uint256(1125902728883689137508324551765647737011904363437526425591650949891310723812),uint256(15919834918458423371681379777897287057084498811382451098590568497815773165692)]); + vk.delta2 = Pairing.G2Point([uint256(2231852960373618563799433391860999041123211180191070579878255252716013298732),uint256(14291274065364399133654336098495355501982202302398283580502954673818060696633)], [uint256(3168628806727684542778047539988455291220201924183716864807010176642260685841),uint256(12606002808572759608577415926932586006638023328815450374325080704700677189688)]); + vk.IC[0] = Pairing.G1Point(uint256(12848020380718535565089853534681145843006092696634142199856299025847321502371),uint256(6468756580219346512614969481554956146762400832923090074339557565951026058536)); + vk.IC[1] = Pairing.G1Point(uint256(789092430114940371944840041796419370450123967868354406244601329700742570445),uint256(11703230415288173665088837798624967250284180660322246777462631600764972864812)); + vk.IC[2] = Pairing.G1Point(uint256(16252197430844492890521435281772316410665185290137018091020232186750112907512),uint256(20861485175504002710376158881527553699531789728793309486150649246737774028347)); + vk.IC[3] = Pairing.G1Point(uint256(16969171625806775801891191965047460974818115969312194891897374689668597542196),uint256(14389419046525510722177847778450425484834864589330386321604392542455541983572)); + vk.IC[4] = Pairing.G1Point(uint256(13928883789499754049998767198742842124977905594692254232979837689918838899511),uint256(6757216204221511030872544186493375503384465407204524181513380457112801460878)); + vk.IC[5] = Pairing.G1Point(uint256(12615105472464956174046705416720445236758313003314061110048664932376957788951),uint256(1115476865907623432334995719744390855110066393577587591466560011685797098103)); + vk.IC[6] = Pairing.G1Point(uint256(12126180897004602060892141406139130628195608764592739755066384985876875328223),uint256(837414672224275155302376389224725114262382901229023048656048324984574980028)); + vk.IC[7] = Pairing.G1Point(uint256(721442001352764820041409242091349606527760014067614573870735409795650532250),uint256(5871690341119940542207233131936464616602051666920986699510353544932455895913)); + vk.IC[8] = Pairing.G1Point(uint256(11936590707137322489603100954274435093115715779097755089203405884503252799861),uint256(5832382048375298946996376174464817616061448358844675910441699738844607159400)); + vk.IC[9] = Pairing.G1Point(uint256(1150487096467611973629613424410317323826245588905051816727229632029031650443),uint256(19621934380117246037511579161242972091034040331100068879508644849318614270487)); + vk.IC[10] = Pairing.G1Point(uint256(14447725242028063610944438927433683612461991907837633029384208510392253681728),uint256(15642702797143402072429225245488363130997179355079100914641555016655302069615)); + + } + + /* + * @returns Whether the proof is valid given the hardcoded verifying key + * above and the public inputs + */ + function verifyProof( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[] memory input + ) public view returns (bool) { + + Proof memory proof; + proof.A = Pairing.G1Point(a[0], a[1]); + proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); + proof.C = Pairing.G1Point(c[0], c[1]); + + VerifyingKey memory vk = verifyingKey(); + + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + + // Make sure that proof.A, B, and C are each less than the prime q + require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); + require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); + + require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); + require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); + + require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); + require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); + + require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); + require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); + + // Make sure that every input is less than the snark scalar field + //for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < 10; i++) { + require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field"); + vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i])); + } + + vk_x = Pairing.plus(vk_x, vk.IC[0]); + + return Pairing.pairing( + Pairing.negate(proof.A), + proof.B, + vk.alpha1, + vk.beta2, + vk_x, + vk.gamma2, + proof.C, + vk.delta2 + ); + } +} diff --git a/contracts/contracts/snarkVerifiers/QuadVoteTallyVerifierCustom.sol b/contracts/contracts/snarkVerifiers/QuadVoteTallyVerifierCustom.sol new file mode 100644 index 000000000..a6a396654 --- /dev/null +++ b/contracts/contracts/snarkVerifiers/QuadVoteTallyVerifierCustom.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: MIT + +// Copyright 2017 Christian Reitwiessner +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. + +// 2019 OKIMS + +pragma solidity ^0.6.12; + +library Pairing { + + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct G1Point { + uint256 X; + uint256 Y; + } + + // Encoding of field elements is: X[0] * z + X[1] + struct G2Point { + uint256[2] X; + uint256[2] Y; + } + + /* + * @return The negation of p, i.e. p.plus(p.negate()) should be zero. + */ + function negate(G1Point memory p) internal pure returns (G1Point memory) { + + // The prime q in the base field F_q for G1 + if (p.X == 0 && p.Y == 0) { + return G1Point(0, 0); + } else { + return G1Point(p.X, PRIME_Q - (p.Y % PRIME_Q)); + } + } + + /* + * @return The sum of two points of G1 + */ + function plus( + G1Point memory p1, + G1Point memory p2 + ) internal view returns (G1Point memory r) { + + uint256[4] memory input; + input[0] = p1.X; + input[1] = p1.Y; + input[2] = p2.X; + input[3] = p2.Y; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 6, input, 0xc0, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-add-failed"); + } + + /* + * @return The product of a point on G1 and a scalar, i.e. + * p == p.scalar_mul(1) and p.plus(p) == p.scalar_mul(2) for all + * points p. + */ + function scalar_mul(G1Point memory p, uint256 s) internal view returns (G1Point memory r) { + + uint256[3] memory input; + input[0] = p.X; + input[1] = p.Y; + input[2] = s; + bool success; + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 7, input, 0x80, r, 0x60) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + require (success,"pairing-mul-failed"); + } + + /* @return The result of computing the pairing check + * e(p1[0], p2[0]) * .... * e(p1[n], p2[n]) == 1 + * For example, + * pairing([P1(), P1().negate()], [P2(), P2()]) should return true. + */ + function pairing( + G1Point memory a1, + G2Point memory a2, + G1Point memory b1, + G2Point memory b2, + G1Point memory c1, + G2Point memory c2, + G1Point memory d1, + G2Point memory d2 + ) internal view returns (bool) { + + G1Point[4] memory p1 = [a1, b1, c1, d1]; + G2Point[4] memory p2 = [a2, b2, c2, d2]; + + uint256 inputSize = 24; + uint256[] memory input = new uint256[](inputSize); + + for (uint256 i = 0; i < 4; i++) { + uint256 j = i * 6; + input[j + 0] = p1[i].X; + input[j + 1] = p1[i].Y; + input[j + 2] = p2[i].X[0]; + input[j + 3] = p2[i].X[1]; + input[j + 4] = p2[i].Y[0]; + input[j + 5] = p2[i].Y[1]; + } + + uint256[1] memory out; + bool success; + + // solium-disable-next-line security/no-inline-assembly + assembly { + success := staticcall(sub(gas(), 2000), 8, add(input, 0x20), mul(inputSize, 0x20), out, 0x20) + // Use "invalid" to make gas estimation work + switch success case 0 { invalid() } + } + + require(success,"pairing-opcode-failed"); + + return out[0] != 0; + } +} + +contract QuadVoteTallyVerifierCustom { + + using Pairing for *; + + uint256 constant SNARK_SCALAR_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617; + uint256 constant PRIME_Q = 21888242871839275222246405745257275088696311157297823662689037894645226208583; + + struct VerifyingKey { + Pairing.G1Point alpha1; + Pairing.G2Point beta2; + Pairing.G2Point gamma2; + Pairing.G2Point delta2; + Pairing.G1Point[11] IC; + } + + struct Proof { + Pairing.G1Point A; + Pairing.G2Point B; + Pairing.G1Point C; + } + + function verifyingKey() internal pure returns (VerifyingKey memory vk) { + vk.alpha1 = Pairing.G1Point(uint256(16943336223702478034729513866374796851228809856161008919800913520856468181573),uint256(2995090004627639065569871279932395584911899798711981190091501417285102191177)); + vk.beta2 = Pairing.G2Point([uint256(16224714118819881643530609308327988952043965596591357129331660443749437880225),uint256(18713080770238836914085757668180363883442449561648958290173009680301883643312)], [uint256(22349732573235212308155999603848231077445494488349558443704557051018923559),uint256(10320633006958853808156951459730803259989331403375988741484169048853496829865)]); + vk.gamma2 = Pairing.G2Point([uint256(13754685977183525838201298718645487522999887715054765691404125733830578085397),uint256(6942074706854035824685190568710272703891322307475144729076362801105779834805)], [uint256(5140158617774088963306972183201877929593031398407933973109566549607124831751),uint256(13035987335313190818598467074385857301795834547346013393313010045759712554988)]); + vk.delta2 = Pairing.G2Point([uint256(8060558336832861646731122586124893496721636529597338211955002623228447877246),uint256(11266091873376325088415037049219024114103234301565804309675027383590258801637)], [uint256(14778268722321702691286210615443377029665021234872720602744008230960335632554),uint256(159950337696748857596651038995043496935979997396302102605500326482927324500)]); + vk.IC[0] = Pairing.G1Point(uint256(16066585270605647433163972725844364442561475974556889399601021622371369271144),uint256(12676191990324881131751023451007364920714885702228311161299501530006872841519)); + vk.IC[1] = Pairing.G1Point(uint256(8741724346703266580019385312120367474943720529314996811441101272835876516470),uint256(17031176066038457326863997348851727124563803542303563511325983859631762911934)); + vk.IC[2] = Pairing.G1Point(uint256(18066750137770170845946733820200480964706781331673770545419839050076884756612),uint256(9347679342275024796450198445411694201271359973979853238013376665002795681996)); + vk.IC[3] = Pairing.G1Point(uint256(12615311434592602984490157419406590901170885801626249228431749604898753676825),uint256(14923844847934190225877627597124089862553879390935260800034269416514426560258)); + vk.IC[4] = Pairing.G1Point(uint256(20169822918031976507682131782892646776809554001149512021190639599804349326690),uint256(16500262714506045360275229153359308289644927300370679736657829361303433615345)); + vk.IC[5] = Pairing.G1Point(uint256(17087302943942300743176792228086268327696285793900716344408973607382655072319),uint256(18235408216267296600740961289869198243767504308584360263468406970385003239783)); + vk.IC[6] = Pairing.G1Point(uint256(19545026119096666217656647361860838923223293971001406747125714826757828149809),uint256(6559080491999014353344865620771583944769725829299641810721028046777843584264)); + vk.IC[7] = Pairing.G1Point(uint256(19060100374012958103958897290158267767597513329256041185857758273030309802587),uint256(9555898096763236508022594162106408962958705890577795983031819651275877959038)); + vk.IC[8] = Pairing.G1Point(uint256(11259424594567992022915148822616114808210551795687973599385042152935186891352),uint256(8847372443318686693647511285032355230775674903720747383283857986227997292264)); + vk.IC[9] = Pairing.G1Point(uint256(20034938052071061170101345512267783478448487278351248710639589348238685776221),uint256(20456838147183647185441901749475039833247081714013147449136222686509036222717)); + vk.IC[10] = Pairing.G1Point(uint256(14726963331140212908790922462256415603139476517884646197732123280907881194129),uint256(19499071686152952417621806302814788689759796377680900572072370183655072718752)); + + } + + /* + * @returns Whether the proof is valid given the hardcoded verifying key + * above and the public inputs + */ + function verifyProof( + uint256[2] memory a, + uint256[2][2] memory b, + uint256[2] memory c, + uint256[] memory input + ) public view returns (bool) { + + Proof memory proof; + proof.A = Pairing.G1Point(a[0], a[1]); + proof.B = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]); + proof.C = Pairing.G1Point(c[0], c[1]); + + VerifyingKey memory vk = verifyingKey(); + + // Compute the linear combination vk_x + Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0); + + // Make sure that proof.A, B, and C are each less than the prime q + require(proof.A.X < PRIME_Q, "verifier-aX-gte-prime-q"); + require(proof.A.Y < PRIME_Q, "verifier-aY-gte-prime-q"); + + require(proof.B.X[0] < PRIME_Q, "verifier-bX0-gte-prime-q"); + require(proof.B.Y[0] < PRIME_Q, "verifier-bY0-gte-prime-q"); + + require(proof.B.X[1] < PRIME_Q, "verifier-bX1-gte-prime-q"); + require(proof.B.Y[1] < PRIME_Q, "verifier-bY1-gte-prime-q"); + + require(proof.C.X < PRIME_Q, "verifier-cX-gte-prime-q"); + require(proof.C.Y < PRIME_Q, "verifier-cY-gte-prime-q"); + + // Make sure that every input is less than the snark scalar field + //for (uint256 i = 0; i < input.length; i++) { + for (uint256 i = 0; i < 10; i++) { + require(input[i] < SNARK_SCALAR_FIELD,"verifier-gte-snark-scalar-field"); + vk_x = Pairing.plus(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i])); + } + + vk_x = Pairing.plus(vk_x, vk.IC[0]); + + return Pairing.pairing( + Pairing.negate(proof.A), + proof.B, + vk.alpha1, + vk.beta2, + vk_x, + vk.gamma2, + proof.C, + vk.delta2 + ); + } +} diff --git a/vue-app/.env.example b/vue-app/.env.example index ed8ba9ee7..0ca3ec51c 100644 --- a/vue-app/.env.example +++ b/vue-app/.env.example @@ -19,6 +19,10 @@ VUE_APP_USER_REGISTRY_TYPE=simple # Learn more about BrightID and context in /docs/brightid.md VUE_APP_BRIGHTID_CONTEXT=clr.fund +# Optional matching pool address to override the funding round factory address +# e.g. if using a safe multisig for the matching pool +VUE_APP_MATCHING_POOL_ADDRESS= + # Supported values: simple, optimistic, kleros VUE_APP_RECIPIENT_REGISTRY_TYPE=simple @@ -30,7 +34,12 @@ VUE_APP_EXTRA_ROUNDS= # Operator of clr.fund instance VUE_APP_OPERATOR= +# Index of first round to consider +VUE_APP_FIRST_ROUND= + # Google Service Account credentials in JSON format GOOGLE_APPLICATION_CREDENTIALS= # Spreadsheet ID to send recipients data -VUE_APP_GOOGLE_SPREADSHEET_ID= \ No newline at end of file +VUE_APP_GOOGLE_SPREADSHEET_ID= +# Select the sheet's name to write the data, by default 'Raw' +GOOGLE_SHEET_NAME= \ No newline at end of file diff --git a/vue-app/src/App.vue b/vue-app/src/App.vue index 47d53d00a..f9d3ff55e 100644 --- a/vue-app/src/App.vue +++ b/vue-app/src/App.vue @@ -58,15 +58,18 @@ import { LOAD_CONTRIBUTOR_DATA, LOGIN_USER, LOAD_FACTORY_INFO, + LOAD_MACI_FACTORY_INFO, + LOAD_BRIGHT_ID, } from '@/store/action-types' import { SET_CURRENT_USER } from '@/store/mutation-types' +import { operator } from '@/api/core' @Component({ name: 'clr.fund', metaInfo() { return { title: this.$route.meta.title, - titleTemplate: 'clr.fund - %s', + titleTemplate: `${operator} - %s`, meta: [ { name: 'git-commit', @@ -111,6 +114,7 @@ export default class App extends Vue { await this.$store.dispatch(SELECT_ROUND, roundAddress) this.$store.dispatch(LOAD_ROUND_INFO) this.$store.dispatch(LOAD_FACTORY_INFO) + this.$store.dispatch(LOAD_MACI_FACTORY_INFO) await this.$store.dispatch(LOAD_RECIPIENT_REGISTRY_INFO) } @@ -124,15 +128,30 @@ export default class App extends Vue { loginUser = async () => { if (!this.$web3.user) return + // Connect & auth to gun db + await this.$store.dispatch(LOGIN_USER, this.$web3.user) + this.$store.commit(SET_CURRENT_USER, this.$web3.user) - await this.$store.dispatch(LOGIN_USER) this.$store.dispatch(LOAD_USER_INFO) - if (this.$store.state.currentRound) { - // Load cart & contributor data for current round - this.$store.dispatch(LOAD_CART) - this.$store.dispatch(LOAD_COMMITTED_CART) - this.$store.dispatch(LOAD_CONTRIBUTOR_DATA) + this.$store.dispatch(LOAD_BRIGHT_ID) + } + + @Watch('isUserAndRoundLoaded') + loadUserRoundData = async () => { + if (!this.isUserAndRoundLoaded) { + return } + + this.$store.dispatch(LOAD_USER_INFO) + + // Load cart & contributor data for current round + this.$store.dispatch(LOAD_CART) + this.$store.dispatch(LOAD_COMMITTED_CART) + this.$store.dispatch(LOAD_CONTRIBUTOR_DATA) + } + + get isUserAndRoundLoaded(): boolean { + return !!this.currentUser && !!this.$store.state.currentRound } @Watch('$store.state.theme') @@ -194,6 +213,7 @@ export default class App extends Vue { const excludedRoutes = [ 'landing', 'join', + 'join-step', 'transaction-success', 'verify', 'project-added', diff --git a/vue-app/src/api/abi.ts b/vue-app/src/api/abi.ts index 4a511c12d..f8ffc89d8 100644 --- a/vue-app/src/api/abi.ts +++ b/vue-app/src/api/abi.ts @@ -1,6 +1,7 @@ import { abi as ERC20 } from '../../../contracts/build/contracts/@openzeppelin/contracts/token/ERC20/ERC20.sol/ERC20.json' import { abi as FundingRoundFactory } from '../../../contracts/build/contracts/contracts/FundingRoundFactory.sol/FundingRoundFactory.json' import { abi as FundingRound } from '../../../contracts/build/contracts/contracts/FundingRound.sol/FundingRound.json' +import { abi as MACIFactory } from '../../../contracts/build/contracts/contracts/MACIFactory.sol/MACIFactory.json' import { abi as MACI } from '../../../contracts/build/contracts/maci-contracts/sol/MACI.sol/MACI.json' import { abi as UserRegistry } from '../../../contracts/build/contracts/contracts/userRegistry/IUserRegistry.sol/IUserRegistry.json' import { abi as BrightIdUserRegistry } from '../../../contracts/build/contracts/contracts/userRegistry/BrightIdUserRegistry.sol/BrightIdUserRegistry.json' @@ -13,6 +14,7 @@ export { ERC20, FundingRoundFactory, FundingRound, + MACIFactory, MACI, UserRegistry, BrightIdUserRegistry, diff --git a/vue-app/src/api/contributions.ts b/vue-app/src/api/contributions.ts index 179dfc727..32e92df79 100644 --- a/vue-app/src/api/contributions.ts +++ b/vue-app/src/api/contributions.ts @@ -5,7 +5,8 @@ import { TransactionResponse } from '@ethersproject/abstract-provider' import { Keypair, PrivKey } from 'maci-domainobjs' import { RoundInfo } from './round' -import { FundingRound } from './abi' +import { FundingRound, ERC20 } from './abi' +import { factory, provider } from './core' import { Project } from './projects' import sdk from '@/graphql/sdk' @@ -13,7 +14,7 @@ export const DEFAULT_CONTRIBUTION_AMOUNT = 5 export const MAX_CONTRIBUTION_AMOUNT = 10000 // See FundingRound.sol // The batch of maximum size will burn 9100000 gas at 700000 gas per message -export const MAX_CART_SIZE = 13 +export const MAX_CART_SIZE = 8 export interface CartItem extends Project { amount: string @@ -89,29 +90,21 @@ export async function getContributionAmount( export async function getTotalContributed( fundingRoundAddress: string ): Promise<{ count: number; amount: BigNumber }> { + const nativeTokenAddress = await factory.nativeToken() + const nativeToken = new Contract(nativeTokenAddress, ERC20, provider) + const balance = await nativeToken.balanceOf(fundingRoundAddress) + const data = await sdk.GetTotalContributed({ fundingRoundAddress: fundingRoundAddress.toLowerCase(), }) - if (!data.fundingRound?.contributors) { + if (!data.fundingRound?.contributorCount) { return { count: 0, amount: BigNumber.from(0) } } const count = parseInt(data.fundingRound.contributorCount) - const amount = data.fundingRound.contributors.reduce((total, contributor) => { - if (!contributor.contributions?.length) { - return total - } - - const subtotal = contributor.contributions.reduce((total, contribution) => { - return total.add(contribution.amount) - }, BigNumber.from(0)) - - return total.add(subtotal) - }, BigNumber.from(0)) - - return { count, amount } + return { count, amount: balance } } export async function withdrawContribution( diff --git a/vue-app/src/api/core.ts b/vue-app/src/api/core.ts index 159c88748..015b69c07 100644 --- a/vue-app/src/api/core.ts +++ b/vue-app/src/api/core.ts @@ -44,14 +44,14 @@ if ( } export const recipientRegistryPolicy = process.env.VUE_APP_RECIPIENT_REGISTRY_POLICY -export const operator: string = process.env.VUE_APP_OPERATOR || 'Clr.fund team' +export const operator: string = process.env.VUE_APP_OPERATOR || 'Clr.fund' export const extraRounds: string[] = process.env.VUE_APP_EXTRA_ROUNDS ? process.env.VUE_APP_EXTRA_ROUNDS.split(',') : [] export const SUBGRAPH_ENDPOINT = process.env.VUE_APP_SUBGRAPH_URL || - 'https://api.thegraph.com/subgraphs/name/daodesigner/clrfund' + 'https://api.thegraph.com/subgraphs/name/clrfund/clrfund' // application theme export enum ThemeMode { diff --git a/vue-app/src/api/factory.ts b/vue-app/src/api/factory.ts index d8559378c..933774569 100644 --- a/vue-app/src/api/factory.ts +++ b/vue-app/src/api/factory.ts @@ -7,6 +7,7 @@ export interface Factory { nativeTokenAddress: string nativeTokenSymbol: string nativeTokenDecimals: number + userRegistryAddress: string } export async function getFactoryInfo() { @@ -16,10 +17,13 @@ export async function getFactoryInfo() { const nativeTokenSymbol = await nativeToken.symbol() const nativeTokenDecimals = await nativeToken.decimals() + const userRegistryAddress = await factory.userRegistry() + return { fundingRoundAddress: factory.address, nativeTokenAddress, nativeTokenSymbol, nativeTokenDecimals, + userRegistryAddress, } } diff --git a/vue-app/src/api/maci-factory.ts b/vue-app/src/api/maci-factory.ts new file mode 100644 index 000000000..3cc909dbd --- /dev/null +++ b/vue-app/src/api/maci-factory.ts @@ -0,0 +1,20 @@ +import { Contract } from 'ethers' +import { MACIFactory as MACIFactoryABI } from './abi' +import { factory, provider } from './core' + +export interface MACIFactory { + maciFactoryAddress: string + maxRecipients: number +} + +export async function getMACIFactoryInfo(): Promise { + const maciFactoryAddress = await factory.maciFactory() + + const maciFactory = new Contract(maciFactoryAddress, MACIFactoryABI, provider) + const treeDepths = await maciFactory.treeDepths() + + return { + maciFactoryAddress, + maxRecipients: 5 ** treeDepths.voteOptionTreeDepth - 1, + } +} diff --git a/vue-app/src/api/round.ts b/vue-app/src/api/round.ts index 360b68e37..ac2aa5209 100644 --- a/vue-app/src/api/round.ts +++ b/vue-app/src/api/round.ts @@ -5,6 +5,9 @@ import { PubKey } from 'maci-domainobjs' import { FundingRound, MACI, ERC20 } from './abi' import { provider, factory } from './core' import { getTotalContributed } from './contributions' +import { getRounds } from './rounds' + +import { isSameAddress } from '@/utils/accounts' export interface RoundInfo { fundingRoundAddress: string @@ -50,7 +53,17 @@ export async function getCurrentRound(): Promise { if (fundingRoundAddress === '0x0000000000000000000000000000000000000000') { return null } - return fundingRoundAddress + const rounds = await getRounds() + // round.address is empty if the round is configured in VUE_APP_EXTRA_ROUNDS + const roundIndex = rounds.findIndex( + (round) => + !!round.address && isSameAddress(round.address, fundingRoundAddress) + ) + + if (roundIndex >= Number(process.env.VUE_APP_FIRST_ROUND || 0)) { + return fundingRoundAddress + } + return null } //TODO: update to take factory address as a parameter, default to env. variable diff --git a/vue-app/src/api/user.ts b/vue-app/src/api/user.ts index 3f4cbc216..d24ac076d 100644 --- a/vue-app/src/api/user.ts +++ b/vue-app/src/api/user.ts @@ -3,11 +3,11 @@ import { BigNumber, Contract } from 'ethers' import { Web3Provider } from '@ethersproject/providers' import { UserRegistry, ERC20 } from './abi' -import { factory, ipfsGatewayUrl, provider } from './core' +import { factory, ipfsGatewayUrl, provider, operator } from './core' import { BrightId } from './bright-id' //TODO: update anywhere this is called to take factory address as a parameter, default to env. variable -export const LOGIN_MESSAGE = `Welcome to clr.fund! +export const LOGIN_MESSAGE = `Welcome to ${operator}! To get logged in, sign this message to prove you have access to this wallet. This does not cost any ether. diff --git a/vue-app/src/components/CriteriaModal.vue b/vue-app/src/components/CriteriaModal.vue index e75a30902..cba535d4f 100644 --- a/vue-app/src/components/CriteriaModal.vue +++ b/vue-app/src/components/CriteriaModal.vue @@ -11,9 +11,13 @@

- The registry admin ({{ operator }}) will remove any projects that - don't meet the round criteria. So read carefully! In later rounds - we're hoping that this review process can be done by the community. + The registry admin will remove any projects that don't meet the round + criteria. So read carefully! In later rounds we're hoping that this + review process can be done by the community. +

+

+ Learn more about the project application process in our + recipient guide.

diff --git a/vue-app/src/components/FundsNeededWarning.vue b/vue-app/src/components/FundsNeededWarning.vue index ad071972c..1459e2d04 100644 --- a/vue-app/src/components/FundsNeededWarning.vue +++ b/vue-app/src/components/FundsNeededWarning.vue @@ -59,7 +59,7 @@ export default class FundsNeededWarning extends Vue { } get tokenBalance(): number | null { - if (!this.currentUser) return null + if (!this.currentUser?.balance) return null return parseFloat( formatAmount( this.currentUser.balance as BigNumber, @@ -69,7 +69,7 @@ export default class FundsNeededWarning extends Vue { } get etherBalance(): number | null { - if (!this.currentUser) return null + if (!this.currentUser?.etherBalance) return null return parseFloat(formatAmount(this.currentUser.etherBalance as BigNumber)) } diff --git a/vue-app/src/components/Markdown.vue b/vue-app/src/components/Markdown.vue index eba07b4ac..53338a5ce 100644 --- a/vue-app/src/components/Markdown.vue +++ b/vue-app/src/components/Markdown.vue @@ -38,6 +38,14 @@ export default class Transaction extends Vue { p { line-height: 150%; } + img { + display: block; + max-width: 100%; + height: auto; + } + pre { + white-space: pre-wrap; + } } } diff --git a/vue-app/src/components/MatchingFundsModal.vue b/vue-app/src/components/MatchingFundsModal.vue index 7a6dd1893..e04373124 100644 --- a/vue-app/src/components/MatchingFundsModal.vue +++ b/vue-app/src/components/MatchingFundsModal.vue @@ -155,10 +155,15 @@ export default class MatchingFundsModal extends Vue { this.$store.state.currentRound const token = new Contract(nativeTokenAddress, ERC20, this.signer) const amount = parseFixed(this.amount, nativeTokenDecimals) + + // TODO: update to take factory address as a parameter from the route props, default to env. variable + const matchingPoolAddress = process.env.VUE_APP_MATCHING_POOL_ADDRESS + ? process.env.VUE_APP_MATCHING_POOL_ADDRESS + : factory.address + try { await waitForTransaction( - //TODO: update to take factory address as a parameter from the route props, default to env. variable - token.transfer(factory.address, amount), + token.transfer(matchingPoolAddress, amount), (hash) => (this.transferTxHash = hash) ) } catch (error) { diff --git a/vue-app/src/components/NavBar.vue b/vue-app/src/components/NavBar.vue index aceace4a8..3b5c4934a 100644 --- a/vue-app/src/components/NavBar.vue +++ b/vue-app/src/components/NavBar.vue @@ -1,7 +1,11 @@