From 4883617c1c8f2229cf1b9044273db072ac879e16 Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 13:33:49 +0100 Subject: [PATCH 01/10] refactor(imt.sol): cache tree size in the lean imt the tree size value is cached in the lean imt to optimize gas re #209 --- .../contracts/internal/InternalLeanIMT.sol | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index 572dbde29..9202cbd81 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -46,11 +46,13 @@ library InternalLeanIMT { revert LeafAlreadyExists(); } - while (2 ** self.depth < self.size + 1) { + uint256 treeSize = self.size; + + while (2 ** self.depth < treeSize + 1) { self.depth += 1; } - uint256 index = self.size; + uint256 index = treeSize; uint256 node = leaf; for (uint256 level = 0; level < self.depth; ) { @@ -80,6 +82,8 @@ library InternalLeanIMT { /// @param leaves: The values of the new leaves to be inserted into the tree. /// @return The root after the leaves have been inserted. function _insertMany(LeanIMTData storage self, uint256[] calldata leaves) internal returns (uint256) { + uint256 treeSize = self.size; + // Check that all the new values are correct to be added. for (uint256 i = 0; i < leaves.length; ) { if (leaves[i] >= SNARK_SCALAR_FIELD) { @@ -90,7 +94,7 @@ library InternalLeanIMT { revert LeafAlreadyExists(); } - self.leaves[leaves[i]] = self.size + 1 + i; + self.leaves[leaves[i]] = treeSize + 1 + i; unchecked { ++i; @@ -103,15 +107,15 @@ library InternalLeanIMT { currentLevel = leaves; // Calculate the depth of the tree after adding the new values. - while (2 ** self.depth < self.size + leaves.length) { + while (2 ** self.depth < treeSize + leaves.length) { self.depth += 1; } // First index to change in every level. - uint256 currentLevelStartIndex = self.size; + uint256 currentLevelStartIndex = treeSize; // Size of the level used to create the next level. - uint256 currentLevelSize = self.size + leaves.length; + uint256 currentLevelSize = treeSize + leaves.length; // The index where changes begin at the next level. uint256 nextLevelStartIndex = currentLevelStartIndex >> 1; From 9e0d2e3a8682eb4c54b37eff081686defc4a5622 Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 13:57:30 +0100 Subject: [PATCH 02/10] refactor(imt.sol): cache tree depth in the lean imt cache the tree depth in the lean imt to optimize gas re #209 --- .../imt.sol/contracts/internal/InternalLeanIMT.sol | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index 9202cbd81..048cb4214 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -52,10 +52,12 @@ library InternalLeanIMT { self.depth += 1; } + uint256 treeDepth = self.depth; + uint256 index = treeSize; uint256 node = leaf; - for (uint256 level = 0; level < self.depth; ) { + for (uint256 level = 0; level < treeDepth; ) { if ((index >> level) & 1 == 1) { node = PoseidonT3.hash([self.sideNodes[level], node]); } else { @@ -69,7 +71,7 @@ library InternalLeanIMT { self.size += 1; - self.sideNodes[self.depth] = node; + self.sideNodes[treeDepth] = node; self.leaves[leaf] = self.size; return node; @@ -233,7 +235,9 @@ library InternalLeanIMT { uint256 lastIndex = self.size - 1; uint256 i = 0; - for (uint256 level = 0; level < self.depth; ) { + uint256 treeDepth = self.depth; + + for (uint256 level = 0; level < treeDepth; ) { if ((index >> level) & 1 == 1) { if (siblingNodes[i] >= SNARK_SCALAR_FIELD) { revert LeafGreaterThanSnarkScalarField(); @@ -271,7 +275,7 @@ library InternalLeanIMT { revert WrongSiblingNodes(); } - self.sideNodes[self.depth] = node; + self.sideNodes[treeDepth] = node; self.leaves[newLeaf] = self.leaves[oldLeaf]; self.leaves[oldLeaf] = 0; From 3b353fa44d522da00fcbcc5cde00a63356c52bd4 Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 14:07:00 +0100 Subject: [PATCH 03/10] docs(imt.sol): add code comments in the lean imt add code comments in the lean imt when the variables tree size and depth are created to optimize gas re #209 --- packages/imt.sol/contracts/internal/InternalLeanIMT.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index 048cb4214..af353604d 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -46,12 +46,14 @@ library InternalLeanIMT { revert LeafAlreadyExists(); } + // Cache tree size to optimize gas uint256 treeSize = self.size; while (2 ** self.depth < treeSize + 1) { self.depth += 1; } + // Cache tree depth to optimize gas uint256 treeDepth = self.depth; uint256 index = treeSize; @@ -84,6 +86,7 @@ library InternalLeanIMT { /// @param leaves: The values of the new leaves to be inserted into the tree. /// @return The root after the leaves have been inserted. function _insertMany(LeanIMTData storage self, uint256[] calldata leaves) internal returns (uint256) { + // Cache tree size to optimize gas uint256 treeSize = self.size; // Check that all the new values are correct to be added. @@ -235,6 +238,7 @@ library InternalLeanIMT { uint256 lastIndex = self.size - 1; uint256 i = 0; + // Cache tree depth to optimize gas uint256 treeDepth = self.depth; for (uint256 level = 0; level < treeDepth; ) { From 5daeb449990bc81235f65547b92a05b8a34bd6c7 Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 19:39:37 +0100 Subject: [PATCH 04/10] refactor(imt.sol): refactor code to optimize gas re #209 --- packages/imt.sol/.prettierrc.json | 6 ++++++ .../contracts/internal/InternalLeanIMT.sol | 18 +++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 packages/imt.sol/.prettierrc.json diff --git a/packages/imt.sol/.prettierrc.json b/packages/imt.sol/.prettierrc.json new file mode 100644 index 000000000..717fb520a --- /dev/null +++ b/packages/imt.sol/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "semi": false, + "arrowParens": "always", + "trailingComma": "none", + "plugins": ["prettier-plugin-solidity"] +} diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index af353604d..ad06c7670 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -46,17 +46,17 @@ library InternalLeanIMT { revert LeafAlreadyExists(); } - // Cache tree size to optimize gas - uint256 treeSize = self.size; - - while (2 ** self.depth < treeSize + 1) { - self.depth += 1; - } + uint256 index = self.size; // Cache tree depth to optimize gas uint256 treeDepth = self.depth; - uint256 index = treeSize; + while (2 ** treeDepth < index + 1) { + ++treeDepth; + } + + self.depth = treeDepth; + uint256 node = leaf; for (uint256 level = 0; level < treeDepth; ) { @@ -71,10 +71,10 @@ library InternalLeanIMT { } } - self.size += 1; + self.size = ++index; self.sideNodes[treeDepth] = node; - self.leaves[leaf] = self.size; + self.leaves[leaf] = index; return node; } From a41477c57eafc4cb8d4e1d82f1cf41f5d68b3fee Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 19:51:11 +0100 Subject: [PATCH 05/10] chore(imt.sol): merge main into ref/cache-tree-values --- .../imt.sol/contracts/internal/InternalLeanIMT.sol | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index 4c6969854..7f5ac5af4 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -46,19 +46,15 @@ library InternalLeanIMT { revert LeafAlreadyExists(); } - // A new insertion can increase a tree's depth by at most 1, - // and only if the number of leaves supported by the current - // depth is less than the number of leaves to be supported after insertion. - if (2 ** self.depth < self.size + 1) { - self.depth += 1; - } - uint256 index = self.size; // Cache tree depth to optimize gas uint256 treeDepth = self.depth; - while (2 ** treeDepth < index + 1) { + // A new insertion can increase a tree's depth by at most 1, + // and only if the number of leaves supported by the current + // depth is less than the number of leaves to be supported after insertion. + if (2 ** self.depth < index + 1) { ++treeDepth; } From 22b6a1266e1102c84c08aa70d114dce2fb66e8de Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 19:57:57 +0100 Subject: [PATCH 06/10] refactor(imt.sol): update the way depth is increased to optimize gas re #209 --- packages/imt.sol/contracts/internal/InternalLeanIMT.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index 7f5ac5af4..439ed24b9 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -118,7 +118,7 @@ library InternalLeanIMT { // Unlike the 'insert' function, we need a while here as // N insertions can increase the tree's depth more than once. while (2 ** self.depth < treeSize + leaves.length) { - self.depth += 1; + ++self.depth; } // First index to change in every level. From 4d5bf813f49bdda4f107cb13e83ae60c80b33d4b Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 20:14:40 +0100 Subject: [PATCH 07/10] refactor(imt.sol): use the cached tree depth re #209 --- packages/imt.sol/contracts/internal/InternalLeanIMT.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index 439ed24b9..dee104917 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -54,7 +54,7 @@ library InternalLeanIMT { // A new insertion can increase a tree's depth by at most 1, // and only if the number of leaves supported by the current // depth is less than the number of leaves to be supported after insertion. - if (2 ** self.depth < index + 1) { + if (2 ** treeDepth < index + 1) { ++treeDepth; } From 0e4048d0ae8c4ff32f25ab3cafb4a3cd43a5543c Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 21:15:33 +0100 Subject: [PATCH 08/10] chore(lazytower.sol): add prettier config to the contracts --- packages/lazytower.sol/.prettierrc.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 packages/lazytower.sol/.prettierrc.json diff --git a/packages/lazytower.sol/.prettierrc.json b/packages/lazytower.sol/.prettierrc.json new file mode 100644 index 000000000..717fb520a --- /dev/null +++ b/packages/lazytower.sol/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "semi": false, + "arrowParens": "always", + "trailingComma": "none", + "plugins": ["prettier-plugin-solidity"] +} From 464a4ae120688c605b8d2657c82bd1200b83c127 Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 22:18:43 +0100 Subject: [PATCH 09/10] refactor(imt.sol): update variable assignment to optimize gas re #209 --- packages/imt.sol/contracts/internal/InternalLeanIMT.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index dee104917..716a9cf28 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -205,7 +205,7 @@ library InternalLeanIMT { } // Update tree size - self.size += leaves.length; + self.size = treeSize + leaves.length; // Update tree root self.sideNodes[self.depth] = currentLevel[0]; From f9f409f190e8de427e67d9270bad3a3e07403783 Mon Sep 17 00:00:00 2001 From: Vivian Plasencia Date: Wed, 20 Mar 2024 22:57:35 +0100 Subject: [PATCH 10/10] refactor(imt.sol): cache tree depth to optimize gas in insert many function in the lean imt re #209 --- .../contracts/internal/InternalLeanIMT.sol | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol index 716a9cf28..bbafc7b19 100644 --- a/packages/imt.sol/contracts/internal/InternalLeanIMT.sol +++ b/packages/imt.sol/contracts/internal/InternalLeanIMT.sol @@ -114,13 +114,18 @@ library InternalLeanIMT { currentLevel = leaves; + // Cache tree depth to optimize gas + uint256 treeDepth = self.depth; + // Calculate the depth of the tree after adding the new values. // Unlike the 'insert' function, we need a while here as // N insertions can increase the tree's depth more than once. - while (2 ** self.depth < treeSize + leaves.length) { - ++self.depth; + while (2 ** treeDepth < treeSize + leaves.length) { + ++treeDepth; } + self.depth = treeDepth; + // First index to change in every level. uint256 currentLevelStartIndex = treeSize; @@ -133,20 +138,14 @@ library InternalLeanIMT { // The size of the next level. uint256 nextLevelSize = ((currentLevelSize - 1) >> 1) + 1; - for (uint256 level = 0; level < self.depth; ) { + for (uint256 level = 0; level < treeDepth; ) { // The number of nodes for the new level that will be created, // only the new values, not the entire level. uint256 numberOfNodes = nextLevelSize - nextLevelStartIndex; uint256[] memory nextLevel = new uint256[](numberOfNodes); for (uint256 i = 0; i < numberOfNodes; ) { - uint256 rightNode; uint256 leftNode; - // Assign the right node if the value exists. - if ((i + nextLevelStartIndex) * 2 + 1 < currentLevelSize) { - rightNode = currentLevel[(i + nextLevelStartIndex) * 2 + 1 - currentLevelStartIndex]; - } - // Assign the left node using the saved path or the position in the array. if ((i + nextLevelStartIndex) * 2 < currentLevelStartIndex) { leftNode = self.sideNodes[level]; @@ -154,6 +153,13 @@ library InternalLeanIMT { leftNode = currentLevel[(i + nextLevelStartIndex) * 2 - currentLevelStartIndex]; } + uint256 rightNode; + + // Assign the right node if the value exists. + if ((i + nextLevelStartIndex) * 2 + 1 < currentLevelSize) { + rightNode = currentLevel[(i + nextLevelStartIndex) * 2 + 1 - currentLevelStartIndex]; + } + uint256 parentNode; // Assign the parent node. @@ -208,7 +214,7 @@ library InternalLeanIMT { self.size = treeSize + leaves.length; // Update tree root - self.sideNodes[self.depth] = currentLevel[0]; + self.sideNodes[treeDepth] = currentLevel[0]; return currentLevel[0]; }