halfway through contract rewrite to support proxies

This commit is contained in:
Divide-By-0
2023-05-23 18:37:12 +09:00
parent 10a840db73
commit 2551000d4b
17 changed files with 3633 additions and 933 deletions

2
.gitignore vendored
View File

@@ -53,8 +53,6 @@ cache/
test.log
src/contracts/out/
.next
*Wallet*
*wallet*
node_modules.nosync
# Files that never should be committed, but can be obtained by asking Aayush or generating them yourself

View File

@@ -43,7 +43,7 @@
"react-use": "^17.3.2",
"readline": "^1.3.0",
"serve": "^14.0.1",
"snarkjs": "git+https://github.com/vb7401/snarkjs.git#24981febe8826b6ab76ae4d76cf7f9142919d2b8",
"snarkjs": "latest",
"sshpk": "^1.17.0",
"styled-components": "^5.3.5",
"ts-node": "^10.9.1",

View File

@@ -58,3 +58,7 @@ Maybe fullnode is on [old geth](https://github.com/ethereum/go-ethereum/issues/2
```
forge script script/Deploy.s.sol:Deploy -vvvv --rpc-url $RPC_URL --broadcast --slow
```
### Versions
10a840db7305d9cdcd1fa56aee88ec77db86a562 is the last stable wallet version before breaking changes to add on-chain anonymity.

View File

@@ -0,0 +1,48 @@
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "forge-std/Script.sol";
import "../src/WalletEmailHandler.sol";
import "../src/WalletEmailHandlerLogic.sol";
import "../src/WalletEmailHandlerProxy.sol";
import "../src/StringUtils.sol";
import "../src/Groth16VerifierWalletAnon.sol";
import "../src/TestERC20.sol";
contract Deploy is Script, Test {
function getPrivateKey() internal returns (uint256) {
try vm.envUint("PRIVATE_KEY") returns (uint256 privateKey) {
return privateKey;
} catch {
// This is the anvil default exposed secret key
return 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;
}
}
// function oldRunDeployNonproxy() public {
// uint256 sk = getPrivateKey();
// vm.startBroadcast(sk);
// Verifier proofVerifier = new Verifier();
// MailServer mailServer = new MailServer();
// TestEmailToken erc20 = new TestEmailToken(5000);
// VerifiedWalletEmail testVerifier = new VerifiedWalletEmail(proofVerifier, mailServer, erc20);
// vm.stopBroadcast();
// }
function run() public {
uint256 sk = getPrivateKey();
vm.startBroadcast(sk);
Verifier proofVerifier = new Verifier();
MailServer mailServer = new MailServer();
TokenRegistry tokenRegistry = new TokenRegistry();
TestEmailToken erc20 = new TestEmailToken(5000);
WalletEmailHandlerLogic logic = new WalletEmailHandlerLogic();
address admin = 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D; // HEVM Cheat Code Address
bytes memory initData =
abi.encodeWithSignature("initialize(Verifier,MailServer,TestEmailToken,TokenRegistry)", proofVerifier, mailServer, erc20, tokenRegistry);
WalletEmailHandlerProxy proxy = new WalletEmailHandlerProxy(address(logic), admin, initData);
vm.stopBroadcast();
}
}

View File

@@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
contract AutoApproveWallet is Ownable, Initializable {
uint256 constant MAX_UINT256 = type(uint256).max;
uint256 public constant version = 1;
mapping(string => address) public customVerifiers;
constructor() {}
function initialize(address tokenAddress, address approver) public onlyOwner initializer {
IERC20 token = IERC20(tokenAddress);
token.approve(approver, MAX_UINT256);
}
// These are here as placeholders, but eventually can gate withdraws from the account
function setVerifier(string memory command, address verifier) public onlyOwner {
customVerifiers[command] = verifier;
}
// These are here as placeholders, but eventually can gate withdraws from the account
function getVerifier(string memory command) public view returns (address) {
return customVerifiers[command];
}
}

View File

@@ -1,425 +0,0 @@
//
// 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
// ported to solidity 0.6
// fixed linter warnings
// added requiere error messages
//
//
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.11;
library Pairing {
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 generator of G1
function P1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
/// @return the generator of G2
function P2() internal pure returns (G2Point memory) {
// Original code point
return
G2Point(
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
);
/*
// Changed by Jordi point
return G2Point(
[10857046999023057135944570762232829481370756359578518086990519993285655852781,
11559732032986387107991004021392285783925812861821192530917403151452391805634],
[8495653923123431417604973247489272438418190587263600148770280649306958101930,
4082367875863433681332203403145435568316851327593401208105741076214120093531]
);*/
}
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
// The prime q in the base field F_q for G1
uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
return G1Point(p.X, q - (p.Y % q));
}
/// @return r the sum of two points of G1
function addition(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 r the product of a point on G1 and a scalar, i.e.
/// p == p.scalar_mul(1) and p.addition(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 p1, G2Point[] memory p2) internal view returns (bool) {
require(p1.length == p2.length, "pairing-lengths-failed");
uint256 elements = p1.length;
uint256 inputSize = elements * 6;
uint256[] memory input = new uint[](inputSize);
for (uint256 i = 0; i < elements; i++) {
input[i * 6 + 0] = p1[i].X;
input[i * 6 + 1] = p1[i].Y;
input[i * 6 + 2] = p2[i].X[0];
input[i * 6 + 3] = p2[i].X[1];
input[i * 6 + 4] = p2[i].Y[0];
input[i * 6 + 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;
}
/// Convenience method for a pairing check for two pairs.
function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](2);
G2Point[] memory p2 = new G2Point[](2);
p1[0] = a1;
p1[1] = b1;
p2[0] = a2;
p2[1] = b2;
return pairing(p1, p2);
}
/// Convenience method for a pairing check for three pairs.
function pairingProd3(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2, G1Point memory c1, G2Point memory c2) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](3);
G2Point[] memory p2 = new G2Point[](3);
p1[0] = a1;
p1[1] = b1;
p1[2] = c1;
p2[0] = a2;
p2[1] = b2;
p2[2] = c2;
return pairing(p1, p2);
}
/// Convenience method for a pairing check for four pairs.
function pairingProd4(
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[] memory p1 = new G1Point[](4);
G2Point[] memory p2 = new G2Point[](4);
p1[0] = a1;
p1[1] = b1;
p1[2] = c1;
p1[3] = d1;
p2[0] = a2;
p2[1] = b2;
p2[2] = c2;
p2[3] = d2;
return pairing(p1, p2);
}
}
contract Verifier {
using Pairing for *;
struct VerifyingKey {
Pairing.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[] IC;
}
struct Proof {
Pairing.G1Point A;
Pairing.G2Point B;
Pairing.G1Point C;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(
20491192805390485299153009773594534940189261866228447918068658471970481763042,
9383485363053290200918347156157836566562967994039712273449902621266178545958
);
vk.beta2 = Pairing.G2Point(
[4252822878758300859123897981450591353533073413197771768651442665752259397132, 6375614351688725206403948262868962793625744043794305715222011528459656738731],
[21847035105528745403288232691147584728191162732299865338377159692350059136679, 10505242626370262277552901082094356697409835680220590971873171140371331206856]
);
vk.gamma2 = Pairing.G2Point(
[11559732032986387107991004021392285783925812861821192530917403151452391805634, 10857046999023057135944570762232829481370756359578518086990519993285655852781],
[4082367875863433681332203403145435568316851327593401208105741076214120093531, 8495653923123431417604973247489272438418190587263600148770280649306958101930]
);
vk.delta2 = Pairing.G2Point(
[1513129022268504209358763521777110646559191312944535983790343685540452445612, 12195000584114682213316512369139242650405673197296308277565313794080256232701],
[8360772797757285669693588223224368324652217960280535125476432579464480898413, 520894096459672819696448581165649200639580943346471218758886059131097679200]
);
vk.IC = new Pairing.G1Point[](35);
vk.IC[0] = Pairing.G1Point(
11537328283226768393732428330956073110918624081922605220849994462257164716154,
21836950190585457727786541794875874204342457073442279657287670575335295859939
);
vk.IC[1] = Pairing.G1Point(
2646307150509379456483324388204599416197823343744573056776580441757968778128,
14368370831419521837815784599306984151624772530159432470308283448431330483438
);
vk.IC[2] = Pairing.G1Point(
16971624750468472956383457363672950966338870541072731251437414691217134411120,
8171770470043873321771537156021753591829282779422875691859226771666334115210
);
vk.IC[3] = Pairing.G1Point(
17003248154885997024042723500051841853373913724967509464380365353802575974728,
7268687626523176370339202918269959082095447462862347953587350206552205642559
);
vk.IC[4] = Pairing.G1Point(
9147424234223179035895482077044817222415554273556867838268712944193520459125,
21578705994822322813698189503736815299185497639660343771670537250469027755948
);
vk.IC[5] = Pairing.G1Point(
16948417333412259029407181850226843375508933350680650127572170476630717282234,
9665563179883720332817127456522837835577284997912132910805194791161889398512
);
vk.IC[6] = Pairing.G1Point(
9319994583370423638602672994382300950312503535351144768052894387193551537459,
19171031111811822064434269353657536224339902296340766041252096715670766153578
);
vk.IC[7] = Pairing.G1Point(
9371603635986601434942264619807195148477116766259006354709633771674992552724,
11103293940652841271958831421967970005702672122254116205870466468493278826958
);
vk.IC[8] = Pairing.G1Point(
3561757143230234005632456376532802121489770329687277876867422136368222736134,
11734814901687269585580202167146602218102049962630061844516173384912435005151
);
vk.IC[9] = Pairing.G1Point(
16676238706569507337573176601181670688707264606170712075388734731572549373015,
11158592034910239776632780480510406146251140896522712858892689171974748083747
);
vk.IC[10] = Pairing.G1Point(
9822993420542467873912218347366938030193407618831916003032667186678059114349,
6863223533781083402167797754430699108902384970089129346113470095574463296737
);
vk.IC[11] = Pairing.G1Point(
4866721301322510775531094866459672146070982818674092273969310571220291630556,
9846640210996171367705574856419053631553093780701851221089070591862473684456
);
vk.IC[12] = Pairing.G1Point(
11121919417977708038028243712145798541888876824913262431306415256568966652024,
12609770408698645498350514024296607224475353273713173177251900216074217014850
);
vk.IC[13] = Pairing.G1Point(
14840050373651993593383756529988239317958775236310376879012996717055845150487,
16683016138329247649135414505803188190565946292769946693147365719646757192352
);
vk.IC[14] = Pairing.G1Point(
12331471170495648864015185687161748641193043583582691780623236535573811426959,
11714531956225516771424892308789701085922077646343882797070434158311220922011
);
vk.IC[15] = Pairing.G1Point(
20857366342421630154576707922479406842052250461154457549932210545889187427626,
19731871495034015170236307573440414758884960293847791130561428843792753429086
);
vk.IC[16] = Pairing.G1Point(
16209406523379999199236029863462575441846948663847567298395841886469797214082,
20718988335004414356172240953954108902387108184538803896124546491420460740169
);
vk.IC[17] = Pairing.G1Point(
8397179853577453858892608876746062307226175687475102639712031393841003387919,
7154528864876839718549215559715861424372452521581853695056564308745992761672
);
vk.IC[18] = Pairing.G1Point(
15000425614899689626185464253582174491572354417903507356426258449407132127046,
13602373364472815067534003393627538602411204908660744540946955553406039633451
);
vk.IC[19] = Pairing.G1Point(
5545252939208963791309142258539499940294193526436021787091019273144246885261,
2675833647327088808677716501235324721140769279456271927816550777395489852251
);
vk.IC[20] = Pairing.G1Point(
2537047834074478023060297384888724410218136939149736151242515713484635642789,
3193147508270124393755481767836877808561856886429215905854576456538260642120
);
vk.IC[21] = Pairing.G1Point(
13819984179784763421503658502014430958296359352165019537755065452645355466085,
18994178127386658997162783196808799873728043389545801736093808610346271161658
);
vk.IC[22] = Pairing.G1Point(
6589370869708283514329318539826110923064805897289825414235274269171407140900,
9625792054986455984618262255946246252268480571067521104852530192902386719593
);
vk.IC[23] = Pairing.G1Point(
14453270681522455527144160729543145025587040770258214008741757192612444737673,
20143300120523312381709649823189168032874279267240134119356286009260298185475
);
vk.IC[24] = Pairing.G1Point(
8846979524751473322994268234754538401482894217522349506066299416316984717260,
14238278950875447301003974303300008365515832894025960431754069250458716988985
);
vk.IC[25] = Pairing.G1Point(
1407828395464361821905309927863167268883721160860555328581866885124611897528,
15987854649208373291691713233856831065610123809032682196292981616658494776987
);
vk.IC[26] = Pairing.G1Point(
20657483817998070093869648777824400547660398552294774144027193285451239619382,
21826613460615350291777797645121185151869814459512699120411019581958075991542
);
vk.IC[27] = Pairing.G1Point(
20624928617240774729161710928323453036077574938263627000040473137006018415415,
5131454817008842892320970265930200727731593782574755474310860262365965990664
);
vk.IC[28] = Pairing.G1Point(
13895249990164764352618348094492621630575085672410127797817030490897283284460,
11796920177436300262655702063092510613755939821586920802170520825907606184557
);
vk.IC[29] = Pairing.G1Point(
3135811994020780743324086008729747183900335277491189104250977002197575809365,
19637480211624980857501152516366188197895066151113910763057713919944678029866
);
vk.IC[30] = Pairing.G1Point(
606103022421566187118689786319065266252504972683102249226453076393842587324,
10274587862652629723961486759561149321143114208996217550785138347396636546119
);
vk.IC[31] = Pairing.G1Point(
17876636758905622230688544604495142065478803287376541138330575508029036910921,
845514606482911379811389987862147832754587150586772012493153117259870748818
);
vk.IC[32] = Pairing.G1Point(
3055934057788309375612017678081361462642124203699024466299800387864964176162,
6215463225683464296816602777995066291454589608119271052091343612576965188139
);
vk.IC[33] = Pairing.G1Point(
15655816926053512024400351678850471800118254812943223488624167720306406940886,
1318451441938966067851976459607266837893918068783070216555767331693196884126
);
vk.IC[34] = Pairing.G1Point(
13942491345765259320815758221010163510633256522676138655574537850419728932090,
2449428677603436766608016448595332101485961035234011384134049218692804513154
);
}
function verify(uint256[] memory input, Proof memory proof) public view returns (uint256) {
uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
VerifyingKey memory vk = verifyingKey();
require(input.length + 1 == vk.IC.length, "verifier-bad-input");
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
for (uint256 i = 0; i < input.length; i++) {
require(input[i] < snark_scalar_field, "verifier-gte-snark-scalar-field");
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}
vk_x = Pairing.addition(vk_x, vk.IC[0]);
if (!Pairing.pairingProd4(Pairing.negate(proof.A), proof.B, vk.alfa1, vk.beta2, vk_x, vk.gamma2, proof.C, vk.delta2)) return 1;
return 0;
}
/// @return r bool true if proof is valid
function verifyProof(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[34] memory input) public view returns (bool r) {
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]);
uint256[] memory inputValues = new uint[](input.length);
for (uint256 i = 0; i < input.length; i++) {
inputValues[i] = input[i];
}
if (verify(inputValues, proof) == 0) {
return true;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,423 @@
//
// 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
// ported to solidity 0.6
// fixed linter warnings
// added requiere error messages
//
//
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.11;
library Pairing {
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 generator of G1
function P1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
/// @return the generator of G2
function P2() internal pure returns (G2Point memory) {
// Original code point
return G2Point(
[
11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781
],
[
4082367875863433681332203403145435568316851327593401208105741076214120093531,
8495653923123431417604973247489272438418190587263600148770280649306958101930
]
);
/*
// Changed by Jordi point
return G2Point(
[10857046999023057135944570762232829481370756359578518086990519993285655852781,
11559732032986387107991004021392285783925812861821192530917403151452391805634],
[8495653923123431417604973247489272438418190587263600148770280649306958101930,
4082367875863433681332203403145435568316851327593401208105741076214120093531]
);*/
}
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
// The prime q in the base field F_q for G1
uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
if (p.X == 0 && p.Y == 0) {
return G1Point(0, 0);
}
return G1Point(p.X, q - (p.Y % q));
}
/// @return r the sum of two points of G1
function addition(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 r the product of a point on G1 and a scalar, i.e.
/// p == p.scalar_mul(1) and p.addition(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 p1, G2Point[] memory p2) internal view returns (bool) {
require(p1.length == p2.length, "pairing-lengths-failed");
uint256 elements = p1.length;
uint256 inputSize = elements * 6;
uint256[] memory input = new uint[](inputSize);
for (uint256 i = 0; i < elements; i++) {
input[i * 6 + 0] = p1[i].X;
input[i * 6 + 1] = p1[i].Y;
input[i * 6 + 2] = p2[i].X[0];
input[i * 6 + 3] = p2[i].X[1];
input[i * 6 + 4] = p2[i].Y[0];
input[i * 6 + 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;
}
/// Convenience method for a pairing check for two pairs.
function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2)
internal
view
returns (bool)
{
G1Point[] memory p1 = new G1Point[](2);
G2Point[] memory p2 = new G2Point[](2);
p1[0] = a1;
p1[1] = b1;
p2[0] = a2;
p2[1] = b2;
return pairing(p1, p2);
}
/// Convenience method for a pairing check for three pairs.
function pairingProd3(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
G1Point memory c1,
G2Point memory c2
) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](3);
G2Point[] memory p2 = new G2Point[](3);
p1[0] = a1;
p1[1] = b1;
p1[2] = c1;
p2[0] = a2;
p2[1] = b2;
p2[2] = c2;
return pairing(p1, p2);
}
/// Convenience method for a pairing check for four pairs.
function pairingProd4(
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[] memory p1 = new G1Point[](4);
G2Point[] memory p2 = new G2Point[](4);
p1[0] = a1;
p1[1] = b1;
p1[2] = c1;
p1[3] = d1;
p2[0] = a2;
p2[1] = b2;
p2[2] = c2;
p2[3] = d2;
return pairing(p1, p2);
}
}
contract Verifier {
using Pairing for *;
struct VerifyingKey {
Pairing.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[] IC;
}
struct Proof {
Pairing.G1Point A;
Pairing.G2Point B;
Pairing.G1Point C;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(
20491192805390485299153009773594534940189261866228447918068658471970481763042,
9383485363053290200918347156157836566562967994039712273449902621266178545958
);
vk.beta2 = Pairing.G2Point(
[
4252822878758300859123897981450591353533073413197771768651442665752259397132,
6375614351688725206403948262868962793625744043794305715222011528459656738731
],
[
21847035105528745403288232691147584728191162732299865338377159692350059136679,
10505242626370262277552901082094356697409835680220590971873171140371331206856
]
);
vk.gamma2 = Pairing.G2Point(
[
11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781
],
[
4082367875863433681332203403145435568316851327593401208105741076214120093531,
8495653923123431417604973247489272438418190587263600148770280649306958101930
]
);
vk.delta2 = Pairing.G2Point(
[
11290853248376174984358904800549027724052843605043786792250961933582559784416,
19088798087869078895098454041957301282545108040983567066634161706546841377180
],
[
2613549324505590928634725081465721100391648691301061977245024195885201175000,
16900711717488710996527635174082863643624363550916773862600170691920516587179
]
);
vk.IC = new Pairing.G1Point[](27);
vk.IC[0] = Pairing.G1Point(
19979818312650249281018016981052950968675990952627346907483591409385889371334,
14675330954786426353321242780661450755204240791125907513954615409547191814285
);
vk.IC[1] = Pairing.G1Point(
5829003113921117432206175983572010026961474373472855647935001341465808816339,
19782944755006085617526906642429180306138946798837355469130386866160911601011
);
vk.IC[2] = Pairing.G1Point(
12588535573679309327992671270902604443900873491408310753588166121975917769023,
8219477726636990585687042181227262680748408234115867638626693699807529624430
);
vk.IC[3] = Pairing.G1Point(
16419566697611418137868233429812774560970274989774297684287789963725247230594,
16739285080804767717417083389880687834670337497889709161148870804899232453933
);
vk.IC[4] = Pairing.G1Point(
16265380280881177170721470304996523746928776552716196405150646742099377669931,
19638885469076190510322825514105945378813865877644439953162824321505237392070
);
vk.IC[5] = Pairing.G1Point(
6796494020533467002863143139458992782913216971691648483712266994414215291949,
9707665463330159969812119514363493770851005080885912428651081561719521094933
);
vk.IC[6] = Pairing.G1Point(
21544862858428587736887827977241946178420087708877339811347489861404200945678,
2500513994651656141840810268606328266633030698182038222554168868998950911851
);
vk.IC[7] = Pairing.G1Point(
1230711131658930327648030092718986304477079060235946436492012186862476441198,
2924327811705850943352392316547023661300393599290009775476572413301695659477
);
vk.IC[8] = Pairing.G1Point(
4593824226566234819232211820162561709239122795404699806154965441179806789185,
9489624936684684917819113770049200619128274295587594808702038943690694641556
);
vk.IC[9] = Pairing.G1Point(
2920165003775211935037985895884368960628002338669905002705242721889293661500,
19325075961718529602218214575556958381871801093875004623824789089894826295257
);
vk.IC[10] = Pairing.G1Point(
798816348892537467071912883235430376248661781038228218104756604377494380304,
11781557459672808558858136202683204507290115613628432130488538278285238135010
);
vk.IC[11] = Pairing.G1Point(
12364547354186510362274174544601523892849537922961743171051730704612856215491,
1940501514916509359250239441208143117730918007390438148791819014454436111300
);
vk.IC[12] = Pairing.G1Point(
19374594578181611716973001664490855487600832530676195301956619061624976816219,
14177536357346623944961788153399669848956517572484804092424908023324631652114
);
vk.IC[13] = Pairing.G1Point(
13278036469043701173415661010457809115535144694893393869550044351530605334767,
17412800250990974973260595224521507568391191455832434113883056179425663097823
);
vk.IC[14] = Pairing.G1Point(
9915947382759385783486901323053324677105393406725118625368034668340091270389,
16486030947539867411575826079702988059459377097615987701025504390895170318699
);
vk.IC[15] = Pairing.G1Point(
10032727001807914582863553934823535175601796608073836535872731307698533085785,
3081085031123792073612063961064472989961107449944557192691595148458951458227
);
vk.IC[16] = Pairing.G1Point(
12861664141229362488575301280006958327960741616945360590354230646571221543276,
9465078685742145838883804510121221671872710132970132842647710792990557843321
);
vk.IC[17] = Pairing.G1Point(
5005765047541457738526606112299880823121166660296178632472432158059866023590,
800508591566316812025121111977116353798488540486465046929671355188348296221
);
vk.IC[18] = Pairing.G1Point(
8233737885441865638780817092959136454488041028245340573670741092114576019836,
1905649541669160515152119016722479564120799606865265758708514046334911361937
);
vk.IC[19] = Pairing.G1Point(
18783017403141463325045721611662222497187068636914351056270968332721736892315,
8662142271865866054873642500672292664959895899040501045955952275645237184560
);
vk.IC[20] = Pairing.G1Point(
10527153227169483592824206336300422008699543534031594270383348457913225882068,
12804751884521386825828658510190690148865851791523892609868081031939605961358
);
vk.IC[21] = Pairing.G1Point(
5041944607478059177624125335241251234509534257695185749390941036022860960816,
14715110692011943220034133025600270907853469183775324358827935283475641931483
);
vk.IC[22] = Pairing.G1Point(
6488469541772400815270748483575613205398117193227035746999009466332002218363,
18106240437835593271599347077483242513463538315052985603960081531158477315442
);
vk.IC[23] = Pairing.G1Point(
1230872150417054152826647550639910854731193569978803913801080823930604821153,
4441239834640115257417519970272248211734460485545616030668417315357415914154
);
vk.IC[24] = Pairing.G1Point(
6406343488542573114884112413464096480828930995605113106495691687545227712109,
17426609856363619856299264045979224732259410005301876158111198396440753914863
);
vk.IC[25] = Pairing.G1Point(
3552034247779690337035218009628728589744256014872407428271837630950111570086,
6728361796164047891923136810794508198912347890773721917248600316699829273168
);
vk.IC[26] = Pairing.G1Point(
948342784008576109120692844149854821628015902379340058471998856660569607438,
4633908852276178055277686663038238149681803147457082772385350331688615889853
);
}
function verify(uint256[] memory input, Proof memory proof) internal view returns (uint256) {
uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
VerifyingKey memory vk = verifyingKey();
require(input.length + 1 == vk.IC.length, "verifier-bad-input");
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
for (uint256 i = 0; i < input.length; i++) {
require(input[i] < snark_scalar_field, "verifier-gte-snark-scalar-field");
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}
vk_x = Pairing.addition(vk_x, vk.IC[0]);
if (
!Pairing.pairingProd4(
Pairing.negate(proof.A), proof.B, vk.alfa1, vk.beta2, vk_x, vk.gamma2, proof.C, vk.delta2
)
) return 1;
return 0;
}
/// @return r bool true if proof is valid
function verifyProof(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[26] memory input)
public
view
returns (bool r)
{
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]);
uint256[] memory inputValues = new uint[](input.length);
for (uint256 i = 0; i < input.length; i++) {
inputValues[i] = input[i];
}
if (verify(inputValues, proof) == 0) {
return true;
} else {
return false;
}
}
}

