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 let commitmentsArray;
45
46 before(async () => {
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 console.log(res[0]);
142 commitmentsArray = res[0];
143 });
144
145 it("Rebuild the tree from sc commitments", async () => {
146 let treeTmp = await smt.newMemEmptyTrie();
147 await treeTmp.insert(1, 0);
148 for (let i=0; i<commitmentsArray.length; i++) {
149 await treeTmp.insert(commitmentsArray[i], 0);
150 }
151 expect(treeTmp.root).to.be.equal(tree.root);
152 });
153
154 it("Calculate witness and generate the zkProof", async () => {
155 const resC = await tree.find(commitment);
156 assert(resC.found);
157 let siblings = resC.siblings;
158 while (siblings.length < nLevels) {
159 siblings.push("0");
160 };
161 // console.log("siblings", siblings);
162
163 // calculate witness
164 const wasm = await fs.promises.readFile("./build/withdraw.wasm");
165 const input = unstringifyBigInts({
166 "coinCode": coinCode,
167 "amount": amount,
168 "secret": secret,
169 "nullifier": nullifier,
170 "siblings": siblings,
171 "root": tree.root,
172 "address": addr2
173 });
174 const options = {};
175 // console.log("Calculate witness");
176 const wc = await WitnessCalculatorBuilder(wasm, options);
177 const w = await wc.calculateWitness(input);
178 const witness = unstringifyBigInts(stringifyBigInts(w));
179
180 // generate zkproof of commitment using snarkjs (as is a test)
181 const provingKey = unstringifyBigInts(JSON.parse(fs.readFileSync("./build/withdraw-proving_key.json", "utf8")));
182
183 // console.log("Generate zkSNARK proof");
184 const res = groth.genProof(provingKey, witness);
185 proof = res.proof;
186 publicSignals = res.publicSignals;
187 });
188
189 it("Try to use the zkProof with another address and get revert", async () => {
190 // console.log("Try to reuse the zkproof and expect revert");
191 await truffleAssert.fails(
192 insMiksi.withdraw(
193 addr1,
194 nullifier,
195 [proof.pi_a[0].toString(), proof.pi_a[1].toString()],
196 [
197 [proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
198 [proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString()]
199 ],
200 [proof.pi_c[0].toString(), proof.pi_c[1].toString()]
201 ),
202 truffleAssert.ErrorType.REVERT,
203 "zkProof withdraw could not be verified"
204 );
205 });
206
207 it("Withdraw 1 ETH with the zkProof", async () => {
208 // withdraw
209 // console.log("Withdraw of " + ethAmount + " ETH to " + addr2);
210 let resW = await insMiksi.withdraw(
211 addr2,
212 nullifier,
213 [proof.pi_a[0].toString(), proof.pi_a[1].toString()],
214 [
215 [proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
216 [proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString()]
217 ],
218 [proof.pi_c[0].toString(), proof.pi_c[1].toString()]
219 );
220 // console.log("resW", resW);
221
222 balance_wei = await web3.eth.getBalance(addr2);
223 // console.log("Balance at " + addr2, web3.utils.fromWei(balance_wei, 'ether'));
224 expect(balance_wei).to.be.equal('101000000000000000000');
225 });
226
227 it("Try to reuse the zkProof and get revert", async () => {
228 // console.log("Try to reuse the zkproof and expect revert");
229 await truffleAssert.fails(
230 insMiksi.withdraw(
231 addr2,
232 nullifier,
233 [proof.pi_a[0].toString(), proof.pi_a[1].toString()],
234 [
235 [proof.pi_b[0][1].toString(), proof.pi_b[0][0].toString()],
236 [proof.pi_b[1][1].toString(), proof.pi_b[1][0].toString()]
237 ],
238 [proof.pi_c[0].toString(), proof.pi_c[1].toString()]
239 ),
240 truffleAssert.ErrorType.REVERT,
241 "nullifier already used"
242 );
243 balance_wei = await web3.eth.getBalance(addr2);
244 expect(balance_wei).to.be.equal('101000000000000000000');
245 });
246});