merge and add cli.js erc20 commands

This commit is contained in:
Roman Storm 2019-09-14 11:21:53 -07:00
commit a64f41a44e
11 changed files with 164 additions and 61 deletions

View File

@ -2,7 +2,7 @@ MERKLE_TREE_HEIGHT=16
# in wei
ETH_AMOUNT=100000000000000000
TOKEN_AMOUNT=100000000000000000
EMPTY_ELEMENT=1337
EMPTY_ELEMENT=1
PRIVATE_KEY=
ERC20_TOKEN=

View File

@ -1,20 +1,29 @@
# Tornado mixer [![Build Status](https://travis-ci.org/peppersec/tornado-mixer.svg?branch=master)](https://travis-ci.org/peppersec/tornado-mixer)
![mixer image](./mixer.png)
Tornado is a non-custodial Ethereum and ERC20 mixer based on zkSNARKs. It improves transaction privacy by breaking the on-chain link between recipient and destination addresses. It uses a smart contract that accepts ETH deposits that can be withdrawn by a different address. Whenever ETH is withdrawn by the new address, there is no way to link the withdrawal to the deposit, ensuring complete privacy.
To make a deposit user generates a secret and sends its hash (called a commitment) along with deposit amount to the Tornado smart contract. The contract accepts the deposit and adds the commitment to its list of deposits.
Later, the user decides to make a withdraw. In order to do that the user should provide a proof that he or she possesses a secret to an unspent commitment from the smart contracts list of deposits. zkSnark technology allows to do that without revealing which exact deposit corresponds to this secret. The smart contract will check the proof, and transfer deposited funds to the address specified for withdrawal. An external observer will be unable to determine which deposit this withdrawal comes from.
You can read more about it in [this medium article](https://medium.com/@tornado.cash.mixer/introducing-private-transactions-on-ethereum-now-42ee915babe0)
## Specs
- Deposit gas cost: deposit 888054
- Deposit gas const: 888054 (43381 + 50859 * tree_depth)
- Withdraw gas cost: 692133
- Circuit constraints: 22617
- Circuit proving time: 6116ms
- Circuit Constraints = 22617 (1869 + 1325 * tree_depth)
- Circuit Proof time = 6116ms (1071 + 347 * tree_depth)
- Serverless
![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.
* 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. The relayer will not receive profit this way, but this approach is acceptable until we develop more sophisticated system that prevents frontrunning
* 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
* Workaround: we can set high gas price so that (almost) all fee is used on gas
* Second workaround: allow only single hardcoded relayer, we use this approach for now
* ~~Nullifier griefing. when you submit a withdraw transaction you reveal the nullifier for your note. If someone manages to
make a deposit with the same nullifier and withdraw it while your transaction is still in tx pool, your note will be considered
spent since it has the same nullifier and it will prevent you from withdrawing your funds~~
@ -25,21 +34,38 @@ spent since it has the same nullifier and it will prevent you from withdrawing y
2. `npm install -g npx`
## Usage
1. `npm i`
You can see example usage in cli.js, it works both in console and in browser.
1. `npm install`
1. `cp .env.example .env`
1. `npm run build:circuit` - may take 10 minutes or more
1. `npm run build:circuit` - this may take 10 minutes or more
1. `npm run build:contract`
1. `npm run browserify`
1. `npx ganache-cli`
1. `npm run test` - optionally run tests. It may fail for the first time, just run one more time.
Use browser version on Kovan:
1. `vi .env` - add your Kovan private key to deploy contracts
1. `npm run migrate`
1. `npx http-server` - serve current dir, you can use any other static http server
1. Open `localhost:8080`
Use with command line version with Ganache:
### ETHMixer
1. `npm run migrate:dev`
1. `./cli.js deposit`
1. `./cli.js withdraw <note from previous step> <destination eth address>`
1. `./cli.js balance <destination eth address>`
1. `vi .env` - add your Kovan private key to deploy contracts
1. `npm run migrate`
1. `npx http-server` - serve current dir, you can use any other http server
1. Open `localhost:8080`
### ERC20Mixer
1. `npm run migrate:dev`
1. `./cli.js depositErc20`
1. `./cli.js withdraw <note from previous step> <destination eth address> <relayer eth address>`
1. `./cli.js balanceErc20 <destination eth address> <relayer eth address>`
If you want, you can point the app to existing tornado contracts on Mainnet or Kovan, it should work without any changes
## Deploy ETH Tornado Cash
1. `cp .env.example .env`

View File

@ -31,6 +31,7 @@ template Withdraw(levels, rounds) {
signal input root;
signal input nullifierHash;
signal input receiver; // not taking part in any computations
signal input relayer; // not taking part in any computations
signal input fee; // not taking part in any computations
signal private input nullifier;
signal private input secret;
@ -56,8 +57,10 @@ template Withdraw(levels, rounds) {
// Squares are used to prevent optimizer from removing those constraints
signal receiverSquare;
signal feeSquare;
signal relayerSquare;
receiverSquare <== receiver * receiver;
feeSquare <== fee * fee;
relayerSquare <== relayer * relayer;
}
component main = Withdraw(16, 220);

108
cli.js
View File

@ -16,9 +16,15 @@ let web3, mixer, erc20mixer, circuit, proving_key, groth16, erc20
let MERKLE_TREE_HEIGHT, ETH_AMOUNT, EMPTY_ELEMENT, ERC20_TOKEN
const inBrowser = (typeof window !== 'undefined')
/** Generate random number of specified byte length */
const rbigint = (nbytes) => snarkjs.bigInt.leBuff2int(crypto.randomBytes(nbytes))
/** Compute pedersen hash */
const pedersenHash = (data) => circomlib.babyJub.unpackPoint(circomlib.pedersenHash.hash(data))[0]
/**
* Create deposit object from secret and nullifier
*/
function createDeposit(nullifier, secret) {
let deposit = { nullifier, secret }
deposit.preimage = Buffer.concat([deposit.nullifier.leInt2Buff(31), deposit.secret.leInt2Buff(31)])
@ -26,6 +32,10 @@ function createDeposit(nullifier, secret) {
return deposit
}
/**
* Make a deposit
* @returns {Promise<string>}
*/
async function deposit() {
const deposit = createDeposit(rbigint(31), rbigint(31))
@ -41,10 +51,10 @@ async function depositErc20() {
const account = (await web3.eth.getAccounts())[0]
const tokenAmount = process.env.TOKEN_AMOUNT
await erc20.methods.mint(account, tokenAmount).send({ from: account, gas:1e6 })
const allowance = await erc20.methods.allowance(account, erc20mixer.address).call()
console.log('erc20mixer allowance', allowance.toString(10))
await erc20.methods.approve(erc20mixer.address, tokenAmount).send({ from: account, gas:1e6 })
const allowance = await erc20.methods.allowance(account, erc20mixer.address).call()
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 })
@ -56,7 +66,7 @@ async function depositErc20() {
return note
}
async function withdrawErc20(note, receiver) {
async function withdrawErc20(note, receiver, relayer) {
let buf = Buffer.from(note.slice(2), 'hex')
let deposit = createDeposit(bigInt.leBuff2int(buf.slice(0, 31)), bigInt.leBuff2int(buf.slice(31, 62)))
@ -77,7 +87,7 @@ async function withdrawErc20(note, receiver) {
const validRoot = await erc20mixer.methods.isKnownRoot(await tree.root()).call()
const nullifierHash = pedersenHash(deposit.nullifier.leInt2Buff(31))
const nullifierHashToCheck = nullifierHash.toString(16).padStart('66', '0x000000')
const isSpent = await mixer.methods.isSpent(nullifierHashToCheck).call()
const isSpent = await erc20mixer.methods.isSpent(nullifierHashToCheck).call()
assert(validRoot === true)
assert(isSpent === false)
@ -89,7 +99,8 @@ async function withdrawErc20(note, receiver) {
root: root,
nullifierHash,
receiver: bigInt(receiver),
fee: bigInt(0),
relayer: bigInt(relayer),
fee: bigInt(web3.utils.toWei('0.01')),
// private
nullifier: deposit.nullifier,
@ -105,7 +116,7 @@ async function withdrawErc20(note, receiver) {
console.timeEnd('Proof time')
console.log('Submitting withdraw transaction')
await mixer.methods.withdraw(pi_a, pi_b, pi_c, publicSignals).send({ from: (await web3.eth.getAccounts())[0], gas: 1e6 })
await erc20mixer.methods.withdraw(pi_a, pi_b, pi_c, publicSignals).send({ from: (await web3.eth.getAccounts())[0], gas: 1e6 })
console.log('Done')
}
@ -114,42 +125,58 @@ async function getBalance(receiver) {
console.log('Balance is ', web3.utils.fromWei(balance))
}
async function getBalanceErc20(receiver, relayer) {
const balanceReceiver = await web3.eth.getBalance(receiver)
const balanceRelayer = await web3.eth.getBalance(relayer)
const tokenBalanceReceiver = await erc20.methods.balanceOf(receiver).call()
const tokenBalanceRelayer = await erc20.methods.balanceOf(relayer).call()
console.log('Receiver eth Balance is ', web3.utils.fromWei(balanceReceiver))
console.log('Relayer eth Balance is ', web3.utils.fromWei(balanceRelayer))
console.log('Receiver token Balance is ', web3.utils.fromWei(tokenBalanceReceiver.toString()))
console.log('Relayer token Balance is ', web3.utils.fromWei(tokenBalanceRelayer.toString()))
}
async function withdraw(note, receiver) {
// Decode hex string and restore the deposit object
let buf = Buffer.from(note.slice(2), 'hex')
let deposit = createDeposit(bigInt.leBuff2int(buf.slice(0, 31)), bigInt.leBuff2int(buf.slice(31, 62)))
const nullifierHash = pedersenHash(deposit.nullifier.leInt2Buff(31))
const paddedNullifierHash = nullifierHash.toString(16).padStart('66', '0x000000')
const paddedCommitment = deposit.commitment.toString(16).padStart('66', '0x000000')
// Get all deposit events from smart contract and assemble merkle tree from them
console.log('Getting current state from mixer contract')
const events = await mixer.getPastEvents('Deposit', { fromBlock: mixer.deployedBlock, toBlock: 'latest' })
let leafIndex
const commitment = deposit.commitment.toString(16).padStart('66', '0x000000')
const leaves = events
.sort((a, b) => a.returnValues.leafIndex.sub(b.returnValues.leafIndex))
.map(e => {
if (e.returnValues.commitment.eq(commitment)) {
leafIndex = e.returnValues.leafIndex.toNumber()
}
return e.returnValues.commitment
})
.sort((a, b) => a.returnValues.leafIndex.sub(b.returnValues.leafIndex)) // Sort events in chronological order
.map(e => e.returnValues.commitment)
const tree = new merkleTree(MERKLE_TREE_HEIGHT, EMPTY_ELEMENT, leaves)
const validRoot = await mixer.methods.isKnownRoot(await tree.root()).call()
const nullifierHash = pedersenHash(deposit.nullifier.leInt2Buff(31))
const nullifierHashToCheck = nullifierHash.toString(16).padStart('66', '0x000000')
const isSpent = await mixer.methods.isSpent(nullifierHashToCheck).call()
assert(validRoot === true)
assert(isSpent === false)
assert(leafIndex >= 0)
// Find current commitment in the tree
let depositEvent = events.find(e => e.returnValues.commitment.eq(paddedCommitment))
let leafIndex = depositEvent ? depositEvent.returnValues.leafIndex.toNumber() : -1
// Validate that our data is correct
const isValidRoot = await mixer.methods.isKnownRoot(await tree.root()).call()
const isSpent = await mixer.methods.isSpent(paddedNullifierHash).call()
assert(isValidRoot === true) // Merkle tree assembled correctly
assert(isSpent === false) // The note is not spent
assert(leafIndex >= 0) // Our deposit is present in the tree
// Compute merkle proof of our commitment
const { root, path_elements, path_index } = await tree.path(leafIndex)
// Circuit input
// Prepare circuit input
const input = {
// public
// Public snark inputs
root: root,
nullifierHash,
receiver: bigInt(receiver),
relayer: bigInt(0),
fee: bigInt(0),
// private
// Private snark inputs
nullifier: deposit.nullifier,
secret: deposit.secret,
pathElements: path_elements,
@ -167,17 +194,23 @@ async function withdraw(note, receiver) {
console.log('Done')
}
/**
* Init web3, contracts, and snark
*/
async function init() {
let contractJson, erc20ContractJson, erc20mixerJson
if (inBrowser) {
// Initialize using injected web3 (Metamask)
// To assemble web version run `npm run browserify`
web3 = new Web3(window.web3.currentProvider, null, { transactionConfirmationBlocks: 1 })
contractJson = await (await fetch('build/contracts/ETHMixer.json')).json()
circuit = await (await fetch('build/circuits/withdraw.json')).json()
proving_key = await (await fetch('build/circuits/withdraw_proving_key.bin')).arrayBuffer()
MERKLE_TREE_HEIGHT = 16
ETH_AMOUNT = 1e18
EMPTY_ELEMENT = 0
EMPTY_ELEMENT = 1
} else {
// Initialize from local node
web3 = new Web3('http://localhost:8545', null, { transactionConfirmationBlocks: 1 })
contractJson = require('./build/contracts/ETHMixer.json')
circuit = require('./build/circuits/withdraw.json')
@ -192,9 +225,11 @@ async function init() {
}
groth16 = await buildGroth16()
let netId = await web3.eth.net.getId()
// const tx = await web3.eth.getTransaction(contractJson.networks[netId].transactionHash)
// mixer = new web3.eth.Contract(contractJson.abi, contractJson.networks[netId].address)
// mixer.deployedBlock = tx.blockNumber
if (contractJson.networks[netId]) {
const tx = await web3.eth.getTransaction(contractJson.networks[netId].transactionHash)
mixer = new web3.eth.Contract(contractJson.abi, contractJson.networks[netId].address)
mixer.deployedBlock = tx.blockNumber
}
const tx3 = await web3.eth.getTransaction(erc20mixerJson.networks[netId].transactionHash)
erc20mixer = new web3.eth.Contract(erc20mixerJson.abi, erc20mixerJson.networks[netId].address)
@ -265,6 +300,12 @@ if (inBrowser) {
} else
printHelp(1)
break
case 'balanceErc20':
if (args.length === 3 && /^0x[0-9a-fA-F]{40}$/.test(args[1]) && /^0x[0-9a-fA-F]{40}$/.test(args[2])) {
init().then(() => getBalanceErc20(args[1], args[2])).then(() => process.exit(0)).catch(err => {console.log(err); process.exit(1)})
} else
printHelp(1)
break
case 'withdraw':
if (args.length === 3 && /^0x[0-9a-fA-F]{124}$/.test(args[1]) && /^0x[0-9a-fA-F]{40}$/.test(args[2])) {
init().then(() => withdraw(args[1], args[2])).then(() => process.exit(0)).catch(err => {console.log(err); process.exit(1)})
@ -272,6 +313,13 @@ if (inBrowser) {
else
printHelp(1)
break
case 'withdrawErc20':
if (args.length === 4 && /^0x[0-9a-fA-F]{124}$/.test(args[1]) && /^0x[0-9a-fA-F]{40}$/.test(args[2]) && /^0x[0-9a-fA-F]{40}$/.test(args[3])) {
init().then(() => withdrawErc20(args[1], args[2], args[3])).then(() => process.exit(0)).catch(err => {console.log(err); process.exit(1)})
}
else
printHelp(1)
break
case 'auto':
if (args.length === 1) {
(async () => {

View File

@ -36,12 +36,12 @@ contract ERC20Mixer is Mixer {
safeErc20TransferFrom(msg.sender, address(this), mixDenomination);
}
function _processWithdraw(address payable _receiver, uint256 _fee) internal {
function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee) internal {
_receiver.transfer(userEther);
safeErc20Transfer(_receiver, mixDenomination - _fee);
if (_fee > 0) {
safeErc20Transfer(operator, _fee);
safeErc20Transfer(_relayer, _fee);
}
}
@ -56,6 +56,8 @@ contract ERC20Mixer is Mixer {
)
);
require(success, "not enough allowed tokens");
// if contract returns some data let's make sure that is `true` according to standard
if (data.length > 0) {
assembly {
success := mload(add(data, 0x20))
@ -75,6 +77,8 @@ contract ERC20Mixer is Mixer {
)
);
require(success, "not enough tokens");
// if contract returns some data let's make sure that is `true` according to standard
if (data.length > 0) {
assembly {
success := mload(add(data, 0x20))

View File

@ -23,10 +23,10 @@ contract ETHMixer is Mixer {
) Mixer(_verifier, _mixDenomination, _merkleTreeHeight, _emptyElement, _operator) public {
}
function _processWithdraw(address payable _receiver, uint256 _fee) internal {
function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee) internal {
_receiver.transfer(mixDenomination - _fee);
if (_fee > 0) {
operator.transfer(_fee);
_relayer.transfer(_fee);
}
}

View File

@ -58,7 +58,7 @@ contract MerkleTreeWithHistory {
function _insert(uint256 leaf) internal {
uint32 current_index = next_index;
require(current_index != 2**(levels - 1), "Merkle tree is full");
require(current_index != 2**(levels - 1), "Merkle tree is full. No more leafs can be added");
next_index += 1;
uint256 current_level_hash = leaf;
uint256 left;
@ -133,5 +133,3 @@ contract MerkleTreeWithHistory {
return _zeros;
}
}

View File

@ -14,7 +14,7 @@ pragma solidity ^0.5.8;
import "./MerkleTreeWithHistory.sol";
contract IVerifier {
function verifyProof(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[4] memory input) public returns(bool);
function verifyProof(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[5] memory input) public returns(bool);
}
contract Mixer is MerkleTreeWithHistory {
@ -29,7 +29,7 @@ contract Mixer is MerkleTreeWithHistory {
uint256 public mixDenomination;
event Deposit(uint256 indexed commitment, uint256 leafIndex, uint256 timestamp);
event Withdraw(address to, uint256 nullifierHash, uint256 fee);
event Withdraw(address to, uint256 nullifierHash, address indexed relayer, uint256 fee);
/**
@dev The constructor
@ -75,19 +75,20 @@ contract Mixer is MerkleTreeWithHistory {
- the receiver of funds
- optional fee that goes to the transaction sender (usually a relay)
*/
function withdraw(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[4] memory input) public {
function withdraw(uint256[2] memory a, uint256[2][2] memory b, uint256[2] memory c, uint256[5] memory input) public {
uint256 root = input[0];
uint256 nullifierHash = input[1];
address payable receiver = address(input[2]);
uint256 fee = input[3];
address payable relayer = address(input[3]);
uint256 fee = input[4];
require(fee < mixDenomination, "Fee exceeds transfer value");
require(!nullifierHashes[nullifierHash], "The note has been already spent");
require(isKnownRoot(root), "Cannot find your merkle root"); // Make sure to use a recent one
require(verifier.verifyProof(a, b, c, input), "Invalid withdraw proof");
nullifierHashes[nullifierHash] = true;
_processWithdraw(receiver, fee);
emit Withdraw(receiver, nullifierHash, fee);
_processWithdraw(receiver, relayer, fee);
emit Withdraw(receiver, nullifierHash, relayer, fee);
}
function toggleDeposits() external {
@ -105,6 +106,6 @@ contract Mixer is MerkleTreeWithHistory {
}
function _processDeposit() internal {}
function _processWithdraw(address payable _receiver, uint256 _fee) internal {}
function _processWithdraw(address payable _receiver, address payable _relayer, uint256 _fee) internal {}
}

View File

@ -127,6 +127,7 @@ contract('ERC20Mixer', accounts => {
// public
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
relayer,
receiver,
fee,
@ -159,14 +160,15 @@ contract('ERC20Mixer', accounts => {
const balanceRecieverAfter = await token.balanceOf(toHex(receiver.toString()))
const ethBalanceRecieverAfter = await web3.eth.getBalance(toHex(receiver.toString()))
const feeBN = toBN(fee.toString())
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(value)))
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore))
balanceMixerAfter.should.be.eq.BN(toBN(balanceMixerBefore).sub(toBN(tokenDenomination)))
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN))
ethBalanceOperatorAfter.should.be.eq.BN(toBN(ethBalanceOperatorBefore))
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(tokenDenomination).sub(feeBN)))
ethBalanceRecieverAfter.should.be.eq.BN(toBN(ethBalanceRecieverBefore).add(toBN(value)))
logs[0].event.should.be.equal('Withdraw')
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.relayer.should.be.eq.BN(relayer)
logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(true)
@ -207,6 +209,7 @@ contract('ERC20Mixer', accounts => {
// public
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
relayer: operator,
receiver,
fee,
@ -249,6 +252,7 @@ contract('ERC20Mixer', accounts => {
logs[0].event.should.be.equal('Withdraw')
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.relayer.should.be.eq.BN(operator)
logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(true)
@ -285,6 +289,7 @@ contract('ERC20Mixer', accounts => {
// public
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
relayer: operator,
receiver,
fee,
@ -328,6 +333,7 @@ contract('ERC20Mixer', accounts => {
logs[0].event.should.be.equal('Withdraw')
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.relayer.should.be.eq.BN(operator)
logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(true)

View File

@ -141,6 +141,7 @@ contract('ETHMixer', accounts => {
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
nullifier: deposit.nullifier,
relayer: operator,
receiver,
fee,
secret: deposit.secret,
@ -196,6 +197,7 @@ contract('ETHMixer', accounts => {
// public
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
relayer: operator,
receiver,
fee,
@ -235,6 +237,7 @@ contract('ETHMixer', accounts => {
logs[0].event.should.be.equal('Withdraw')
logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.relayer.should.be.eq.BN(operator)
logs[0].args.fee.should.be.eq.BN(feeBN)
isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(true)
@ -251,6 +254,7 @@ contract('ETHMixer', accounts => {
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
nullifier: deposit.nullifier,
relayer: operator,
receiver,
fee,
secret: deposit.secret,
@ -275,6 +279,7 @@ contract('ETHMixer', accounts => {
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
nullifier: deposit.nullifier,
relayer: operator,
receiver,
fee,
secret: deposit.secret,
@ -299,6 +304,7 @@ contract('ETHMixer', accounts => {
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
nullifier: deposit.nullifier,
relayer: operator,
receiver,
fee: oneEtherFee,
secret: deposit.secret,
@ -323,6 +329,7 @@ contract('ETHMixer', accounts => {
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
root,
nullifier: deposit.nullifier,
relayer: operator,
receiver,
fee,
secret: deposit.secret,
@ -350,6 +357,7 @@ contract('ETHMixer', accounts => {
root,
nullifierHash: pedersenHash(deposit.nullifier.leInt2Buff(31)),
nullifier: deposit.nullifier,
relayer: operator,
receiver,
fee,
secret: deposit.secret,

View File

@ -185,10 +185,19 @@ contract('MerkleTreeWithHistory', accounts => {
}
let error = await merkleTreeWithHistory.insert(1337).should.be.rejected
error.reason.should.be.equal('Merkle tree is full')
error.reason.should.be.equal('Merkle tree is full. No more leafs can be added')
error = await merkleTreeWithHistory.insert(1).should.be.rejected
error.reason.should.be.equal('Merkle tree is full')
error.reason.should.be.equal('Merkle tree is full. No more leafs can be added')
})
it.skip('mimc gas', async () => {
levels = 6
zeroValue = 1337
merkleTreeWithHistory = await MerkleTreeWithHistory.new(levels, zeroValue)
const gas = await merkleTreeWithHistory.hashLeftRight.estimateGas(zeroValue, zeroValue)
console.log('gas', gas - 21000)
})
})