Skip to content

Commit a8f654a

Browse files
authored
test: add tests for ccip rate limit changes (#427)
* test(ccip-steward): disable rate limit * test: fix revert upd rate limit to zero * test: fix change{enabled,disabled}rateLimit * test: document rate limit update revert code path, rename test
1 parent fca6888 commit a8f654a

File tree

1 file changed

+220
-4
lines changed

1 file changed

+220
-4
lines changed

src/test/TestGhoCcipSteward.t.sol

Lines changed: 220 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,17 +239,227 @@ contract TestGhoCcipSteward is TestGhoBase {
239239
capacity: 0,
240240
rate: 0
241241
});
242+
243+
// reverts because capacity or rate cannot be set to 0 when rate limit is enabled
244+
// this check is enforced on the token pool (see RateLimiter._validateTokenBucketConfig)
242245
vm.prank(RISK_COUNCIL);
243-
vm.expectRevert();
246+
vm.expectRevert(
247+
abi.encodeWithSelector(RateLimiter.InvalidRatelimitRate.selector, invalidConfig)
248+
);
244249
GHO_CCIP_STEWARD.updateRateLimit(
245250
remoteChainSelector,
246251
invalidConfig.isEnabled,
247252
invalidConfig.capacity,
248253
invalidConfig.rate,
249-
rateLimitConfig.isEnabled,
250-
rateLimitConfig.capacity,
251-
rateLimitConfig.rate
254+
invalidConfig.isEnabled,
255+
invalidConfig.capacity,
256+
invalidConfig.rate
257+
);
258+
259+
invalidConfig.rate = 10;
260+
vm.prank(RISK_COUNCIL);
261+
vm.expectRevert(
262+
abi.encodeWithSelector(RateLimiter.InvalidRatelimitRate.selector, invalidConfig)
263+
);
264+
GHO_CCIP_STEWARD.updateRateLimit(
265+
remoteChainSelector,
266+
invalidConfig.isEnabled,
267+
invalidConfig.capacity,
268+
invalidConfig.rate,
269+
invalidConfig.isEnabled,
270+
invalidConfig.capacity,
271+
invalidConfig.rate
272+
);
273+
}
274+
275+
function testRevertUpdateRateLimitToZeroWhenDisabled() public {
276+
RateLimiter.Config memory invalidConfig = RateLimiter.Config({
277+
isEnabled: false,
278+
capacity: 10,
279+
rate: 0
280+
});
281+
282+
// reverts because capacity and rate both have be set to 0 when rate limit is disabled
283+
// this check is enforced on the token pool (see RateLimiter._validateTokenBucketConfig)
284+
vm.prank(RISK_COUNCIL);
285+
vm.expectRevert(
286+
abi.encodeWithSelector(RateLimiter.DisabledNonZeroRateLimit.selector, invalidConfig)
287+
);
288+
GHO_CCIP_STEWARD.updateRateLimit(
289+
remoteChainSelector,
290+
invalidConfig.isEnabled,
291+
invalidConfig.capacity,
292+
invalidConfig.rate,
293+
invalidConfig.isEnabled,
294+
invalidConfig.capacity,
295+
invalidConfig.rate
296+
);
297+
}
298+
299+
function testDisableRateLimit() public {
300+
RateLimiter.TokenBucket memory outboundConfig = MockUpgradeableLockReleaseTokenPool(
301+
GHO_TOKEN_POOL
302+
).getCurrentOutboundRateLimiterState(remoteChainSelector);
303+
RateLimiter.TokenBucket memory inboundConfig = MockUpgradeableLockReleaseTokenPool(
304+
GHO_TOKEN_POOL
305+
).getCurrentInboundRateLimiterState(remoteChainSelector);
306+
307+
// assert both inbound & outbound rate limiters are enabled
308+
assertTrue(outboundConfig.isEnabled);
309+
assertGt(outboundConfig.capacity, 0);
310+
assertGt(outboundConfig.rate, 0);
311+
312+
assertTrue(inboundConfig.isEnabled);
313+
assertGt(inboundConfig.capacity, 0);
314+
assertGt(inboundConfig.rate, 0);
315+
316+
// capacity and rate both have be set to 0 when rate limit is disabled, enforced by token pool
317+
RateLimiter.Config memory disableLimitConfig = RateLimiter.Config({
318+
isEnabled: false,
319+
capacity: 0,
320+
rate: 0
321+
});
322+
323+
// disable both inbound & outbound config
324+
vm.prank(RISK_COUNCIL);
325+
GHO_CCIP_STEWARD.updateRateLimit(
326+
remoteChainSelector,
327+
disableLimitConfig.isEnabled,
328+
disableLimitConfig.capacity,
329+
disableLimitConfig.rate,
330+
disableLimitConfig.isEnabled,
331+
disableLimitConfig.capacity,
332+
disableLimitConfig.rate
333+
);
334+
335+
outboundConfig = MockUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL)
336+
.getCurrentOutboundRateLimiterState(remoteChainSelector);
337+
inboundConfig = MockUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL)
338+
.getCurrentInboundRateLimiterState(remoteChainSelector);
339+
340+
assertFalse(outboundConfig.isEnabled);
341+
assertEq(outboundConfig.capacity, 0);
342+
assertEq(outboundConfig.rate, 0);
343+
344+
assertFalse(inboundConfig.isEnabled);
345+
assertEq(inboundConfig.capacity, 0);
346+
assertEq(inboundConfig.rate, 0);
347+
}
348+
349+
function testChangeEnabledRateLimit() public {
350+
RateLimiter.TokenBucket memory outboundConfig = MockUpgradeableLockReleaseTokenPool(
351+
GHO_TOKEN_POOL
352+
).getCurrentOutboundRateLimiterState(remoteChainSelector);
353+
RateLimiter.TokenBucket memory inboundConfig = MockUpgradeableLockReleaseTokenPool(
354+
GHO_TOKEN_POOL
355+
).getCurrentInboundRateLimiterState(remoteChainSelector);
356+
357+
RateLimiter.Config memory disableLimitConfig = RateLimiter.Config({
358+
isEnabled: false,
359+
capacity: 0,
360+
rate: 0
361+
});
362+
363+
// disable both inbound & outbound config
364+
vm.prank(RISK_COUNCIL);
365+
GHO_CCIP_STEWARD.updateRateLimit(
366+
remoteChainSelector,
367+
disableLimitConfig.isEnabled,
368+
disableLimitConfig.capacity,
369+
disableLimitConfig.rate,
370+
disableLimitConfig.isEnabled,
371+
disableLimitConfig.capacity,
372+
disableLimitConfig.rate
373+
);
374+
375+
skip(GHO_CCIP_STEWARD.MINIMUM_DELAY() + 1);
376+
377+
// steward is not allowed to re-enable rate limit
378+
vm.expectRevert('INVALID_RATE_LIMIT_UPDATE');
379+
vm.prank(RISK_COUNCIL);
380+
GHO_CCIP_STEWARD.updateRateLimit(
381+
remoteChainSelector,
382+
outboundConfig.isEnabled,
383+
outboundConfig.capacity,
384+
outboundConfig.rate,
385+
inboundConfig.isEnabled,
386+
inboundConfig.capacity,
387+
inboundConfig.rate
388+
);
389+
390+
// risk admin/DAO can re-enable rate limit on token pool
391+
vm.prank(GHO_TOKEN_POOL.owner());
392+
GHO_TOKEN_POOL.setChainRateLimiterConfig(
393+
remoteChainSelector,
394+
_castTokenBucketToConfig(outboundConfig),
395+
_castTokenBucketToConfig(inboundConfig)
396+
);
397+
398+
RateLimiter.TokenBucket memory outboundConfigNew = MockUpgradeableLockReleaseTokenPool(
399+
GHO_TOKEN_POOL
400+
).getCurrentOutboundRateLimiterState(remoteChainSelector);
401+
RateLimiter.TokenBucket memory inboundConfigNew = MockUpgradeableLockReleaseTokenPool(
402+
GHO_TOKEN_POOL
403+
).getCurrentInboundRateLimiterState(remoteChainSelector);
404+
405+
assertTrue(outboundConfigNew.isEnabled);
406+
assertEq(outboundConfigNew.capacity, outboundConfig.capacity);
407+
assertEq(outboundConfigNew.rate, outboundConfig.rate);
408+
409+
assertTrue(inboundConfigNew.isEnabled);
410+
assertEq(inboundConfigNew.capacity, inboundConfig.capacity);
411+
assertEq(inboundConfigNew.rate, inboundConfig.rate);
412+
}
413+
414+
function testChangeEnabledRateLimitOnlyOneSide() public {
415+
RateLimiter.TokenBucket memory outboundConfig = MockUpgradeableLockReleaseTokenPool(
416+
GHO_TOKEN_POOL
417+
).getCurrentOutboundRateLimiterState(remoteChainSelector);
418+
RateLimiter.TokenBucket memory inboundConfig = MockUpgradeableLockReleaseTokenPool(
419+
GHO_TOKEN_POOL
420+
).getCurrentInboundRateLimiterState(remoteChainSelector);
421+
422+
assertTrue(outboundConfig.isEnabled);
423+
assertGt(outboundConfig.capacity, 0);
424+
assertGt(outboundConfig.rate, 0);
425+
426+
assertTrue(inboundConfig.isEnabled);
427+
assertGt(inboundConfig.capacity, 0);
428+
assertGt(inboundConfig.rate, 0);
429+
430+
RateLimiter.Config memory disableLimitConfig = RateLimiter.Config({
431+
isEnabled: false,
432+
capacity: 0,
433+
rate: 0
434+
});
435+
436+
// disable only outbound config
437+
vm.prank(RISK_COUNCIL);
438+
GHO_CCIP_STEWARD.updateRateLimit(
439+
remoteChainSelector,
440+
disableLimitConfig.isEnabled,
441+
disableLimitConfig.capacity,
442+
disableLimitConfig.rate,
443+
// preserve inboundConfig
444+
inboundConfig.isEnabled,
445+
inboundConfig.capacity,
446+
inboundConfig.rate
252447
);
448+
449+
RateLimiter.TokenBucket memory outboundConfigNew = MockUpgradeableLockReleaseTokenPool(
450+
GHO_TOKEN_POOL
451+
).getCurrentOutboundRateLimiterState(remoteChainSelector);
452+
RateLimiter.TokenBucket memory inboundConfigNew = MockUpgradeableLockReleaseTokenPool(
453+
GHO_TOKEN_POOL
454+
).getCurrentInboundRateLimiterState(remoteChainSelector);
455+
456+
assertFalse(outboundConfigNew.isEnabled);
457+
assertEq(outboundConfigNew.capacity, 0);
458+
assertEq(outboundConfigNew.rate, 0);
459+
460+
assertTrue(inboundConfigNew.isEnabled);
461+
assertEq(inboundConfigNew.capacity, inboundConfig.capacity);
462+
assertEq(inboundConfigNew.rate, inboundConfig.rate);
253463
}
254464

255465
function testRevertUpdateRateLimitRateGreaterThanCapacity() public {
@@ -305,4 +515,10 @@ contract TestGhoCcipSteward is TestGhoBase {
305515
inboundRate
306516
);
307517
}
518+
519+
function _castTokenBucketToConfig(
520+
RateLimiter.TokenBucket memory arg
521+
) private view returns (RateLimiter.Config memory) {
522+
return RateLimiter.Config({isEnabled: arg.isEnabled, capacity: arg.capacity, rate: arg.rate});
523+
}
308524
}

0 commit comments

Comments
 (0)