Skip to content

Commit

Permalink
Merge branch 'kashif/limit-default' of github.com:smartcontractkit/cc…
Browse files Browse the repository at this point in the history
…ip into kashif/limit-default
  • Loading branch information
amaechiokolobi committed Jun 26, 2024
2 parents a35dc78 + bbfaf38 commit 20005ef
Show file tree
Hide file tree
Showing 60 changed files with 6,645 additions and 2,017 deletions.
6 changes: 6 additions & 0 deletions .changeset/early-readers-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"ccip": patch
---

New function on NetworkSelector to get ChainID #added
Align logs and include chainID #changed
5 changes: 5 additions & 0 deletions .changeset/wild-gifts-refuse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ccip": patch
---

Increased gas limit for LM transmitter to 5e6 #changed
1 change: 1 addition & 0 deletions .github/workflows/ci-scripts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
with:
id: scripts
name: lint-scripts
version: v1.56
go-directory: core/scripts/ccip
go-version-file: core/scripts/go.mod
go-module-file: core/scripts/go.sum
Expand Down
5 changes: 5 additions & 0 deletions contracts/.changeset/gentle-spoons-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@chainlink/contracts-ccip': minor
---

#changed MultiOffRamp contract size optimizations
551 changes: 298 additions & 253 deletions contracts/gas-snapshots/ccip.gas-snapshot

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions contracts/gas-snapshots/shared.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
AuthorizedCallers_applyAuthorizedCallerUpdates:test_AddAndRemove_Success() (gas: 125205)
AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyAdd_Success() (gas: 133100)
AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyCallableByOwner_Revert() (gas: 12350)
AuthorizedCallers_applyAuthorizedCallerUpdates:test_OnlyRemove_Success() (gas: 45064)
AuthorizedCallers_applyAuthorizedCallerUpdates:test_RemoveThenAdd_Success() (gas: 57241)
AuthorizedCallers_applyAuthorizedCallerUpdates:test_SkipRemove_Success() (gas: 32121)
AuthorizedCallers_applyAuthorizedCallerUpdates:test_ZeroAddressNotAllowed_Revert() (gas: 64473)
AuthorizedCallers_constructor:test_ZeroAddressNotAllowed_Revert() (gas: 64473)
AuthorizedCallers_constructor:test_constructor_Success() (gas: 720513)
BurnMintERC677_approve:testApproveSuccess() (gas: 55512)
BurnMintERC677_approve:testInvalidAddressReverts() (gas: 10663)
BurnMintERC677_burn:testBasicBurnSuccess() (gas: 173939)
Expand Down
8 changes: 7 additions & 1 deletion contracts/scripts/native_solc_compile_all_ccip
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ SOLC_VERSION="0.8.24"
OPTIMIZE_RUNS=26000
OPTIMIZE_RUNS_OFFRAMP=18000
OPTIMIZE_RUNS_ONRAMP=3600
OPTIMIZE_RUNS_MULTI_OFFRAMP=1800


SCRIPTPATH="$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
Expand All @@ -27,10 +28,14 @@ compileContract () {
local optimize_runs=$OPTIMIZE_RUNS

case $1 in
"ccip/offRamp/EVM2EVMOffRamp.sol" | "ccip/offRamp/EVM2EVMMultiOffRamp.sol")
"ccip/offRamp/EVM2EVMOffRamp.sol")
echo "OffRamp uses $OPTIMIZE_RUNS_OFFRAMP optimizer runs."
optimize_runs=$OPTIMIZE_RUNS_OFFRAMP
;;
"ccip/offRamp/EVM2EVMMultiOffRamp.sol")
echo "MultiOffRamp uses $OPTIMIZE_RUNS_MULTI_OFFRAMP optimizer runs."
optimize_runs=$OPTIMIZE_RUNS_MULTI_OFFRAMP
;;
"ccip/onRamp/EVM2EVMMultiOnRamp.sol" | "ccip/onRamp/EVM2EVMOnRamp.sol")
echo "OnRamp uses $OPTIMIZE_RUNS_ONRAMP optimizer runs."
optimize_runs=$OPTIMIZE_RUNS_ONRAMP
Expand Down Expand Up @@ -70,6 +75,7 @@ compileContract ccip/RMN.sol
compileContract ccip/ARMProxy.sol
compileContract ccip/tokenAdminRegistry/TokenAdminRegistry.sol
compileContract ccip/tokenAdminRegistry/RegistryModuleOwnerCustom.sol
compileContract ccip/capability/CCIPCapabilityConfiguration.sol

