tornado-core/contracts/MerkleTreeWithHistory.sol

138 lines
4.1 KiB
Solidity
Raw Normal View History

2019-08-02 13:12:30 -04:00
// https://tornado.cash
/*
* d888888P dP a88888b. dP
* 88 88 d8' `88 88
* 88 .d8888b. 88d888b. 88d888b. .d8888b. .d888b88 .d8888b. 88 .d8888b. .d8888b. 88d888b.
* 88 88' `88 88' `88 88' `88 88' `88 88' `88 88' `88 88 88' `88 Y8ooooo. 88' `88
* 88 88. .88 88 88 88 88. .88 88. .88 88. .88 dP Y8. .88 88. .88 88 88 88
* dP `88888P' dP dP dP `88888P8 `88888P8 `88888P' 88 Y88888P' `88888P8 `88888P' dP dP
* ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
*/
2019-07-09 09:05:30 -04:00
pragma solidity ^0.5.8;
library Hasher {
2019-07-09 09:05:30 -04:00
function MiMCSponge(uint256 in_xL, uint256 in_xR, uint256 in_k) public pure returns (uint256 xL, uint256 xR);
}
contract MerkleTreeWithHistory {
2019-07-16 07:04:14 -04:00
uint256 public levels;
2019-07-09 09:05:30 -04:00
uint256 constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
2019-10-31 21:30:07 -04:00
uint256 constant ROOT_HISTORY_SIZE = 100;
2019-10-31 21:32:26 -04:00
uint256[ROOT_HISTORY_SIZE] public _roots;
2019-10-31 21:33:02 -04:00
uint256 public current_root_index = 0;
2019-07-09 09:05:30 -04:00
2019-07-10 12:58:21 -04:00
uint256[] private _filled_subtrees;
uint256[] private _zeros;
2019-07-09 09:05:30 -04:00
uint32 public next_index = 0;
2019-07-16 07:04:14 -04:00
constructor(uint256 tree_levels, uint256 zero_value) public {
require(tree_levels > 0, "tree_levels should be greater than zero");
2019-07-09 09:05:30 -04:00
levels = tree_levels;
uint256 current_zero = zero_value;
2019-07-10 12:58:21 -04:00
_zeros.push(zero_value);
_filled_subtrees.push(current_zero);
2019-07-09 09:05:30 -04:00
for (uint8 i = 1; i < levels; i++) {
current_zero = hashLeftRight(current_zero, current_zero);
_zeros.push(current_zero);
_filled_subtrees.push(current_zero);
2019-07-09 09:05:30 -04:00
}
_roots[0] = hashLeftRight(current_zero, current_zero);
2019-07-09 09:05:30 -04:00
}
function hashLeftRight(uint256 left, uint256 right) public pure returns (uint256 hash) {
uint256 R = left; // left is already checked to be less than field_size by snark verifier
2019-07-09 09:05:30 -04:00
uint256 C = 0;
(R, C) = Hasher.MiMCSponge(R, C, 0);
2019-07-09 09:05:30 -04:00
R = addmod(R, right, FIELD_SIZE);
(R, C) = Hasher.MiMCSponge(R, C, 0);
2019-07-09 09:05:30 -04:00
return R;
2019-07-09 09:05:30 -04:00
}
2019-10-31 21:07:11 -04:00
function _insert(uint256 leaf) internal returns(uint256 index) {
2019-07-09 09:05:30 -04:00
uint32 current_index = next_index;
2019-09-16 06:07:14 -04:00
require(current_index != 2**levels, "Merkle tree is full. No more leafs can be added");
2019-07-09 09:05:30 -04:00
next_index += 1;
uint256 current_level_hash = leaf;
uint256 left;
uint256 right;
2019-07-16 07:04:14 -04:00
for (uint256 i = 0; i < levels; i++) {
2019-07-09 09:05:30 -04:00
if (current_index % 2 == 0) {
left = current_level_hash;
2019-07-10 12:58:21 -04:00
right = _zeros[i];
2019-07-09 09:05:30 -04:00
2019-07-10 12:58:21 -04:00
_filled_subtrees[i] = current_level_hash;
2019-07-09 09:05:30 -04:00
} else {
2019-07-10 12:58:21 -04:00
left = _filled_subtrees[i];
2019-07-09 09:05:30 -04:00
right = current_level_hash;
}
2019-07-10 12:58:21 -04:00
current_level_hash = hashLeftRight(left, right);
2019-07-09 09:05:30 -04:00
current_index /= 2;
}
2019-10-31 21:33:02 -04:00
current_root_index = (current_root_index + 1) % ROOT_HISTORY_SIZE;
_roots[current_root_index] = current_level_hash;
2019-10-31 21:07:11 -04:00
return next_index - 1;
2019-07-09 09:05:30 -04:00
}
2019-07-16 07:04:14 -04:00
function isKnownRoot(uint256 root) public view returns(bool) {
2019-07-10 12:58:21 -04:00
if (root == 0) {
2019-07-09 09:05:30 -04:00
return false;
}
// search most recent first
uint256 i;
2019-10-31 21:33:02 -04:00
for(i = current_root_index; i < 2**256 - 1; i--) {
2019-07-10 12:58:21 -04:00
if (root == _roots[i]) {
2019-07-09 09:05:30 -04:00
return true;
}
}
2019-07-15 11:33:46 -04:00
// process the rest of roots
2019-10-31 21:33:02 -04:00
for(i = ROOT_HISTORY_SIZE - 1; i > current_root_index; i--) {
2019-07-10 12:58:21 -04:00
if (root == _roots[i]) {
2019-07-09 09:05:30 -04:00
return true;
}
}
return false;
2019-07-15 11:33:46 -04:00
// or we can do that in other way
// uint256 i = _current_root;
// do {
// if (root == _roots[i]) {
// return true;
// }
// if (i == 0) {
// i = ROOT_HISTORY_SIZE;
// }
// i--;
// } while (i != _current_root);
2019-07-09 09:05:30 -04:00
}
function getLastRoot() public view returns(uint256) {
2019-10-31 21:33:02 -04:00
return _roots[current_root_index];
2019-07-10 12:58:21 -04:00
}
2019-10-31 21:31:57 -04:00
function roots() public view returns(uint256[ROOT_HISTORY_SIZE] memory) {
2019-07-10 12:58:21 -04:00
return _roots;
}
function filled_subtrees() public view returns(uint256[] memory) {
return _filled_subtrees;
}
function zeros() public view returns(uint256[] memory) {
return _zeros;
2019-07-09 09:05:30 -04:00
}
}