Skip to content

Commit 42e736b

Browse files
authored
Merge pull request #56 from eco/claude/pr53-address-feedback-01Mu5xjLme7mv5nn2QUpN6XQ
refactor: improve TreeNodeLib code clarity
2 parents b8226bb + 40d22b9 commit 42e736b

2 files changed

Lines changed: 25 additions & 6 deletions

File tree

src/lib/TreeNodeLib.sol

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,29 @@ library TreeNodeLib {
105105
// Type flags occupy bits 247 down to (247 - proof.length + 1)
106106
// This validates that bits (247 - proof.length) down to 0 are all zero
107107
if (proof.length < 247) {
108+
// --- Bit manipulation explanation for unused type flags ---
109+
// proofStructure layout (bytes32, 256 bits):
110+
// - Bits 255-248: Position index (8 bits, currently unused)
111+
// - Bits 247-0: Type flags (1 bit per proof element, up to 247 bits)
112+
//
113+
// For proof.length = N:
114+
// - Type flags use bits 247 down to (247 - N + 1)
115+
// - Unused type flag bits are (247 - N) down to 0, and must be zero
116+
//
117+
// Example: proof.length = 2
118+
// - mask = 0xFF00000000000000000000000000000000000000000000000000000000000000
119+
// (keeps bits 255-248 position index, plus bits 247-246 for 2 type flags)
120+
// - flagBits extracts bits 247-0 (all type flag bits)
121+
// - unusedMask = 0x0000000000000000000000000000000000000000000000000000003FFFFFFFFFFF
122+
// (checks bits 245-0 are zero, since bits 247-246 are used)
123+
//
124+
// Step 1: Create mask to keep position index and used type flag bits
108125
uint256 mask = type(uint256).max << (256 - 8 - proof.length);
126+
// Step 2: Extract only the type flag bits (bits 247-0)
109127
uint256 flagBits = uint256(proofStructure) & ~mask;
128+
// Step 3: Create mask for unused type flag bits (bits below used flags)
110129
uint256 unusedMask = type(uint256).max >> (8 + proof.length);
130+
// Step 4: Require that all unused type flag bits are zero
111131
require((flagBits & unusedMask) == 0, "Unused type flags must be zero");
112132
}
113133

@@ -132,12 +152,11 @@ library TreeNodeLib {
132152
currentHash = combineLeafAndLeaf(typehash, currentHash, proof[i]);
133153
} else if (currentIsNode && proofIsNode) {
134154
currentHash = combineNodeAndNode(typehash, currentHash, proof[i]);
155+
} else if (currentIsNode && !proofIsNode) {
156+
currentHash = combineNodeAndLeaf(typehash, currentHash, proof[i]);
135157
} else {
136-
if (currentIsNode) {
137-
currentHash = combineNodeAndLeaf(typehash, currentHash, proof[i]);
138-
} else {
139-
currentHash = combineNodeAndLeaf(typehash, proof[i], currentHash);
140-
}
158+
// !currentIsNode && proofIsNode
159+
currentHash = combineNodeAndLeaf(typehash, proof[i], currentHash);
141160
}
142161

143162
// After first combine, result is always a Node

test/Permit3Edge.t.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -816,7 +816,7 @@ contract Permit3EdgeTest is Test {
816816
(amount, expiration, ts) = permit3.allowance(owner, address(token), spender);
817817
assertEq(amount, 0); // Amount remains unchanged by unlock operation
818818
assertEq(expiration, 0); // No expiration (unlocked)
819-
// Note: timestamp should remain from lock operation since unlock only changes expiration
819+
// Note: timestamp should remain from lock operation since unlock only changes expiration
820820
assertEq(ts, uint48(block.timestamp)); // Timestamp remains from lock operation
821821
}
822822

0 commit comments

Comments
 (0)