# Test helpers
compileContract ccip/test/helpers/BurnMintERC677Helper.sol
Expand Down
116 changes: 28 additions & 88 deletions contracts/src/v0.8/ccip/MultiAggregateRateLimiter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pragma solidity 0.8.24;
import {IMessageInterceptor} from "./interfaces/IMessageInterceptor.sol";
import {IPriceRegistry} from "./interfaces/IPriceRegistry.sol";

import {OwnerIsCreator} from "./../shared/access/OwnerIsCreator.sol";
import {AuthorizedCallers} from "../shared/access/AuthorizedCallers.sol";
import {EnumerableMapAddresses} from "./../shared/enumerable/EnumerableMapAddresses.sol";
import {Client} from "./libraries/Client.sol";
import {RateLimiter} from "./libraries/RateLimiter.sol";
Expand All @@ -17,23 +17,19 @@ import {EnumerableSet} from "./../vendor/openzeppelin-solidity/v4.7.3/contracts/
/// token transfers, using a price registry to convert to a numeraire asset (e.g. USD).
/// The contract is a standalone multi-lane message validator contract, which can be called by authorized
/// ramp contracts to apply rate limit changes to lanes, and revert when the rate limits get breached.
contract MultiAggregateRateLimiter is IMessageInterceptor, OwnerIsCreator {
contract MultiAggregateRateLimiter is IMessageInterceptor, AuthorizedCallers {
using RateLimiter for RateLimiter.TokenBucket;
using USDPriceWith18Decimals for uint224;
using EnumerableMapAddresses for EnumerableMapAddresses.AddressToBytes32Map;
using EnumerableSet for EnumerableSet.AddressSet;

error UnauthorizedCaller(address caller);
error PriceNotFoundForToken(address token);
error ZeroAddressNotAllowed();
error ZeroChainSelectorNotAllowed();

event RateLimiterConfigUpdated(uint64 indexed remoteChainSelector, bool isOutgoingLane, RateLimiter.Config config);
event RateLimiterConfigUpdated(uint64 indexed remoteChainSelector, bool isOutboundLane, RateLimiter.Config config);
event PriceRegistrySet(address newPriceRegistry);
event TokenAggregateRateLimitAdded(uint64 remoteChainSelector, bytes32 remoteToken, address localToken);
event TokenAggregateRateLimitRemoved(uint64 remoteChainSelector, address localToken);
event AuthorizedCallerAdded(address caller);
event AuthorizedCallerRemoved(address caller);

/// @notice RemoteRateLimitToken struct containing the local token address with the chain selector
/// The struct is used for removals and updates, since the local -> remote token mappings are scoped per-chain
Expand All @@ -48,54 +44,38 @@ contract MultiAggregateRateLimiter is IMessageInterceptor, OwnerIsCreator {
bytes32 remoteToken; // Token on the remote chain (for OnRamp - dest, of OffRamp - source)
}

/// @notice Update args for changing the authorized callers
struct AuthorizedCallerArgs {
address[] addedCallers;
address[] removedCallers;
}

/// @notice Update args for a single rate limiter config update
struct RateLimiterConfigArgs {
uint64 remoteChainSelector; // ────╮ Chain selector to set config for
bool isOutgoingLane; // ───────────╯ If set to true, represents the outgoing message lane (OnRamp), and the incoming message lane otherwise (OffRamp)
bool isOutboundLane; // ───────────╯ If set to true, represents the outbound message lane (OnRamp), and the inbound message lane otherwise (OffRamp)
RateLimiter.Config rateLimiterConfig; // Rate limiter config to set
}

/// @notice Struct to store rate limit token buckets for both lane directions
struct RateLimiterBuckets {
RateLimiter.TokenBucket incomingLaneBucket; // Bucket for the incoming lane (remote -> local)
RateLimiter.TokenBucket outgoingLaneBucket; // Bucket for the outgoing lane (local -> remote)
RateLimiter.TokenBucket inboundLaneBucket; // Bucket for the inbound lane (remote -> local)
RateLimiter.TokenBucket outboundLaneBucket; // Bucket for the outbound lane (local -> remote)
}

/// @dev Tokens that should be included in Aggregate Rate Limiting (from local chain (this chain) -> remote),
/// grouped per-remote chain.
mapping(uint64 remoteChainSelector => EnumerableMapAddresses.AddressToBytes32Map tokensLocalToRemote) internal
s_rateLimitedTokensLocalToRemote;

/// @dev Set of callers that can call the validation functions (this is required since the validations modify state)
EnumerableSet.AddressSet internal s_authorizedCallers;

/// @notice The address of the PriceRegistry used to query token values for ratelimiting
address internal s_priceRegistry;

/// @notice Rate limiter token bucket states per chain, with separate buckets for incoming and outgoing lanes.
/// @notice Rate limiter token bucket states per chain, with separate buckets for inbound and outbound lanes.
mapping(uint64 remoteChainSelector => RateLimiterBuckets buckets) internal s_rateLimitersByChainSelector;

/// @param priceRegistry the price registry to set
/// @param authorizedCallers the authorized callers to set
constructor(address priceRegistry, address[] memory authorizedCallers) {
constructor(address priceRegistry, address[] memory authorizedCallers) AuthorizedCallers(authorizedCallers) {
_setPriceRegistry(priceRegistry);
_applyAuthorizedCallerUpdates(
AuthorizedCallerArgs({addedCallers: authorizedCallers, removedCallers: new address[](0)})
);
}

/// @inheritdoc IMessageInterceptor
function onIncomingMessage(Client.Any2EVMMessage memory message) external {
if (!s_authorizedCallers.contains(msg.sender)) {
revert UnauthorizedCaller(msg.sender);
}

function onInboundMessage(Client.Any2EVMMessage memory message) external onlyAuthorizedCallers {
uint64 remoteChainSelector = message.sourceChainSelector;
RateLimiter.TokenBucket storage tokenBucket = _getTokenBucket(remoteChainSelector, false);

Expand All @@ -114,23 +94,23 @@ contract MultiAggregateRateLimiter is IMessageInterceptor, OwnerIsCreator {
}

/// @inheritdoc IMessageInterceptor
function onOutgoingMessage(Client.EVM2AnyMessage memory message, uint64 destChainSelector) external {
// TODO: to be implemented (assuming the same rate limiter states are shared for incoming and outgoing messages)
function onOutboundMessage(Client.EVM2AnyMessage memory message, uint64 destChainSelector) external {
// TODO: to be implemented (assuming the same rate limiter states are shared for inbound and outbound messages)
}

/// @param remoteChainSelector chain selector to retrieve token bucket for
/// @param isOutgoingLane if set to true, fetches the bucket for the outgoing message lane (OnRamp).
/// Otherwise fetches for the incoming message lane (OffRamp).
/// @param isOutboundLane if set to true, fetches the bucket for the outbound message lane (OnRamp).
/// Otherwise fetches for the inbound message lane (OffRamp).
/// @return bucket Storage pointer to the token bucket representing a specific lane
function _getTokenBucket(
uint64 remoteChainSelector,
bool isOutgoingLane
bool isOutboundLane
) internal view returns (RateLimiter.TokenBucket storage) {
RateLimiterBuckets storage rateLimiterBuckets = s_rateLimitersByChainSelector[remoteChainSelector];
if (isOutgoingLane) {
return rateLimiterBuckets.outgoingLaneBucket;
if (isOutboundLane) {
return rateLimiterBuckets.outboundLaneBucket;
} else {
return rateLimiterBuckets.incomingLaneBucket;
return rateLimiterBuckets.inboundLaneBucket;
}
}

Expand All @@ -146,15 +126,15 @@ contract MultiAggregateRateLimiter is IMessageInterceptor, OwnerIsCreator {

/// @notice Gets the token bucket with its values for the block it was requested at.
/// @param remoteChainSelector chain selector to retrieve state for
/// @param isOutgoingLane if set to true, fetches the rate limit state for the outgoing message lane (OnRamp).
/// Otherwise fetches for the incoming message lane (OffRamp).
/// The outgoing and incoming message rate limit state is completely separated.
/// @param isOutboundLane if set to true, fetches the rate limit state for the outbound message lane (OnRamp).
/// Otherwise fetches for the inbound message lane (OffRamp).
/// The outbound and inbound message rate limit state is completely separated.
/// @return The token bucket.
function currentRateLimiterState(
uint64 remoteChainSelector,
bool isOutgoingLane
bool isOutboundLane
) external view returns (RateLimiter.TokenBucket memory) {
return _getTokenBucket(remoteChainSelector, isOutgoingLane)._currentTokenBucketState();
return _getTokenBucket(remoteChainSelector, isOutboundLane)._currentTokenBucketState();
}

/// @notice Applies the provided rate limiter config updates.
Expand All @@ -170,9 +150,9 @@ contract MultiAggregateRateLimiter is IMessageInterceptor, OwnerIsCreator {
revert ZeroChainSelectorNotAllowed();
}

bool isOutgoingLane = updateArgs.isOutgoingLane;
bool isOutboundLane = updateArgs.isOutboundLane;

RateLimiter.TokenBucket storage tokenBucket = _getTokenBucket(remoteChainSelector, isOutgoingLane);
RateLimiter.TokenBucket storage tokenBucket = _getTokenBucket(remoteChainSelector, isOutboundLane);

if (tokenBucket.lastUpdated == 0) {
// Token bucket needs to be newly added
Expand All @@ -184,15 +164,15 @@ contract MultiAggregateRateLimiter is IMessageInterceptor, OwnerIsCreator {
isEnabled: configUpdate.isEnabled
});

if (isOutgoingLane) {
s_rateLimitersByChainSelector[remoteChainSelector].outgoingLaneBucket = newTokenBucket;
if (isOutboundLane) {
s_rateLimitersByChainSelector[remoteChainSelector].outboundLaneBucket = newTokenBucket;
} else {
s_rateLimitersByChainSelector[remoteChainSelector].incomingLaneBucket = newTokenBucket;
s_rateLimitersByChainSelector[remoteChainSelector].inboundLaneBucket = newTokenBucket;
}
} else {
tokenBucket._setTokenBucketConfig(configUpdate);
}
emit RateLimiterConfigUpdated(remoteChainSelector, isOutgoingLane, configUpdate);
emit RateLimiterConfigUpdated(remoteChainSelector, isOutboundLane, configUpdate);
}
}

Expand Down Expand Up @@ -276,44 +256,4 @@ contract MultiAggregateRateLimiter is IMessageInterceptor, OwnerIsCreator {
s_priceRegistry = newPriceRegistry;
emit PriceRegistrySet(newPriceRegistry);
}

// ================================================================
// │ Access │
// ================================================================

/// @return authorizedCallers Returns all callers that are authorized to call the validation functions
function getAllAuthorizedCallers() external view returns (address[] memory) {
return s_authorizedCallers.values();
}

/// @notice Updates the callers that are authorized to call the message validation functions
/// @param authorizedCallerArgs Callers to add and remove
function applyAuthorizedCallerUpdates(AuthorizedCallerArgs memory authorizedCallerArgs) external onlyOwner {
_applyAuthorizedCallerUpdates(authorizedCallerArgs);
}

/// @notice Updates the callers that are authorized to call the message validation functions
/// @param authorizedCallerArgs Callers to add and remove
function _applyAuthorizedCallerUpdates(AuthorizedCallerArgs memory authorizedCallerArgs) internal {
address[] memory removedCallers = authorizedCallerArgs.removedCallers;
for (uint256 i = 0; i < removedCallers.length; ++i) {
address caller = removedCallers[i];

if (s_authorizedCallers.remove(caller)) {
emit AuthorizedCallerRemoved(caller);
}
}

address[] memory addedCallers = authorizedCallerArgs.addedCallers;
for (uint256 i = 0; i < addedCallers.length; ++i) {
address caller = addedCallers[i];

if (caller == address(0)) {
revert ZeroAddressNotAllowed();
}

s_authorizedCallers.add(caller);
emit AuthorizedCallerAdded(caller);
}
}
}
96 changes: 96 additions & 0 deletions contracts/src/v0.8/ccip/NonceManager.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {IEVM2AnyOnRamp} from "./interfaces/IEVM2AnyOnRamp.sol";
import {INonceManager} from "./interfaces/INonceManager.sol";

import {AuthorizedCallers} from "../shared/access/AuthorizedCallers.sol";

/// @title NonceManager
/// @notice NonceManager contract that manages sender nonces for the on/off ramps
contract NonceManager is INonceManager, AuthorizedCallers {
error PreviousRampAlreadySet();

event PreviousOnRampUpdated(uint64 indexed destChainSelector, address prevOnRamp);

/// @dev Struct that contains the previous on/off ramp addresses
// TODO: add prevOffRamp
struct PreviousRamps {
address prevOnRamp; // Previous onRamp
}

/// @dev Struct that contains the chain selector and the previous on/off ramps, same as PreviousRamps but with the chain selector
/// so that an array of these can be passed to the applyPreviousRampsUpdates function
struct PreviousRampsArgs {
uint64 remoteChainSelector; // Chain selector
PreviousRamps prevRamps; // Previous on/off ramps
}

/// @dev previous ramps
mapping(uint64 chainSelector => PreviousRamps previousRamps) private s_previousRamps;
/// @dev The current outbound nonce per sender used on the onramp
mapping(uint64 destChainSelector => mapping(address sender => uint64 outboundNonce)) private s_outboundNonces;

constructor(address[] memory authorizedCallers) AuthorizedCallers(authorizedCallers) {}

/// @inheritdoc INonceManager
function getIncrementedOutboundNonce(
uint64 destChainSelector,
address sender
) external onlyAuthorizedCallers returns (uint64) {
uint64 outboundNonce = _getOutboundNonce(destChainSelector, sender) + 1;
s_outboundNonces[destChainSelector][sender] = outboundNonce;

return outboundNonce;
}

/// TODO: add incrementInboundNonce

/// @notice Returns the outbound nonce for the given sender on the given destination chain
/// @param destChainSelector The destination chain selector
/// @param sender The sender address
/// @return The outbound nonce
function getOutboundNonce(uint64 destChainSelector, address sender) external view returns (uint64) {
return _getOutboundNonce(destChainSelector, sender);
}

function _getOutboundNonce(uint64 destChainSelector, address sender) private view returns (uint64) {
uint64 outboundNonce = s_outboundNonces[destChainSelector][sender];

if (outboundNonce == 0) {
address prevOnRamp = s_previousRamps[destChainSelector].prevOnRamp;
if (prevOnRamp != address(0)) {
return IEVM2AnyOnRamp(prevOnRamp).getSenderNonce(sender);
}
}

return outboundNonce;
}

/// TODO: add getInboundNonce

/// @notice Updates the previous ramps addresses
/// @param previousRampsArgs The previous on/off ramps addresses
function applyPreviousRampsUpdates(PreviousRampsArgs[] calldata previousRampsArgs) external onlyOwner {
for (uint256 i = 0; i < previousRampsArgs.length; ++i) {
PreviousRampsArgs calldata previousRampsArg = previousRampsArgs[i];

PreviousRamps storage prevRamps = s_previousRamps[previousRampsArg.remoteChainSelector];

// If the previous onRamp is already set then it should not be updated
if (prevRamps.prevOnRamp != address(0)) {
revert PreviousRampAlreadySet();
}

prevRamps.prevOnRamp = previousRampsArg.prevRamps.prevOnRamp;
emit PreviousOnRampUpdated(previousRampsArg.remoteChainSelector, prevRamps.prevOnRamp);
}
}

/// @notice Gets the previous onRamp address for the given chain selector
/// @param chainSelector The chain selector
/// @return The previous onRamp address
function getPreviousRamps(uint64 chainSelector) external view returns (PreviousRamps memory) {
return s_previousRamps[chainSelector];
}
}
Loading

0 comments on commit 20005ef

Please sign in to comment.