mirror of
https://github.com/tornadocash/tornado-nova
synced 2024-02-02 14:53:56 +01:00
add transparent deposit logic
This commit is contained in:
parent
4b2e1cf273
commit
ca5f4da72d
@ -17,6 +17,7 @@ module.exports = {
|
||||
verifier16: '0x743494b60097a2230018079c02fe21a7b687eaa5',
|
||||
MERKLE_TREE_HEIGHT: 23,
|
||||
hasher: '0x94c92f096437ab9958fc0a37f09348f30389ae79',
|
||||
hasher3: '0x94c92f096437ab9958fc0a37f09348f30389ae79', // TODO
|
||||
gcWeth: '0x6a023ccd1ff6f2045c3309768ead9e68f978f6e1',
|
||||
gcOmniBridge: '0xf6a78083ca3e2a662d6dd1703c939c8ace2e268d',
|
||||
l1Unwrapper: '0x3F615bA21Bc6Cc5D4a6D798c5950cc5c42937fbd',
|
||||
|
@ -17,6 +17,7 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
|
||||
import { IERC20Receiver, IERC6777, IOmniBridge } from "./interfaces/IBridge.sol";
|
||||
import { CrossChainGuard } from "./bridge/CrossChainGuard.sol";
|
||||
import { IVerifier } from "./interfaces/IVerifier.sol";
|
||||
import { IHasher3 } from "./interfaces/IHasher3.sol";
|
||||
import "./MerkleTreeWithHistory.sol";
|
||||
|
||||
/** @dev This contract(pool) allows deposit of an arbitrary amount to it, shielded transfer to another registered user inside the pool
|
||||
@ -28,6 +29,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver, ReentrancyGuard,
|
||||
|
||||
IVerifier public immutable verifier2;
|
||||
IVerifier public immutable verifier16;
|
||||
IHasher3 public immutable hasher3;
|
||||
IERC6777 public immutable token;
|
||||
address public immutable omniBridge;
|
||||
address public immutable l1Unwrapper;
|
||||
@ -78,6 +80,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver, ReentrancyGuard,
|
||||
@param _verifier16 the address of SNARK verifier for 16 inputs
|
||||
@param _levels hight of the commitments merkle tree
|
||||
@param _hasher hasher address for the merkle tree
|
||||
@param _hasher3 hasher address for the commitment
|
||||
@param _token token address for the pool
|
||||
@param _omniBridge omniBridge address for specified token
|
||||
@param _l1Unwrapper address of the L1Helper
|
||||
@ -90,6 +93,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver, ReentrancyGuard,
|
||||
IVerifier _verifier16,
|
||||
uint32 _levels,
|
||||
address _hasher,
|
||||
address _hasher3,
|
||||
IERC6777 _token,
|
||||
address _omniBridge,
|
||||
address _l1Unwrapper,
|
||||
@ -102,6 +106,7 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver, ReentrancyGuard,
|
||||
{
|
||||
verifier2 = _verifier2;
|
||||
verifier16 = _verifier16;
|
||||
hasher3 = IHasher3(_hasher3);
|
||||
token = _token;
|
||||
omniBridge = _omniBridge;
|
||||
l1Unwrapper = _l1Unwrapper;
|
||||
@ -125,6 +130,28 @@ contract TornadoPool is MerkleTreeWithHistory, IERC20Receiver, ReentrancyGuard,
|
||||
_transact(_args, _extData);
|
||||
}
|
||||
|
||||
/** @dev Function that allows transparent deposits without proof verification.
|
||||
*/
|
||||
function transparentDeposit(bytes32 pubkey, uint256 depositAmount) public payable {
|
||||
require(depositAmount <= maximumDepositAmount, "amount is larger than maximumDepositAmount");
|
||||
require(token.transferFrom(msg.sender, address(this), depositAmount), "transfer failed");
|
||||
|
||||
require(depositAmount < FIELD_SIZE, "depositAmount should be inside the field");
|
||||
require(uint256(pubkey) < FIELD_SIZE, "pubkey should be inside the field");
|
||||
bytes32[3] memory input;
|
||||
input[0] = bytes32(depositAmount);
|
||||
input[1] = pubkey;
|
||||
input[2] = bytes32(0);
|
||||
bytes32 commitment = hasher3.poseidon(input);
|
||||
|
||||
bytes memory packedOutput = abi.encodePacked("abi", depositAmount, pubkey);
|
||||
|
||||
lastBalance = token.balanceOf(address(this));
|
||||
_insert(commitment, bytes32(0)); // use second empty commitment for better efficiency
|
||||
emit NewCommitment(commitment, nextIndex - 2, packedOutput);
|
||||
emit NewCommitment(bytes32(0), nextIndex - 1, abi.encodePacked("gap"));
|
||||
}
|
||||
|
||||
function register(Account memory _account) public {
|
||||
require(_account.owner == msg.sender, "only owner can be registered");
|
||||
_register(_account);
|
||||
|
6
contracts/interfaces/IHasher3.sol
Normal file
6
contracts/interfaces/IHasher3.sol
Normal file
@ -0,0 +1,6 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.7.0;
|
||||
|
||||
interface IHasher3 {
|
||||
function poseidon(bytes32[3] calldata inputs) external pure returns (bytes32);
|
||||
}
|
22
scripts/compileHasher3.js
Normal file
22
scripts/compileHasher3.js
Normal file
@ -0,0 +1,22 @@
|
||||
// Generates Hasher artifact at compile-time using external compilermechanism
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
const genContract = require('circomlib/src/poseidon_gencontract.js')
|
||||
const outputPath = path.join(__dirname, '..', 'artifacts', 'contracts')
|
||||
const outputFile = path.join(outputPath, 'Hasher3.json')
|
||||
|
||||
if (!fs.existsSync(outputPath)) {
|
||||
fs.mkdirSync(outputPath, { recursive: true })
|
||||
}
|
||||
|
||||
const contract = {
|
||||
_format: 'hh-sol-artifact-1',
|
||||
sourceName: 'contracts/Hasher.sol',
|
||||
linkReferences: {},
|
||||
deployedLinkReferences: {},
|
||||
contractName: 'Hasher3',
|
||||
abi: genContract.generateABI(3),
|
||||
bytecode: genContract.createCode(3),
|
||||
}
|
||||
|
||||
fs.writeFileSync(outputFile, JSON.stringify(contract, null, 2))
|
@ -24,6 +24,7 @@ async function generate(config = defaultConfig) {
|
||||
config.verifier16,
|
||||
config.MERKLE_TREE_HEIGHT,
|
||||
config.hasher,
|
||||
config.hasher3,
|
||||
config.gcWeth,
|
||||
config.gcOmniBridge,
|
||||
config.l1Unwrapper,
|
||||
|
@ -3,6 +3,7 @@ const { ethers, waffle } = hre
|
||||
const { loadFixture } = waffle
|
||||
const { expect } = require('chai')
|
||||
const { utils } = ethers
|
||||
const { BigNumber } = require('@ethersproject/bignumber')
|
||||
|
||||
const Utxo = require('../src/utxo')
|
||||
const { transaction, registerAndTransact, prepareTransaction, buildMerkleTree } = require('../src/index')
|
||||
@ -27,10 +28,12 @@ describe('TornadoPool', function () {
|
||||
|
||||
async function fixture() {
|
||||
require('../scripts/compileHasher')
|
||||
require('../scripts/compileHasher3')
|
||||
const [sender, gov, multisig] = await ethers.getSigners()
|
||||
const verifier2 = await deploy('Verifier2')
|
||||
const verifier16 = await deploy('Verifier16')
|
||||
const hasher = await deploy('Hasher')
|
||||
const hasher3 = await deploy('Hasher3')
|
||||
|
||||
const token = await deploy('PermittableToken', 'Wrapped ETH', 'WETH', 18, l1ChainId)
|
||||
await token.mint(sender.address, utils.parseEther('10000'))
|
||||
@ -59,6 +62,7 @@ describe('TornadoPool', function () {
|
||||
verifier16.address,
|
||||
MERKLE_TREE_HEIGHT,
|
||||
hasher.address,
|
||||
hasher3.address,
|
||||
token.address,
|
||||
omniBridge.address,
|
||||
l1Unwrapper.address,
|
||||
@ -175,6 +179,67 @@ describe('TornadoPool', function () {
|
||||
expect(registerEvent.args.key).to.be.equal(aliceDepositUtxo.keypair.address())
|
||||
})
|
||||
|
||||
it('should transparent deposit', async function () {
|
||||
let { tornadoPool, token } = await loadFixture(fixture)
|
||||
const sender = (await ethers.getSigners())[0]
|
||||
tornadoPool = tornadoPool.connect(sender)
|
||||
|
||||
// Alice deposits into tornado pool
|
||||
const aliceDepositAmount = utils.parseEther('0.1')
|
||||
const aliceKeypair = new Keypair()
|
||||
const alicePubkey = aliceKeypair.address().slice(0, 66)
|
||||
|
||||
await expect(() =>
|
||||
tornadoPool.transparentDeposit(alicePubkey, aliceDepositAmount),
|
||||
).to.changeTokenBalances(
|
||||
token,
|
||||
[sender, tornadoPool],
|
||||
[BigNumber.from(0).sub(aliceDepositAmount), aliceDepositAmount],
|
||||
)
|
||||
|
||||
const filter = tornadoPool.filters.NewCommitment()
|
||||
let fromBlock = await ethers.provider.getBlock()
|
||||
let events = await tornadoPool.queryFilter(filter, fromBlock.number)
|
||||
|
||||
const packedOutput = utils.solidityPack(
|
||||
['string', 'uint256', 'bytes32'],
|
||||
['abi', aliceDepositAmount, alicePubkey],
|
||||
)
|
||||
expect(events[0].args.encryptedOutput).to.be.equal(packedOutput)
|
||||
|
||||
const aliceDepositUtxo = new Utxo({
|
||||
amount: aliceDepositAmount,
|
||||
keypair: aliceKeypair,
|
||||
blinding: 0,
|
||||
index: 0,
|
||||
})
|
||||
|
||||
// Bob gives Alice address to send some eth inside the shielded pool
|
||||
const bobKeypair = new Keypair() // contains private and public keys
|
||||
const bobAddress = bobKeypair.address() // contains only public key
|
||||
|
||||
// Alice sends some funds to Bob
|
||||
const bobSendAmount = utils.parseEther('0.06')
|
||||
const bobSendUtxo = new Utxo({ amount: bobSendAmount, keypair: Keypair.fromString(bobAddress) })
|
||||
const aliceChangeUtxo = new Utxo({
|
||||
amount: aliceDepositAmount.sub(bobSendAmount),
|
||||
keypair: aliceDepositUtxo.keypair,
|
||||
})
|
||||
await transaction({ tornadoPool, inputs: [aliceDepositUtxo], outputs: [bobSendUtxo, aliceChangeUtxo] })
|
||||
|
||||
// Bob parses chain to detect incoming funds
|
||||
fromBlock = await ethers.provider.getBlock()
|
||||
events = await tornadoPool.queryFilter(filter, fromBlock.number)
|
||||
let bobReceiveUtxo
|
||||
try {
|
||||
bobReceiveUtxo = Utxo.decrypt(bobKeypair, events[0].args.encryptedOutput, events[0].args.index)
|
||||
} catch (e) {
|
||||
// we try to decrypt another output here because it shuffles outputs before sending to blockchain
|
||||
bobReceiveUtxo = Utxo.decrypt(bobKeypair, events[1].args.encryptedOutput, events[1].args.index)
|
||||
}
|
||||
expect(bobReceiveUtxo.amount).to.be.equal(bobSendAmount)
|
||||
})
|
||||
|
||||
it('should deposit, transact and withdraw', async function () {
|
||||
const { tornadoPool, token } = await loadFixture(fixture)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user