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;
|
|
|
|
|
2019-10-04 08:12:22 -04:00
|
|
|
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-11-02 08:35:22 -04:00
|
|
|
uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
|
|
|
|
uint256 public constant ZERO_VALUE = 5702960885942360421128284892092891246826997279710054143430547229469817701242; // = MiMC("tornado")
|
2019-07-09 09:05:30 -04:00
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
uint256 public constant ROOT_HISTORY_SIZE = 100;
|
|
|
|
uint256[ROOT_HISTORY_SIZE] public roots;
|
|
|
|
uint256 public currentRootIndex = 0;
|
2019-07-09 09:05:30 -04:00
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
uint256 public levels;
|
|
|
|
uint32 public nextIndex = 0;
|
|
|
|
uint256[] public filledSubtrees;
|
|
|
|
uint256[] public zeros;
|
2019-07-09 09:05:30 -04:00
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
constructor(uint256 _treeLevels) public {
|
|
|
|
require(_treeLevels > 0, "_treeLevels should be greater than zero");
|
|
|
|
levels = _treeLevels;
|
2019-07-09 09:05:30 -04:00
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
uint256 currentZero = ZERO_VALUE;
|
|
|
|
zeros.push(ZERO_VALUE);
|
|
|
|
filledSubtrees.push(currentZero);
|
2019-07-09 09:05:30 -04:00
|
|
|
|
|
|
|
for (uint8 i = 1; i < levels; i++) {
|
2019-11-02 09:04:17 -04:00
|
|
|
currentZero = hashLeftRight(currentZero, currentZero);
|
|
|
|
zeros.push(currentZero);
|
|
|
|
filledSubtrees.push(currentZero);
|
2019-07-09 09:05:30 -04:00
|
|
|
}
|
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
roots[0] = hashLeftRight(currentZero, currentZero);
|
2019-07-09 09:05:30 -04:00
|
|
|
}
|
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
function hashLeftRight(uint256 _left, uint256 _right) public pure returns (uint256 hash) {
|
2019-11-02 09:24:17 -04:00
|
|
|
// those checks should never trigger in practice, because they're already performed by the snark verifier
|
|
|
|
// added for convenience if someone decides to call this function directly
|
|
|
|
require(_left < FIELD_SIZE, "_left should be inside the field");
|
|
|
|
require(_right < FIELD_SIZE, "_right should be inside the field");
|
|
|
|
uint256 R = _left;
|
2019-07-09 09:05:30 -04:00
|
|
|
uint256 C = 0;
|
|
|
|
|
2019-10-04 08:12:22 -04:00
|
|
|
(R, C) = Hasher.MiMCSponge(R, C, 0);
|
2019-07-09 09:05:30 -04:00
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
R = addmod(R, _right, FIELD_SIZE);
|
2019-10-04 08:12:22 -04:00
|
|
|
(R, C) = Hasher.MiMCSponge(R, C, 0);
|
2019-07-09 09:05:30 -04:00
|
|
|
|
2019-11-02 06:19:06 -04:00
|
|
|
return R;
|
2019-07-09 09:05:30 -04:00
|
|
|
}
|
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
function _insert(uint256 _leaf) internal returns(uint256 index) {
|
|
|
|
uint32 currentIndex = nextIndex;
|
|
|
|
require(currentIndex != 2**levels, "Merkle tree is full. No more leafs can be added");
|
|
|
|
nextIndex += 1;
|
|
|
|
uint256 currentLevelHash = _leaf;
|
2019-07-09 09:05:30 -04:00
|
|
|
uint256 left;
|
|
|
|
uint256 right;
|
|
|
|
|
2019-07-16 07:04:14 -04:00
|
|
|
for (uint256 i = 0; i < levels; i++) {
|
2019-11-02 09:04:17 -04:00
|
|
|
if (currentIndex % 2 == 0) {
|
|
|
|
left = currentLevelHash;
|
|
|
|
right = zeros[i];
|
2019-07-09 09:05:30 -04:00
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
filledSubtrees[i] = currentLevelHash;
|
2019-07-09 09:05:30 -04:00
|
|
|
} else {
|
2019-11-02 09:04:17 -04:00
|
|
|
left = filledSubtrees[i];
|
|
|
|
right = currentLevelHash;
|
2019-07-09 09:05:30 -04:00
|
|
|
}
|
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
currentLevelHash = hashLeftRight(left, right);
|
2019-07-09 09:05:30 -04:00
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
currentIndex /= 2;
|
2019-07-09 09:05:30 -04:00
|
|
|
}
|
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
currentRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE;
|
|
|
|
roots[currentRootIndex] = currentLevelHash;
|
|
|
|
return nextIndex - 1;
|
2019-07-09 09:05:30 -04:00
|
|
|
}
|
|
|
|
|
2019-11-02 09:04:17 -04:00
|
|
|
function isKnownRoot(uint256 _root) public view returns(bool) {
|
|
|
|
if (_root == 0) {
|
2019-07-09 09:05:30 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// search most recent first
|
|
|
|
uint256 i;
|
2019-11-02 09:04:17 -04:00
|
|
|
for(i = currentRootIndex; i < 2**256 - 1; i--) {
|
|
|
|
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-11-02 09:04:17 -04:00
|
|
|
for(i = ROOT_HISTORY_SIZE - 1; i > currentRootIndex; i--) {
|
|
|
|
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
|
2019-11-02 09:04:17 -04:00
|
|
|
// uint256 i = currentRootIndex;
|
2019-07-15 11:33:46 -04:00
|
|
|
// do {
|
2019-11-02 09:04:17 -04:00
|
|
|
// if (root == roots[i]) {
|
2019-07-15 11:33:46 -04:00
|
|
|
// return true;
|
|
|
|
// }
|
|
|
|
// if (i == 0) {
|
|
|
|
// i = ROOT_HISTORY_SIZE;
|
|
|
|
// }
|
|
|
|
// i--;
|
2019-11-02 09:04:17 -04:00
|
|
|
// } while (i != currentRootIndex);
|
2019-07-09 09:05:30 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function getLastRoot() public view returns(uint256) {
|
2019-11-02 09:04:17 -04:00
|
|
|
return roots[currentRootIndex];
|
2019-07-09 09:05:30 -04:00
|
|
|
}
|
|
|
|
}
|