Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/src/ProtocolAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ contract ProtocolAdapter is IProtocolAdapter, ReentrancyGuardTransient, Commitme
/// @return root The root of the corresponding tree.
function _computeActionTreeRoot(Action calldata action, uint256 complianceUnitCount)
internal
pure
view
returns (bytes32 root)
{
bytes32[] memory actionTreeTags = new bytes32[](complianceUnitCount * 2);
Expand Down
36 changes: 18 additions & 18 deletions contracts/src/libs/MerkleTree.sol
Original file line number Diff line number Diff line change
Expand Up @@ -108,21 +108,14 @@ library MerkleTree {
treeCapacity = uint256(1) << depth(self); // 2^treeDepth
}

/// @notice Checks whether a node is the left or right child according to its index.
/// @param index The index to check.
/// @return isLeft Whether this node is the left or right child.
function isLeftChild(uint256 index) internal pure returns (bool isLeft) {
isLeft = (index & 1) == 0;
}

/// @notice Processes a Merkle proof consisting of siblings and direction bits and returns the resulting root.
/// @param siblings The siblings.
/// @param directionBits The direction bits indicating whether the siblings are left of right.
/// @param leaf The leaf.
/// @return root The resulting root obtained by processing the leaf, siblings, and direction bits.
function processProof(bytes32[] memory siblings, uint256 directionBits, bytes32 leaf)
internal
pure
view
returns (bytes32 root)
{
bytes32 computedHash = leaf;
Expand All @@ -140,20 +133,12 @@ library MerkleTree {
root = computedHash;
}

/// @notice Checks whether a direction bit encodes the left or right sibling.
/// @param directionBits The direction bits.
/// @param d The index of the bit to check.
/// @return isLeft Whether the sibling is left or right.
function isLeftSibling(uint256 directionBits, uint256 d) internal pure returns (bool isLeft) {
isLeft = (directionBits >> d) & 1 == 0;
}

/// @notice Computes the root of a Merkle tree.
/// @param leaves The leaves of the tree.
/// @param treeDepth The depth of the tree.
/// @return root The computed root.
/// @dev This method should only be used for trees with low depth.
function computeRoot(bytes32[] memory leaves, uint8 treeDepth) internal pure returns (bytes32 root) {
function computeRoot(bytes32[] memory leaves, uint8 treeDepth) internal view returns (bytes32 root) {
uint256 treeCapacity = uint256(1) << treeDepth; // 2^treeDepth

// Create array of full leaf set with padding if necessary
Expand Down Expand Up @@ -183,10 +168,25 @@ library MerkleTree {
/// @param leaves The leaves of the tree.
/// @return root The computed root.
/// @dev This method should only be used for trees with low depth.
function computeRoot(bytes32[] memory leaves) internal pure returns (bytes32 root) {
function computeRoot(bytes32[] memory leaves) internal view returns (bytes32 root) {
root = MerkleTree.computeRoot({leaves: leaves, treeDepth: computeMinimalTreeDepth(leaves.length)});
}

/// @notice Checks whether a node is the left or right child according to its index.
/// @param index The index to check.
/// @return isLeft Whether this node is the left or right child.
function isLeftChild(uint256 index) internal pure returns (bool isLeft) {
isLeft = (index & 1) == 0;
}

/// @notice Checks whether a direction bit encodes the left or right sibling.
/// @param directionBits The direction bits.
/// @param d The index of the bit to check.
/// @return isLeft Whether the sibling is left or right.
function isLeftSibling(uint256 directionBits, uint256 d) internal pure returns (bool isLeft) {
isLeft = (directionBits >> d) & 1 == 0;
}

/// @notice Computes the minimal required tree depth for a number of leaves.
/// @param leavesCount The number of leaves.
/// @return treeDepth The minimal required tree depth.
Expand Down
10 changes: 6 additions & 4 deletions contracts/src/libs/SHA256.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

import {EfficientHashLib} from "@solady/utils/EfficientHashLib.sol";

/// @title SHA256
/// @author Anoma Foundation, 2025
/// @notice A library for computing SHA256 hashes.
Expand All @@ -13,15 +15,15 @@ library SHA256 {
/// @notice Hashes a single `bytes32` value.
/// @param a The value to hash.
/// @return ha The resulting hash.
function hash(bytes32 a) internal pure returns (bytes32 ha) {
ha = sha256(abi.encode(a));
function hash(bytes32 a) internal view returns (bytes32 ha) {
ha = EfficientHashLib.sha2(a);
}

/// @notice Hashes two `bytes32` values.
/// @param a The first value to hash.
/// @param b The second value to hash.
/// @return hab The resulting hash.
function hash(bytes32 a, bytes32 b) internal pure returns (bytes32 hab) {
hab = sha256(abi.encode(a, b));
function hash(bytes32 a, bytes32 b) internal view returns (bytes32 hab) {
hab = EfficientHashLib.sha2(abi.encode(a, b));
Comment on lines +26 to +27
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this really save gas? This is the function that is used the most whereas hash(bytes32 a) is not used that much.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minimally so, it seems, I can provide more scrupulous gas metering tomorrow

}
}
2 changes: 1 addition & 1 deletion contracts/test/examples/MerkleTree.e.sol
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ contract MerkleTreeExample {
}
}

function _calculateNextLevel(bytes32[] memory x) internal pure returns (bytes32[] memory y) {
function _calculateNextLevel(bytes32[] memory x) internal view returns (bytes32[] memory y) {
uint256 len = x.length / 2;
y = new bytes32[](len);
for (uint256 i = 0; i < len; ++i) {
Expand Down
16 changes: 8 additions & 8 deletions contracts/test/examples/transactions/Transaction.e.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ library TransactionExample {
bytes internal constant _DELTA_PROOF =
hex"b91ec8ea76d57a4a370d4fcff04f8d6f178dc0e370dfd9573ba5638c10c06bb705cf39747cbefa0f2e14bd44d9b7b1542d033b5b6ecf6017fb08f0b7c5c1cbb41c";

function treeRoot() internal view returns (bytes32 root) {
bytes32[] memory leaves = new bytes32[](2);
leaves[0] = _CONSUMED_NULLIFIER;
leaves[1] = _CREATED_COMMITMENT;

root = leaves.computeRoot();
}

function complianceInstance() internal pure returns (Compliance.Instance memory instance) {
instance = Compliance.Instance({
consumed: Compliance.ConsumedRefs({
Expand Down Expand Up @@ -105,12 +113,4 @@ library TransactionExample {

txn = Transaction({actions: actions, deltaProof: _DELTA_PROOF});
}

function treeRoot() internal pure returns (bytes32 root) {
bytes32[] memory leaves = new bytes32[](2);
leaves[0] = _CONSUMED_NULLIFIER;
leaves[1] = _CREATED_COMMITMENT;

root = leaves.computeRoot();
}
}
Loading