tornado-core/contracts/MerkleTreeWithHistory.sol

130 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-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;
(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);
(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-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
}
}