Skip to content

Commit

Permalink
test: add tests for ccip rate limit changes (#427)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
DhairyaSethi authored Oct 11, 2024
1 parent fca6888 commit a8f654a
Showing 1 changed file with 220 additions and 4 deletions.
224 changes: 220 additions & 4 deletions src/test/TestGhoCcipSteward.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -239,17 +239,227 @@ contract TestGhoCcipSteward is TestGhoBase {
capacity: 0,
rate: 0
});

// reverts because capacity or rate cannot be set to 0 when rate limit is enabled
// this check is enforced on the token pool (see RateLimiter._validateTokenBucketConfig)
vm.prank(RISK_COUNCIL);
vm.expectRevert();
vm.expectRevert(
abi.encodeWithSelector(RateLimiter.InvalidRatelimitRate.selector, invalidConfig)
);
GHO_CCIP_STEWARD.updateRateLimit(
remoteChainSelector,
invalidConfig.isEnabled,
invalidConfig.capacity,
invalidConfig.rate,
rateLimitConfig.isEnabled,
rateLimitConfig.capacity,
rateLimitConfig.rate
invalidConfig.isEnabled,
invalidConfig.capacity,
invalidConfig.rate
);

invalidConfig.rate = 10;
vm.prank(RISK_COUNCIL);
vm.expectRevert(
abi.encodeWithSelector(RateLimiter.InvalidRatelimitRate.selector, invalidConfig)
);
GHO_CCIP_STEWARD.updateRateLimit(
remoteChainSelector,
invalidConfig.isEnabled,
invalidConfig.capacity,
invalidConfig.rate,
invalidConfig.isEnabled,
invalidConfig.capacity,
invalidConfig.rate
);
}

function testRevertUpdateRateLimitToZeroWhenDisabled() public {
RateLimiter.Config memory invalidConfig = RateLimiter.Config({
isEnabled: false,
capacity: 10,
rate: 0
});

// reverts because capacity and rate both have be set to 0 when rate limit is disabled
// this check is enforced on the token pool (see RateLimiter._validateTokenBucketConfig)
vm.prank(RISK_COUNCIL);
vm.expectRevert(
abi.encodeWithSelector(RateLimiter.DisabledNonZeroRateLimit.selector, invalidConfig)
);
GHO_CCIP_STEWARD.updateRateLimit(
remoteChainSelector,
invalidConfig.isEnabled,
invalidConfig.capacity,
invalidConfig.rate,
invalidConfig.isEnabled,
invalidConfig.capacity,
invalidConfig.rate
);
}

function testDisableRateLimit() public {
RateLimiter.TokenBucket memory outboundConfig = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentOutboundRateLimiterState(remoteChainSelector);
RateLimiter.TokenBucket memory inboundConfig = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentInboundRateLimiterState(remoteChainSelector);

// assert both inbound & outbound rate limiters are enabled
assertTrue(outboundConfig.isEnabled);
assertGt(outboundConfig.capacity, 0);
assertGt(outboundConfig.rate, 0);

assertTrue(inboundConfig.isEnabled);
assertGt(inboundConfig.capacity, 0);
assertGt(inboundConfig.rate, 0);

// capacity and rate both have be set to 0 when rate limit is disabled, enforced by token pool
RateLimiter.Config memory disableLimitConfig = RateLimiter.Config({
isEnabled: false,
capacity: 0,
rate: 0
});

// disable both inbound & outbound config
vm.prank(RISK_COUNCIL);
GHO_CCIP_STEWARD.updateRateLimit(
remoteChainSelector,
disableLimitConfig.isEnabled,
disableLimitConfig.capacity,
disableLimitConfig.rate,
disableLimitConfig.isEnabled,
disableLimitConfig.capacity,
disableLimitConfig.rate
);

outboundConfig = MockUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL)
.getCurrentOutboundRateLimiterState(remoteChainSelector);
inboundConfig = MockUpgradeableLockReleaseTokenPool(GHO_TOKEN_POOL)
.getCurrentInboundRateLimiterState(remoteChainSelector);

assertFalse(outboundConfig.isEnabled);
assertEq(outboundConfig.capacity, 0);
assertEq(outboundConfig.rate, 0);

assertFalse(inboundConfig.isEnabled);
assertEq(inboundConfig.capacity, 0);
assertEq(inboundConfig.rate, 0);
}

