poseidon in merkle tree

This commit is contained in:
poma 2019-10-04 15:36:17 +03:00
parent c7f0ca9dfa
commit 71f6fce20e
8 changed files with 34 additions and 29 deletions

View File

@ -18,8 +18,8 @@ You can read more about it in [this medium article](https://medium.com/@tornado.
![mixer image](./mixer.png)
## Security risks
* Cryptographic tools used by mixer (zkSNARKS, Pedersen commitment, MiMC hash) are not yet extensively audited by cryptographic experts and may be vulnerable
* Note: we use MiMC hash only for merkle tree, so even if a preimage attack on MiMC is discovered, it will not allow to deanonymize users. To drain funds attacker needs to be able to generate arbitrary hash collisions, which is a pretty strong assumption.
* Cryptographic tools used by mixer (zkSNARKS, Pedersen commitment, Poseidon hash) are not yet extensively audited by cryptographic experts and may be vulnerable
* Note: we use Poseidon hash only for merkle tree, so even if a preimage attack on Poseidon is discovered, it will not allow to deanonymize users. To drain funds attacker needs to be able to generate arbitrary hash collisions, which is a pretty strong assumption.
* Bugs in contract. Even though we have an extensive experience in smart contract security audits, we can still make mistakes. An external audit is needed to reduce probablility of bugs. Our mixer is currently being audited, stay tuned.
* Relayer is frontrunnable. When relayer submits a transaction someone can see it in tx pool and frontrun it with higher gas price to get the fee and drain relayer funds.
* Workaround: we can set high gas price so that (almost) all fee is used on gas

View File

@ -1,18 +1,17 @@
include "../node_modules/circomlib/circuits/mimcsponge.circom";
include "../node_modules/circomlib/circuits/poseidon.circom";
// Computes MiMC(left + right)
// Computes Poseidon(left + right)
template HashLeftRight(rounds) {
signal input left;
signal input right;
signal output hash;
component hasher = MiMCSponge(2, rounds, 1);
hasher.ins[0] <== left;
hasher.ins[1] <== right;
hasher.k <== 0;
component hasher = Poseidon(2, 6, 8, 57);
hasher.inputs[0] <== left;
hasher.inputs[1] <== right;
hash <== hasher.outs[0];
hash <== hasher.out;
}
// if pathIndex == 0 returns (left = inputElement, right = pathElement)

8
cli.js
View File

@ -40,7 +40,7 @@ async function deposit() {
const deposit = createDeposit(rbigint(31), rbigint(31))
console.log('Submitting deposit transaction')
await mixer.methods.deposit('0x' + deposit.commitment.toString(16)).send({ value: ETH_AMOUNT, from: (await web3.eth.getAccounts())[0], gas:1e6 })
await mixer.methods.deposit('0x' + deposit.commitment.toString(16)).send({ value: ETH_AMOUNT, from: (await web3.eth.getAccounts())[0], gas:4e6 })
const note = '0x' + deposit.preimage.toString('hex')
console.log('Your note:', note)
@ -57,7 +57,7 @@ async function depositErc20() {
console.log('erc20mixer allowance', allowance.toString(10))
const deposit = createDeposit(rbigint(31), rbigint(31))
await erc20mixer.methods.deposit('0x' + deposit.commitment.toString(16)).send({ value: ETH_AMOUNT, from: account, gas:1e6 })
await erc20mixer.methods.deposit('0x' + deposit.commitment.toString(16)).send({ value: ETH_AMOUNT, from: account, gas:4e6 })
const balance = await erc20.methods.balanceOf(erc20mixer.address).call()
console.log('erc20mixer balance', balance.toString(10))
@ -116,7 +116,7 @@ async function withdrawErc20(note, receiver, relayer) {
console.timeEnd('Proof time')
console.log('Submitting withdraw transaction')
await erc20mixer.methods.withdraw(proof, publicSignals).send({ from: (await web3.eth.getAccounts())[0], gas: 1e6 })
await erc20mixer.methods.withdraw(proof, publicSignals).send({ from: (await web3.eth.getAccounts())[0], gas: 4e6 })
console.log('Done')
}
@ -190,7 +190,7 @@ async function withdraw(note, receiver) {
console.timeEnd('Proof time')
console.log('Submitting withdraw transaction')
await mixer.methods.withdraw(proof, publicSignals).send({ from: (await web3.eth.getAccounts())[0], gas: 1e6 })
await mixer.methods.withdraw(proof, publicSignals).send({ from: (await web3.eth.getAccounts())[0], gas: 4e6 })
console.log('Done')
}

View File

@ -12,7 +12,7 @@
pragma solidity ^0.5.8;
library Hasher {
function MiMCSponge(uint256 in_xL, uint256 in_xR, uint256 in_k) public pure returns (uint256 xL, uint256 xR);
function poseidon(uint256[] memory input) public pure returns (uint256);
}
contract MerkleTreeWithHistory {
@ -43,17 +43,10 @@ contract MerkleTreeWithHistory {
}
function hashLeftRight(uint256 left, uint256 right) public pure returns (uint256 hash) {
uint256 k = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
uint256 R = 0;
uint256 C = 0;
R = addmod(R, left, k);
(R, C) = Hasher.MiMCSponge(R, C, 0);
R = addmod(R, right, k);
(R, C) = Hasher.MiMCSponge(R, C, 0);
hash = R;
uint256[] memory data = new uint256[](2);
data[0] = left;
data[1] = right;
hash = MiMC.poseidon(data);
}
function _insert(uint256 leaf) internal {

View File

@ -1,5 +1,5 @@
const jsStorage = require('./Storage')
const hasherImpl = require('./MiMC')
const hasherImpl = require('./Poseidon')
class MerkleTree {

13
lib/Poseidon.js Normal file
View File

@ -0,0 +1,13 @@
const poseidon = require('circomlib/src/poseidon')
const snarkjs = require('snarkjs')
const bigInt = snarkjs.bigInt
class PoseidonHasher {
hash(level, left, right) {
const hash = poseidon.createHash(6, 8, 57)
return hash([bigInt(left), bigInt(right)]).toString()
}
}
module.exports = PoseidonHasher

View File

@ -1,7 +1,7 @@
/* global artifacts */
const path = require('path')
const genContract = require('circomlib/src/mimcsponge_gencontract.js')
const genContract = require('circomlib/src/poseidon_gencontract.js')
const Artifactor = require('truffle-artifactor')
module.exports = function(deployer) {
@ -12,7 +12,7 @@ module.exports = function(deployer) {
await artifactor.save({
contractName,
abi: genContract.abi,
unlinked_binary: genContract.createCode('mimcsponge', 220),
unlinked_binary: genContract.createCode(),
}).then(async () => {
const hasherContract = artifacts.require(contractName)
await deployer.deploy(hasherContract)

View File

@ -10,7 +10,7 @@ const MerkleTreeWithHistory = artifacts.require('./MerkleTreeWithHistoryMock.sol
const hasherContract = artifacts.require('./Hasher.sol')
const MerkleTree = require('../lib/MerkleTree')
const hasherImpl = require('../lib/MiMC')
const hasherImpl = require('../lib/Poseidon')
const { ETH_AMOUNT, MERKLE_TREE_HEIGHT, EMPTY_ELEMENT } = process.env