nullifier hash refactoring

This commit is contained in:
Alexey 2019-07-25 16:58:21 +03:00
parent 63f984b2d5
commit fc5eb51c3b
2 changed files with 14 additions and 9 deletions

View File

@ -8,13 +8,13 @@ contract IVerifier {
contract Mixer is MerkleTreeWithHistory { contract Mixer is MerkleTreeWithHistory {
uint256 public transferValue; uint256 public transferValue;
mapping(uint256 => bool) public nullifiers; mapping(uint256 => bool) public nullifierHashes;
// we store all commitments just to prevent accidental deposits with the same commitment // we store all commitments just to prevent accidental deposits with the same commitment
mapping(uint256 => bool) public commitments; mapping(uint256 => bool) public commitments;
IVerifier verifier; IVerifier verifier;
event Deposit(uint256 indexed commitment, uint256 leafIndex, uint256 timestamp); event Deposit(uint256 indexed commitment, uint256 leafIndex, uint256 timestamp);
event Withdraw(address to, uint256 nullifier, uint256 fee); event Withdraw(address to, uint256 nullifierHash, uint256 fee);
/** /**
@dev The constructor @dev The constructor
@ -47,30 +47,30 @@ contract Mixer is MerkleTreeWithHistory {
@dev Withdraw deposit from the mixer. `a`, `b`, and `c` are zkSNARK proof data, and input is an array of circuit public inputs @dev Withdraw deposit from the mixer. `a`, `b`, and `c` are zkSNARK proof data, and input is an array of circuit public inputs
`input` array consists of: `input` array consists of:
- merkle root of all deposits in the mixer - merkle root of all deposits in the mixer
- unique deposit nullifier to prevent double spends - hash of unique deposit nullifier to prevent double spends
- the receiver of funds - the receiver of funds
- optional fee that goes to the transaction sender (usually a relay) - 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[4] memory input) public {
uint256 root = input[0]; uint256 root = input[0];
uint256 nullifier = input[1]; uint256 nullifierHash = input[1];
address payable receiver = address(input[2]); address payable receiver = address(input[2]);
uint256 fee = input[3]; uint256 fee = input[3];
require(fee < transferValue, "Fee exceeds transfer value"); require(fee < transferValue, "Fee exceeds transfer value");
require(!nullifiers[nullifier], "The note has been already spent"); 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(isKnownRoot(root), "Cannot find your merkle root"); // Make sure to use a recent one
require(verifier.verifyProof(a, b, c, input), "Invalid withdraw proof"); require(verifier.verifyProof(a, b, c, input), "Invalid withdraw proof");
nullifiers[nullifier] = true; nullifierHashes[nullifierHash] = true;
receiver.transfer(transferValue - fee); receiver.transfer(transferValue - fee);
if (fee > 0) { if (fee > 0) {
msg.sender.transfer(fee); msg.sender.transfer(fee);
} }
emit Withdraw(receiver, nullifier, fee); emit Withdraw(receiver, nullifierHash, fee);
} }
function isSpent(uint256 nullifier) public view returns(bool) { function isSpent(uint256 nullifier) public view returns(bool) {
return nullifiers[nullifier]; return nullifierHashes[nullifier];
} }
} }

View File

@ -198,6 +198,8 @@ contract('Mixer', accounts => {
const balanceMixerBefore = await web3.eth.getBalance(mixer.address) const balanceMixerBefore = await web3.eth.getBalance(mixer.address)
const balanceRelayerBefore = await web3.eth.getBalance(relayer) const balanceRelayerBefore = await web3.eth.getBalance(relayer)
const balanceRecieverBefore = await web3.eth.getBalance(toHex(receiver.toString())) const balanceRecieverBefore = await web3.eth.getBalance(toHex(receiver.toString()))
let isSpent = await mixer.isSpent(input.nullifierHash.toString(16).padStart(66, '0x00000'))
isSpent.should.be.equal(false)
const { logs } = await mixer.withdraw(pi_a, pi_b, pi_c, publicSignals, { from: relayer, gasPrice: '0' }) const { logs } = await mixer.withdraw(pi_a, pi_b, pi_c, publicSignals, { from: relayer, gasPrice: '0' })
@ -209,9 +211,12 @@ contract('Mixer', accounts => {
balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN)) balanceRelayerAfter.should.be.eq.BN(toBN(balanceRelayerBefore).add(feeBN))
balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN)) balanceRecieverAfter.should.be.eq.BN(toBN(balanceRecieverBefore).add(toBN(value)).sub(feeBN))
logs[0].event.should.be.equal('Withdraw') logs[0].event.should.be.equal('Withdraw')
logs[0].args.nullifier.should.be.eq.BN(toBN(input.nullifierHash.toString())) logs[0].args.nullifierHash.should.be.eq.BN(toBN(input.nullifierHash.toString()))
logs[0].args.fee.should.be.eq.BN(feeBN) 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)
}) })
it('should prevent double spend', async () => { it('should prevent double spend', async () => {