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) ![mixer image](./mixer.png)
## Security risks ## Security risks
* Cryptographic tools used by mixer (zkSNARKS, Pedersen commitment, MiMC hash) are not yet extensively audited by cryptographic experts and may be vulnerable * 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 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. * 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. * 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. * 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 * 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) { template HashLeftRight(rounds) {
signal input left; signal input left;
signal input right; signal input right;
signal output hash; signal output hash;
component hasher = MiMCSponge(2, rounds, 1); component hasher = Poseidon(2, 6, 8, 57);
hasher.ins[0] <== left; hasher.inputs[0] <== left;
hasher.ins[1] <== right; hasher.inputs[1] <== right;
hasher.k <== 0;
hash <== hasher.outs[0]; hash <== hasher.out;
} }
// if pathIndex == 0 returns (left = inputElement, right = pathElement) // 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)) const deposit = createDeposit(rbigint(31), rbigint(31))
console.log('Submitting deposit transaction') 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') const note = '0x' + deposit.preimage.toString('hex')
console.log('Your note:', note) console.log('Your note:', note)
@ -57,7 +57,7 @@ async function depositErc20() {
console.log('erc20mixer allowance', allowance.toString(10)) console.log('erc20mixer allowance', allowance.toString(10))
const deposit = createDeposit(rbigint(31), rbigint(31)) 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() const balance = await erc20.methods.balanceOf(erc20mixer.address).call()
console.log('erc20mixer balance', balance.toString(10)) console.log('erc20mixer balance', balance.toString(10))
@ -116,7 +116,7 @@ async function withdrawErc20(note, receiver, relayer) {
console.timeEnd('Proof time') console.timeEnd('Proof time')
console.log('Submitting withdraw transaction') 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') console.log('Done')
} }
@ -190,7 +190,7 @@ async function withdraw(note, receiver) {
console.timeEnd('Proof time') console.timeEnd('Proof time')
console.log('Submitting withdraw transaction') 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') console.log('Done')
} }

View File

@ -12,7 +12,7 @@
pragma solidity ^0.5.8; pragma solidity ^0.5.8;
library Hasher { 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 { contract MerkleTreeWithHistory {
@ -43,17 +43,10 @@ contract MerkleTreeWithHistory {
} }
function hashLeftRight(uint256 left, uint256 right) public pure returns (uint256 hash) { function hashLeftRight(uint256 left, uint256 right) public pure returns (uint256 hash) {
uint256 k = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint256[] memory data = new uint256[](2);
uint256 R = 0; data[0] = left;
uint256 C = 0; data[1] = right;
hash = MiMC.poseidon(data);
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;
} }
function _insert(uint256 leaf) internal { function _insert(uint256 leaf) internal {

View File

@ -1,5 +1,5 @@
const jsStorage = require('./Storage') const jsStorage = require('./Storage')
const hasherImpl = require('./MiMC') const hasherImpl = require('./Poseidon')
class MerkleTree { 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 */ /* global artifacts */
const path = require('path') 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') const Artifactor = require('truffle-artifactor')
module.exports = function(deployer) { module.exports = function(deployer) {
@ -12,7 +12,7 @@ module.exports = function(deployer) {
await artifactor.save({ await artifactor.save({
contractName, contractName,
abi: genContract.abi, abi: genContract.abi,
unlinked_binary: genContract.createCode('mimcsponge', 220), unlinked_binary: genContract.createCode(),
}).then(async () => { }).then(async () => {
const hasherContract = artifacts.require(contractName) const hasherContract = artifacts.require(contractName)
await deployer.deploy(hasherContract) await deployer.deploy(hasherContract)

View File

@ -10,7 +10,7 @@ const MerkleTreeWithHistory = artifacts.require('./MerkleTreeWithHistoryMock.sol
const hasherContract = artifacts.require('./Hasher.sol') const hasherContract = artifacts.require('./Hasher.sol')
const MerkleTree = require('../lib/MerkleTree') 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 const { ETH_AMOUNT, MERKLE_TREE_HEIGHT, EMPTY_ELEMENT } = process.env