View File

@@ -0,0 +1,461 @@
//
// 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
// ported to solidity 0.6
// fixed linter warnings
// added requiere error messages
//
//
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.11;
library Pairing {
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 generator of G1
function P1() internal pure returns (G1Point memory) {
return G1Point(1, 2);
}
/// @return the generator of G2
function P2() internal pure returns (G2Point memory) {
// Original code point
return G2Point(
[
11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781
],
[
4082367875863433681332203403145435568316851327593401208105741076214120093531,
8495653923123431417604973247489272438418190587263600148770280649306958101930
]
);
/*
// Changed by Jordi point
return G2Point(
[10857046999023057135944570762232829481370756359578518086990519993285655852781,
11559732032986387107991004021392285783925812861821192530917403151452391805634],
[8495653923123431417604973247489272438418190587263600148770280649306958101930,
4082367875863433681332203403145435568316851327593401208105741076214120093531]
);*/
}
/// @return r the negation of p, i.e. p.addition(p.negate()) should be zero.
function negate(G1Point memory p) internal pure returns (G1Point memory r) {
// The prime q in the base field F_q for G1
uint256 q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
if (p.X == 0 && p.Y == 0) return G1Point(0, 0);
return G1Point(p.X, q - (p.Y % q));
}
/// @return r the sum of two points of G1
function addition(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 r the product of a point on G1 and a scalar, i.e.
/// p == p.scalar_mul(1) and p.addition(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 p1, G2Point[] memory p2) internal view returns (bool) {
require(p1.length == p2.length, "pairing-lengths-failed");
uint256 elements = p1.length;
uint256 inputSize = elements * 6;
uint256[] memory input = new uint[](inputSize);
for (uint256 i = 0; i < elements; i++) {
input[i * 6 + 0] = p1[i].X;
input[i * 6 + 1] = p1[i].Y;
input[i * 6 + 2] = p2[i].X[0];
input[i * 6 + 3] = p2[i].X[1];
input[i * 6 + 4] = p2[i].Y[0];
input[i * 6 + 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;
}
/// Convenience method for a pairing check for two pairs.
function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2)
internal
view
returns (bool)
{
G1Point[] memory p1 = new G1Point[](2);
G2Point[] memory p2 = new G2Point[](2);
p1[0] = a1;
p1[1] = b1;
p2[0] = a2;
p2[1] = b2;
return pairing(p1, p2);
}
/// Convenience method for a pairing check for three pairs.
function pairingProd3(
G1Point memory a1,
G2Point memory a2,
G1Point memory b1,
G2Point memory b2,
G1Point memory c1,
G2Point memory c2
) internal view returns (bool) {
G1Point[] memory p1 = new G1Point[](3);
G2Point[] memory p2 = new G2Point[](3);
p1[0] = a1;
p1[1] = b1;
p1[2] = c1;
p2[0] = a2;
p2[1] = b2;
p2[2] = c2;
return pairing(p1, p2);
}
/// Convenience method for a pairing check for four pairs.
function pairingProd4(
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[] memory p1 = new G1Point[](4);
G2Point[] memory p2 = new G2Point[](4);
p1[0] = a1;
p1[1] = b1;
p1[2] = c1;
p1[3] = d1;
p2[0] = a2;
p2[1] = b2;
p2[2] = c2;
p2[3] = d2;
return pairing(p1, p2);
}
}
contract VerifierNonAnonWallet {
using Pairing for *;
struct VerifyingKey {
Pairing.G1Point alfa1;
Pairing.G2Point beta2;
Pairing.G2Point gamma2;
Pairing.G2Point delta2;
Pairing.G1Point[] IC;
}
struct Proof {
Pairing.G1Point A;
Pairing.G2Point B;
Pairing.G1Point C;
}
function verifyingKey() internal pure returns (VerifyingKey memory vk) {
vk.alfa1 = Pairing.G1Point(
20491192805390485299153009773594534940189261866228447918068658471970481763042,
9383485363053290200918347156157836566562967994039712273449902621266178545958
);
vk.beta2 = Pairing.G2Point(
[
4252822878758300859123897981450591353533073413197771768651442665752259397132,
6375614351688725206403948262868962793625744043794305715222011528459656738731
],
[
21847035105528745403288232691147584728191162732299865338377159692350059136679,
10505242626370262277552901082094356697409835680220590971873171140371331206856
]
);
vk.gamma2 = Pairing.G2Point(
[
11559732032986387107991004021392285783925812861821192530917403151452391805634,
10857046999023057135944570762232829481370756359578518086990519993285655852781
],
[
4082367875863433681332203403145435568316851327593401208105741076214120093531,
8495653923123431417604973247489272438418190587263600148770280649306958101930
]
);
vk.delta2 = Pairing.G2Point(
[
1513129022268504209358763521777110646559191312944535983790343685540452445612,
12195000584114682213316512369139242650405673197296308277565313794080256232701
],
[
8360772797757285669693588223224368324652217960280535125476432579464480898413,
520894096459672819696448581165649200639580943346471218758886059131097679200
]
);
vk.IC = new Pairing.G1Point[](35);
vk.IC[0] = Pairing.G1Point(
11537328283226768393732428330956073110918624081922605220849994462257164716154,
21836950190585457727786541794875874204342457073442279657287670575335295859939
);
vk.IC[1] = Pairing.G1Point(
2646307150509379456483324388204599416197823343744573056776580441757968778128,
14368370831419521837815784599306984151624772530159432470308283448431330483438
);
vk.IC[2] = Pairing.G1Point(
16971624750468472956383457363672950966338870541072731251437414691217134411120,
8171770470043873321771537156021753591829282779422875691859226771666334115210
);
vk.IC[3] = Pairing.G1Point(
17003248154885997024042723500051841853373913724967509464380365353802575974728,
7268687626523176370339202918269959082095447462862347953587350206552205642559
);
vk.IC[4] = Pairing.G1Point(
9147424234223179035895482077044817222415554273556867838268712944193520459125,
21578705994822322813698189503736815299185497639660343771670537250469027755948
);
vk.IC[5] = Pairing.G1Point(
16948417333412259029407181850226843375508933350680650127572170476630717282234,
9665563179883720332817127456522837835577284997912132910805194791161889398512
);
vk.IC[6] = Pairing.G1Point(
9319994583370423638602672994382300950312503535351144768052894387193551537459,
19171031111811822064434269353657536224339902296340766041252096715670766153578
);
vk.IC[7] = Pairing.G1Point(
9371603635986601434942264619807195148477116766259006354709633771674992552724,
11103293940652841271958831421967970005702672122254116205870466468493278826958
);
vk.IC[8] = Pairing.G1Point(
3561757143230234005632456376532802121489770329687277876867422136368222736134,
11734814901687269585580202167146602218102049962630061844516173384912435005151
);
vk.IC[9] = Pairing.G1Point(
16676238706569507337573176601181670688707264606170712075388734731572549373015,
11158592034910239776632780480510406146251140896522712858892689171974748083747
);
vk.IC[10] = Pairing.G1Point(
9822993420542467873912218347366938030193407618831916003032667186678059114349,
6863223533781083402167797754430699108902384970089129346113470095574463296737
);
vk.IC[11] = Pairing.G1Point(
4866721301322510775531094866459672146070982818674092273969310571220291630556,
9846640210996171367705574856419053631553093780701851221089070591862473684456
);
vk.IC[12] = Pairing.G1Point(
11121919417977708038028243712145798541888876824913262431306415256568966652024,
12609770408698645498350514024296607224475353273713173177251900216074217014850
);
vk.IC[13] = Pairing.G1Point(
14840050373651993593383756529988239317958775236310376879012996717055845150487,
16683016138329247649135414505803188190565946292769946693147365719646757192352
);
vk.IC[14] = Pairing.G1Point(
12331471170495648864015185687161748641193043583582691780623236535573811426959,
11714531956225516771424892308789701085922077646343882797070434158311220922011
);
vk.IC[15] = Pairing.G1Point(
20857366342421630154576707922479406842052250461154457549932210545889187427626,
19731871495034015170236307573440414758884960293847791130561428843792753429086
);
vk.IC[16] = Pairing.G1Point(
16209406523379999199236029863462575441846948663847567298395841886469797214082,
20718988335004414356172240953954108902387108184538803896124546491420460740169
);
vk.IC[17] = Pairing.G1Point(
8397179853577453858892608876746062307226175687475102639712031393841003387919,
7154528864876839718549215559715861424372452521581853695056564308745992761672
);
vk.IC[18] = Pairing.G1Point(
15000425614899689626185464253582174491572354417903507356426258449407132127046,
13602373364472815067534003393627538602411204908660744540946955553406039633451
);
vk.IC[19] = Pairing.G1Point(
5545252939208963791309142258539499940294193526436021787091019273144246885261,
2675833647327088808677716501235324721140769279456271927816550777395489852251
);
vk.IC[20] = Pairing.G1Point(
2537047834074478023060297384888724410218136939149736151242515713484635642789,
3193147508270124393755481767836877808561856886429215905854576456538260642120
);
vk.IC[21] = Pairing.G1Point(
13819984179784763421503658502014430958296359352165019537755065452645355466085,
18994178127386658997162783196808799873728043389545801736093808610346271161658
);
vk.IC[22] = Pairing.G1Point(
6589370869708283514329318539826110923064805897289825414235274269171407140900,
9625792054986455984618262255946246252268480571067521104852530192902386719593
);
vk.IC[23] = Pairing.G1Point(
14453270681522455527144160729543145025587040770258214008741757192612444737673,
20143300120523312381709649823189168032874279267240134119356286009260298185475
);
vk.IC[24] = Pairing.G1Point(
8846979524751473322994268234754538401482894217522349506066299416316984717260,
14238278950875447301003974303300008365515832894025960431754069250458716988985
);
vk.IC[25] = Pairing.G1Point(
1407828395464361821905309927863167268883721160860555328581866885124611897528,
15987854649208373291691713233856831065610123809032682196292981616658494776987
);
vk.IC[26] = Pairing.G1Point(
20657483817998070093869648777824400547660398552294774144027193285451239619382,
21826613460615350291777797645121185151869814459512699120411019581958075991542
);
vk.IC[27] = Pairing.G1Point(
20624928617240774729161710928323453036077574938263627000040473137006018415415,
5131454817008842892320970265930200727731593782574755474310860262365965990664
);
vk.IC[28] = Pairing.G1Point(
13895249990164764352618348094492621630575085672410127797817030490897283284460,
11796920177436300262655702063092510613755939821586920802170520825907606184557
);
vk.IC[29] = Pairing.G1Point(
3135811994020780743324086008729747183900335277491189104250977002197575809365,
19637480211624980857501152516366188197895066151113910763057713919944678029866
);
vk.IC[30] = Pairing.G1Point(
606103022421566187118689786319065266252504972683102249226453076393842587324,
10274587862652629723961486759561149321143114208996217550785138347396636546119
);
vk.IC[31] = Pairing.G1Point(
17876636758905622230688544604495142065478803287376541138330575508029036910921,
845514606482911379811389987862147832754587150586772012493153117259870748818
);
vk.IC[32] = Pairing.G1Point(
3055934057788309375612017678081361462642124203699024466299800387864964176162,
6215463225683464296816602777995066291454589608119271052091343612576965188139
);
vk.IC[33] = Pairing.G1Point(
15655816926053512024400351678850471800118254812943223488624167720306406940886,
1318451441938966067851976459607266837893918068783070216555767331693196884126
);
vk.IC[34] = Pairing.G1Point(
13942491345765259320815758221010163510633256522676138655574537850419728932090,
2449428677603436766608016448595332101485961035234011384134049218692804513154
);
}
function verify(uint256[] memory input, Proof memory proof) public view returns (uint256) {
uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
VerifyingKey memory vk = verifyingKey();
require(input.length + 1 == vk.IC.length, "verifier-bad-input");
// Compute the linear combination vk_x
Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
for (uint256 i = 0; i < input.length; i++) {
require(input[i] < snark_scalar_field, "verifier-gte-snark-scalar-field");
vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.IC[i + 1], input[i]));
}
vk_x = Pairing.addition(vk_x, vk.IC[0]);
if (
!Pairing.pairingProd4(
Pairing.negate(proof.A), proof.B, vk.alfa1, vk.beta2, vk_x, vk.gamma2, proof.C, vk.delta2
)
) return 1;
return 0;
}
/// @return r bool true if proof is valid
function verifyProof(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[34] memory input)
public
view
returns (bool r)
{
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]);
uint256[] memory inputValues = new uint[](input.length);
for (uint256 i = 0; i < input.length; i++) {
inputValues[i] = input[i];
}
if (verify(inputValues, proof) == 0) {
return true;
} else {
return false;
}
}
}

