tornado-trees/contracts/TornadoTrees.sol

283 lines
11 KiB
Solidity
Raw Normal View History

2021-02-01 14:40:32 +01:00
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
2021-02-03 18:59:35 +01:00
import "./interfaces/ITornadoTreesV1.sol";
2021-02-10 18:45:19 +01:00
import "./interfaces/IBatchTreeUpdateVerifier.sol";
2021-02-26 12:01:34 +01:00
import "@openzeppelin/upgrades-core/contracts/Initializable.sol";
2021-02-01 14:40:32 +01:00
2021-03-06 13:08:11 +01:00
/// @dev This contract holds a merkle tree of all tornado cash deposit and withdrawal events
2021-02-26 12:01:34 +01:00
contract TornadoTrees is Initializable {
2021-02-01 14:40:32 +01:00
address public immutable governance;
bytes32 public depositRoot;
bytes32 public previousDepositRoot;
bytes32 public withdrawalRoot;
bytes32 public previousWithdrawalRoot;
address public tornadoProxy;
2021-02-10 18:45:19 +01:00
IBatchTreeUpdateVerifier public treeUpdateVerifier;
2021-02-05 00:13:34 +01:00
ITornadoTreesV1 public immutable tornadoTreesV1;
2021-02-01 14:40:32 +01:00
2021-02-26 09:17:13 +01:00
uint256 public constant CHUNK_TREE_HEIGHT = 8;
2021-02-01 14:40:32 +01:00
uint256 public constant CHUNK_SIZE = 2**CHUNK_TREE_HEIGHT;
uint256 public constant ITEM_SIZE = 32 + 20 + 4;
uint256 public constant BYTES_SIZE = 32 + 32 + 4 + CHUNK_SIZE * ITEM_SIZE;
uint256 public constant SNARK_FIELD = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
2021-02-04 18:47:47 +01:00
mapping(uint256 => bytes32) public deposits;
uint256 public depositsLength;
2021-02-01 14:40:32 +01:00
uint256 public lastProcessedDepositLeaf;
2021-02-18 16:50:19 +01:00
uint256 public immutable depositsV1Length;
2021-02-01 14:40:32 +01:00
2021-02-04 18:47:47 +01:00
mapping(uint256 => bytes32) public withdrawals;
uint256 public withdrawalsLength;
2021-02-01 14:40:32 +01:00
uint256 public lastProcessedWithdrawalLeaf;
2021-02-05 18:19:36 +01:00
uint256 public immutable withdrawalsV1Length;
2021-02-03 18:59:35 +01:00
2021-02-01 14:40:32 +01:00
event DepositData(address instance, bytes32 indexed hash, uint256 block, uint256 index);
event WithdrawalData(address instance, bytes32 indexed hash, uint256 block, uint256 index);
2021-03-20 22:45:14 +01:00
event VerifierUpdated(address newVerifier);
event ProxyUpdated(address newProxy);
2021-02-01 14:40:32 +01:00
struct TreeLeaf {
bytes32 hash;
address instance;
uint32 block;
}
modifier onlyTornadoProxy {
require(msg.sender == tornadoProxy, "Not authorized");
_;
}
modifier onlyGovernance() {
require(msg.sender == governance, "Only governance can perform this action");
_;
}
2021-02-06 14:13:22 +01:00
struct SearchParams {
2021-02-12 01:51:03 +01:00
uint256 depositsFrom;
uint256 depositsStep;
uint256 withdrawalsFrom;
uint256 withdrawalsStep;
2021-02-06 14:13:22 +01:00
}
2021-02-01 14:40:32 +01:00
constructor(
2021-02-03 18:59:35 +01:00
address _governance,
ITornadoTreesV1 _tornadoTreesV1,
2021-02-06 14:13:22 +01:00
SearchParams memory _searchParams
2021-02-01 14:40:32 +01:00
) public {
2021-02-03 18:59:35 +01:00
governance = _governance;
2021-02-05 00:13:34 +01:00
tornadoTreesV1 = _tornadoTreesV1;
2021-02-03 18:59:35 +01:00
2021-02-26 12:01:34 +01:00
depositsV1Length = findArrayLength(
2021-02-06 14:13:22 +01:00
_tornadoTreesV1,
"deposits(uint256)",
2021-02-12 01:51:03 +01:00
_searchParams.depositsFrom,
_searchParams.depositsStep
2021-02-06 14:13:22 +01:00
);
2021-02-03 18:59:35 +01:00
2021-02-26 12:01:34 +01:00
withdrawalsV1Length = findArrayLength(
2021-02-06 14:13:22 +01:00
_tornadoTreesV1,
"withdrawals(uint256)",
2021-02-12 01:51:03 +01:00
_searchParams.withdrawalsFrom,
_searchParams.withdrawalsStep
2021-02-06 14:13:22 +01:00
);
2021-02-05 18:19:36 +01:00
}
2021-02-26 12:01:34 +01:00
function initialize(address _tornadoProxy, IBatchTreeUpdateVerifier _treeUpdateVerifier) public initializer onlyGovernance {
tornadoProxy = _tornadoProxy;
treeUpdateVerifier = _treeUpdateVerifier;
depositRoot = tornadoTreesV1.depositRoot();
uint256 lastDepositLeaf = tornadoTreesV1.lastProcessedDepositLeaf();
require(lastDepositLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
lastProcessedDepositLeaf = lastDepositLeaf;
depositsLength = depositsV1Length;
withdrawalRoot = tornadoTreesV1.withdrawalRoot();
uint256 lastWithdrawalLeaf = tornadoTreesV1.lastProcessedWithdrawalLeaf();
require(lastWithdrawalLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
lastProcessedWithdrawalLeaf = lastWithdrawalLeaf;
withdrawalsLength = withdrawalsV1Length;
}
2021-03-20 21:48:26 +01:00
/// @dev Queue a new deposit data to be inserted into a merkle tree
2021-02-05 21:28:51 +01:00
function registerDeposit(address _instance, bytes32 _commitment) public onlyTornadoProxy {
2021-02-04 18:47:47 +01:00
uint256 _depositsLength = depositsLength;
deposits[_depositsLength] = keccak256(abi.encode(_instance, _commitment, blockNumber()));
2021-02-05 18:19:36 +01:00
emit DepositData(_instance, _commitment, blockNumber(), _depositsLength);
depositsLength = _depositsLength + 1;
2021-02-01 14:40:32 +01:00
}
2021-03-20 21:48:26 +01:00
/// @dev Queue a new withdrawal data to be inserted into a merkle tree
2021-02-05 21:28:51 +01:00
function registerWithdrawal(address _instance, bytes32 _nullifierHash) public onlyTornadoProxy {
2021-02-04 18:47:47 +01:00
uint256 _withdrawalsLength = withdrawalsLength;
withdrawals[_withdrawalsLength] = keccak256(abi.encode(_instance, _nullifierHash, blockNumber()));
2021-02-05 18:19:36 +01:00
emit WithdrawalData(_instance, _nullifierHash, blockNumber(), _withdrawalsLength);
withdrawalsLength = _withdrawalsLength + 1;
2021-02-01 14:40:32 +01:00
}
2021-03-20 21:48:26 +01:00
/// @dev Insert a full batch of queued deposits into a merkle tree
/// @param _proof A snark proof that elements were inserted correctly
/// @param _argsHash A hash of snark inputs
/// @param _currentRoot Current merkle tree root
2021-03-20 21:48:26 +01:00
/// @param _newRoot Updated merkle tree root
/// @param _pathIndices Merkle path to inserted batch
/// @param _events A batch of inserted events (leaves)
2021-02-01 14:40:32 +01:00
function updateDepositTree(
bytes calldata _proof,
bytes32 _argsHash,
bytes32 _currentRoot,
bytes32 _newRoot,
uint32 _pathIndices,
TreeLeaf[CHUNK_SIZE] calldata _events
2021-02-05 00:13:34 +01:00
) public {
2021-02-01 14:40:32 +01:00
uint256 offset = lastProcessedDepositLeaf;
require(_currentRoot == depositRoot, "Proposed deposit root is invalid");
2021-03-03 10:47:31 +01:00
require(_pathIndices == offset >> CHUNK_TREE_HEIGHT, "Incorrect deposit insert index");
2021-02-01 14:40:32 +01:00
bytes memory data = new bytes(BYTES_SIZE);
assembly {
mstore(add(data, 0x44), _pathIndices)
mstore(add(data, 0x40), _newRoot)
mstore(add(data, 0x20), _currentRoot)
}
for (uint256 i = 0; i < CHUNK_SIZE; i++) {
2021-02-02 20:46:51 +01:00
(bytes32 hash, address instance, uint32 blockNumber) = (_events[i].hash, _events[i].instance, _events[i].block);
bytes32 leafHash = keccak256(abi.encode(instance, hash, blockNumber));
2021-02-18 16:50:19 +01:00
bytes32 deposit = offset + i >= depositsV1Length ? deposits[offset + i] : tornadoTreesV1.deposits(offset + i);
2021-02-05 00:13:34 +01:00
require(leafHash == deposit, "Incorrect deposit");
2021-02-01 14:40:32 +01:00
assembly {
2021-03-22 18:16:14 +01:00
let itemOffset := add(data, mul(ITEM_SIZE, i))
mstore(add(itemOffset, 0x7c), blockNumber)
mstore(add(itemOffset, 0x78), instance)
mstore(add(itemOffset, 0x64), hash)
2021-02-01 14:40:32 +01:00
}
2021-02-18 16:50:19 +01:00
if (offset + i >= depositsV1Length) {
2021-02-05 00:13:34 +01:00
delete deposits[offset + i];
} else {
emit DepositData(instance, hash, blockNumber, offset + i);
}
2021-02-01 14:40:32 +01:00
}
uint256 argsHash = uint256(sha256(data)) % SNARK_FIELD;
require(argsHash == uint256(_argsHash), "Invalid args hash");
require(treeUpdateVerifier.verifyProof(_proof, [argsHash]), "Invalid deposit tree update proof");
previousDepositRoot = _currentRoot;
depositRoot = _newRoot;
lastProcessedDepositLeaf = offset + CHUNK_SIZE;
}
2021-03-20 21:48:26 +01:00
/// @dev Insert a full batch of queued withdrawals into a merkle tree
/// @param _proof A snark proof that elements were inserted correctly
/// @param _argsHash A hash of snark inputs
/// @param _currentRoot Current merkle tree root
2021-03-20 21:48:26 +01:00
/// @param _newRoot Updated merkle tree root
/// @param _pathIndices Merkle path to inserted batch
/// @param _events A batch of inserted events (leaves)
2021-02-01 14:40:32 +01:00
function updateWithdrawalTree(
bytes calldata _proof,
bytes32 _argsHash,
bytes32 _currentRoot,
bytes32 _newRoot,
2021-03-03 10:47:31 +01:00
uint32 _pathIndices,
2021-02-01 14:40:32 +01:00
TreeLeaf[CHUNK_SIZE] calldata _events
2021-02-05 00:13:34 +01:00
) public {
2021-02-01 14:40:32 +01:00
uint256 offset = lastProcessedWithdrawalLeaf;
require(_currentRoot == withdrawalRoot, "Proposed withdrawal root is invalid");
2021-03-03 10:47:31 +01:00
require(_pathIndices == offset >> CHUNK_TREE_HEIGHT, "Incorrect withdrawal insert index");
2021-02-01 14:40:32 +01:00
bytes memory data = new bytes(BYTES_SIZE);
assembly {
mstore(add(data, 0x44), _pathIndices)
mstore(add(data, 0x40), _newRoot)
mstore(add(data, 0x20), _currentRoot)
}
for (uint256 i = 0; i < CHUNK_SIZE; i++) {
2021-02-02 20:46:51 +01:00
(bytes32 hash, address instance, uint32 blockNumber) = (_events[i].hash, _events[i].instance, _events[i].block);
bytes32 leafHash = keccak256(abi.encode(instance, hash, blockNumber));
2021-02-05 18:19:36 +01:00
bytes32 withdrawal = offset + i >= withdrawalsV1Length ? withdrawals[offset + i] : tornadoTreesV1.withdrawals(offset + i);
2021-02-05 00:13:34 +01:00
require(leafHash == withdrawal, "Incorrect withdrawal");
2021-02-01 14:40:32 +01:00
assembly {
2021-03-22 18:16:14 +01:00
let itemOffset := add(data, mul(ITEM_SIZE, i))
mstore(add(itemOffset, 0x7c), blockNumber)
mstore(add(itemOffset, 0x78), instance)
mstore(add(itemOffset, 0x64), hash)
2021-02-01 14:40:32 +01:00
}
2021-02-05 18:19:36 +01:00
if (offset + i >= withdrawalsV1Length) {
2021-02-05 00:13:34 +01:00
delete withdrawals[offset + i];
} else {
emit WithdrawalData(instance, hash, blockNumber, offset + i);
}
2021-02-01 14:40:32 +01:00
}
uint256 argsHash = uint256(sha256(data)) % SNARK_FIELD;
require(argsHash == uint256(_argsHash), "Invalid args hash");
require(treeUpdateVerifier.verifyProof(_proof, [argsHash]), "Invalid withdrawal tree update proof");
previousWithdrawalRoot = _currentRoot;
withdrawalRoot = _newRoot;
lastProcessedWithdrawalLeaf = offset + CHUNK_SIZE;
}
function validateRoots(bytes32 _depositRoot, bytes32 _withdrawalRoot) public view {
require(_depositRoot == depositRoot || _depositRoot == previousDepositRoot, "Incorrect deposit tree root");
require(_withdrawalRoot == withdrawalRoot || _withdrawalRoot == previousWithdrawalRoot, "Incorrect withdrawal tree root");
}
2021-03-06 13:05:54 +01:00
/// @dev There is no array length getter for deposit and withdrawal arrays
2021-03-06 13:08:11 +01:00
/// in the previous contract, so we have to find them length manually.
2021-03-06 13:05:54 +01:00
/// Used only during deployment
function findArrayLength(
ITornadoTreesV1 _tornadoTreesV1,
string memory _type,
uint256 _from, // most likely array length after the proposal has passed
uint256 _step // optimal step size to find first match, approximately equals dispersion
2021-03-22 18:25:42 +01:00
) internal view virtual returns (uint256) {
2021-03-06 13:05:54 +01:00
// Find the segment with correct array length
bool direction = elementExists(_tornadoTreesV1, _type, _from);
do {
_from = direction ? _from + _step : _from - _step;
} while (direction == elementExists(_tornadoTreesV1, _type, _from));
uint256 high = direction ? _from : _from + _step;
uint256 low = direction ? _from - _step : _from;
uint256 mid = (high + low) / 2;
// Perform a binary search in this segment
while (low < mid) {
if (elementExists(_tornadoTreesV1, _type, mid)) {
low = mid;
} else {
high = mid;
}
mid = (low + high) / 2;
}
return mid + 1;
}
function elementExists(
ITornadoTreesV1 _tornadoTreesV1,
string memory _type,
uint256 index
) public view returns (bool success) {
// Try to get the element. If it succeeds the array length is higher, it it reverts the length is equal or lower
(success, ) = address(_tornadoTreesV1).staticcall{ gas: 2500 }(abi.encodeWithSignature(_type, index));
}
2021-02-01 14:40:32 +01:00
function setTornadoProxyContract(address _tornadoProxy) external onlyGovernance {
tornadoProxy = _tornadoProxy;
2021-03-20 22:45:14 +01:00
emit ProxyUpdated(_tornadoProxy);
2021-02-01 14:40:32 +01:00
}
2021-02-10 18:45:19 +01:00
function setVerifierContract(IBatchTreeUpdateVerifier _treeUpdateVerifier) external onlyGovernance {
2021-02-03 18:59:35 +01:00
treeUpdateVerifier = _treeUpdateVerifier;
2021-03-20 22:45:14 +01:00
emit VerifierUpdated(address(_treeUpdateVerifier));
2021-02-03 18:59:35 +01:00
}
2021-02-01 14:40:32 +01:00
function blockNumber() public view virtual returns (uint256) {
return block.number;
}
}