From a6cda4a5014fd87705dd1f745460cdd3ac54b3f3 Mon Sep 17 00:00:00 2001 From: Roman Storm Date: Wed, 27 Nov 2019 19:45:52 -0800 Subject: [PATCH] add isSpendArray view function --- contracts/Mixer.sol | 12 ++++++++++- test/ETHMixer.test.js | 50 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/contracts/Mixer.sol b/contracts/Mixer.sol index a17f827..a9d6951 100644 --- a/contracts/Mixer.sol +++ b/contracts/Mixer.sol @@ -95,10 +95,20 @@ contract Mixer is MerkleTreeWithHistory, ReentrancyGuard { function _processWithdraw(address payable _recipient, address payable _relayer, uint256 _fee, uint256 _refund) internal; /** @dev whether a note is already spent */ - function isSpent(bytes32 _nullifierHash) external view returns(bool) { + function isSpent(bytes32 _nullifierHash) public view returns(bool) { return nullifierHashes[_nullifierHash]; } + /** @dev whether an array of notes is already spent */ + function isSpentArray(bytes32[] calldata _nullifierHashes) external view returns(bool[] memory spent) { + spent = new bool[](_nullifierHashes.length); + for(uint i = 0; i < _nullifierHashes.length; i++) { + if (isSpent(_nullifierHashes[i])) { + spent[i] = true; + } + } + } + /** @dev allow operator to update SNARK verification keys. This is needed to update keys after the final trusted setup ceremony is held. After that operator rights are supposed to be transferred to zero address diff --git a/test/ETHMixer.test.js b/test/ETHMixer.test.js index a2622af..bf90557 100644 --- a/test/ETHMixer.test.js +++ b/test/ETHMixer.test.js @@ -542,6 +542,56 @@ contract('ETHMixer', accounts => { }) }) + describe('#isSpent', () => { + it('should work', async () => { + const deposit1 = generateDeposit() + const deposit2 = generateDeposit() + await tree.insert(deposit1.commitment) + await tree.insert(deposit2.commitment) + await mixer.deposit(toFixedHex(deposit1.commitment), { value, gasPrice: '0' }) + await mixer.deposit(toFixedHex(deposit2.commitment), { value, gasPrice: '0' }) + + const { root, path_elements, path_index } = await tree.path(1) + + // Circuit input + const input = stringifyBigInts({ + // public + root, + nullifierHash: pedersenHash(deposit2.nullifier.leInt2Buff(31)), + relayer: operator, + recipient, + fee, + refund, + + // private + nullifier: deposit2.nullifier, + secret: deposit2.secret, + pathElements: path_elements, + pathIndices: path_index, + }) + + + const proofData = await websnarkUtils.genWitnessAndProve(groth16, input, circuit, proving_key) + const { proof } = websnarkUtils.toSolidityInput(proofData) + + const args = [ + toFixedHex(input.root), + toFixedHex(input.nullifierHash), + toFixedHex(input.recipient, 20), + toFixedHex(input.relayer, 20), + toFixedHex(input.fee), + toFixedHex(input.refund) + ] + + await mixer.withdraw(proof, ...args, { from: relayer, gasPrice: '0' }) + + const nullifierHash1 = toFixedHex(pedersenHash(deposit1.nullifier.leInt2Buff(31))) + const nullifierHash2 = toFixedHex(pedersenHash(deposit2.nullifier.leInt2Buff(31))) + const spentArray = await mixer.isSpentArray([nullifierHash1, nullifierHash2]) + spentArray.should.be.deep.equal([false, true]) + }) + }) + afterEach(async () => { await revertSnapshot(snapshotId.result) // eslint-disable-next-line require-atomic-updates