Remove root from MerkleTree (#4949)

Co-authored-by: Hadrien Croubois <hadrien.croubois@gmail.com>
This commit is contained in:
Ernesto García
2024-03-12 10:30:54 -06:00
committed by GitHub
parent 8cc7f2dcbf
commit 6b4ec6c6c6
2 changed files with 18 additions and 26 deletions

View File

@ -9,19 +9,20 @@ contract MerkleTreeMock {
MerkleTree.Bytes32PushTree private _tree; MerkleTree.Bytes32PushTree private _tree;
// This mock only stored the latest root.
// Production contract may want to store historical values.
bytes32 public root;
event LeafInserted(bytes32 leaf, uint256 index, bytes32 root); event LeafInserted(bytes32 leaf, uint256 index, bytes32 root);
function setup(uint8 _depth, bytes32 _zero) public { function setup(uint8 _depth, bytes32 _zero) public {
_tree.setup(_depth, _zero); root = _tree.setup(_depth, _zero);
} }
function push(bytes32 leaf) public { function push(bytes32 leaf) public {
(uint256 leafIndex, bytes32 currentRoot) = _tree.push(leaf); (uint256 leafIndex, bytes32 currentRoot) = _tree.push(leaf);
emit LeafInserted(leaf, leafIndex, currentRoot); emit LeafInserted(leaf, leafIndex, currentRoot);
} root = currentRoot;
function root() public view returns (bytes32) {
return _tree.root();
} }
function depth() public view returns (uint256) { function depth() public view returns (uint256) {

View File

@ -11,7 +11,7 @@ import {Panic} from "../Panic.sol";
* *
* Each tree is a complete binary tree with the ability to sequentially insert leaves, changing them from a zero to a * Each tree is a complete binary tree with the ability to sequentially insert leaves, changing them from a zero to a
* non-zero value and updating its root. This structure allows inserting commitments (or other entries) that are not * non-zero value and updating its root. This structure allows inserting commitments (or other entries) that are not
* stored, but can be proven to be part of the tree at a later time. See {MerkleProof}. * stored, but can be proven to be part of the tree at a later time if the root is kept. See {MerkleProof}.
* *
* A tree is defined by the following parameters: * A tree is defined by the following parameters:
* *
@ -34,13 +34,13 @@ library MerkleTree {
* directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and * directly. Use the functions provided below instead. Modifying the struct manually may violate assumptions and
* lead to unexpected behavior. * lead to unexpected behavior.
* *
* NOTE: The `root` is kept up to date after each insertion without keeping track of its history. Consider * NOTE: The `root` and the updates history is not stored within the tree. Consider using a secondary structure to
* using a secondary structure to store a list of historical roots (e.g. a mapping, {BitMaps} or {Checkpoints}). * store a list of historical roots from the values returned from {setup} and {push} (e.g. a mapping, {BitMaps} or
* {Checkpoints}).
* *
* WARNING: Updating any of the tree's parameters after the first insertion will result in a corrupted tree. * WARNING: Updating any of the tree's parameters after the first insertion will result in a corrupted tree.
*/ */
struct Bytes32PushTree { struct Bytes32PushTree {
bytes32 _root;
uint256 _nextLeafIndex; uint256 _nextLeafIndex;
bytes32[] _sides; bytes32[] _sides;
bytes32[] _zeros; bytes32[] _zeros;
@ -56,7 +56,7 @@ library MerkleTree {
* IMPORTANT: The zero value should be carefully chosen since it will be stored in the tree representing * IMPORTANT: The zero value should be carefully chosen since it will be stored in the tree representing
* empty leaves. It should be a value that is not expected to be part of the tree. * empty leaves. It should be a value that is not expected to be part of the tree.
*/ */
function setup(Bytes32PushTree storage self, uint8 levels, bytes32 zero) internal { function setup(Bytes32PushTree storage self, uint8 levels, bytes32 zero) internal returns (bytes32 initialRoot) {
return setup(self, levels, zero, Hashes.commutativeKeccak256); return setup(self, levels, zero, Hashes.commutativeKeccak256);
} }
@ -71,7 +71,7 @@ library MerkleTree {
uint8 levels, uint8 levels,
bytes32 zero, bytes32 zero,
function(bytes32, bytes32) view returns (bytes32) fnHash function(bytes32, bytes32) view returns (bytes32) fnHash
) internal { ) internal returns (bytes32 initialRoot) {
// Store depth in the dynamic array // Store depth in the dynamic array
Arrays.unsafeSetLength(self._sides, levels); Arrays.unsafeSetLength(self._sides, levels);
Arrays.unsafeSetLength(self._zeros, levels); Arrays.unsafeSetLength(self._zeros, levels);
@ -84,9 +84,10 @@ library MerkleTree {
} }
// Set the first root // Set the first root
self._root = currentZero;
self._nextLeafIndex = 0; self._nextLeafIndex = 0;
self._fnHash = fnHash; self._fnHash = fnHash;
return currentZero;
} }
/** /**
@ -102,15 +103,15 @@ library MerkleTree {
function(bytes32, bytes32) view returns (bytes32) fnHash = self._fnHash; function(bytes32, bytes32) view returns (bytes32) fnHash = self._fnHash;
// Get leaf index // Get leaf index
uint256 leafIndex = self._nextLeafIndex++; index = self._nextLeafIndex++;
// Check if tree is full. // Check if tree is full.
if (leafIndex >= 1 << levels) { if (index >= 1 << levels) {
Panic.panic(Panic.RESOURCE_ERROR); Panic.panic(Panic.RESOURCE_ERROR);
} }
// Rebuild branch from leaf to root // Rebuild branch from leaf to root
uint256 currentIndex = leafIndex; uint256 currentIndex = index;
bytes32 currentLevelHash = leaf; bytes32 currentLevelHash = leaf;
for (uint32 i = 0; i < levels; i++) { for (uint32 i = 0; i < levels; i++) {
// Reaching the parent node, is currentLevelHash the left child? // Reaching the parent node, is currentLevelHash the left child?
@ -132,17 +133,7 @@ library MerkleTree {
currentIndex >>= 1; currentIndex >>= 1;
} }
// Record new root return (index, currentLevelHash);
self._root = currentLevelHash;
return (leafIndex, currentLevelHash);
}
/**
* @dev Tree's current root
*/
function root(Bytes32PushTree storage self) internal view returns (bytes32) {
return self._root;
} }
/** /**