diff --git a/contracts/.solhintignore b/contracts/.solhintignore new file mode 100644 index 000000000..7147ec21a --- /dev/null +++ b/contracts/.solhintignore @@ -0,0 +1 @@ +contracts/snarkVerifiers/ diff --git a/contracts/buidler.config.ts b/contracts/buidler.config.ts index f4406db28..d08fe83cb 100644 --- a/contracts/buidler.config.ts +++ b/contracts/buidler.config.ts @@ -9,7 +9,11 @@ usePlugin('@nomiclabs/buidler-ganache') const GAS_LIMIT = 10000000 -const config: BuidlerConfig = { +interface CustomBuidlerConfig extends BuidlerConfig { + typechain?: any; +} + +const config: CustomBuidlerConfig = { networks: { buidlerevm: { gas: GAS_LIMIT, @@ -27,6 +31,11 @@ const config: BuidlerConfig = { url: process.env.ETHEREUM_JSONRPC_HTTP_URL || 'http://127.0.0.1:8545', accounts: { mnemonic: '' }, }, + xdai: { + url: 'https://rpc.xdaichain.com', + timeout: 60000, + accounts: { mnemonic: '' }, + }, }, paths: { artifacts: "build/contracts", @@ -50,6 +59,17 @@ task('compile', 'Compiles the entire project, building all artifacts', async (_, // Copy Poseidon artifacts to target directory fs.copyFileSync('../node_modules/maci-contracts/compiled/PoseidonT3.json', path.join(config.paths.artifacts, 'PoseidonT3.json')); fs.copyFileSync('../node_modules/maci-contracts/compiled/PoseidonT6.json', path.join(config.paths.artifacts, 'PoseidonT6.json')); + // Prepare verifier artifacts for 'test' circuits + const verifiers = ['BatchUpdateStateTreeVerifier', 'QuadVoteTallyVerifier'] + for (const contractName of verifiers) { + const abi = JSON.parse(fs.readFileSync(`../node_modules/maci-contracts/compiled/${contractName}.abi`).toString()) + const bytecode = fs.readFileSync(`../node_modules/maci-contracts/compiled/${contractName}.bin`).toString() + const artifact = { contractName, abi, bytecode } + fs.writeFileSync( + path.join(config.paths.artifacts, `${contractName}.json`), + JSON.stringify(artifact), + ) + } }); export default config diff --git a/contracts/contracts/MACIFactory.sol b/contracts/contracts/MACIFactory.sol index a171c6c25..e1dd6bdb2 100644 --- a/contracts/contracts/MACIFactory.sol +++ b/contracts/contracts/MACIFactory.sol @@ -8,11 +8,6 @@ import 'maci-contracts/sol/MACISharedObjs.sol'; import 'maci-contracts/sol/gatekeepers/SignUpGatekeeper.sol'; import 'maci-contracts/sol/initialVoiceCreditProxy/InitialVoiceCreditProxy.sol'; -import { BatchUpdateStateTreeVerifier } from 'maci-contracts/sol/BatchUpdateStateTreeVerifier.sol'; -import { QuadVoteTallyVerifier } from 'maci-contracts/sol/QuadVoteTallyVerifier.sol'; -import { BatchUpdateStateTreeVerifierSmall } from 'maci-contracts/sol/BatchUpdateStateTreeVerifierSmall.sol'; -import { QuadVoteTallyVerifierSmall } from 'maci-contracts/sol/QuadVoteTallyVerifierSmall.sol'; - contract MACIFactory is Ownable, MACIParameters, MACISharedObjs { // Constants uint256 private constant STATE_TREE_BASE = 2; diff --git a/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifierSmall.sol b/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifierSmall.sol new file mode 100644 index 000000000..9e7f41202 --- /dev/null +++ b/contracts/contracts/snarkVerifiers/BatchUpdateStateTreeVerifierSmall.sol @@ -0,0 +1,248 @@ +// 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.5.0; + +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 BatchUpdateStateTreeVerifierSmall { + + 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[21] 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(16583938912798357867337365680645490945981557109369454180505842114308066785186),uint256(20757853840804934747929226034774154442541633971318863331800390606646606025466)); + vk.beta2 = Pairing.G2Point([uint256(5354211930222392331268842548771975814530806089479797964197427880271989421511),uint256(4968281061914355069725144181562861610613982917439314273280548954133453497301)], [uint256(12035947561935720523496667016709705196292996437795022838077910411303785686533),uint256(16079226534539334773336773482371912036234729143595908478453324236020236439887)]); + vk.gamma2 = Pairing.G2Point([uint256(9712523846117928141588156514399784766496101555105705485395051052064402193567),uint256(2241817746781364062120326250585472035052519034421741157519505381194689286366)], [uint256(13697494508951944803338897872283492305185662876639179909034133651567238684462),uint256(5572595615558866953587505236329478119392163024045038943611587753490466818060)]); + vk.delta2 = Pairing.G2Point([uint256(18115650704376235385370480997786995541070696376503210213701452914337018689762),uint256(18476593100016373277138021698247640422238760717119016246553919458641256794376)], [uint256(18328410860266063929212714519960187221271601138795113986214153459251396487646),uint256(17830636401446366248305925261955692419030811037343914073774245089570058204816)]); + vk.IC[0] = Pairing.G1Point(uint256(3105743762471496710290515055939609897246982937684129867553835021883530495284),uint256(5775475865603324687116541339746372000460752568190948236127716345133930946008)); + vk.IC[1] = Pairing.G1Point(uint256(13381106124549339190226051643361226352208086686852619350899494989951995165229),uint256(9551279751719992209393777869618332024593659037913442110676477019093336542396)); + vk.IC[2] = Pairing.G1Point(uint256(2052298766207343563340598843916725106021284627365324976621746560935890359228),uint256(21327055154581654203655037002620357430196819084537036273716603014075899579090)); + vk.IC[3] = Pairing.G1Point(uint256(20581870196603457906578003242348568222283386262980518403326210287310944720452),uint256(5465484007726939955842681771126376927556047060229264643604764943015198628940)); + vk.IC[4] = Pairing.G1Point(uint256(21660844096247794612314577409584990203482750906723799955808833406802637219405),uint256(17027722275860534834136807215616178847987175509289393923309318487795690054468)); + vk.IC[5] = Pairing.G1Point(uint256(17482044811508911017018997237012399211432315124813196346850120032350873519482),uint256(12459575097675183077125922240392601342024896556491493807720620962990084961577)); + vk.IC[6] = Pairing.G1Point(uint256(9905760825582796064753319801866339398497520045195821096613500622882688350114),uint256(20595430202071332357466630106039871351435634628470974414623621376036056029055)); + vk.IC[7] = Pairing.G1Point(uint256(13524105871168428582929137726783488656588125146031568692916335167155760690313),uint256(1565334639198013533051013918862193448732548650345189167119900189657078423103)); + vk.IC[8] = Pairing.G1Point(uint256(7558561241624168950710673315666104822141019280035091794726363799232780508152),uint256(11341973238039887112824323124954729755093559020192930218175760765953366282883)); + vk.IC[9] = Pairing.G1Point(uint256(10031306598742050384421184106839275426352483810745545102667988996985373077889),uint256(12494340940484058840704391849557267756851196494763777065726553217481732559978)); + vk.IC[10] = Pairing.G1Point(uint256(14711792710652155791379699938990241560409187386345013634823329071801097101688),uint256(2827641495317293024690164430429026513205853716719635892283633013215896756064)); + vk.IC[11] = Pairing.G1Point(uint256(4084851069007237880287866909959012008506817356427754983021817962638652529861),uint256(18493274979941494058772436252943758297851000973683100821680504375823944654818)); + vk.IC[12] = Pairing.G1Point(uint256(21751036910040733319318588571581249034639650125210236549920305503729148822587),uint256(1874735309075676549287638147280830439151379054286839624629799027242814084346)); + vk.IC[13] = Pairing.G1Point(uint256(15513515978910948133730093407261641079842360214839766696170557217380721718663),uint256(21005443222464009510035271124522685836104033221498792689549724148154531090579)); + vk.IC[14] = Pairing.G1Point(uint256(18834627083868891908995311572811831589398363484962277081959386240589428686498),uint256(312085109456128333556831663883657160827068724442658583549371961191193904293)); + vk.IC[15] = Pairing.G1Point(uint256(166305728605763205834777245017059919070519356907557220426306469098995174837),uint256(18939000766743056309853291866131205650809906335195504505668969372688240847326)); + vk.IC[16] = Pairing.G1Point(uint256(17556452942397133693419032160015656011592286344419508203011307575515832755894),uint256(7413225909684712702682551364754669593480691013420553327166717489810031746524)); + vk.IC[17] = Pairing.G1Point(uint256(3394714965279517624354067762935201681938198121612921317346581782862791287606),uint256(12854859510680434303809258079140466092609125262021277235555399670452692459659)); + vk.IC[18] = Pairing.G1Point(uint256(13439415310854451589099742781777066530204358292337938456565531920087426659142),uint256(1720302805958671684306016859364426816691368924632518514468096409691915651572)); + vk.IC[19] = Pairing.G1Point(uint256(21729451407777426291636528006449849940822354038721105154713822700088568821083),uint256(14489374982979388910883759028028883679999120685438717311935993967420660027749)); + vk.IC[20] = Pairing.G1Point(uint256(1876399965866804740543527668364407300210858993648033819579101332412569635997),uint256(19990619256484609678830063554269256407087013744540193474689542385972486296683)); + + } + + /* + * @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 < 20; 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/QuadVoteTallyVerifierSmall.sol b/contracts/contracts/snarkVerifiers/QuadVoteTallyVerifierSmall.sol new file mode 100644 index 000000000..59f7ae439 --- /dev/null +++ b/contracts/contracts/snarkVerifiers/QuadVoteTallyVerifierSmall.sol @@ -0,0 +1,238 @@ +// 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.5.0; + +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 QuadVoteTallyVerifierSmall { + + 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(11864421997626308621737197370680052311896145516646957787412054200590306558280),uint256(7120239010102393838850304497924066491138218644775568648679402317743369698899)); + vk.beta2 = Pairing.G2Point([uint256(14816238325391857269156182976198625717303533540915656313628452863897943815706),uint256(17302662719468239179298495857785789259854967451607436697118275217896372565398)], [uint256(20818077938974009503119546822583564712481612775175227412258536667787587579842),uint256(17382067811436302045440541486844070097444386258159703634338305828264930542706)]); + vk.gamma2 = Pairing.G2Point([uint256(10359869501156693784648875332947622005773803374456476132185215559045825960396),uint256(19709034605062944902568307521809437950811088876431158330405830403128456770439)], [uint256(7251708916269765912621246930741824516399358883093945611100976535488279469374),uint256(3585631687077508206832572076293134983677215973685242814835038797236713806931)]); + vk.delta2 = Pairing.G2Point([uint256(11844142819159093547916424478780545043316393514729078450488711265768388040809),uint256(10208226297689928545241253925085106687448294504830863690320166396594419194614)], [uint256(169225507690081341975299448171644678050344563385947384584633680616806164128),uint256(12026378321266788476840921406920550182328644317333728547214038275686391279484)]); + vk.IC[0] = Pairing.G1Point(uint256(11160411138808886406725413055064673211241851292776678842603741433014003863189),uint256(12045051457154081721769550430620333148909539207133051655668301327650520804130)); + vk.IC[1] = Pairing.G1Point(uint256(21414809048043950204175409572285583875690248274131782375828580695569964172973),uint256(10290115518699301011193018433204320235184988912601751307692516618376298878707)); + vk.IC[2] = Pairing.G1Point(uint256(12303380013200069331334803404586359411731786360448395051374203036582109817305),uint256(19830899610583132256262826108814826063314737543374151854333330328379160977784)); + vk.IC[3] = Pairing.G1Point(uint256(14671499868827432339792937780444347877854733682342484626069309891664837002648),uint256(2209880611231788766958113269018926359544730224276988251182963243067919274742)); + vk.IC[4] = Pairing.G1Point(uint256(16990914691449723004885956785663260284389982974052215253398758703526687813985),uint256(11243965610000705390718667958695973782430124176403776029695560332023323905369)); + vk.IC[5] = Pairing.G1Point(uint256(5489221431619859282809178812619255014905034881681682315799410242961305649895),uint256(17713462541896700983807102392654565708232385829822245596233151388626084331818)); + vk.IC[6] = Pairing.G1Point(uint256(3267710295382845682951716005162791254119246161949903037372106983817214245291),uint256(8617003933682405249190330295603009646788526586195309672821066758615424492673)); + vk.IC[7] = Pairing.G1Point(uint256(17294736648373359372569914797687932235322160584327147144500017178969990428258),uint256(17678859673096187500445369924817819537880683925786114998376026911869502037221)); + vk.IC[8] = Pairing.G1Point(uint256(1123824607322750711941845123576448738342895315883016121121460604886319096461),uint256(12000312208269618723855201774577007190568363498218660236852828213171024869343)); + vk.IC[9] = Pairing.G1Point(uint256(18657369399957015450701112811096520702997716414671844523145076185788206970401),uint256(5498723815699290039298635461700394289526319358988560988886785682863166517855)); + vk.IC[10] = Pairing.G1Point(uint256(15605709166243729662592538680270637173571150650813811365783520170029334798567),uint256(4345473546780187573696151628194422341215917155792411747260297969138473728308)); + + } + + /* + * @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/scripts/linkPoseidon.ts b/contracts/scripts/linkPoseidon.ts new file mode 100644 index 000000000..4dfc4d89d --- /dev/null +++ b/contracts/scripts/linkPoseidon.ts @@ -0,0 +1,20 @@ +import { linkBytecode } from '../utils/deployment' + +async function main() { + const contractName = process.argv[2] + const poseidonT3Address = process.argv[3] + const poseidonT6Address = process.argv[4] + const artifact = await import(`../build/contracts/${contractName}.json`) + const result = linkBytecode(artifact.deployedBytecode, { + 'maci-contracts/sol/Poseidon.sol:PoseidonT3': poseidonT3Address, + 'maci-contracts/sol/Poseidon.sol:PoseidonT6': poseidonT6Address, + }) + console.info(result) +} + +main() + .then(() => process.exit(0)) + .catch(error => { + console.error(error) + process.exit(1) + }) diff --git a/contracts/utils/deployment.ts b/contracts/utils/deployment.ts index d2d1be7bb..6b151da66 100644 --- a/contracts/utils/deployment.ts +++ b/contracts/utils/deployment.ts @@ -1,9 +1,11 @@ -import { ethers } from '@nomiclabs/buidler' +import bre from '@nomiclabs/buidler' import { Signer, Contract } from 'ethers' import { link } from 'ethereum-waffle' import MACIFactoryArtifact from '../build/contracts/MACIFactory.json' +const ethers = (bre as any).ethers + async function deployContract( account: Signer, name: string, @@ -19,21 +21,31 @@ async function deployContract( return contract } +export function linkBytecode( + bytecode: string, + libraries: {[name: string]: string}, +): string { + // Workarounds for https://github.com/nomiclabs/buidler/issues/611 + const linkable = { evm: { bytecode: { object: bytecode } } } + for (const [libraryName, libraryAddress] of Object.entries(libraries)) { + link(linkable, libraryName, libraryAddress.toLowerCase()) + } + return linkable.evm.bytecode.object +} + export async function deployMaciFactory(account: Signer): Promise { const batchTreeVerifier = await deployContract(account, 'BatchUpdateStateTreeVerifier') const voteTallyVerifier = await deployContract(account, 'QuadVoteTallyVerifier') const poseidonT3 = await deployContract(account, 'PoseidonT3') const poseidonT6 = await deployContract(account, 'PoseidonT6') - // Workarounds for https://github.com/nomiclabs/buidler/issues/611 - const MACIFactoryArtifactCopy = {...MACIFactoryArtifact} - const linkable = { evm: { bytecode: { object: MACIFactoryArtifactCopy.bytecode } } } - link(linkable, 'maci-contracts/sol/Poseidon.sol:PoseidonT3', poseidonT3.address) - link(linkable, 'maci-contracts/sol/Poseidon.sol:PoseidonT6', poseidonT6.address) - MACIFactoryArtifactCopy.bytecode = linkable.evm.bytecode.object + const linkedBytecode = linkBytecode(MACIFactoryArtifact.bytecode, { + 'maci-contracts/sol/Poseidon.sol:PoseidonT3': poseidonT3.address, + 'maci-contracts/sol/Poseidon.sol:PoseidonT6': poseidonT6.address, + }) const MACIFactory = await ethers.getContractFactory( - MACIFactoryArtifactCopy.abi, - MACIFactoryArtifactCopy.bytecode, + MACIFactoryArtifact.abi, + linkedBytecode, account, )