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
16let insVerifier;
17let insMiksi;
18
19const nLevels = 17;
20const secret = ["1234567890", "987654321", "123"];
21
22const coinCode = "0"; // refearing to ETH
23const ethAmount = '1';
24const amount = web3.utils.toWei(ethAmount, 'ether');
25const nullifier = ["567891234", "432198765", "321"];
26let commitment = [];
27let tree;
28let oldKey = [];
29let oldValue = [];
30let siblingsOld = [];
31let siblingsNew = [];
32let rootOld = [];
33let rootNew = [];
34// let commitment = [];
35let proof = [];
36let publicSignals = [];
37let commitmentsArray = [];
38let currKey=0;
39let u = 0;
40
41contract("miksi", (accounts) => {
42
43
44 const {
45 0: owner,
46 1: addr1, // used for the deposit
47 2: addr2, // used for the withdraw
48 3: addr3,
49 4: addr4,
50 } = accounts;
51
52
53 before(async () => {
54 insDepositVerifier = await DepositVerifier.new();
55 insWithdrawVerifier = await WithdrawVerifier.new();
56 insMiksi = await Miksi.new(insDepositVerifier.address, insWithdrawVerifier.address);
57 });
58
59 before(async() => {
60 let balance_wei = await web3.eth.getBalance(addr1);
61 // console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
62 expect(balance_wei).to.be.equal('100000000000000000000');
63
64 tree = await smt.newMemEmptyTrie();
65 await tree.insert(currKey, 0);
66
67 await computeTree(0);
68
69 expect(rootOld[0].toString()).to.be.equal('7191590165524151132621032034309259185021876706372059338263145339926209741311');
70 // expect(rootNew[0].toString()).to.be.equal('9328869343897770565751281504295758914771207504252217956739346620422361279598');
71 });
72
73 it("Make first deposit", async () => {
74 await makeDeposit(0, addr1);
75 balance_wei = await web3.eth.getBalance(addr1);
76 // console.log("Balance at " + addr1, web3.utils.fromWei(balance_wei, 'ether'));
77 // expect(balance_wei).to.be.equal('98993526980000000000');
78 });
79 it("Make second deposit", async () => {
80 await computeTree(1);
81 await makeDeposit(1, addr3);
82 });
83 it("Make 3rd deposit", async () => {
84 await computeTree(2);
85 await makeDeposit(2, addr3);
86 });
87
88 it("Get the commitments data", async () => {
89 // getCommitments data
90 let res = await insMiksi.getCommitments();
91 expect(res[0][0].toString()).to.be.equal('189025084074544266465422070282645213792582195466360448472858620722286781863');
92 // expect(res[1].toString()).to.be.equal('9328869343897770565751281504295758914771207504252217956739346620422361279598');
93 console.log(res[0]);
94 commitmentsArray[0] = res[0];
95 currKey = res[2];
96 });
97
98 it("Rebuild the tree from sc commitments", async () => {
99 let treeTmp = await smt.newMemEmptyTrie();
100 await treeTmp.insert(0, 0);
101 for (let i=0; i<commitmentsArray[0].length; i++) {
102 await treeTmp.insert(i+1, commitmentsArray[0][i]);
103 }
104 expect(treeTmp.root).to.be.equal(tree.root);
105 });
106
107 it("Calculate witness and generate the zkProof", async () => {
108 this.timeout(10000000);
109 await genZKProof(0, addr2, "1");
110 await genZKProof(1, addr4, "2");
111 await genZKProof(2, addr4, "3");
112 });
113
114 it("Try to use the zkProof with another address and get revert", async () => {
115 // console.log("Try to reuse the zkproof and expect revert");
116 await truffleAssert.fails(
117 withdrawSC(0, addr1),
118 truffleAssert.ErrorType.REVERT,
119 "zkProof withdraw could not be verified"
120 );
121 });
122
123 it("Withdraw 1 ETH with the zkProof of the 1st deposit to addr2", async () => {
124 // withdraw
125 // console.log("Withdraw of " + ethAmount + " ETH to " + addr2);
126 let resW = await withdrawSC(0, addr2);
127 // console.log("resW", resW);
128
129 balance_wei = await web3.eth.getBalance(addr2);
130 // console.log("Balance at " + addr2, web3.utils.fromWei(balance_wei, 'ether'));
131 expect(balance_wei).to.be.equal('101000000000000000000');
132 });
133
134 it("Try to reuse the zkProof and get revert", async () => {
135 // console.log("Try to reuse the zkproof and expect revert");
136 await truffleAssert.fails(
137 withdrawSC(0, addr2),
138 truffleAssert.ErrorType.REVERT,
139 "nullifier already used"
140 );
141 balance_wei = await web3.eth.getBalance(addr2);
142 expect(balance_wei).to.be.equal('101000000000000000000');
143 });
144 it("Withdraw 1 ETH with the zkProof of the 2nd deposit to addr4", async () => {
145 let resW = await withdrawSC(1, addr4);
146 balance_wei = await web3.eth.getBalance(addr4);
147 expect(balance_wei).to.be.equal('101000000000000000000');
148 });
149 it("Withdraw 1 ETH with the zkProof of the 3rd deposit to addr4", async () => {
150 let resW = await withdrawSC(2, addr4);
151 balance_wei = await web3.eth.getBalance(addr4);
152 expect(balance_wei).to.be.equal('102000000000000000000');
153 });
154});
155
156
157async function computeTree(u) {
158 const poseidon = circomlib.poseidon.createHash(6, 8, 57);
159 commitment[u] = poseidon([coinCode, amount, secret[u], nullifier[u]]).toString();
160
161 // deposit
162 // add commitment into SMT
163
164 // console.log("currKey", currKey);
165 rootOld[u] = tree.root;
166 const resC = await tree.find(currKey+1);
167 assert(!resC.found);
168 oldKey[u] = "0";
169 oldValue[u] = "0";
170 if (!resC.found) {
171 oldKey[u] = resC.notFoundKey.toString();
172 oldValue[u] = resC.notFoundValue.toString();
173 }
174 // console.log(oldValue[u]);
175 // console.log("FIND", resC);
176 siblingsOld[u] = resC.siblings;
177 while (siblingsOld[u].length < nLevels) {
178 siblingsOld[u].push("0");
179 };
180
181 await tree.insert(currKey+1, commitment[u]);
182 rootNew[u] = tree.root;
183 currKey += 1;
184 // console.log("currKey", currKey);
185}
186
187async function makeDeposit(u, addr) {
188 const resC = await tree.find(currKey);
189 assert(resC.found);
190 siblingsNew[u] = resC.siblings;
191 while (siblingsNew[u].length < nLevels) {
192 siblingsNew[u].push("0");
193 };
194
195 // calculate witness
196 const wasm = await fs.promises.readFile("./build/deposit.wasm");
197 const input = unstringifyBigInts({
198 "coinCode": coinCode,
199 "amount": amount,
200 "secret": secret[u],
201 "nullifier": nullifier[u],
202 "oldKey": oldKey[u],
203 "oldValue": oldValue[u],
204 "siblingsOld": siblingsOld[u],
205 "siblingsNew": siblingsNew[u],
206 "rootOld": rootOld[u],
207 "rootNew": rootNew[u],
208 "commitment": commitment[u],
209 "key": currKey
210 });
211 const options = {};
212 // console.log("Calculate witness");
213 const wc = await WitnessCalculatorBuilder(wasm, options);
214 const w = await wc.calculateWitness(input);
215 const witness = unstringifyBigInts(stringifyBigInts(w));
216
217 // generate zkproof of commitment using snarkjs (as is a test)
218 const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-proving_key.json", "utf8")));
219
220 // console.log("Generate zkSNARK proof");
221 const res = groth.genProof(provingKey, witness);
222 proof[u] = res.proof;
223 publicSignals[u] = res.publicSignals;
224
225 const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-verification_key.json", "utf8")));
226 let pubI = unstringifyBigInts([coinCode, amount, rootOld[u].toString(), rootNew[u].toString(), commitment[u], currKey]);
227 let validCheck = groth.isValid(verificationKey, proof[u], pubI);
228 assert(validCheck);
229 await insMiksi.deposit(
230 commitment[u],
231 tree.root.toString(),
232 [proof[u].pi_a[0].toString(), proof[u].pi_a[1].toString()],
233 [
234 [proof[u].pi_b[0][1].toString(), proof[u].pi_b[0][0].toString()],
235 [proof[u].pi_b[1][1].toString(), proof[u].pi_b[1][0].toString()]
236 ],
237 [proof[u].pi_c[0].toString(), proof[u].pi_c[1].toString()],
238 {from: addr, value: amount}
239 );
240
241}
242
243async function genZKProof(u, addr, k) {
244 const resC = await tree.find(k);
245 assert(resC.found);
246 let siblings = resC.siblings;
247 while (siblings.length < nLevels) {
248 siblings.push("0");
249 };
250 // console.log("siblings", siblings);
251
252 // calculate witness
253 const wasm = await fs.promises.readFile("./build/withdraw.wasm");
254 const input = unstringifyBigInts({
255 "coinCode": coinCode,
256 "amount": amount,
257 "secret": secret[u],
258 "nullifier": nullifier[u],
259 "siblings": siblings,
260 "root": tree.root,
261 "address": addr,
262 "key": k
263 });
264 const options = {};
265 // console.log("Calculate witness");
266 const wc = await WitnessCalculatorBuilder(wasm, options);
267 const w = await wc.calculateWitness(input);
268 const witness = unstringifyBigInts(stringifyBigInts(w));
269
270 // generate zkproof of commitment using snarkjs (as is a test)
271 const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/withdraw-proving_key.json", "utf8")));
272
273 // console.log("Generate zkSNARK proof");
274 const res = groth.genProof(provingKey, witness);
275 proof[u] = res.proof;
276 publicSignals[u] = res.publicSignals;
277}
278
279async function withdrawSC(u, addr) {
280 return insMiksi.withdraw(
281 addr,
282 nullifier[u],
283 [proof[u].pi_a[0].toString(), proof[u].pi_a[1].toString()],
284 [
285 [proof[u].pi_b[0][1].toString(), proof[u].pi_b[0][0].toString()],
286 [proof[u].pi_b[1][1].toString(), proof[u].pi_b[1][0].toString()]
287 ],
288 [proof[u].pi_c[0].toString(), proof[u].pi_c[1].toString()]
289 );
290}
291