diff --git a/README.md b/README.md index 574fad8..0dedb30 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,13 @@ *From Esperanto, **miksi** (miksĀ·i): to mingle, to blend, to mix, to shuffle* -![](miksi-logo00-small.png) +Ethereum mixer where all the computation & constructions are done offchain and then proved inside a zkSNARK to the Smart Contract (for the *deposit* and for the *withdraw*). +This means that the client builds a MerkleTree and makes all the needed computation, and then generates a zk-proof where proves that all the offchain computation is done following all the rules (no leaf deletion, only one leaf addition, correct leaf format). +This allows to use only `~325.000 gas` for the *deposit*, and `~308.000 gas` for the withdraw. +![](miksi-logo00-small.png) -**Warning:** This repository is in a very early stage. +**Warning:** This repository is in a very early stage. The current version works, but is not finished and there are some improvements to be added. WebApp to use miksi-core can be found at https://github.com/arnaucube/miksi-app @@ -27,8 +30,10 @@ npm run test-sc ## Spec draft +**Note:** The spec & code is a work in progress, there are some pending works & improvements planned to do, and some diagrams for better explanation. ### Deposit +*All computation & constructions are done offchain and then proved inside a zkSNARK to the Smart Contract* - user generates a random `secret` & `nullifier` - computes the `commitment`, which is the Poseidon hash: `commitment = H(coinCode, amount, secret, nullifier)`, where: - `coinCode`: code that specifies which currency is being used (`0`==ETH) @@ -39,16 +44,18 @@ npm run test-sc - build the MerkleTree with the getted commitments - add the new computed `commitment` into the MerkleTree - generate zkSNARK proof, where is proving: - - prover knows the `secret` & `nullifier` for the `commitment` - - the transition from `RootOld` (the current one in the Smart Contract) to `RootNew` has been done following the rules (only one addition, no deletion) + - prover knows the `secret` & `nullifier` for the `commitment` which is in a leaf in the merkletree + - the transition from `RootOld` (the current one in the Smart Contract) to `RootNew` has been done following the rules (only one leaf addition, no leaf deletion, correct leaf format, etc) - user sends ETH to the smart contract `deposit` call, together with the zkProof data +- smart contract verifies the zkProof of the deposit, and if everything is ok stores the commitment & the new root Deposit circuit can be found [here](https://github.com/miksi-labs/miksi-core/blob/master/circuits/deposit.circom). ### Withdraw +*All computation & constructions are done offchain and then proved inside a zkSNARK to the Smart Contract* - user gets all the commitments from the SmartContract - build the MerkleTree with the getted commitments -- generate the siblings for the `commitment` of which the user knows the `secret` & `nullifier` +- generate the siblings (merkle proof) for the `commitment` of which the user knows the `secret` & `nullifier` - generate zkSNARK proof, where is proving: - user knows a `secret` for a public `nullifier` - which `commitment` is in the MerkleTree @@ -57,3 +64,6 @@ Deposit circuit can be found [here](https://github.com/miksi-labs/miksi-core/blo Withdraw circuit can be found [here](https://github.com/miksi-labs/miksi-core/blob/master/circuits/withdraw.circom). + +# Thanks +Miksi is possible thanks to [circom](https://github.com/iden3/circom), [circomlib](https://github.com/iden3/circomlib), [wasmsnark](https://github.com/iden3/wasmsnark), and thanks to the ideas about offchain computation validated with a zkSNARK in the [Zexe paper](https://eprint.iacr.org/2018/962.pdf). diff --git a/circuits/deposit.circom b/circuits/deposit.circom index ace9f26..9478276 100644 --- a/circuits/deposit.circom +++ b/circuits/deposit.circom @@ -55,7 +55,11 @@ template Deposit(nLevels) { comCheck.in[1] <== commitment; comCheck.out === 1; - // TODO instead of 2 siblings input, get siblingsOld from siblingsNew[len-1] + // TODO instead of 2 siblings input, get siblingsOld from + // siblingsNew[len-1] both siblingsOld & siblingsNew have same values + // except for one, can be merged into one, to ensure that the circuit + // checks that the leaf non existing under rootOld is in the same + // position than the check that the leaf exists under the rootNew // check that nLevels-1 siblings match from siblingsOld & siblingsNew component siblEq[nLevels]; diff --git a/test/contracts/miksi.test.ts b/test/contracts/miksi.test.ts index e815aa1..6b98aef 100644 --- a/test/contracts/miksi.test.ts +++ b/test/contracts/miksi.test.ts @@ -94,7 +94,7 @@ contract("miksi", (accounts) => { commitmentsArray[0] = res[0]; currKey = res[2]; }); - + it("Rebuild the tree from sc commitments", async () => { let treeTmp = await smt.newMemEmptyTrie(); await treeTmp.insert(0, 0);