Skip to content

Commit dd965b4

Browse files
Snezhkkoernestognw
andauthored
Move multiproof hashes allocation under condition to avoid zero-length alloc (#6092)
Co-authored-by: ernestognw <[email protected]>
1 parent a0eb08b commit dd965b4

File tree

2 files changed

+90
-95
lines changed

2 files changed

+90
-95
lines changed

contracts/utils/cryptography/MerkleProof.sol

Lines changed: 72 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -217,26 +217,25 @@ library MerkleProof {
217217
revert MerkleProofInvalidMultiproof();
218218
}
219219

220-
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
221-
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
222-
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
223-
uint256 leafPos = 0;
224-
uint256 hashPos = 0;
225-
uint256 proofPos = 0;
226-
// At each step, we compute the next hash using two values:
227-
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
228-
// get the next hash.
229-
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
230-
// `proof` array.
231-
for (uint256 i = 0; i < proofFlagsLen; i++) {
232-
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
233-
bytes32 b = proofFlags[i]
234-
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
235-
: proof[proofPos++];
236-
hashes[i] = Hashes.commutativeKeccak256(a, b);
237-
}
238-
239220
if (proofFlagsLen > 0) {
221+
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
222+
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
223+
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
224+
uint256 leafPos = 0;
225+
uint256 hashPos = 0;
226+
uint256 proofPos = 0;
227+
// At each step, we compute the next hash using two values:
228+
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
229+
// get the next hash.
230+
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
231+
// `proof` array.
232+
for (uint256 i = 0; i < proofFlagsLen; i++) {
233+
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
234+
bytes32 b = proofFlags[i]
235+
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
236+
: proof[proofPos++];
237+
hashes[i] = Hashes.commutativeKeccak256(a, b);
238+
}
240239
if (proofPos != proof.length) {
241240
revert MerkleProofInvalidMultiproof();
242241
}
@@ -305,26 +304,25 @@ library MerkleProof {
305304
revert MerkleProofInvalidMultiproof();
306305
}
307306

308-
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
309-
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
310-
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
311-
uint256 leafPos = 0;
312-
uint256 hashPos = 0;
313-
uint256 proofPos = 0;
314-
// At each step, we compute the next hash using two values:
315-
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
316-
// get the next hash.
317-
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
318-
// `proof` array.
319-
for (uint256 i = 0; i < proofFlagsLen; i++) {
320-
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
321-
bytes32 b = proofFlags[i]
322-
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
323-
: proof[proofPos++];
324-
hashes[i] = hasher(a, b);
325-
}
326-
327307
if (proofFlagsLen > 0) {
308+
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
309+
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
310+
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
311+
uint256 leafPos = 0;
312+
uint256 hashPos = 0;
313+
uint256 proofPos = 0;
314+
// At each step, we compute the next hash using two values:
315+
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
316+
// get the next hash.
317+
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
318+
// `proof` array.
319+
for (uint256 i = 0; i < proofFlagsLen; i++) {
320+
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
321+
bytes32 b = proofFlags[i]
322+
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
323+
: proof[proofPos++];
324+
hashes[i] = hasher(a, b);
325+
}
328326
if (proofPos != proof.length) {
329327
revert MerkleProofInvalidMultiproof();
330328
}
@@ -391,26 +389,25 @@ library MerkleProof {
391389
revert MerkleProofInvalidMultiproof();
392390
}
393391

394-
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
395-
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
396-
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
397-
uint256 leafPos = 0;
398-
uint256 hashPos = 0;
399-
uint256 proofPos = 0;
400-
// At each step, we compute the next hash using two values:
401-
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
402-
// get the next hash.
403-
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
404-
// `proof` array.
405-
for (uint256 i = 0; i < proofFlagsLen; i++) {
406-
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
407-
bytes32 b = proofFlags[i]
408-
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
409-
: proof[proofPos++];
410-
hashes[i] = Hashes.commutativeKeccak256(a, b);
411-
}
412-
413392
if (proofFlagsLen > 0) {
393+
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
394+
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
395+
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
396+
uint256 leafPos = 0;
397+
uint256 hashPos = 0;
398+
uint256 proofPos = 0;
399+
// At each step, we compute the next hash using two values:
400+
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
401+
// get the next hash.
402+
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
403+
// `proof` array.
404+
for (uint256 i = 0; i < proofFlagsLen; i++) {
405+
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
406+
bytes32 b = proofFlags[i]
407+
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
408+
: proof[proofPos++];
409+
hashes[i] = Hashes.commutativeKeccak256(a, b);
410+
}
414411
if (proofPos != proof.length) {
415412
revert MerkleProofInvalidMultiproof();
416413
}
@@ -479,26 +476,25 @@ library MerkleProof {
479476
revert MerkleProofInvalidMultiproof();
480477
}
481478

482-
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
483-
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
484-
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
485-
uint256 leafPos = 0;
486-
uint256 hashPos = 0;
487-
uint256 proofPos = 0;
488-
// At each step, we compute the next hash using two values:
489-
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
490-
// get the next hash.
491-
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
492-
// `proof` array.
493-
for (uint256 i = 0; i < proofFlagsLen; i++) {
494-
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
495-
bytes32 b = proofFlags[i]
496-
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
497-
: proof[proofPos++];
498-
hashes[i] = hasher(a, b);
499-
}
500-
501479
if (proofFlagsLen > 0) {
480+
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
481+
// `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
482+
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
483+
uint256 leafPos = 0;
484+
uint256 hashPos = 0;
485+
uint256 proofPos = 0;
486+
// At each step, we compute the next hash using two values:
487+
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
488+
// get the next hash.
489+
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
490+
// `proof` array.
491+
for (uint256 i = 0; i < proofFlagsLen; i++) {
492+
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
493+
bytes32 b = proofFlags[i]
494+
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
495+
: proof[proofPos++];
496+
hashes[i] = hasher(a, b);
497+
}
502498
if (proofPos != proof.length) {
503499
revert MerkleProofInvalidMultiproof();
504500
}

scripts/generate/templates/MerkleProof.js

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -138,26 +138,25 @@ function processMultiProof${suffix}(${formatArgsMultiline(
138138
revert MerkleProofInvalidMultiproof();
139139
}
140140
141-
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
142-
// \`xxx[xxxPos++]\`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
143-
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
144-
uint256 leafPos = 0;
145-
uint256 hashPos = 0;
146-
uint256 proofPos = 0;
147-
// At each step, we compute the next hash using two values:
148-
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
149-
// get the next hash.
150-
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
151-
// \`proof\` array.
152-
for (uint256 i = 0; i < proofFlagsLen; i++) {
153-
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
154-
bytes32 b = proofFlags[i]
155-
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
156-
: proof[proofPos++];
157-
hashes[i] = ${hash ?? DEFAULT_HASH}(a, b);
158-
}
159-
160141
if (proofFlagsLen > 0) {
142+
// The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
143+
// \`xxx[xxxPos++]\`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
144+
bytes32[] memory hashes = new bytes32[](proofFlagsLen);
145+
uint256 leafPos = 0;
146+
uint256 hashPos = 0;
147+
uint256 proofPos = 0;
148+
// At each step, we compute the next hash using two values:
149+
// - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
150+
// get the next hash.
151+
// - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
152+
// \`proof\` array.
153+
for (uint256 i = 0; i < proofFlagsLen; i++) {
154+
bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
155+
bytes32 b = proofFlags[i]
156+
? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
157+
: proof[proofPos++];
158+
hashes[i] = ${hash ?? DEFAULT_HASH}(a, b);
159+
}
161160
if (proofPos != proof.length) {
162161
revert MerkleProofInvalidMultiproof();
163162
}

0 commit comments

Comments
 (0)