@@ -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