function testChangeEnabledRateLimit() public {
RateLimiter.TokenBucket memory outboundConfig = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentOutboundRateLimiterState(remoteChainSelector);
RateLimiter.TokenBucket memory inboundConfig = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentInboundRateLimiterState(remoteChainSelector);

RateLimiter.Config memory disableLimitConfig = RateLimiter.Config({
isEnabled: false,
capacity: 0,
rate: 0
});

// disable both inbound & outbound config
vm.prank(RISK_COUNCIL);
GHO_CCIP_STEWARD.updateRateLimit(
remoteChainSelector,
disableLimitConfig.isEnabled,
disableLimitConfig.capacity,
disableLimitConfig.rate,
disableLimitConfig.isEnabled,
disableLimitConfig.capacity,
disableLimitConfig.rate
);

skip(GHO_CCIP_STEWARD.MINIMUM_DELAY() + 1);

// steward is not allowed to re-enable rate limit
vm.expectRevert('INVALID_RATE_LIMIT_UPDATE');
vm.prank(RISK_COUNCIL);
GHO_CCIP_STEWARD.updateRateLimit(
remoteChainSelector,
outboundConfig.isEnabled,
outboundConfig.capacity,
outboundConfig.rate,
inboundConfig.isEnabled,
inboundConfig.capacity,
inboundConfig.rate
);

// risk admin/DAO can re-enable rate limit on token pool
vm.prank(GHO_TOKEN_POOL.owner());
GHO_TOKEN_POOL.setChainRateLimiterConfig(
remoteChainSelector,
_castTokenBucketToConfig(outboundConfig),
_castTokenBucketToConfig(inboundConfig)
);

RateLimiter.TokenBucket memory outboundConfigNew = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentOutboundRateLimiterState(remoteChainSelector);
RateLimiter.TokenBucket memory inboundConfigNew = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentInboundRateLimiterState(remoteChainSelector);

assertTrue(outboundConfigNew.isEnabled);
assertEq(outboundConfigNew.capacity, outboundConfig.capacity);
assertEq(outboundConfigNew.rate, outboundConfig.rate);

assertTrue(inboundConfigNew.isEnabled);
assertEq(inboundConfigNew.capacity, inboundConfig.capacity);
assertEq(inboundConfigNew.rate, inboundConfig.rate);
}

function testChangeEnabledRateLimitOnlyOneSide() public {
RateLimiter.TokenBucket memory outboundConfig = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentOutboundRateLimiterState(remoteChainSelector);
RateLimiter.TokenBucket memory inboundConfig = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentInboundRateLimiterState(remoteChainSelector);

assertTrue(outboundConfig.isEnabled);
assertGt(outboundConfig.capacity, 0);
assertGt(outboundConfig.rate, 0);

assertTrue(inboundConfig.isEnabled);
assertGt(inboundConfig.capacity, 0);
assertGt(inboundConfig.rate, 0);

RateLimiter.Config memory disableLimitConfig = RateLimiter.Config({
isEnabled: false,
capacity: 0,
rate: 0
});

// disable only outbound config
vm.prank(RISK_COUNCIL);
GHO_CCIP_STEWARD.updateRateLimit(
remoteChainSelector,
disableLimitConfig.isEnabled,
disableLimitConfig.capacity,
disableLimitConfig.rate,
// preserve inboundConfig
inboundConfig.isEnabled,
inboundConfig.capacity,
inboundConfig.rate
);

RateLimiter.TokenBucket memory outboundConfigNew = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentOutboundRateLimiterState(remoteChainSelector);
RateLimiter.TokenBucket memory inboundConfigNew = MockUpgradeableLockReleaseTokenPool(
GHO_TOKEN_POOL
).getCurrentInboundRateLimiterState(remoteChainSelector);

assertFalse(outboundConfigNew.isEnabled);
assertEq(outboundConfigNew.capacity, 0);
assertEq(outboundConfigNew.rate, 0);

assertTrue(inboundConfigNew.isEnabled);
assertEq(inboundConfigNew.capacity, inboundConfig.capacity);
assertEq(inboundConfigNew.rate, inboundConfig.rate);
}

function testRevertUpdateRateLimitRateGreaterThanCapacity() public {
Expand Down Expand Up @@ -305,4 +515,10 @@ contract TestGhoCcipSteward is TestGhoBase {
inboundRate
);
}

function _castTokenBucketToConfig(
RateLimiter.TokenBucket memory arg
) private view returns (RateLimiter.Config memory) {
return RateLimiter.Config({isEnabled: arg.isEnabled, capacity: arg.capacity, rate: arg.rate});
}
}

0 comments on commit a8f654a

Please sign in to comment.