View File

@@ -3,171 +3,248 @@ pragma solidity >=0.7.6;
// https://github.com/nalinbhardwaj/ethdosnumber/blob/main/ethdos-contracts/src/HexStrings.sol
library StringUtils {
bytes16 internal constant ALPHABET = "0123456789abcdef";
bytes16 internal constant ALPHABET = "0123456789abcdef";
/// @notice Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
/// @dev Credit to Open Zeppelin under MIT license https://github.com/OpenZeppelin/openzeppelin-contracts/blob/243adff49ce1700e0ecb99fe522fb16cff1d1ddc/contracts/utils/Strings.sol#L55
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = ALPHABET[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length);
for (uint256 i = buffer.length; i > 0; i--) {
buffer[i - 1] = ALPHABET[value & 0xf];
value >>= 4;
}
return string(buffer);
}
function toString(uint256 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
function toString(bytes32 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
function toString(address account) internal pure returns (string memory) {
return toString(abi.encodePacked(account));
}
function stringEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
function toString(bytes memory data) internal pure returns (string memory) {
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(2 + data.length * 2);
str[0] = "0";
str[1] = "x";
for (uint256 i = 0; i < data.length; i++) {
str[2 + i * 2] = alphabet[uint256(uint8(data[i] >> 4))];
str[3 + i * 2] = alphabet[uint256(uint8(data[i] & 0x0f))];
}
return string(str);
}
// Unpacks uint256s into bytes and then extracts the non-zero characters
// Only extracts contiguous non-zero characters and ensures theres only 1 such state
// Note that unpackedLen may be more than packedBytes.length * 8 since there may be 0s
// TODO: Remove console.logs and define this as a pure function instead of a view
function convertPackedBytesToString(uint256[] memory packedBytes, uint256 maxBytes, uint256 packSize) internal pure returns (string memory extractedString) {
uint8 state = 0;
// bytes: 0 0 0 0 y u s h _ g 0 0 0
// state: 0 0 0 0 1 1 1 1 1 1 2 2 2
bytes memory nonzeroBytesArray = new bytes(packedBytes.length * 7);
uint256 nonzeroBytesArrayIndex = 0;
for (uint16 i = 0; i < packedBytes.length; i++) {
uint256 packedByte = packedBytes[i];
uint8[] memory unpackedBytes = new uint8[](packSize);
for (uint256 j = 0; j < packSize; j++) {
unpackedBytes[j] = uint8(packedByte >> (j * 8));
}
for (uint256 j = 0; j < packSize; j++) {
uint256 unpackedByte = unpackedBytes[j]; //unpackedBytes[j];
// console.log(i, j, state, unpackedByte);
if (unpackedByte != 0) {
nonzeroBytesArray[nonzeroBytesArrayIndex] = bytes1(uint8(unpackedByte));
nonzeroBytesArrayIndex++;
if (state % 2 == 0) {
state += 1;
}
} else {
if (state % 2 == 1) {
state += 1;
}
/// @notice Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
/// @dev Credit to Open Zeppelin under MIT license https://github.com/OpenZeppelin/openzeppelin-contracts/blob/243adff49ce1700e0ecb99fe522fb16cff1d1ddc/contracts/utils/Strings.sol#L55
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = ALPHABET[value & 0xf];
value >>= 4;
}
packedByte = packedByte >> 8;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
require(state >= 1, "Invalid final state of packed bytes in email");
require(nonzeroBytesArrayIndex <= maxBytes, "Packed bytes more than allowed max length!");
string memory returnValue = removeTrailingZeros(string(nonzeroBytesArray));
return returnValue;
// Have to end at the end of the email -- state cannot be 1 since there should be an email footer
}
function bytes32ToString(bytes32 input) internal pure returns (string memory) {
uint256 i;
for (i = 0; i < 32 && input[i] != 0; i++) {}
bytes memory resultBytes = new bytes(i);
for (i = 0; i < 32 && input[i] != 0; i++) {
resultBytes[i] = input[i];
function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length);
for (uint256 i = buffer.length; i > 0; i--) {
buffer[i - 1] = ALPHABET[value & 0xf];
value >>= 4;
}
return string(buffer);
}
return string(resultBytes);
}
// sliceArray is used to slice an array of uint256s from start-end into a new array of uint256s
function sliceArray(uint256[] memory input, uint256 start, uint256 end) internal pure returns (uint256[] memory) {
require(start <= end && end <= input.length, "Invalid slice indices");
uint256[] memory result = new uint256[](end - start);
for (uint256 i = start; i < end; i++) {
result[i - start] = input[i];
function toString(uint256 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
return result;
}
// stringToUint is used to convert a string like "45" to a uint256 4
function stringToUint(string memory s) internal pure returns (uint256) {
bytes memory b = bytes(s);
uint256 result = 0;
for (uint256 i = 0; i < b.length; i++) {
if (b[i] >= 0x30 && b[i] <= 0x39) {
result = result * 10 + (uint256(uint8(b[i])) - 48);
}
function toString(bytes32 value) internal pure returns (string memory) {
return toString(abi.encodePacked(value));
}
// TODO: Currently truncates decimals
if (b[i] == 0x2E) {
function toString(address account) internal pure returns (string memory) {
return toString(abi.encodePacked(account));
}
function stringEq(string memory a, string memory b) internal pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
function toString(bytes memory data) internal pure returns (string memory) {
bytes memory alphabet = "0123456789abcdef";
bytes memory str = new bytes(2 + data.length * 2);
str[0] = "0";
str[1] = "x";
for (uint256 i = 0; i < data.length; i++) {
str[2 + i * 2] = alphabet[uint256(uint8(data[i] >> 4))];
str[3 + i * 2] = alphabet[uint256(uint8(data[i] & 0x0f))];
}
return string(str);
}
// Unpacks uint256s into bytes and then extracts the non-zero characters
// Only extracts contiguous non-zero characters and ensures theres only 1 such state
// Note that unpackedLen may be more than packedBytes.length * 8 since there may be 0s
// TODO: Remove console.logs and define this as a pure function instead of a view
function convertPackedBytesToString(uint256[] memory packedBytes, uint256 maxBytes, uint256 packSize)
internal
pure
returns (string memory extractedString)
{
uint8 state = 0;
// bytes: 0 0 0 0 y u s h _ g 0 0 0
// state: 0 0 0 0 1 1 1 1 1 1 2 2 2
bytes memory nonzeroBytesArray = new bytes(packedBytes.length * 7);
uint256 nonzeroBytesArrayIndex = 0;
for (uint16 i = 0; i < packedBytes.length; i++) {
uint256 packedByte = packedBytes[i];
uint8[] memory unpackedBytes = new uint8[](packSize);
for (uint256 j = 0; j < packSize; j++) {
unpackedBytes[j] = uint8(packedByte >> (j * 8));
}
for (uint256 j = 0; j < packSize; j++) {
uint256 unpackedByte = unpackedBytes[j]; //unpackedBytes[j];
// console.log(i, j, state, unpackedByte);
if (unpackedByte != 0) {
nonzeroBytesArray[nonzeroBytesArrayIndex] = bytes1(uint8(unpackedByte));
nonzeroBytesArrayIndex++;
if (state % 2 == 0) {
state += 1;
}
} else {
if (state % 2 == 1) {
state += 1;
}
}
packedByte = packedByte >> 8;
}
}
require(state >= 1, "Invalid final state of packed bytes in email");
require(nonzeroBytesArrayIndex <= maxBytes, "Packed bytes more than allowed max length!");
string memory returnValue = removeTrailingZeros(string(nonzeroBytesArray));
return returnValue;
// Have to end at the end of the email -- state cannot be 1 since there should be an email footer
}
function bytes32ToString(bytes32 input) internal pure returns (string memory) {
uint256 i;
for (i = 0; i < 32 && input[i] != 0; i++) {}
bytes memory resultBytes = new bytes(i);
for (i = 0; i < 32 && input[i] != 0; i++) {
resultBytes[i] = input[i];
}
return string(resultBytes);
}
// sliceArray is used to slice an array of uint256s from start-end into a new array of uint256s
function sliceArray(uint256[] memory input, uint256 start, uint256 end) internal pure returns (uint256[] memory) {
require(start <= end && end <= input.length, "Invalid slice indices");
uint256[] memory result = new uint256[](end - start);
for (uint256 i = start; i < end; i++) {
result[i - start] = input[i];
}
return result;
}
}
return result;
}
// getDomainFromEmail is used to extract the domain from an email i.e. the part after the @
function getDomainFromEmail(string memory fromEmail) internal pure returns (string memory) {
bytes memory emailBytes = bytes(fromEmail);
uint256 atIndex;
for (uint256 i = 0; i < emailBytes.length; i++) {
if (emailBytes[i] == "@") {
atIndex = i;
break;
}
}
bytes memory domainBytes = new bytes(emailBytes.length - atIndex - 1);
for (uint256 j = 0; j < domainBytes.length; j++) {
domainBytes[j] = emailBytes[atIndex + 1 + j];
}
return bytes32ToString(bytes32(bytes(domainBytes)));
}
// stringToUint is used to convert a string like "45" to a uint256 4
function stringToUint(string memory s) internal pure returns (uint256) {
bytes memory b = bytes(s);
uint256 result = 0;
for (uint256 i = 0; i < b.length; i++) {
if (b[i] >= 0x30 && b[i] <= 0x39) {
result = result * 10 + (uint256(uint8(b[i])) - 48);
}
function removeTrailingZeros(string memory input) public pure returns (string memory) {
bytes memory inputBytes = bytes(input);
uint256 endIndex = inputBytes.length;
for (uint256 i = 0; i < inputBytes.length; i++) {
if (inputBytes[i] == 0) {
endIndex = i;
break;
// TODO: Currently truncates decimals
if (b[i] == 0x2E) {
return result;
}
}
return result;
}
bytes memory resultBytes = new bytes(endIndex);
for (uint256 i = 0; i < endIndex; i++) {
resultBytes[i] = inputBytes[i];
// getDomainFromEmail is used to extract the domain from an email i.e. the part after the @
function getDomainFromEmail(string memory fromEmail) internal pure returns (string memory) {
bytes memory emailBytes = bytes(fromEmail);
uint256 atIndex;
for (uint256 i = 0; i < emailBytes.length; i++) {
if (emailBytes[i] == "@") {
atIndex = i;
break;
}
}
bytes memory domainBytes = new bytes(emailBytes.length - atIndex - 1);
for (uint256 j = 0; j < domainBytes.length; j++) {
domainBytes[j] = emailBytes[atIndex + 1 + j];
}
return bytes32ToString(bytes32(bytes(domainBytes)));
}
return string(resultBytes);
}
function removeTrailingZeros(string memory input) public pure returns (string memory) {
bytes memory inputBytes = bytes(input);
uint256 endIndex = inputBytes.length;
for (uint256 i = 0; i < inputBytes.length; i++) {
if (inputBytes[i] == 0) {
endIndex = i;
break;
}
}
bytes memory resultBytes = new bytes(endIndex);
for (uint256 i = 0; i < endIndex; i++) {
resultBytes[i] = inputBytes[i];
}
return string(resultBytes);
}
// Upper/lower string utils from https://github.com/willitscale/solidity-util/blob/master/lib/Strings.sol
/**
* Upper
*
* Converts all the values of a string to their corresponding upper case
* value.
*
* @param _base When being used for a data type this is the extended object
* otherwise this is the string base to convert to upper case
* @return string
*/
function upper(string memory _base) public pure returns (string memory) {
bytes memory _baseBytes = bytes(_base);
for (uint256 i = 0; i < _baseBytes.length; i++) {
_baseBytes[i] = _upper(_baseBytes[i]);
}
return string(_baseBytes);
}
/**
* Lower
*
* Converts all the values of a string to their corresponding lower case
* value.
*
* @param _base When being used for a data type this is the extended object
* otherwise this is the string base to convert to lower case
* @return string
*/
function lower(string memory _base) public pure returns (string memory) {
bytes memory _baseBytes = bytes(_base);
for (uint256 i = 0; i < _baseBytes.length; i++) {
_baseBytes[i] = _lower(_baseBytes[i]);
}
return string(_baseBytes);
}
/**
* Upper
*
* Convert an alphabetic character to upper case and return the original
* value when not alphabetic
*
* @param _b1 The byte to be converted to upper case
* @return bytes1 The converted value if the passed value was alphabetic
* and in a lower case otherwise returns the original value
*/
function _upper(bytes1 _b1) private pure returns (bytes1) {
if (_b1 >= 0x61 && _b1 <= 0x7A) {
return bytes1(uint8(_b1) - 32);
}
return _b1;
}
/**
* Lower
*
* Convert an alphabetic character to lower case and return the original
* value when not alphabetic
*
* @param _b1 The byte to be converted to lower case
* @return bytes1 The converted value if the passed value was alphabetic
* and in a upper case otherwise returns the original value
*/
function _lower(bytes1 _b1) private pure returns (bytes1) {
if (_b1 >= 0x41 && _b1 <= 0x5A) {
return bytes1(uint8(_b1) + 32);
}
return _b1;
}
}

View File

@@ -0,0 +1,104 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract TokenRegistry {
// Define a structure that represents a token
struct Token {
address optimism;
address arbitrum;
address xdai;
address goerli;
address mainnet;
}
// Define a mapping from token names to their addresses on each chain
mapping(string => Token) private tokens;
mapping(uint256 => string) private chainIdToName;
// Define the owner of the contract
address private owner;
// Ensure that only the owner can call certain functions
modifier onlyOwner() {
require(msg.sender == owner, "Only the owner can call this function.");
_;
}
// Set the owner of the contract
constructor() {
owner = msg.sender;
// Initialize the mapping with some hardcoded addresses
// TODO: Add RAI, and some other tokens
tokens["DAI"] = Token({
optimism: 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1,
arbitrum: 0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1,
xdai: 0x44fA8E6f47987339850636F88629646662444217,
goerli: 0x11fE4B6AE13d2a6055C8D9cF65c55bac32B5d844,
mainnet: 0x6B175474E89094C44Da98b954EedeAC495271d0F
});
tokens["USDC"] = Token({
optimism: 0x7F5c764cBc14f9669B88837ca1490cCa17c31607,
arbitrum: 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8,
xdai: 0xDDAfbb505ad214D7b80b1f830fcCc89B60fb7A83,
goerli: 0x07865c6E87B9F70255377e024ace6630C1Eaa37F,
mainnet: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
});
tokens["WETH"] = Token({
optimism: 0x4200000000000000000000000000000000000006,
arbitrum: 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1,
xdai: 0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1,
goerli: 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6,
mainnet: 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
});
chainIdToName[0] = "mainnet";
chainIdToName[10] = "optimism";
chainIdToName[100] = "xdai";
chainIdToName[42161] = "arbitrum";
chainIdToName[5] = "goerli";
chainIdToName[31337] = "goerli"; // Local foundry test chain goerli fork
}
// Return the address of a token on a specific chain
function getTokenAddress(string memory tokenName, uint256 chainId) public view returns (address) {
return getTokenAddress(tokenName, chainIdToName[chainId]);
}
// Return the address of a token on a specific chain
function getTokenAddress(string memory tokenName, string memory chainName) public view returns (address) {
if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("optimism"))) {
return tokens[tokenName].optimism;
} else if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("arbitrum"))) {
return tokens[tokenName].arbitrum;
} else if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("xdai"))) {
return tokens[tokenName].xdai;
} else if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("goerli"))) {
return tokens[tokenName].goerli;
} else if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("mainnet"))) {
return tokens[tokenName].mainnet;
} else {
revert("Invalid chain name.");
}
}
// Update the address of a token on a specific chain
function updateTokenAddress(string memory tokenName, string memory chainName, address newAddress)
public
onlyOwner
{
if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("optimism"))) {
tokens[tokenName].optimism = newAddress;
} else if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("arbitrum"))) {
tokens[tokenName].arbitrum = newAddress;
} else if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("xdai"))) {
tokens[tokenName].xdai = newAddress;
} else if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("goerli"))) {
tokens[tokenName].goerli = newAddress;
} else if (keccak256(abi.encodePacked(chainName)) == keccak256(abi.encodePacked("mainnet"))) {
tokens[tokenName].mainnet = newAddress;
} else {
revert("Invalid chain name.");
}
}
}

