fix: optimized MerkleInsert MerkleErasures

This commit is contained in:
Roman 2025-10-03 09:52:05 +10:00
parent b811398a13
commit 5a4e007a14
No known key found for this signature in database
GPG Key ID: 583BDF43C238B83E

View File

@ -342,14 +342,13 @@ contract WakuRlnV2Test is Test {
return current == root; return current == root;
} }
// Fuzz Test: Merkle Tree Insertions and Proofs via Registrations // Merkle Tree Insertions and Proofs via Registrations
function testFuzz_MerkleInserts(uint8 numInserts) external { function testFuzz_MerkleInserts(uint8 numInserts) external {
vm.assume(numInserts > 0 && numInserts <= 32); vm.assume(numInserts > 0 && numInserts <= 16);
uint32 rateLimit = w.minMembershipRateLimit(); uint32 rateLimit = w.minMembershipRateLimit();
uint256[] memory ids = new uint256[](numInserts); uint256[] memory ids = new uint256[](numInserts);
uint32[] memory indices = new uint32[](numInserts); uint32[] memory indices = new uint32[](numInserts);
uint256[] memory expectedRoots = new uint256[](numInserts);
// Sequence: Fuzz registrations, track indices and commitments // Sequence: Fuzz registrations, track indices and commitments
for (uint8 i = 0; i < numInserts; i++) { for (uint8 i = 0; i < numInserts; i++) {
@ -367,17 +366,18 @@ contract WakuRlnV2Test is Test {
assertEq(rl, rateLimit); assertEq(rl, rateLimit);
assertTrue(commitment != 0); // Inserted assertTrue(commitment != 0); // Inserted
// Invariant: Proof valid for leaf // Sampled proof verification: Only check every other for gas savings
uint256[20] memory proof = w.getMerkleProof(idx); if (i % 2 == 0) {
uint256 root = w.root(); uint256[20] memory proof = w.getMerkleProof(idx);
assertTrue(_verifyMerkleProof(proof, root, idx, commitment, 20)); uint256 root = w.root();
assertTrue(_verifyMerkleProof(proof, root, idx, commitment, 20));
expectedRoots[i] = root; // Snapshot for later }
} }
// Post-sequence invariants: Roots evolved correctly, no overwrites // Post-sequence invariants: Roots evolved correctly, no overwrites - sampled checks
assertEq(w.nextFreeIndex(), numInserts); // Filled sequentially assertEq(w.nextFreeIndex(), numInserts); // Filled sequentially
for (uint8 i = 0; i < numInserts; i++) { for (uint8 i = 0; i < numInserts; i += 2) {
// Sample every other
(, uint32 idx,) = w.getMembershipInfo(ids[i]); (, uint32 idx,) = w.getMembershipInfo(ids[i]);
assertEq(idx, i); // Sequential indices assertEq(idx, i); // Sequential indices
assertEq( assertEq(
@ -386,9 +386,9 @@ contract WakuRlnV2Test is Test {
} }
} }
// Fuzz Test: Merkle Tree Erasures and Reuses (Lazy/Full) // Merkle Tree Erasures and Reuses (Lazy/Full)
function testFuzz_MerkleErasures(uint8 numOps, bool fullErase) external { function testFuzz_MerkleErasures(uint8 numOps, bool fullErase) external {
vm.assume(numOps > 0 && numOps <= 16); // Small for gas vm.assume(numOps > 0 && numOps <= 8); // Low for gas optimization
uint32 rateLimit = w.minMembershipRateLimit(); uint32 rateLimit = w.minMembershipRateLimit();
uint256[] memory ids = new uint256[](numOps); uint256[] memory ids = new uint256[](numOps);
@ -412,11 +412,12 @@ contract WakuRlnV2Test is Test {
uint256(w.activeDurationForNewMemberships()) + uint256(w.gracePeriodDurationForNewMemberships()) + 1; uint256(w.activeDurationForNewMemberships()) + uint256(w.gracePeriodDurationForNewMemberships()) + 1;
vm.warp(block.timestamp + minDelta); vm.warp(block.timestamp + minDelta);
// Phase 2: Erase all (lazy or full), check proofs/roots // Phase 2: Erase all (lazy or full), check proofs/roots - sampled
w.eraseMemberships(ids, fullErase); w.eraseMemberships(ids, fullErase);
uint256 postEraseRoot = w.root(); uint256 postEraseRoot = w.root();
for (uint8 i = 0; i < numOps; i++) { for (uint8 i = 0; i < numOps; i += 2) {
// Sample every other
assertFalse(w.isInMembershipSet(ids[i])); // Erased assertFalse(w.isInMembershipSet(ids[i])); // Erased
(,, uint256 commitment) = w.getMembershipInfo(ids[i]); (,, uint256 commitment) = w.getMembershipInfo(ids[i]);
assertEq(commitment, 0); assertEq(commitment, 0);
@ -429,8 +430,9 @@ contract WakuRlnV2Test is Test {
assertTrue(_verifyMerkleProof(proof, postEraseRoot, indices[i], expectedLeaf, 20)); assertTrue(_verifyMerkleProof(proof, postEraseRoot, indices[i], expectedLeaf, 20));
} }
// Phase 3: Reuse erased indices via new registrations, check no overwrite issues // Phase 3: Reuse erased indices via new registrations, check no overwrite issues - sampled
for (uint8 i = 0; i < numOps; i++) { for (uint8 i = 0; i < numOps; i += 2) {
// Sample every other
uint256 newId = uint256(keccak256(abi.encodePacked(i + numOps, block.timestamp))) % (w.Q() - 1) + 1; uint256 newId = uint256(keccak256(abi.encodePacked(i + numOps, block.timestamp))) % (w.Q() - 1) + 1;
vm.assume(w.currentTotalRateLimit() + rateLimit <= w.maxTotalRateLimit()); vm.assume(w.currentTotalRateLimit() + rateLimit <= w.maxTotalRateLimit());
@ -448,7 +450,7 @@ contract WakuRlnV2Test is Test {
assertTrue(newRoot != postEraseRoot); // Root changed assertTrue(newRoot != postEraseRoot); // Root changed
} }
// Final invariant: Tree size matches ops (reuses don't grow beyond) // Final invariant: Tree size matches ops
assertEq(w.nextFreeIndex(), numOps); assertEq(w.nextFreeIndex(), numOps);
} }