tornado-trees/contracts/TornadoTrees.sol

230 lines
8.7 KiB
Solidity
Raw Normal View History

2021-02-01 16:40:32 +03:00
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
import "torn-token/contracts/ENS.sol";
2021-02-03 20:59:35 +03:00
import "./interfaces/ITornadoTreesV1.sol";
2021-02-01 16:40:32 +03:00
import "./interfaces/IVerifier.sol";
2021-02-04 17:17:21 +03:00
import "hardhat/console.sol";
2021-02-03 20:59:35 +03:00
contract TornadoTrees is EnsResolve {
2021-02-01 16:40:32 +03:00
address public immutable governance;
bytes32 public depositRoot;
bytes32 public previousDepositRoot;
bytes32 public withdrawalRoot;
bytes32 public previousWithdrawalRoot;
address public tornadoProxy;
2021-02-03 20:59:35 +03:00
IVerifier public treeUpdateVerifier;
2021-02-05 02:13:34 +03:00
ITornadoTreesV1 public immutable tornadoTreesV1;
2021-02-01 16:40:32 +03:00
2021-02-02 14:20:59 +03:00
// make sure CHUNK_TREE_HEIGHT has the same value in BatchTreeUpdate.circom
uint256 public constant CHUNK_TREE_HEIGHT = 2;
2021-02-01 16:40:32 +03: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 20:47:47 +03:00
mapping(uint256 => bytes32) public deposits;
uint256 public depositsLength;
2021-02-01 16:40:32 +03:00
uint256 public lastProcessedDepositLeaf;
2021-02-05 02:13:34 +03:00
uint256 public immutable lastV1Deposit;
2021-02-01 16:40:32 +03:00
2021-02-04 20:47:47 +03:00
mapping(uint256 => bytes32) public withdrawals;
uint256 public withdrawalsLength;
2021-02-01 16:40:32 +03:00
uint256 public lastProcessedWithdrawalLeaf;
2021-02-05 02:13:34 +03:00
uint256 public immutable lastV1Withdrawal;
2021-02-03 20:59:35 +03:00
2021-02-01 16:40:32 +03:00
event DepositData(address instance, bytes32 indexed hash, uint256 block, uint256 index);
event WithdrawalData(address instance, bytes32 indexed hash, uint256 block, uint256 index);
struct TreeLeaf {
bytes32 hash;
address instance;
uint32 block;
}
struct Batch {
bytes32 oldRoot;
bytes32 newRoot;
uint8 pathIndices;
TreeLeaf[CHUNK_SIZE] events;
}
modifier onlyTornadoProxy {
require(msg.sender == tornadoProxy, "Not authorized");
_;
}
modifier onlyGovernance() {
require(msg.sender == governance, "Only governance can perform this action");
_;
}
constructor(
2021-02-03 20:59:35 +03:00
address _governance,
address _tornadoProxy,
ITornadoTreesV1 _tornadoTreesV1,
IVerifier _treeUpdateVerifier
2021-02-01 16:40:32 +03:00
) public {
2021-02-03 20:59:35 +03:00
governance = _governance;
tornadoProxy = _tornadoProxy;
treeUpdateVerifier = _treeUpdateVerifier;
2021-02-05 02:13:34 +03:00
tornadoTreesV1 = _tornadoTreesV1;
2021-02-03 20:59:35 +03:00
depositRoot = _tornadoTreesV1.depositRoot();
withdrawalRoot = _tornadoTreesV1.withdrawalRoot();
2021-02-04 17:17:21 +03:00
uint256 depositLeaf = _tornadoTreesV1.lastProcessedDepositLeaf();
require(depositLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
lastProcessedDepositLeaf = depositLeaf;
2021-02-05 02:16:40 +03:00
depositsLength = lastV1Deposit = 1; // todo
2021-02-03 20:59:35 +03:00
2021-02-04 17:17:21 +03:00
uint256 withdrawalLeaf = _tornadoTreesV1.lastProcessedWithdrawalLeaf();
require(withdrawalLeaf % CHUNK_SIZE == 0, "Incorrect TornadoTrees state");
lastProcessedWithdrawalLeaf = withdrawalLeaf;
2021-02-05 02:16:40 +03:00
withdrawalsLength = lastV1Withdrawal = 1; // todo
2021-02-01 16:40:32 +03:00
}
2021-02-05 02:13:34 +03:00
function registerDeposit(address _instance, bytes32 _commitment) external onlyTornadoProxy {
2021-02-04 20:47:47 +03:00
uint256 _depositsLength = depositsLength;
deposits[_depositsLength] = keccak256(abi.encode(_instance, _commitment, blockNumber()));
emit DepositData(_instance, _commitment, blockNumber(), _depositsLength - 1);
2021-02-01 16:40:32 +03:00
}
2021-02-05 02:13:34 +03:00
function registerWithdrawal(address _instance, bytes32 _nullifierHash) external onlyTornadoProxy {
2021-02-04 20:47:47 +03:00
uint256 _withdrawalsLength = withdrawalsLength;
withdrawals[_withdrawalsLength] = keccak256(abi.encode(_instance, _nullifierHash, blockNumber()));
emit WithdrawalData(_instance, _nullifierHash, blockNumber(), _withdrawalsLength - 1);
2021-02-01 16:40:32 +03:00
}
function updateDepositTree(
bytes calldata _proof,
bytes32 _argsHash,
bytes32 _currentRoot,
bytes32 _newRoot,
uint32 _pathIndices,
TreeLeaf[CHUNK_SIZE] calldata _events
2021-02-05 02:13:34 +03:00
) public {
2021-02-01 16:40:32 +03:00
uint256 offset = lastProcessedDepositLeaf;
require(_newRoot != previousDepositRoot, "Outdated deposit root");
require(_currentRoot == depositRoot, "Proposed deposit root is invalid");
require(_pathIndices == offset >> CHUNK_TREE_HEIGHT, "Incorrect insert index");
bytes memory data = new bytes(BYTES_SIZE);
assembly {
mstore(add(data, 0x44), _pathIndices)
mstore(add(data, 0x40), _newRoot)
mstore(add(data, 0x20), _currentRoot)
}
2021-02-05 02:13:34 +03:00
uint256 _lastV1Deposit = lastV1Deposit;
2021-02-01 16:40:32 +03:00
for (uint256 i = 0; i < CHUNK_SIZE; i++) {
2021-02-02 22:46:51 +03: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 02:13:34 +03:00
bytes32 deposit = offset + i > _lastV1Deposit ? deposits[offset + i] : tornadoTreesV1.deposits(offset + i);
require(leafHash == deposit, "Incorrect deposit");
2021-02-01 16:40:32 +03:00
assembly {
2021-02-02 22:46:51 +03:00
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), blockNumber)
2021-02-01 16:40:32 +03:00
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x78), instance)
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x64), hash)
}
2021-02-05 02:13:34 +03:00
if (offset + i > _lastV1Deposit) {
delete deposits[offset + i];
} else {
emit DepositData(instance, hash, blockNumber, offset + i);
}
2021-02-01 16:40:32 +03: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;
}
function updateWithdrawalTree(
bytes calldata _proof,
bytes32 _argsHash,
bytes32 _currentRoot,
bytes32 _newRoot,
uint256 _pathIndices,
TreeLeaf[CHUNK_SIZE] calldata _events
2021-02-05 02:13:34 +03:00
) public {
2021-02-01 16:40:32 +03:00
uint256 offset = lastProcessedWithdrawalLeaf;
require(_newRoot != previousWithdrawalRoot, "Outdated withdrawal root");
require(_currentRoot == withdrawalRoot, "Proposed withdrawal root is invalid");
require(_pathIndices == offset >> CHUNK_TREE_HEIGHT, "Incorrect insert index");
require(uint256(_newRoot) < SNARK_FIELD, "Proposed root is out of range");
bytes memory data = new bytes(BYTES_SIZE);
assembly {
mstore(add(data, 0x44), _pathIndices)
mstore(add(data, 0x40), _newRoot)
mstore(add(data, 0x20), _currentRoot)
}
2021-02-05 02:13:34 +03:00
uint256 _lastV1Withdrawal = lastV1Withdrawal;
2021-02-01 16:40:32 +03:00
for (uint256 i = 0; i < CHUNK_SIZE; i++) {
2021-02-02 22:46:51 +03: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 02:13:34 +03:00
bytes32 withdrawal = offset + i > _lastV1Withdrawal ? withdrawals[offset + i] : tornadoTreesV1.withdrawals(offset + i);
require(leafHash == withdrawal, "Incorrect withdrawal");
2021-02-01 16:40:32 +03:00
require(uint256(hash) < SNARK_FIELD, "Hash out of range");
assembly {
2021-02-02 22:46:51 +03:00
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x7c), blockNumber)
2021-02-01 16:40:32 +03:00
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x78), instance)
mstore(add(add(data, mul(ITEM_SIZE, i)), 0x64), hash)
}
2021-02-05 02:13:34 +03:00
if (offset + i > _lastV1Withdrawal) {
delete withdrawals[offset + i];
} else {
emit WithdrawalData(instance, hash, blockNumber, offset + i);
}
2021-02-01 16:40:32 +03: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-02-04 20:47:47 +03:00
function getRegisteredDeposits() external view returns (bytes32[] memory _deposits) {
uint256 count = depositsLength - lastProcessedDepositLeaf;
2021-02-01 16:40:32 +03:00
_deposits = new bytes32[](count);
for (uint256 i = 0; i < count; i++) {
_deposits[i] = deposits[lastProcessedDepositLeaf + i];
}
}
2021-02-04 20:47:47 +03:00
function getRegisteredWithdrawals() external view returns (bytes32[] memory _withdrawals) {
uint256 count = withdrawalsLength - lastProcessedWithdrawalLeaf;
2021-02-01 16:40:32 +03:00
_withdrawals = new bytes32[](count);
for (uint256 i = 0; i < count; i++) {
_withdrawals[i] = withdrawals[lastProcessedWithdrawalLeaf + i];
}
}
function setTornadoProxyContract(address _tornadoProxy) external onlyGovernance {
tornadoProxy = _tornadoProxy;
}
2021-02-03 20:59:35 +03:00
function setVerifierContract(IVerifier _treeUpdateVerifier) external onlyGovernance {
treeUpdateVerifier = _treeUpdateVerifier;
}
2021-02-01 16:40:32 +03:00
function blockNumber() public view virtual returns (uint256) {
return block.number;
}
}