mirror of
https://github.com/logos-messaging/logos-messaging-rlnv2-contract.git
synced 2026-01-05 15:33:08 +00:00
Add root cache tests and emit root storage event
This commit is contained in:
parent
1fd8247640
commit
ee07450b65
@ -37,6 +37,10 @@ contract WakuRlnV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable, Member
|
||||
/// @notice The Merkle tree that stores rate commitments of memberships
|
||||
LazyIMTData public merkleTree;
|
||||
|
||||
/// @notice Emitted whenever a new Merkle tree root is stored
|
||||
/// @param newRoot The newly stored Merkle tree root
|
||||
event RootStored(uint256 newRoot);
|
||||
|
||||
/// @notice Сheck if the idCommitment is valid
|
||||
/// @param idCommitment The idCommitment of the membership
|
||||
modifier onlyValidIdCommitment(uint256 idCommitment) {
|
||||
@ -59,6 +63,7 @@ contract WakuRlnV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable, Member
|
||||
|
||||
// Fixed-size circular buffer for recent roots
|
||||
uint8 public constant HISTORY_SIZE = 5;
|
||||
|
||||
/// @notice Fixed-size circular buffer storing the most recent HISTORY_SIZE roots
|
||||
/// @dev Organized as a ring buffer where rootIndex points to the next write position
|
||||
uint256[HISTORY_SIZE] private recentRoots;
|
||||
@ -203,6 +208,8 @@ contract WakuRlnV2 is Initializable, OwnableUpgradeable, UUPSUpgradeable, Member
|
||||
recentRoots[rootIndex] = newRoot;
|
||||
rootIndex = (rootIndex + 1) % HISTORY_SIZE;
|
||||
}
|
||||
|
||||
emit RootStored(newRoot);
|
||||
}
|
||||
|
||||
/// @notice Returns the list of recent roots, newest first
|
||||
|
||||
@ -1279,6 +1279,24 @@ contract WakuRlnV2Test is Test {
|
||||
assertEq(token.balanceOf(address(w)), newPrice);
|
||||
}
|
||||
|
||||
function test__RootStoredEvent_OnRegister() external {
|
||||
uint32 rateLimit = w.minMembershipRateLimit();
|
||||
(, uint256 price) = w.priceCalculator().calculate(rateLimit);
|
||||
|
||||
token.approve(address(w), price);
|
||||
uint256 idCommitment = 42;
|
||||
|
||||
// First: expect RootStored (emitted from _upsertInTree via _storeNewRoot)
|
||||
vm.expectEmit(false, false, false, false); // only match event signature
|
||||
emit WakuRlnV2.RootStored(0); // value ignored because all data=false
|
||||
|
||||
// Then: expect MembershipRegistered (emitted afterward)
|
||||
vm.expectEmit(true, true, false, false); // check idCommitment + rateLimit
|
||||
emit MembershipUpgradeable.MembershipRegistered(idCommitment, rateLimit, 0);
|
||||
|
||||
w.register(idCommitment, rateLimit, noIdCommitmentsToErase);
|
||||
}
|
||||
|
||||
function test__ZeroPriceEdgeCase() external {
|
||||
MockPriceCalculator zeroPriceCalc = new MockPriceCalculator(address(token), 0);
|
||||
|
||||
@ -1699,4 +1717,118 @@ contract WakuRlnV2Test is Test {
|
||||
assertEq(recent[4], r1);
|
||||
}
|
||||
}
|
||||
|
||||
function test__RecentRoots_BatchFullEraseNoWrap() external {
|
||||
uint256 initialRoot = w.root();
|
||||
uint32 rate = w.minMembershipRateLimit();
|
||||
(, uint256 price) = w.priceCalculator().calculate(rate);
|
||||
|
||||
// Register 2 memberships to avoid wrap-around (2 registers + 2 erases = 4 < 5)
|
||||
uint256 root1;
|
||||
token.approve(address(w), price);
|
||||
w.register(1, rate, noIdCommitmentsToErase);
|
||||
root1 = w.root();
|
||||
|
||||
uint256 root2;
|
||||
token.approve(address(w), price);
|
||||
w.register(2, rate, noIdCommitmentsToErase);
|
||||
root2 = w.root();
|
||||
|
||||
// Warp to expire all (assuming same registration time, all expire together)
|
||||
(,, uint256 graceStart,,,,,) = w.memberships(1);
|
||||
vm.warp(graceStart + w.gracePeriodDurationForNewMemberships() + 1);
|
||||
|
||||
// Prepare ids to erase
|
||||
uint256[] memory ids = new uint256[](2);
|
||||
ids[0] = 1;
|
||||
ids[1] = 2;
|
||||
|
||||
// Full erase all 2 in one tx
|
||||
w.eraseMemberships(ids, true);
|
||||
|
||||
// After erases, roots added for each update
|
||||
uint256 finalRoot = w.root(); // Final root after all erases (should be initial empty tree root)
|
||||
assertEq(finalRoot, initialRoot);
|
||||
|
||||
// History should have the 2 new roots from erases + previous 2, with one 0
|
||||
uint256[HISTORY] memory recent = w.getRecentRoots();
|
||||
assertEq(recent[0], finalRoot); // Newest: after last erase (initialRoot)
|
||||
assertEq(recent[2], root1); // After second register
|
||||
assertEq(recent[3], 0); // After first register
|
||||
assertEq(recent[4], 0); // Unused slot
|
||||
// recent[1] = after first erase (intermediate, non-zero, different)
|
||||
assertNotEq(recent[1], 0);
|
||||
assertNotEq(recent[1], recent[0]);
|
||||
assertNotEq(recent[1], recent[2]);
|
||||
}
|
||||
|
||||
function test__RecentRoots_BatchFullEraseWithWrap() external {
|
||||
uint256 initialRoot = w.root();
|
||||
uint32 rate = w.minMembershipRateLimit();
|
||||
(, uint256 price) = w.priceCalculator().calculate(rate);
|
||||
|
||||
// Register 5 memberships to fill history
|
||||
for (uint256 i = 1; i <= 5; i++) {
|
||||
token.approve(address(w), price);
|
||||
w.register(i, rate, noIdCommitmentsToErase);
|
||||
}
|
||||
uint256 root5 = w.root();
|
||||
|
||||
// Warp to expire all
|
||||
(,, uint256 graceStart,,,,,) = w.memberships(1);
|
||||
vm.warp(graceStart + w.gracePeriodDurationForNewMemberships() + 1);
|
||||
|
||||
// Erase ids one at a time to update root each time
|
||||
uint256[] memory ids = new uint256[](1);
|
||||
for (uint256 i = 0; i < 5; i++) {
|
||||
ids[0] = i + 1;
|
||||
w.eraseMemberships(ids, true);
|
||||
}
|
||||
|
||||
uint256 finalRoot = w.root(); // Should be initial empty tree root
|
||||
assertEq(finalRoot, initialRoot);
|
||||
|
||||
// History should now contain the 5 new roots from the erases, newest first
|
||||
uint256[HISTORY] memory recent = w.getRecentRoots();
|
||||
assertEq(recent[0], finalRoot);
|
||||
|
||||
// Assert no old roots remain (e.g., root5 not in history)
|
||||
for (uint8 i = 0; i < HISTORY; i++) {
|
||||
assertNotEq(recent[i], root5);
|
||||
assertNotEq(recent[i], 0);
|
||||
}
|
||||
|
||||
// Check getRootAt
|
||||
assertEq(w.getRootAt(0), finalRoot);
|
||||
}
|
||||
|
||||
function test__RecentRoots_UpgradePreservesHistory() external {
|
||||
uint32 rate = w.minMembershipRateLimit();
|
||||
(, uint256 price) = w.priceCalculator().calculate(rate);
|
||||
|
||||
// Fill history with 6 registers to have wrap
|
||||
for (uint256 i = 1; i <= 6; i++) {
|
||||
token.approve(address(w), price);
|
||||
w.register(i, rate, noIdCommitmentsToErase);
|
||||
}
|
||||
uint256[HISTORY] memory preUpgradeRecent = w.getRecentRoots();
|
||||
|
||||
// Deploy new implementation (assuming same contract for simplicity, or a new version)
|
||||
address newImpl = address(new WakuRlnV2());
|
||||
|
||||
// Upgrade as owner
|
||||
vm.prank(w.owner());
|
||||
w.upgradeTo(newImpl);
|
||||
|
||||
// Check history unchanged
|
||||
uint256[HISTORY] memory postUpgradeRecent = w.getRecentRoots();
|
||||
for (uint8 i = 0; i < HISTORY; i++) {
|
||||
assertEq(postUpgradeRecent[i], preUpgradeRecent[i]);
|
||||
}
|
||||
|
||||
// Further operations work
|
||||
token.approve(address(w), price);
|
||||
w.register(7, rate, noIdCommitmentsToErase);
|
||||
assertNotEq(w.getRootAt(0), preUpgradeRecent[0]);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user