miksi-core/test/contracts/miksi.test.ts
test/contracts/miksi.test.tsRaw
1const DepositVerifier = artifacts.require("../../contracts/DepositVerifier");
2const WithdrawVerifier = artifacts.require("../../contracts/WithdrawVerifier");
3const Miksi = artifacts.require("../../contracts/Miksi.sol");
4
5const chai = require("chai");
6const expect = chai.expect;
7const truffleAssert = require('truffle-assertions');
8
9const fs = require("fs");
10const { groth } = require('snarkjs');
11const { stringifyBigInts, unstringifyBigInts } = require('ffjavascript').utils;
12const WitnessCalculatorBuilder = require("circom_runtime").WitnessCalculatorBuilder;
13const circomlib = require("circomlib");
14const smt = require("circomlib").smt;
15
16contract("miksi", (accounts) => {
17
18
19 const {
20 0: owner,
21 1: addr1, // used for the deposit
22 2: addr2, // used for the withdraw
23 3: addr3,
24 } = accounts;
25
26 let insVerifier;
27 let insMiksi;
28
29 const nLevels = 5;
30 const secret = "1234567890";
31
32 const coinCode = "0"; // refearing to ETH
33 const ethAmount = '1';
34 const amount = web3.utils.toWei(ethAmount, 'ether');
35 const nullifier = "567891234";
36 let tree;
37 let siblingsOld;
38 let siblingsNew;
39 let rootOld;
40 let rootNew;
41 let commitment;
42 let proof;
43 let publicSignals;
44
45 before(async () => {
46
47 insDepositVerifier = await DepositVerifier.new();
48 insWithdrawVerifier = await WithdrawVerifier.new();
49 insMiksi = await Miksi.new(insDepositVerifier.address, insWithdrawVerifier.address);
50 });
51
52 before(async() => {
53 let balance_wei = await web3.eth.getBalance(addr1);
54 // console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
55 expect(balance_wei).to.be.equal('100000000000000000000');
56
57 const poseidon = circomlib.poseidon.createHash(6, 8, 57);
58 commitment = poseidon([coinCode, amount, secret, nullifier]).toString();
59
60 // deposit
61 // add commitment into SMT
62 tree = await smt.newMemEmptyTrie();
63 await tree.insert(1, 0);
64
65 rootOld = tree.root;
66 const resC = await tree.find(commitment);
67 assert(!resC.found);
68 siblingsOld = resC.siblings;
69 while (siblingsOld.length < nLevels) {
70 siblingsOld.push("0");
71 };
72
73 await tree.insert(commitment, 0);
74 rootNew = tree.root;
75
76 expect(rootOld.toString()).to.be.equal('11499909227292257605992378629333104385616480982267969744564817844870636870870');
77 expect(rootNew.toString()).to.be.equal('9328869343897770565751281504295758914771207504252217956739346620422361279598');
78 });
79
80 it("Make the deposit", async () => {
81 const resC = await tree.find(commitment);
82 assert(resC.found);
83 siblingsNew = resC.siblings;
84 while (siblingsNew.length < nLevels) {
85 siblingsNew.push("0");
86 };
87
88 // calculate witness
89 const wasm = await fs.promises.readFile("./build/deposit.wasm");
90 const input = unstringifyBigInts({
91 "coinCode": coinCode,
92 "amount": amount,
93 "secret": secret,
94 "nullifier": nullifier,
95 "siblingsOld": siblingsOld,
96 "siblingsNew": siblingsNew,
97 "rootOld": rootOld,
98 "rootNew": rootNew,
99 "commitment": commitment
100 });
101 const options = {};
102 // console.log("Calculate witness");
103 const wc = await WitnessCalculatorBuilder(wasm, options);
104 const w = await wc.calculateWitness(input);
105 const witness = unstringifyBigInts(stringifyBigInts(w));
106
107 // generate zkproof of commitment using snarkjs (as is a test)
108 const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-proving_key.json", "utf8")));
109
110 // console.log("Generate zkSNARK proof");
111 const res = groth.genProof(provingKey, witness);
112 proof = res.proof;
113 publicSignals = res.publicSignals;
114
115 const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-verification_key.json", "utf8")));
116 let pubI = unstringifyBigInts([coinCode, amount, rootOld.toString(), rootNew.toString(), commitment]);
117 let validCheck = groth.isValid(verificationKey, proof, pubI);
118 assert(validCheck);
119 await insMiksi.deposit(
120 commitment,
121 tree.root.toString(),
122 [proof.pi_a[0].toString(), proof.pi_a[1].toString()],
123 [
124 [proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
125 [proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString()]
126 ],
127 [proof.pi_c[0].toString(), proof.pi_c[1].toString()],
128 {from: addr1, value: amount}
129 );
130
131 balance_wei = await web3.eth.getBalance(addr1);
132 // console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
133 // expect(balance_wei).to.be.equal('98993526980000000000');
134 });
135
136 it("Get the commitments data", async () => {
137 // getCommitments data
138 let res = await insMiksi.getCommitments();
139 expect(res[0][0].toString()).to.be.equal('189025084074544266465422070282645213792582195466360448472858620722286781863');
140 expect(res[1].toString()).to.be.equal('9328869343897770565751281504295758914771207504252217956739346620422361279598');
141 });
142
143 it("Calculate witness and generate the zkProof", async () => {
144 const resC = await tree.find(commitment);
145 assert(resC.found);
146 let siblings = resC.siblings;
147 while (siblings.length < nLevels) {
148 siblings.push("0");
149 };
150 // console.log("siblings", siblings);
151
152 // calculate witness
153 const wasm = await fs.promises.readFile("./build/withdraw.wasm");
154 const input = unstringifyBigInts({
155 "coinCode": coinCode,
156 "amount": amount,
157 "secret": secret,
158 "nullifier": nullifier,
159 "siblings": siblings,
160 "root": tree.root,
161 "address": addr2
162 });
163 const options = {};
164 // console.log("Calculate witness");
165 const wc = await WitnessCalculatorBuilder(wasm, options);
166 const w = await wc.calculateWitness(input);
167 const witness = unstringifyBigInts(stringifyBigInts(w));
168
169 // generate zkproof of commitment using snarkjs (as is a test)
170 const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/withdraw-proving_key.json", "utf8")));
171
172 // console.log("Generate zkSNARK proof");
173 const res = groth.genProof(provingKey, witness);
174 proof = res.proof;
175 publicSignals = res.publicSignals;
176 });
177
178 it("Try to use the zkProof with another address and get revert", async () => {
179 // console.log("Try to reuse the zkproof and expect revert");
180 await truffleAssert.fails(
181 insMiksi.withdraw(
182 addr1,
183 nullifier,
184 [proof.pi_a[0].toString(), proof.pi_a[1].toString()],
185 [
186 [proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
187 [proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString()]
188 ],
189 [proof.pi_c[0].toString(), proof.pi_c[1].toString()]
190 ),
191 truffleAssert.ErrorType.REVERT,
192 "zkProof withdraw could not be verified"
193 );
194 });
195
196 it("Withdraw 1 ETH with the zkProof", async () => {
197 // withdraw
198 // console.log("Withdraw of " + ethAmount + " ETH to " + addr2);
199 let resW = await insMiksi.withdraw(
200 addr2,
201 nullifier,
202 [proof.pi_a[0].toString(), proof.pi_a[1].toString()],
203 [
204 [proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
205 [proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString()]
206 ],
207 [proof.pi_c[0].toString(), proof.pi_c[1].toString()]
208 );
209 // console.log("resW", resW);
210
211 balance_wei = await web3.eth.getBalance(addr2);
212 // console.log("Balance at " + addr2, web3.utils.fromWei(balance_wei, 'ether'));
213 expect(balance_wei).to.be.equal('101000000000000000000');
214 });
215
216 it("Try to reuse the zkProof and get revert", async () => {
217 // console.log("Try to reuse the zkproof and expect revert");
218 await truffleAssert.fails(
219 insMiksi.withdraw(
220 addr2,
221 nullifier,
222 [proof.pi_a[0].toString(), proof.pi_a[1].toString()],
223 [
224 [proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
225 [proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString()]
226 ],
227 [proof.pi_c[0].toString(), proof.pi_c[1].toString()]
228 ),
229 truffleAssert.ErrorType.REVERT,
230 "nullifier already used"
231 );
232 balance_wei = await web3.eth.getBalance(addr2);
233 expect(balance_wei).to.be.equal('101000000000000000000');
234 });
235});