From 3ad634594eef7fb9e894033492110633614778ad Mon Sep 17 00:00:00 2001 From: poma Date: Thu, 11 Mar 2021 22:51:09 +0300 Subject: [PATCH] optimize sloads --- contracts/MerkleTreeWithHistory.sol | 57 ++++++++++++++--------------- contracts/Tornado.sol | 13 ++----- test/MerkleTreeWithHistory.test.js | 4 +- 3 files changed, 33 insertions(+), 41 deletions(-) diff --git a/contracts/MerkleTreeWithHistory.sol b/contracts/MerkleTreeWithHistory.sol index 52a8b90..87238fa 100644 --- a/contracts/MerkleTreeWithHistory.sol +++ b/contracts/MerkleTreeWithHistory.sol @@ -20,36 +20,35 @@ contract MerkleTreeWithHistory { uint256 public constant FIELD_SIZE = 21888242871839275222246405745257275088548364400416034343698204186575808495617; uint256 public constant ZERO_VALUE = 21663839004416932945382355908790599225266501822907911457504978515578255421292; // = keccak256("tornado") % FIELD_SIZE - uint32 public levels; + IHasher public immutable hasher; + uint32 public immutable levels; // the following variables are made public for easier testing and debugging and // are not supposed to be accessed in regular code - bytes32[] public filledSubtrees; - bytes32[] public zeros; + + // filledSubtrees, zeros, and roots could be bytes32[size], but using mappings makes it cheaper because + // it removes index range check on every interaction + mapping(uint256 => bytes32) public filledSubtrees; + mapping(uint256 => bytes32) public zeros; + mapping(uint256 => bytes32) public roots; + uint32 public constant ROOT_HISTORY_SIZE = 30; uint32 public currentRootIndex = 0; uint32 public nextIndex = 0; - uint32 public constant ROOT_HISTORY_SIZE = 100; - bytes32[ROOT_HISTORY_SIZE] public roots; - IHasher public immutable hasher; - - constructor(uint32 _treeLevels, IHasher _hasher) public { - require(_treeLevels > 0, "_treeLevels should be greater than zero"); - require(_treeLevels < 32, "_treeLevels should be less than 32"); - levels = _treeLevels; + constructor(uint32 _levels, IHasher _hasher) public { + require(_levels > 0, "_levels should be greater than zero"); + require(_levels < 32, "_levels should be less than 32"); + levels = _levels; hasher = _hasher; bytes32 currentZero = bytes32(ZERO_VALUE); - zeros.push(currentZero); - filledSubtrees.push(currentZero); - - for (uint32 i = 1; i < levels; i++) { + for (uint32 i = 0; i < _levels; i++) { + zeros[i] = currentZero; + filledSubtrees[i] = currentZero; currentZero = hashLeftRight(_hasher, currentZero, currentZero); - zeros.push(currentZero); - filledSubtrees.push(currentZero); } - roots[0] = hashLeftRight(_hasher, currentZero, currentZero); + roots[0] = currentZero; } /** @@ -71,9 +70,9 @@ contract MerkleTreeWithHistory { } function _insert(bytes32 _leaf) internal returns (uint32 index) { - uint32 currentIndex = nextIndex; - require(currentIndex != uint32(2)**levels, "Merkle tree is full. No more leafs can be added"); - nextIndex += 1; + uint32 _nextIndex = nextIndex; + require(_nextIndex != uint32(2)**levels, "Merkle tree is full. No more leaves can be added"); + uint32 currentIndex = _nextIndex; bytes32 currentLevelHash = _leaf; bytes32 left; bytes32 right; @@ -82,21 +81,20 @@ contract MerkleTreeWithHistory { if (currentIndex % 2 == 0) { left = currentLevelHash; right = zeros[i]; - filledSubtrees[i] = currentLevelHash; } else { left = filledSubtrees[i]; right = currentLevelHash; } - currentLevelHash = hashLeftRight(hasher, left, right); - currentIndex /= 2; } - currentRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE; - roots[currentRootIndex] = currentLevelHash; - return nextIndex - 1; + uint32 newRootIndex = (currentRootIndex + 1) % ROOT_HISTORY_SIZE; + currentRootIndex = newRootIndex; + roots[newRootIndex] = currentLevelHash; + nextIndex = _nextIndex + 1; + return _nextIndex; } /** @@ -106,7 +104,8 @@ contract MerkleTreeWithHistory { if (_root == 0) { return false; } - uint32 i = currentRootIndex; + uint32 _currentRootIndex = currentRootIndex; + uint32 i = _currentRootIndex; do { if (_root == roots[i]) { return true; @@ -115,7 +114,7 @@ contract MerkleTreeWithHistory { i = ROOT_HISTORY_SIZE; } i--; - } while (i != currentRootIndex); + } while (i != _currentRootIndex); return false; } diff --git a/contracts/Tornado.sol b/contracts/Tornado.sol index baed866..50b0a48 100644 --- a/contracts/Tornado.sol +++ b/contracts/Tornado.sol @@ -20,19 +20,12 @@ interface IVerifier { } abstract contract Tornado is MerkleTreeWithHistory, ReentrancyGuard { - uint256 public denomination; + IVerifier public immutable verifier; + uint256 public immutable denomination; + mapping(bytes32 => bool) public nullifierHashes; // we store all commitments just to prevent accidental deposits with the same commitment mapping(bytes32 => bool) public commitments; - IVerifier public immutable verifier; - - // operator can update snark verification key - // after the final trusted setup ceremony operator rights are supposed to be transferred to zero address - address public operator; - modifier onlyOperator { - require(msg.sender == operator, "Only operator can call this function."); - _; - } event Deposit(bytes32 indexed commitment, uint32 leafIndex, uint256 timestamp); event Withdrawal(address to, bytes32 nullifierHash, address indexed relayer, uint256 fee); diff --git a/test/MerkleTreeWithHistory.test.js b/test/MerkleTreeWithHistory.test.js index 067b0ec..0be19f6 100644 --- a/test/MerkleTreeWithHistory.test.js +++ b/test/MerkleTreeWithHistory.test.js @@ -159,10 +159,10 @@ contract('MerkleTreeWithHistory', (accounts) => { } let error = await merkleTreeWithHistory.insert(toFixedHex(1337)).should.be.rejected - error.reason.should.be.equal('Merkle tree is full. No more leafs can be added') + error.reason.should.be.equal('Merkle tree is full. No more leaves can be added') error = await merkleTreeWithHistory.insert(toFixedHex(1)).should.be.rejected - error.reason.should.be.equal('Merkle tree is full. No more leafs can be added') + error.reason.should.be.equal('Merkle tree is full. No more leaves can be added') }) it.skip('hasher gas', async () => {