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 = 5;
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 await genZKProof(0, addr2, "1");
109 await genZKProof(1, addr4, "2");
110 await genZKProof(2, addr4, "3");
111 });
112
113 it("Try to use the zkProof with another address and get revert", async () => {
114 // console.log("Try to reuse the zkproof and expect revert");
115 await truffleAssert.fails(
116 withdrawSC(0, addr1),
117 truffleAssert.ErrorType.REVERT,
118 "zkProof withdraw could not be verified"
119 );
120 });
121
122 it("Withdraw 1 ETH with the zkProof of the 1st deposit to addr2", async () => {
123 // withdraw
124 // console.log("Withdraw of " + ethAmount + " ETH to " + addr2);
125 let resW = await withdrawSC(0, addr2);
126 // console.log("resW", resW);
127
128 balance_wei = await web3.eth.getBalance(addr2);
129 // console.log("Balance at " + addr2, web3.utils.fromWei(balance_wei, 'ether'));
130 expect(balance_wei).to.be.equal('101000000000000000000');
131 });
132
133 it("Try to reuse the zkProof and get revert", async () => {
134 // console.log("Try to reuse the zkproof and expect revert");
135 await truffleAssert.fails(
136 withdrawSC(0, addr2),
137 truffleAssert.ErrorType.REVERT,
138 "nullifier already used"
139 );
140 balance_wei = await web3.eth.getBalance(addr2);
141 expect(balance_wei).to.be.equal('101000000000000000000');
142 });
143 it("Withdraw 1 ETH with the zkProof of the 2nd deposit to addr4", async () => {
144 let resW = await withdrawSC(1, addr4);
145 balance_wei = await web3.eth.getBalance(addr4);
146 expect(balance_wei).to.be.equal('101000000000000000000');
147 });
148 it("Withdraw 1 ETH with the zkProof of the 3rd deposit to addr4", async () => {
149 let resW = await withdrawSC(2, addr4);
150 balance_wei = await web3.eth.getBalance(addr4);
151 expect(balance_wei).to.be.equal('102000000000000000000');
152 });
153});
154
155
156async function computeTree(u) {
157 const poseidon = circomlib.poseidon.createHash(6, 8, 57);
158 commitment[u] = poseidon([coinCode, amount, secret[u], nullifier[u]]).toString();
159
160 // deposit
161 // add commitment into SMT
162
163 // console.log("currKey", currKey);
164 rootOld[u] = tree.root;
165 const resC = await tree.find(currKey+1);
166 assert(!resC.found);
167 oldKey[u] = "0";
168 oldValue[u] = "0";
169 if (!resC.found) {
170 oldKey[u] = resC.notFoundKey.toString();
171 oldValue[u] = resC.notFoundValue.toString();
172 }
173 // console.log(oldValue[u]);
174 // console.log("FIND", resC);
175 siblingsOld[u] = resC.siblings;
176 while (siblingsOld[u].length < nLevels) {
177 siblingsOld[u].push("0");
178 };
179
180 await tree.insert(currKey+1, commitment[u]);
181 rootNew[u] = tree.root;
182 currKey += 1;
183 // console.log("currKey", currKey);
184}
185
186async function makeDeposit(u, addr) {
187 const resC = await tree.find(currKey);
188 assert(resC.found);
189 siblingsNew[u] = resC.siblings;
190 while (siblingsNew[u].length < nLevels) {
191 siblingsNew[u].push("0");
192 };
193
194 // calculate witness
195 const wasm = await fs.promises.readFile("./build/deposit.wasm");
196 const input = unstringifyBigInts({
197 "coinCode": coinCode,
198 "amount": amount,
199 "secret": secret[u],
200 "nullifier": nullifier[u],
201 "oldKey": oldKey[u],
202 "oldValue": oldValue[u],
203 "siblingsOld": siblingsOld[u],
204 "siblingsNew": siblingsNew[u],
205 "rootOld": rootOld[u],
206 "rootNew": rootNew[u],
207 "commitment": commitment[u],
208 "key": currKey
209 });
210 const options = {};
211 // console.log("Calculate witness");
212 const wc = await WitnessCalculatorBuilder(wasm, options);
213 const w = await wc.calculateWitness(input);
214 const witness = unstringifyBigInts(stringifyBigInts(w));
215
216 // generate zkproof of commitment using snarkjs (as is a test)
217 const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-proving_key.json", "utf8")));
218
219 // console.log("Generate zkSNARK proof");
220 const res = groth.genProof(provingKey, witness);
221 proof[u] = res.proof;
222 publicSignals[u] = res.publicSignals;
223
224 const verificationKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/deposit-verification_key.json", "utf8")));
225 let pubI = unstringifyBigInts([coinCode, amount, rootOld[u].toString(), rootNew[u].toString(), commitment[u], currKey]);
226 let validCheck = groth.isValid(verificationKey, proof[u], pubI);
227 assert(validCheck);
228 await insMiksi.deposit(
229 commitment[u],
230 tree.root.toString(),
231 [proof[u].pi_a[0].toString(), proof[u].pi_a[1].toString()],
232 [
233 [proof[u].pi_b[0][1].toString(), proof[u].pi_b[0][0].toString()],
234 [proof[u].pi_b[1][1].toString(), proof[u].pi_b[1][0].toString()]
235 ],
236 [proof[u].pi_c[0].toString(), proof[u].pi_c[1].toString()],
237 {from: addr, value: amount}
238 );
239
240}
241
242async function genZKProof(u, addr, k) {
243 const resC = await tree.find(k);
244 assert(resC.found);
245 let siblings = resC.siblings;
246 while (siblings.length < nLevels) {
247 siblings.push("0");
248 };
249 // console.log("siblings", siblings);
250
251 // calculate witness
252 const wasm = await fs.promises.readFile("./build/withdraw.wasm");
253 const input = unstringifyBigInts({
254 "coinCode": coinCode,
255 "amount": amount,
256 "secret": secret[u],
257 "nullifier": nullifier[u],
258 "siblings": siblings,
259 "root": tree.root,
260 "address": addr,
261 "key": k
262 });
263 const options = {};
264 // console.log("Calculate witness");
265 const wc = await WitnessCalculatorBuilder(wasm, options);
266 const w = await wc.calculateWitness(input);
267 const witness = unstringifyBigInts(stringifyBigInts(w));
268
269 // generate zkproof of commitment using snarkjs (as is a test)
270 const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/withdraw-proving_key.json", "utf8")));
271
272 // console.log("Generate zkSNARK proof");
273 const res = groth.genProof(provingKey, witness);
274 proof[u] = res.proof;
275 publicSignals[u] = res.publicSignals;
276}
277
278async function withdrawSC(u, addr) {
279 return insMiksi.withdraw(
280 addr,
281 nullifier[u],
282 [proof[u].pi_a[0].toString(), proof[u].pi_a[1].toString()],
283 [
284 [proof[u].pi_b[0][1].toString(), proof[u].pi_b[0][0].toString()],
285 [proof[u].pi_b[1][1].toString(), proof[u].pi_b[1][0].toString()]
286 ],
287 [proof[u].pi_c[0].toString(), proof[u].pi_c[1].toString()]
288 );
289}
290