View File

@@ -4,157 +4,22 @@ pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/Proxy.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "forge-std/console.sol";
// import "./base64.sol";
import "./StringUtils.sol";
import "./AutoApproveWallet.sol";
import "./TestERC20.sol";
import "./NFTSVG.sol";
import {Verifier} from "./Groth16VerifierWallet.sol";
import {Verifier} from "./Groth16VerifierWalletAnon.sol";
import "./MailServer.sol";
contract VerifiedWalletEmail {
using StringUtils for *;
uint16 public constant packSize = 7; // 7 bytes in a packed item returned from circom
uint16 public constant body_len = 4 * 4;
uint16 public constant rsa_modulus_chunks_len = 17;
uint16 public constant commitment_len = 1;
uint16 public constant msg_len = body_len + rsa_modulus_chunks_len + commitment_len; // 34
uint16 public constant header_len = msg_len - body_len;
uint16 public constant addressIndexInSignals = msg_len - 1; // The last index is the commitment
mapping(string => uint256[rsa_modulus_chunks_len]) public verifiedMailserverKeys;
mapping(string => uint256) public balance;
// NOTE: is Ownable is only for emergency ejects in testing deployments
contract VerifiedWalletEmailStorage {
// mapping(string => uint256) public balance;
mapping(uint256 => bool) public nullifier;
MailServer mailServer;
Verifier public immutable verifier;
TestEmailToken public testToken;
mapping(bytes32 => address) public wallets;
// Arguments are deployed contracts/addresses
constructor(Verifier v, MailServer m, TestEmailToken t) {
// Do dig TXT outgoing._domainkey.twitter.com to verify these.
// This is the base 2^121 representation of that key.
// Circom bigint: represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
require(rsa_modulus_chunks_len + body_len + 1 == msg_len, "Variable counts are wrong!");
verifier = v;
mailServer = m;
testToken = t;
}
// TODO: Make internal
function moveTokens(bytes32 salt1, bytes32 salt2, uint256 amount) public {
address wallet1 = getOrCreateWallet(salt1);
address wallet2 = getOrCreateWallet(salt2);
// Check for allowance and balance
require(testToken.allowance(wallet1, address(this)) >= amount, "Allowance too low");
require(testToken.balanceOf(wallet1) >= amount, "Insufficient balance to perform the transfer");
testToken.transferFrom(wallet1, wallet2, amount);
}
function getOrCreateWallet(bytes32 salt) internal returns (address) {
bytes32 hashedSalt = keccak256(abi.encodePacked(salt));
address wallet = wallets[hashedSalt];
if (wallet == address(0)) {
// Create wallet
bytes memory bytecode = type(AutoApproveWallet).creationCode;
assembly {
wallet := create2(0, add(bytecode, 0x20), mload(bytecode), hashedSalt)
}
console.log("Wallet index at:");
console.logBytes32(hashedSalt);
console.log("Wallet address created:", wallet);
require(wallet != address(0), "Wallet creation failed");
wallets[hashedSalt] = wallet;
// TODO: Remove this mint, it's only for test token
testToken.mint(wallet, 10 * 10 ** testToken.decimals()); // 10 tokens with 18 decimals
// Initialize the wallet with the token address and approver
AutoApproveWallet(wallet).initialize(address(testToken), address(this));
}
return wallet;
}
function convertEmailToIndex(string memory email) public pure returns (bytes32) {
return keccak256(abi.encodePacked(convertEmailToBytes(email)));
}
function convertEmailToBytes(string memory email) public pure returns (bytes32) {
return bytes32(bytes(StringUtils.removeTrailingZeros(email)));
}
// TODO: When anon, it shouldn't be possible to get this via email, you have to pass in the salt
function getBalance(string memory email) public view returns (uint256) {
return testToken.balanceOf(wallets[convertEmailToIndex(email)]);
}
function transfer(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[msg_len] memory signals)
public
{
// TODO no invalid signal check yet, which is fine since the zk proof does it
// Checks: Verify proof and check signals
// require(signals[0] == 1337, "invalid signals");
// 3 public signals are the masked packed message bytes, 17 are the modulus.
uint256[] memory bodySignals = new uint256[](body_len);
uint256[] memory rsaModulusSignals = new uint256[](header_len);
for (uint256 i = 0; i < body_len; i++) {
bodySignals[i] = signals[i];
}
for (uint256 i = body_len; i < msg_len - 1; i++) {
rsaModulusSignals[i - body_len] = signals[i];
}
// Check eth address committed to in proof matches msg.sender, to avoid doublespend and relayer-frontrunning-relayer-for-profit
// require(address(uint160(signals[addressIndexInSignals])) == msg.sender, "Invalid address");
// TODO: Note that this is buggy since it is malleable
require(!nullifier[a[0]], "Value is already true");
nullifier[a[0]] = true;
// Check from/to email domains are correct [in this case, only from domain is checked]
// Right now, we just check that any email was received from anyone at Twitter, which is good enough for now
// We will upload the version with these domain checks soon!
// require(_domainCheck(headerSignals), "Invalid domain");
string memory fromEmail =
StringUtils.convertPackedBytesToString(StringUtils.sliceArray(bodySignals, 0, 5), packSize * 4, packSize);
string memory amount =
StringUtils.convertPackedBytesToString(StringUtils.sliceArray(bodySignals, 5, 10), packSize * 4, packSize);
string memory currency =
StringUtils.convertPackedBytesToString(StringUtils.sliceArray(bodySignals, 10, 11), packSize * 4, packSize);
string memory recipientEmail =
StringUtils.convertPackedBytesToString(StringUtils.sliceArray(bodySignals, 11, 16), packSize * 4, packSize);
string memory domain = StringUtils.getDomainFromEmail(fromEmail);
console.log(domain);
// Verify that the public key for RSA matches the hardcoded one
for (uint256 i = body_len; i < msg_len - 1; i++) {
require(mailServer.isVerified(domain, i - body_len, signals[i]), "Invalid: RSA modulus not matched");
}
require(verifier.verifyProof(a, b, c, signals), "Invalid Proof"); // checks effects iteractions, this should come first
console.log("Proof passed!");
// Print transfer data
uint256 amountToTransfer = StringUtils.stringToUint(amount) * 10 ** testToken.decimals();
console.log("Original from email", fromEmail);
console.log("Original recipient email", recipientEmail);
console.log("Original amount", amount);
console.log("Original currency", currency);
console.log("Transferring", amountToTransfer);
console.log("From", fromEmail, "to", recipientEmail);
// Effects: Send money
// Transfer the tokens
moveTokens(convertEmailToBytes(fromEmail), convertEmailToBytes(recipientEmail), amountToTransfer);
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal {
require(from == address(0), "Cannot transfer - VerifiedEmail is soulbound");
}
}

View File

@@ -0,0 +1,269 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts/proxy/Proxy.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "./WalletEmailHandler.sol";
import "forge-std/console.sol";
import "./StringUtils.sol";
import "./AutoApproveWallet.sol";
import "./TestERC20.sol";
import "./NFTSVG.sol";
import "./TokenRegistry.sol";
import {Verifier} from "./Groth16VerifierWalletAnon.sol";
import "./MailServer.sol";
// Defines upgradable logic
contract WalletEmailHandlerLogic is VerifiedWalletEmailStorage, Ownable, Initializable {
using StringUtils for *;
uint16 public constant packSize = 30; // Bytes in a packed item returned from circom
uint16 public constant body_len = 4 + 4;
uint16 public constant rsa_modulus_chunks_len = 17;
uint16 public constant commitment_len = 1;
uint16 public constant msg_len = body_len + rsa_modulus_chunks_len + commitment_len; // 26
uint16 public constant header_len = msg_len - body_len;
uint16 public constant addressIndexInSignals = msg_len - 1; // The last index is the commitment
Verifier public verifier;
TestEmailToken public testToken;
TokenRegistry public tokenRegistry;
MailServer mailServer;
mapping(string => address) public defaultVerifiers;
// Note that the data lives in the WalletEmailHandlerStorage contract
// Arguments are deployed contracts/addresses
function initialize(Verifier v, MailServer m, TestEmailToken t, TokenRegistry r) public initializer {
// Do dig TXT outgoing._domainkey.twitter.com to verify these.
// This is the base 2^121 representation of that key.
// Circom bigint: represent a = a[0] + a[1] * 2**n + .. + a[k - 1] * 2**(n * k)
console.log("Entering initialize.");
require(rsa_modulus_chunks_len + body_len + 1 == msg_len, "Variable counts are wrong!");
verifier = v;
mailServer = m;
testToken = t;
tokenRegistry = r;
}
function commandString() public pure returns (string memory) {
return "send";
}
// NOTE: This is only for emergency ejects in testing deployments
function upgradeVerifier(Verifier v) public onlyOwner {
verifier = v;
}
// NOTE: This is only for emergency ejects in testing deployments
function migrateAllToken(uint256 fromSalt, uint256 toSalt, address token) public onlyOwner {
address fromWallet = getOrCreateWallet(fromSalt);
uint256 balance = IERC20(token).balanceOf(fromWallet);
moveTokens(fromSalt, toSalt, balance, token);
}
// NOTE: This is only for emergency ejects in testing deployments
function migrateAllToken(uint256 fromSalt, uint256 toSalt, string memory tokenName) public onlyOwner {
address tokenAddress = tokenRegistry.getTokenAddress(tokenName, getChainID());
migrateAllToken(fromSalt, toSalt, tokenAddress);
}
// NOTE: This is only for emergency ejects in testing deployments
function migrateAllToken(bytes32 fromSalt, address toWallet, string memory tokenName) public onlyOwner {
address tokenAddress = tokenRegistry.getTokenAddress(tokenName, getChainID());
migrateAllToken(fromSalt, toWallet, tokenAddress);
}
// NOTE: This is only for emergency ejects in testing deployments
function migrateAllToken(bytes32 fromSalt, address toWallet, address token) public onlyOwner {
address fromWallet = getOrCreateWallet(fromSalt);
uint256 amount = IERC20(token).balanceOf(fromWallet);
IERC20 tokenToUse = token == address(0) ? testToken : IERC20(token);
require(tokenToUse.allowance(fromWallet, address(this)) >= amount, "Allowance too low");
require(tokenToUse.balanceOf(fromWallet) >= amount, "Insufficient balance to perform the transfer");
tokenToUse.transferFrom(fromWallet, toWallet, amount);
}
/**
* @dev Moves tokens from one wallet to another using the provided salts.
* @param fromSalt The salt used to derive the sender's wallet address.
* @param toSalt The salt used to derive the recipient's wallet address.
* @param amount The amount of tokens to transfer.
* @param token The address of the token to transfer. 0 if should use test token.
*/
function moveTokens(uint256 fromSalt, uint256 toSalt, uint256 amount, address token) internal {
address toWallet = getOrCreateWallet(bytes32(toSalt));
address fromWallet = getOrCreateWallet(bytes32(fromSalt));
moveTokens(fromWallet, toWallet, amount, token);
}
/**
* @dev Moves tokens from one wallet to another using the provided wallet addresses.
* @param fromWallet The wallet address of the sender.
* @param toWallet The wallet address of the recipient.
* @param amount The amount of tokens to transfer.
* @param token The address of the token to transfer. 0 if should use test token.
*/
function moveTokens(address fromWallet, address toWallet, uint256 amount, address token) internal {
IERC20 tokenToUse = token == address(0) ? testToken : IERC20(token);
// Check for allowance and balance
require(tokenToUse.allowance(fromWallet, address(this)) >= amount, "Allowance too low");
require(tokenToUse.balanceOf(fromWallet) >= amount, "Insufficient balance to perform the transfer");
tokenToUse.transferFrom(fromWallet, toWallet, amount);
}
/**
* @dev Returns the wallet address associated with the given salt. If the wallet does not exist, it creates a new one and gives it 10 TestTokens.
* @param salt The salt used to derive the wallet address.
* @return wallet The wallet address associated with the given salt.
*/
function getOrCreateWallet(uint256 salt) public returns (address) {
return getOrCreateWallet(bytes32(salt));
}
/**
* @dev Returns the wallet address associated with the given salt. If the wallet does not exist, it creates a new one and gives it 10 TestTokens.
* @param salt The salt used to derive the wallet address.
* @return wallet The wallet address associated with the given salt.
*/
function getOrCreateWallet(bytes32 salt) public returns (address) {
return getOrCreateWallet(salt, false);
}
/**
* @dev Returns the wallet address associated with the given salt. If the wallet does not exist, it creates a new one and gives it 10 TestTokens.
* @param salt The salt used to derive the wallet address.
* @param allowedToCreateWallet A boolean flag indicating if a new wallet is authorized to be created if it does not exist. True if the message id matches an email.
* @return wallet The wallet address associated with the given salt.
*/
function getOrCreateWallet(uint256 salt, bool allowedToCreateWallet) internal returns (address) {
return getOrCreateWallet(bytes32(salt), allowedToCreateWallet);
}
/**
* @dev Returns the wallet address associated with the given salt. If the wallet does not exist and allowedToCreateWallet is true, it creates a new one and gives it 10 TestTokens.
* @param salt The salt used to derive the wallet address.
* @param allowedToCreateWallet A boolean flag indicating if a new wallet is authorized to be created if it does not exist.
* @return wallet The wallet address associated with the given salt.
*/
function getOrCreateWallet(bytes32 salt, bool allowedToCreateWallet) internal returns (address) {
address wallet = wallets[salt];
if (wallet == address(0) && allowedToCreateWallet) {
// Create wallet
bytes memory bytecode = type(AutoApproveWallet).creationCode;
assembly {
wallet := create2(0, add(bytecode, 0x20), mload(bytecode), salt)
}
console.log("Wallet index at:");
console.logBytes32(salt);
console.log("Wallet address created:", wallet);
require(wallet != address(0), "Wallet creation failed");
wallets[salt] = wallet;
// TODO: Remove this mint, it's only for test token
testToken.mint(wallet, 10 * 10 ** testToken.decimals()); // 10 tokens with 18 decimals
// Initialize the wallet with the token address and approver
AutoApproveWallet(wallet).initialize(address(testToken), address(this));
}
return wallet;
}
// OLD DEPRECATED PUBLIC WALLET HELPER FUNCTIONS
// function convertEmailToIndex(string memory email) public pure returns (bytes32) {
// // EDIT: Take MIMC here.
// return keccak256(abi.encodePacked(convertEmailToBytes(email)));
// }
// function convertEmailToBytes(string memory email) public pure returns (bytes32) {
// return bytes32(bytes(StringUtils.removeTrailingZeros(email)));
// }
// // NOTE: Calling this function will break your anonymity to a light/full node.
// function getBalance(string memory email, string memory salt) public view returns (uint256) {
// return testToken.balanceOf(wallets[convertEmailToIndex(email)]);
// }
// MAIN TRANSFER FUNCTION
function transfer(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[msg_len] memory signals)
public
{
// TODO no invalid signal check yet, which is fine since the zk proof does it
// Checks: Verify proof and check signals
// require(signals[0] == 1337, "invalid signals");
// 3 public signals are the masked packed message bytes, 17 are the modulus.
uint256[] memory bodySignals = new uint256[](body_len);
uint256[] memory rsaModulusSignals = new uint256[](header_len);
for (uint256 i = 0; i < body_len; i++) {
bodySignals[i] = signals[i];
}
for (uint256 i = body_len; i < msg_len - 1; i++) {
rsaModulusSignals[i - body_len] = signals[i];
}
// Check eth address committed to in proof matches msg.sender, to avoid doublespend and relayer-frontrunning-relayer-for-profit
// require(address(uint160(signals[addressIndexInSignals])) == msg.sender, "Invalid address");
// TODO: Must edit generate_input to have a unique value for "address" for this nullifier to pass
require(!nullifier[signals[msg_len - 1]], "Value is already true");
nullifier[signals[msg_len - 1]] = true;
// Check from/to email domains are correct [in this case, only from domain is checked]
// We will upload the version with these domain checks soon!
// require(_domainCheck(headerSignals), "Invalid domain");
string memory command =
StringUtils.convertPackedBytesToString(StringUtils.sliceArray(bodySignals, 0, 1), packSize, packSize);
string memory amount =
StringUtils.convertPackedBytesToString(StringUtils.sliceArray(bodySignals, 1, 2), packSize, packSize);
string memory currency =
StringUtils.convertPackedBytesToString(StringUtils.sliceArray(bodySignals, 3, 4), packSize, packSize);
bool canCreateFromWallet = bodySignals[4] == 1;
uint256 fromSalt = bodySignals[5];
bool canCreateToWallet = bodySignals[6] == 1;
uint256 toSalt = bodySignals[7];
// Require that the user is calling with the correct command
require(StringUtils.stringEq(StringUtils.lower(command), commandString()));
string memory additionalInfo = StringUtils.convertPackedBytesToString(
StringUtils.sliceArray(bodySignals, msg_len - 1, msg_len), packSize, packSize
);
string memory domain = additionalInfo; // Change this later to actually parse the domain
// Verify that the public key for RSA matches the hardcoded one
for (uint256 i = body_len; i < msg_len - 1; i++) {
require(mailServer.isVerified(domain, i - body_len, signals[i]), "Invalid: RSA modulus not matched");
}
require(verifier.verifyProof(a, b, c, signals), "Invalid Proof"); // checks effects iteractions, this should come first
console.log("Proof passed!");
// Print transfer data
uint256 amountToTransfer = StringUtils.stringToUint(amount) * 10 ** testToken.decimals();
console.log("Original from salt", fromSalt);
console.log("Original recipient salt", toSalt);
console.log("Original amount", amount);
console.log("Original currency", currency);
console.log("Transferring", amountToTransfer);
console.log("From", fromSalt, "to", toSalt);
// Effects: Send money
// Transfer the tokens
moveTokens(fromSalt, toSalt, amountToTransfer, address(testToken));
}
function getChainID() public view returns (uint256) {
uint256 chainId;
assembly {
chainId := chainid()
}
return chainId;
}
}

View File

@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/proxy/Proxy.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
contract WalletEmailHandlerProxy is TransparentUpgradeableProxy {
constructor(address logic, address admin, bytes memory data)
TransparentUpgradeableProxy(logic, admin, data)
{}
}

File diff suppressed because it is too large Load Diff

View File

@@ -180,7 +180,7 @@ contract TwitterUtilsTest is Test {
assert(bytes(svgValue).length > 0);
}
function testChainID() public {
function testChainID() public view {
uint256 chainId;
assembly {
chainId := chainid()

View File

@@ -3,9 +3,12 @@ pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../WalletEmailHandler.sol";
import "../WalletEmailHandlerLogic.sol";
import "../WalletEmailHandlerProxy.sol";
import "../TestERC20.sol";
import "../StringUtils.sol";
import "../Groth16VerifierWallet.sol";
import "../Groth16VerifierWalletAnon.sol";
import "./MIMC.sol";
contract WalletUtilsTest is Test {
using StringUtils for *;
@@ -16,16 +19,36 @@ contract WalletUtilsTest is Test {
uint16 public constant packSize = 30;
uint16 public constant body_len = 4 * 4;
VerifiedWalletEmail testVerifier;
WalletEmailHandlerProxy testVerifier;
MailServer mailServer;
Verifier proofVerifier;
WalletEmailHandlerLogic logic;
TokenRegistry tokenRegistry;
TestEmailToken erc20;
function setUp() public {
proofVerifier = new Verifier();
mailServer = new MailServer();
erc20 = new TestEmailToken(5000);
testVerifier = new VerifiedWalletEmail(proofVerifier, mailServer, erc20);
logic = new WalletEmailHandlerLogic();
tokenRegistry = new TokenRegistry();
address admin = msg.sender;
console.log("This address:");
console.log(address(this));
console.log("Caller/admin address:");
console.log(msg.sender);
// Initialize logic
logic.initialize(proofVerifier, mailServer, erc20, tokenRegistry);
bytes memory initData = abi.encodeWithSignature(
"initialize(Verifier,MailServer,TestEmailToken,TokenRegistry)",
proofVerifier,
mailServer,
erc20,
tokenRegistry
);
// TODO: Fix admin in place of address(this)
testVerifier = new WalletEmailHandlerProxy(address(logic), address(this), initData);
}
// Old unpacks
@@ -102,109 +125,33 @@ contract WalletUtilsTest is Test {
function testUnpackIntoString_Pack30_1() public {
uint256[] memory packedBytes = new uint256[](1);
packedBytes[0] = 2097152;
packedBytes[0] = 12544;
string memory byteList = StringUtils.convertPackedBytesToString(packedBytes, 30, packSize);
string memory intended_value = "dai";
string memory intended_value = "1";
console.logString(byteList);
assertEq(bytes32(bytes(byteList)), bytes32(bytes(intended_value)));
}
function testUnpackIntoString_Pack30_2() public {
uint256[] memory packedBytes = new uint256[](1);
packedBytes[0] = 452605509681;
packedBytes[0] = 452605509632;
string memory byteList = StringUtils.convertPackedBytesToString(packedBytes, 30, packSize);
string memory intended_value = "2";
string memory intended_value = "dai";
console.logString(byteList);
assertEq(bytes32(bytes(byteList)), bytes32(bytes(intended_value)));
}
// Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits)
function testVerifyWalletEmail() public {
uint256[34] memory publicSignals;
publicSignals[0] = 30515164652858234;
publicSignals[1] = 14207229598262646;
publicSignals[2] = 13067048790615872;
publicSignals[3] = 7171939;
publicSignals[4] = 0;
publicSignals[5] = 3485236;
publicSignals[6] = 0;
publicSignals[7] = 0;
publicSignals[8] = 0;
publicSignals[9] = 0;
publicSignals[10] = 13661285;
publicSignals[11] = 30515164652858234;
publicSignals[12] = 18147879272211830;
publicSignals[13] = 27917065853693287;
publicSignals[14] = 28015;
publicSignals[15] = 0;
publicSignals[16] = 2645260732387577900369388087711111123;
publicSignals[17] = 2332356685544126002119529566553287568;
publicSignals[18] = 587306946802222480578301599869128605;
publicSignals[19] = 1506808391343308562602228807782956759;
publicSignals[20] = 346696857027646434280628892032962406;
publicSignals[21] = 1655371642328152796841392591809876356;
publicSignals[22] = 773654757689631205903545947464515700;
publicSignals[23] = 137546842031326636154929265514533208;
publicSignals[24] = 979104436480501594376401576155183314;
publicSignals[25] = 1231402749194646866996172591430155068;
publicSignals[26] = 1573385231473380013164181608611759098;
publicSignals[27] = 1199794061179553911325952711127005960;
publicSignals[28] = 1393369642957971131987926230229916984;
publicSignals[29] = 2610100650498432208787557818514105421;
publicSignals[30] = 1405475120223887084339881602469286332;
publicSignals[31] = 2000538708964654339221687925776343058;
publicSignals[32] = 3483697379198011592407370076533025;
publicSignals[33] = 0;
uint256[2] memory proof_a = [
18214528451748025070455293058606558684367776249349482399993204103864741723468,
15003530197647463595718037429164132062637106744660222086396269550328064261424
];
// Note: you need to swap the order of the two elements in each subarray
uint256[2][2] memory proof_b = [
[
6461911610358766053365043908758394834732672681413987884242698462904724197255,
342103975494932482608081876029483576044074727035168137477391964391537410934
],
[
18351039964982209778799207158064219024562949371673722720718374575366986849311,
4669785024601609291633792167221088192727471283005169123961871153351390329210
]
];
uint256[2] memory proof_c = [
17308091971421169481892128502517801279695749002269857786558424203436590932091,
14587778590638321976005513090859474748106449498450192078465868665769372103254
];
// Test proof verification
bool verified = proofVerifier.verifyProof(proof_a, proof_b, proof_c, publicSignals);
assertEq(verified, true);
// Test mint after spoofing msg.sender
// Vm vm = Vm(VM_ADDR);
// vm.startPrank(0x0000000000000000000000000000000000000001);
// testVerifier.transfer(proof_a, proof_b, proof_c, publicSignals);
// vm.stopPrank();
}
// Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits)function testTransferWalletEmail3() public {
function testTransferWalletEmail3() public {
uint256[34] memory publicSignals = [
uint256(30515164652858234),
18147879272211830,
27917065853693287,
28015,
0,
50,
0,
0,
0,
0,
13762560,
30515164652858234,
14207229598262646,
13067048790615872,
7171939,
function testVerifyWalletEmailSendVerifier() public {
uint256[26] memory publicSignals = [
uint256(1684956499),
12544,
0,
452605509632,
1,
11578046119786885486589898473893761816011340408005885677852497807442621066251,
1,
668633821978676526869556450266953888005839843040173803440403455913247484181,
1886180949733815343726466520516992271,
1551366393280668736485689616947198994,
1279057759087427731263511728885611780,
@@ -226,24 +173,24 @@ contract WalletUtilsTest is Test {
];
uint256[2] memory proof_a = [
18568569282385577752003966587062685654688127322905645867690168113644909624209,
18759903706259146962639961745797835986209265804220236624283397965640158483190
4872484627504163815767110038859301103806605763419531960813764990208898018369,
3795754906589823037161769237444876552416343748488835176598797103098367263607
];
uint256[2][2] memory proof_b = [
[
5803446705026913357518568395981657569264671269353189435142412707651256173413,
8593766898146870509264586194439641404493723665652266429471383540616111544172
18376557720828688581045481496486129744102916628430359078845301368276153581701,
21877761512995850430716476389626777330111796016898814534754595086427125573614
],
[
16046700810774537572443697469030305204645791362097372667928847558873110846124,
20178806529375442298313753931928150693393269974972769756293026303013302674806
12159889909152109241814076562911568076239686025478424469066060467237613288152,
15701808273441108469395513343272104829223041229728428179673134509885945260640
]
];
uint256[2] memory proof_c = [
13905944782945043014900524454195421236229551576754549828484627488690083504903,
21309166547782902503710040024457074452294672356307955049572131380081774653722
15982822555429931992864851554044019444318888053001687160362952266505524933490,
10069972634237977706980635879331410342197879055629137669963055695388053169516
];
console.log("Calldata");
@@ -256,19 +203,183 @@ contract WalletUtilsTest is Test {
// Test mint after spoofing msg.sender
Vm vm = Vm(VM_ADDR);
vm.startPrank(0x0000000000000000000000000000000000000001);
testVerifier.transfer(proof_a, proof_b, proof_c, publicSignals);
bytes memory encodedData = abi.encodeWithSignature(
"transfer(uint256[2],uint256[2][2],uint256[2],uint256[26])", proof_a, proof_b, proof_c, publicSignals
);
(bool success, bytes memory result) = address(testVerifier).delegatecall(encodedData);
console.log(success);
console.logBytes(result);
vm.stopPrank();
assert(testVerifier.getBalance("zkemailverify@gmail.com") == 8 * 10 ** erc20.decimals());
assert(testVerifier.getBalance("zkemailverify2@gmail.com") == 12 * 10 ** erc20.decimals());
}
function testMoveERC20() public {
string memory fromEmail = "zkemailverify@gmail.com";
string memory recipientEmail = "zkemailverify2@gmail.com";
uint256 amountToTransfer = 1 * 10 ** erc20.decimals();
testVerifier.moveTokens(bytes32(bytes(fromEmail)), bytes32(bytes(recipientEmail)), amountToTransfer);
assert(testVerifier.getBalance(fromEmail) == 9 * 10 ** erc20.decimals());
assert(testVerifier.getBalance(recipientEmail) == 11 * 10 ** erc20.decimals());
// Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits)function testTransferWalletEmail3() public {
// function testTransferWalletEmailSendVerifier2() public {
// uint256[34] memory publicSignals = [
// uint256(30515164652858234),
// 18147879272211830,
// 27917065853693287,
// 28015,
// 0,
// 50,
// 0,
// 0,
// 0,
// 0,
// 13762560,
// 30515164652858234,
// 14207229598262646,
// 13067048790615872,
// 7171939,
// 0,
// 1886180949733815343726466520516992271,
// 1551366393280668736485689616947198994,
// 1279057759087427731263511728885611780,
// 1711061746895435768547617398484429347,
// 2329140368326888129406637741054282011,
// 2094858442222190249786465516374057361,
// 2584558507302599829894674874442909655,
// 1521552483858643935889582214011445675,
// 176847449040377757035522930003764000,
// 632921959964166974634188077062540145,
// 2172441457165086627497230906075093832,
// 248112436365636977369105357296082574,
// 1408592841800630696650784801114783401,
// 364610811473321782531041012695979858,
// 342338521965453258686441392321054163,
// 2269703683857229911110544415296249295,
// 3643644972862751728748413716653892,
// 0
// ];
// uint256[2] memory proof_a = [
// 18568569282385577752003966587062685654688127322905645867690168113644909624209,
// 18759903706259146962639961745797835986209265804220236624283397965640158483190
// ];
// uint256[2][2] memory proof_b = [
// [
// 5803446705026913357518568395981657569264671269353189435142412707651256173413,
// 8593766898146870509264586194439641404493723665652266429471383540616111544172
// ],
// [
// 16046700810774537572443697469030305204645791362097372667928847558873110846124,
// 20178806529375442298313753931928150693393269974972769756293026303013302674806
// ]
// ];
// uint256[2] memory proof_c = [
// 13905944782945043014900524454195421236229551576754549828484627488690083504903,
// 21309166547782902503710040024457074452294672356307955049572131380081774653722
// ];
// console.log("Calldata");
// console.logBytes(abi.encode(proof_a, proof_b, proof_c, publicSignals));
// // Test proof verification
// bool verified = proofVerifier.verifyProof(proof_a, proof_b, proof_c, publicSignals);
// assertEq(verified, true);
// // Test mint after spoofing msg.sender
// Vm vm = Vm(VM_ADDR);
// vm.startPrank(0x0000000000000000000000000000000000000001);
// bytes memory encodedData = abi.encodeWithSignature(
// "transfer(uint256[2],uint256[2][2],uint256[2],uint256[26])",
// proof_a, proof_b, proof_c, publicSignals
// );
// (bool success, bytes memory result) = address(testVerifier).delegatecall(encodedData);
// vm.stopPrank();
// assert(testVerifier.getBalance("zkemailverify@gmail.com") == 8 * 10 ** erc20.decimals());
// assert(testVerifier.getBalance("zkemailverify2@gmail.com") == 12 * 10 ** erc20.decimals());
// }
// Should pass (note that there are extra 0 bytes, which are filtered out but should be noted in audits)function testTransferWalletEmail3() public {
// function testTransferWalletEmailCommandVerifier() public {
// uint256[34] memory publicSignals = [
// uint256(30515164652858234),
// 18147879272211830,
// 27917065853693287,
// 28015,
// 0,
// 50,
// 0,
// 0,
// 0,
// 0,
// 13762560,
// 30515164652858234,
// 14207229598262646,
// 13067048790615872,
// 7171939,
// 0,
// 1886180949733815343726466520516992271,
// 1551366393280668736485689616947198994,
// 1279057759087427731263511728885611780,
// 1711061746895435768547617398484429347,
// 2329140368326888129406637741054282011,
// 2094858442222190249786465516374057361,
// 2584558507302599829894674874442909655,
// 1521552483858643935889582214011445675,
// 176847449040377757035522930003764000,
// 632921959964166974634188077062540145,
// 2172441457165086627497230906075093832,
// 248112436365636977369105357296082574,
// 1408592841800630696650784801114783401,
// 364610811473321782531041012695979858,
// 342338521965453258686441392321054163,
// 2269703683857229911110544415296249295,
// 3643644972862751728748413716653892,
// 0
// ];
// uint256[2] memory proof_a = [
// 18568569282385577752003966587062685654688127322905645867690168113644909624209,
// 18759903706259146962639961745797835986209265804220236624283397965640158483190
// ];
// uint256[2][2] memory proof_b = [
// [
// 5803446705026913357518568395981657569264671269353189435142412707651256173413,
// 8593766898146870509264586194439641404493723665652266429471383540616111544172
// ],
// [
// 16046700810774537572443697469030305204645791362097372667928847558873110846124,
// 20178806529375442298313753931928150693393269974972769756293026303013302674806
// ]
// ];
// uint256[2] memory proof_c = [
// 13905944782945043014900524454195421236229551576754549828484627488690083504903,
// 21309166547782902503710040024457074452294672356307955049572131380081774653722
// ];
// console.log("Calldata");
// console.logBytes(abi.encode(proof_a, proof_b, proof_c, publicSignals));
// // Test proof verification
// bool verified = proofVerifier.verifyProof(proof_a, proof_b, proof_c, publicSignals);
// assertEq(verified, true);
// // Test mint after spoofing msg.sender
// Vm vm = Vm(VM_ADDR);
// vm.startPrank(0x0000000000000000000000000000000000000001);
// testVerifier.transfer(proof_a, proof_b, proof_c, publicSignals);
// vm.stopPrank();
// assert(testVerifier.getBalance("zkemailverify@gmail.com") == 8 * 10 ** erc20.decimals());
// assert(testVerifier.getBalance("zkemailverify2@gmail.com") == 12 * 10 ** erc20.decimals());
// }
function testMigrateAllERC20() public {
uint256 fromSalt = 11578046119786885486589898473893761816011340408005885677852497807442621066251;
uint256 toSalt = 668633821978676526869556450266953888005839843040173803440403455913247484181;
bytes memory encodedData =
abi.encodeWithSignature("migrateAllToken(uint256,uint256,string)", fromSalt, toSalt, "DAI");
(bool success, bytes memory result) = address(testVerifier).delegatecall(encodedData);
console.log(success);
console.logBytes(result);
// assert(testVerifier.getBalance(fromEmail) == 9 * 10 ** erc20.decimals());
// assert(testVerifier.getBalance(recipientEmail) == 11 * 10 ** erc20.decimals());
}
}

145
yarn.lock
View File

@@ -2503,16 +2503,6 @@ __metadata:
languageName: node
linkType: hard
"@iden3/binfileutils@npm:0.0.10":
version: 0.0.10
resolution: "@iden3/binfileutils@npm:0.0.10"
dependencies:
fastfile: 0.0.19
ffjavascript: ^0.2.48
checksum: cdeb8ac01e12f485d9fb236654c00d5d5016fc89eae24f7822885dd42f09935cbef601dbdd8a0c96dfb00ded9f4f623e0eec0b568aa86d16522cf77ce6f9498b
languageName: node
linkType: hard
"@iden3/binfileutils@npm:0.0.11":
version: 0.0.11
resolution: "@iden3/binfileutils@npm:0.0.11"
@@ -6295,13 +6285,6 @@ __metadata:
languageName: node
linkType: hard
"big-integer@npm:^1.6.42, big-integer@npm:^1.6.48":
version: 1.6.51
resolution: "big-integer@npm:1.6.51"
checksum: 3d444173d1b2e20747e2c175568bedeebd8315b0637ea95d75fd27830d3b8e8ba36c6af40374f36bdaea7b5de376dcada1b07587cb2a79a928fccdb6e6e3c518
languageName: node
linkType: hard
"big.js@npm:^5.2.2":
version: 5.2.2
resolution: "big.js@npm:5.2.2"
@@ -7324,17 +7307,6 @@ __metadata:
languageName: node
linkType: hard
"circom_runtime@npm:0.1.17":
version: 0.1.17
resolution: "circom_runtime@npm:0.1.17"
dependencies:
ffjavascript: 0.2.48
bin:
calcwit: calcwit.js
checksum: 595fc0cc3a62ba5daf8d849feae41c48805c0df43965f85dde4dc434efb607e455fa7801d41c1feacfe0c3c71952a45cd3985abf26fde40c54138392891afd8c
languageName: node
linkType: hard
"circom_runtime@npm:0.1.21":
version: 0.1.21
resolution: "circom_runtime@npm:0.1.21"
@@ -7346,6 +7318,17 @@ __metadata:
languageName: node
linkType: hard
"circom_runtime@npm:0.1.22":
version: 0.1.22
resolution: "circom_runtime@npm:0.1.22"
dependencies:
ffjavascript: 0.2.57
bin:
calcwit: calcwit.js
checksum: bf7b2e9f74cd7704ebc45ce686d4df49e58ed09114070a98beaab90e5ff7784d1943468ea3315bd4db638e6a76cda24e8b610aa2cd50be56adb4391e20469366
languageName: node
linkType: hard
"circom_tester@npm:^0.0.19":
version: 0.0.19
resolution: "circom_tester@npm:0.0.19"
@@ -9085,7 +9068,7 @@ __metadata:
readline: ^1.3.0
selenium-webdriver: ^4.8.1
serve: ^14.0.1
snarkjs: "git+https://github.com/vb7401/snarkjs.git#24981febe8826b6ab76ae4d76cf7f9142919d2b8"
snarkjs: latest
sshpk: ^1.17.0
styled-components: ^5.3.5
ts-node: ^10.9.1
@@ -10519,13 +10502,6 @@ __metadata:
languageName: node
linkType: hard
"fastfile@npm:0.0.19, fastfile@npm:^0.0.19":
version: 0.0.19
resolution: "fastfile@npm:0.0.19"
checksum: 6179bdd7c21be9882294dae66103795c099594098b51958bcf08a4545c91387321b43511730d0542a5a9ed8c5ec9069c065e065fd67255453ac900a23895dac1
languageName: node
linkType: hard
"fastfile@npm:0.0.20":
version: 0.0.20
resolution: "fastfile@npm:0.0.20"
@@ -10607,18 +10583,6 @@ __metadata:
languageName: node
linkType: hard
"ffjavascript@npm:0.2.48":
version: 0.2.48
resolution: "ffjavascript@npm:0.2.48"
dependencies:
big-integer: ^1.6.48
wasmbuilder: ^0.0.12
wasmcurves: 0.1.0
web-worker: ^1.2.0
checksum: 68beae9a4f642c06656685353b84fd7655020ca0e628ea046e94452ab779587953cc45cde106d74b68be7177b49c8f19b105d6552c4a1d715e784ae9e7c9ed34
languageName: node
linkType: hard
"ffjavascript@npm:0.2.56":
version: 0.2.56
resolution: "ffjavascript@npm:0.2.56"
@@ -10630,7 +10594,7 @@ __metadata:
languageName: node
linkType: hard
"ffjavascript@npm:^0.2.45":
"ffjavascript@npm:0.2.57, ffjavascript@npm:^0.2.45":
version: 0.2.57
resolution: "ffjavascript@npm:0.2.57"
dependencies:
@@ -18002,18 +17966,6 @@ __metadata:
languageName: node
linkType: hard
"r1csfile@npm:0.0.35":
version: 0.0.35
resolution: "r1csfile@npm:0.0.35"
dependencies:
"@iden3/bigarray": 0.0.2
"@iden3/binfileutils": 0.0.10
fastfile: 0.0.19
ffjavascript: 0.2.48
checksum: 84f7b4eab5bcdd6a3f6d699998c9479a5eff8d670383d4f0c5afc08431f45353abab9a8b07eeabaef89807e24b0ba50611d4d6280eb6c3a7483e1487a91f0ac6
languageName: node
linkType: hard
"r1csfile@npm:0.0.41, r1csfile@npm:^0.0.41":
version: 0.0.41
resolution: "r1csfile@npm:0.0.41"
@@ -18026,6 +17978,18 @@ __metadata:
languageName: node
linkType: hard
"r1csfile@npm:0.0.45":
version: 0.0.45
resolution: "r1csfile@npm:0.0.45"
dependencies:
"@iden3/bigarray": 0.0.2
"@iden3/binfileutils": 0.0.11
fastfile: 0.0.20
ffjavascript: 0.2.57
checksum: ae2d7ab3f2c37640aea2b4f45753d1f2258f5d904a51d7aaff67c32a931c3e90d22d257e8f10752044fa36b3ac31516f60e503f858913a625015b9b05575ba47
languageName: node
linkType: hard
"raf@npm:^3.4.1":
version: 3.4.1
resolution: "raf@npm:3.4.1"
@@ -19778,26 +19742,6 @@ __metadata:
languageName: node
linkType: hard
"snarkjs@git+https://github.com/vb7401/snarkjs.git#24981febe8826b6ab76ae4d76cf7f9142919d2b8":
version: 0.4.12
resolution: "snarkjs@https://github.com/vb7401/snarkjs.git#commit=24981febe8826b6ab76ae4d76cf7f9142919d2b8"
dependencies:
"@iden3/binfileutils": 0.0.10
blake2b-wasm: ^2.4.0
circom_runtime: 0.1.17
ejs: ^3.1.6
fastfile: ^0.0.19
ffjavascript: 0.2.48
js-sha3: ^0.8.0
logplease: ^1.2.15
r1csfile: 0.0.35
readline: ^1.3.0
bin:
snarkjs: build/cli.cjs
checksum: 9011df4b58475a0b4ae988f8b459a9a4d2bb5d2b60221d0ec370a10f2492c88909768215f3b22e514b2cf24dca79818790447005a33ed6aee177b9fda6948a75
languageName: node
linkType: hard
"snarkjs@npm:0.5.0":
version: 0.5.0
resolution: "snarkjs@npm:0.5.0"
@@ -19818,6 +19762,26 @@ __metadata:
languageName: node
linkType: hard
"snarkjs@npm:latest":
version: 0.6.11
resolution: "snarkjs@npm:0.6.11"
dependencies:
"@iden3/binfileutils": 0.0.11
bfj: ^7.0.2
blake2b-wasm: ^2.4.0
circom_runtime: 0.1.22
ejs: ^3.1.6
fastfile: 0.0.20
ffjavascript: 0.2.57
js-sha3: ^0.8.0
logplease: ^1.2.15
r1csfile: 0.0.45
bin:
snarkjs: build/cli.cjs
checksum: 692508b6f8048b6879975844cae4b9a6713713e20fec488ebd21a4b224b7e0318f6aba59ce06d332728d184148c943fa04e3a49e0af173d325011f506e42363e
languageName: node
linkType: hard
"sockjs-client@npm:^1.5.0":
version: 1.6.1
resolution: "sockjs-client@npm:1.6.1"
@@ -21997,25 +21961,6 @@ __metadata:
languageName: node
linkType: hard
"wasmbuilder@npm:^0.0.12":
version: 0.0.12
resolution: "wasmbuilder@npm:0.0.12"
dependencies:
big-integer: ^1.6.48
checksum: 327b3c50b0e1e5e3aac9e218e0f96fdc638b7952ab86acc2ad53960371996826dbb0a8095edce482cf1d9c245d96884449701909bc962920aa7ec8241db01214
languageName: node
linkType: hard
"wasmcurves@npm:0.1.0":
version: 0.1.0
resolution: "wasmcurves@npm:0.1.0"
dependencies:
big-integer: ^1.6.42
blakejs: ^1.1.0
checksum: 6bf6719e659a88904af0b98d152316e3b22435ca6a2cfc8bbf4530576806f17b2776b2c7d91d1a678fe0d51485a0d1748efcd080808c181c7977bee50b26efa9
languageName: node
linkType: hard
"wasmcurves@npm:0.2.0":
version: 0.2.0
resolution: "wasmcurves@npm:0.2.0"