Merge pull request #28 from 0xbok/gas

refactor(incremental-merkle-tree.sol): gas optimizations
This commit is contained in:
cedoor
2022-07-21 11:13:18 +02:00
committed by GitHub
2 changed files with 78 additions and 26 deletions

View File

@@ -6,7 +6,7 @@ import {PoseidonT3} from "./Hashes.sol";
// Each incremental tree has certain properties and data that will
// be used to add new leaves.
struct IncrementalTreeData {
uint8 depth; // Depth of the tree (levels - 1).
uint256 depth; // Depth of the tree (levels - 1).
uint256 root; // Root hash of the tree.
uint256 numberOfLeaves; // Number of leaves of the tree.
mapping(uint256 => uint256) zeroes; // Zero hashes used for empty nodes (level -> zero hash).
@@ -28,7 +28,7 @@ library IncrementalBinaryTree {
/// @param zero: Zero value to be used.
function init(
IncrementalTreeData storage self,
uint8 depth,
uint256 depth,
uint256 zero
) public {
require(zero < SNARK_SCALAR_FIELD, "IncrementalBinaryTree: leaf must be < SNARK_SCALAR_FIELD");
@@ -36,9 +36,12 @@ library IncrementalBinaryTree {
self.depth = depth;
for (uint8 i = 0; i < depth; i++) {
for (uint8 i = 0; i < depth; ) {
self.zeroes[i] = zero;
zero = PoseidonT3.poseidon([zero, zero]);
unchecked {
++i;
}
}
self.root = zero;
@@ -49,20 +52,24 @@ library IncrementalBinaryTree {
/// @param leaf: Leaf to be inserted.
function insert(IncrementalTreeData storage self, uint256 leaf) public {
require(leaf < SNARK_SCALAR_FIELD, "IncrementalBinaryTree: leaf must be < SNARK_SCALAR_FIELD");
require(self.numberOfLeaves < 2**self.depth, "IncrementalBinaryTree: tree is full");
uint256 depth = self.depth;
require(self.numberOfLeaves < 2**depth, "IncrementalBinaryTree: tree is full");
uint256 index = self.numberOfLeaves;
uint256 hash = leaf;
for (uint8 i = 0; i < self.depth; i++) {
if (index % 2 == 0) {
for (uint8 i = 0; i < depth; ) {
if (index & 1 == 0) {
self.lastSubtrees[i] = [hash, self.zeroes[i]];
} else {
self.lastSubtrees[i][1] = hash;
}
hash = PoseidonT3.poseidon(self.lastSubtrees[i]);
index /= 2;
index >>= 1;
unchecked {
++i;
}
}
self.root = hash;
@@ -89,7 +96,8 @@ library IncrementalBinaryTree {
uint256 hash = newLeaf;
for (uint8 i = 0; i < self.depth; i++) {
uint256 depth = self.depth;
for (uint8 i = 0; i < depth; ) {
if (proofPathIndices[i] == 0) {
if (proofSiblings[i] == self.lastSubtrees[i][1]) {
self.lastSubtrees[i][0] = hash;
@@ -103,6 +111,9 @@ library IncrementalBinaryTree {
hash = PoseidonT3.poseidon([proofSiblings[i], hash]);
}
unchecked {
++i;
}
}
self.root = hash;
@@ -126,7 +137,8 @@ library IncrementalBinaryTree {
uint256 hash = self.zeroes[0];
for (uint8 i = 0; i < self.depth; i++) {
uint256 depth = self.depth;
for (uint8 i = 0; i < depth; ) {
if (proofPathIndices[i] == 0) {
if (proofSiblings[i] == self.lastSubtrees[i][1]) {
self.lastSubtrees[i][0] = hash;
@@ -140,6 +152,9 @@ library IncrementalBinaryTree {
hash = PoseidonT3.poseidon([proofSiblings[i], hash]);
}
unchecked {
++i;
}
}
self.root = hash;
@@ -158,14 +173,15 @@ library IncrementalBinaryTree {
uint8[] calldata proofPathIndices
) private view returns (bool) {
require(leaf < SNARK_SCALAR_FIELD, "IncrementalBinaryTree: leaf must be < SNARK_SCALAR_FIELD");
uint256 depth = self.depth;
require(
proofPathIndices.length == self.depth && proofSiblings.length == self.depth,
proofPathIndices.length == depth && proofSiblings.length == depth,
"IncrementalBinaryTree: length of path is not correct"
);
uint256 hash = leaf;
for (uint8 i = 0; i < self.depth; i++) {
for (uint8 i = 0; i < depth; ) {
require(
proofSiblings[i] < SNARK_SCALAR_FIELD,
"IncrementalBinaryTree: sibling node must be < SNARK_SCALAR_FIELD"
@@ -176,6 +192,9 @@ library IncrementalBinaryTree {
} else {
hash = PoseidonT3.poseidon([proofSiblings[i], hash]);
}
unchecked {
++i;
}
}
return hash == self.root;

View File

@@ -6,7 +6,7 @@ import {PoseidonT6} from "./Hashes.sol";
// Each incremental tree has certain properties and data that will
// be used to add new leaves.
struct IncrementalTreeData {
uint8 depth; // Depth of the tree (levels - 1).
uint256 depth; // Depth of the tree (levels - 1).
uint256 root; // Root hash of the tree.
uint256 numberOfLeaves; // Number of leaves of the tree.
mapping(uint256 => uint256) zeroes; // Zero hashes used for empty nodes (level -> zero hash).
@@ -28,23 +28,28 @@ library IncrementalQuinTree {
/// @param zero: Zero value to be used.
function init(
IncrementalTreeData storage self,
uint8 depth,
uint256 depth,
uint256 zero
) public {
require(zero < SNARK_SCALAR_FIELD, "IncrementalBinaryTree: leaf must be < SNARK_SCALAR_FIELD");
require(depth > 0 && depth <= MAX_DEPTH, "IncrementalQuinTree: tree depth must be between 1 and 32");
self.depth = depth;
for (uint8 i = 0; i < depth; i++) {
for (uint8 i = 0; i < depth; ) {
self.zeroes[i] = zero;
uint256[5] memory zeroChildren;
for (uint8 j = 0; j < 5; j++) {
for (uint8 j = 0; j < 5; ) {
zeroChildren[j] = zero;
unchecked {
++j;
}
}
zero = PoseidonT6.poseidon(zeroChildren);
unchecked {
++i;
}
}
self.root = zero;
@@ -55,24 +60,31 @@ library IncrementalQuinTree {
/// @param leaf: Leaf to be inserted.
function insert(IncrementalTreeData storage self, uint256 leaf) public {
require(leaf < SNARK_SCALAR_FIELD, "IncrementalQuinTree: leaf must be < SNARK_SCALAR_FIELD");
require(self.numberOfLeaves < 5**self.depth, "IncrementalQuinTree: tree is full");
uint256 depth = self.depth;
require(self.numberOfLeaves < 5**depth, "IncrementalQuinTree: tree is full");
uint256 index = self.numberOfLeaves;
uint256 hash = leaf;
for (uint8 i = 0; i < self.depth; i++) {
for (uint8 i = 0; i < depth; ) {
uint8 position = uint8(index % 5);
self.lastSubtrees[i][position] = hash;
if (position == 0) {
for (uint8 j = 1; j < 5; j++) {
for (uint8 j = 1; j < 5; ) {
self.lastSubtrees[i][j] = self.zeroes[i];
unchecked {
++j;
}
}
}
hash = PoseidonT6.poseidon(self.lastSubtrees[i]);
index /= 5;
unchecked {
++i;
}
}
self.root = hash;
@@ -99,10 +111,11 @@ library IncrementalQuinTree {
uint256 hash = newLeaf;
for (uint8 i = 0; i < self.depth; i++) {
uint256 depth = self.depth;
for (uint8 i = 0; i < depth; ) {
uint256[5] memory nodes;
for (uint8 j = 0; j < 5; j++) {
for (uint8 j = 0; j < 5; ) {
if (j < proofPathIndices[i]) {
nodes[j] = proofSiblings[i][j];
} else if (j == proofPathIndices[i]) {
@@ -110,6 +123,9 @@ library IncrementalQuinTree {
} else {
nodes[j] = proofSiblings[i][j - 1];
}
unchecked {
++j;
}
}
if (nodes[0] == self.lastSubtrees[i][0] || nodes[4] == self.lastSubtrees[i][4]) {
@@ -117,6 +133,9 @@ library IncrementalQuinTree {
}
hash = PoseidonT6.poseidon(nodes);
unchecked {
++i;
}
}
self.root = hash;
@@ -140,10 +159,11 @@ library IncrementalQuinTree {
uint256 hash = self.zeroes[0];
for (uint8 i = 0; i < self.depth; i++) {
uint256 depth = self.depth;
for (uint8 i = 0; i < depth; ) {
uint256[5] memory nodes;
for (uint8 j = 0; j < 5; j++) {
for (uint8 j = 0; j < 5; ) {
if (j < proofPathIndices[i]) {
nodes[j] = proofSiblings[i][j];
} else if (j == proofPathIndices[i]) {
@@ -151,6 +171,9 @@ library IncrementalQuinTree {
} else {
nodes[j] = proofSiblings[i][j - 1];
}
unchecked {
++j;
}
}
if (nodes[0] == self.lastSubtrees[i][0] || nodes[4] == self.lastSubtrees[i][4]) {
@@ -158,6 +181,9 @@ library IncrementalQuinTree {
}
hash = PoseidonT6.poseidon(nodes);
unchecked {
++i;
}
}
self.root = hash;
@@ -176,17 +202,18 @@ library IncrementalQuinTree {
uint8[] calldata proofPathIndices
) private view returns (bool) {
require(leaf < SNARK_SCALAR_FIELD, "IncrementalQuinTree: leaf must be < SNARK_SCALAR_FIELD");
uint256 depth = self.depth;
require(
proofPathIndices.length == self.depth && proofSiblings.length == self.depth,
proofPathIndices.length == depth && proofSiblings.length == depth,
"IncrementalQuinTree: length of path is not correct"
);
uint256 hash = leaf;
for (uint8 i = 0; i < self.depth; i++) {
for (uint8 i = 0; i < depth; ) {
uint256[5] memory nodes;
for (uint8 j = 0; j < 5; j++) {
for (uint8 j = 0; j < 5; ) {
if (j < proofPathIndices[i]) {
require(
proofSiblings[i][j] < SNARK_SCALAR_FIELD,
@@ -204,9 +231,15 @@ library IncrementalQuinTree {
nodes[j] = proofSiblings[i][j - 1];
}
unchecked {
++j;
}
}
hash = PoseidonT6.poseidon(nodes);
unchecked {
++i;
}
}
return hash == self.root;