Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
677 changes: 669 additions & 8 deletions chains/evm/.gas-snapshot

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions chains/evm/contracts/ccvs/components/BaseVerifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.24;

import {ICrossChainVerifierV1} from "../../interfaces/ICrossChainVerifierV1.sol";
import {IRMNRemote} from "../../interfaces/IRMNRemote.sol";
import {IRMN} from "../../interfaces/IRMN.sol";
import {IRouter} from "../../interfaces/IRouter.sol";
import {ITypeAndVersion} from "@chainlink/contracts/src/v0.8/shared/interfaces/ITypeAndVersion.sol";

Expand Down Expand Up @@ -60,7 +60,7 @@ abstract contract BaseVerifier is ICrossChainVerifierV1, ITypeAndVersion {
}

/// @dev The rmn contract.
IRMNRemote internal immutable i_rmn;
IRMN internal immutable i_rmn;
/// @dev The version tag that this instance of the verifier supports. This could be used as a domain separator, but
/// should never be the primary defense against signature replay attacks across different verifiers. The primary
/// defense should be using non-overlapping signer sets for different verifiers, and the version tag should only be
Expand Down Expand Up @@ -93,7 +93,7 @@ abstract contract BaseVerifier is ICrossChainVerifierV1, ITypeAndVersion {
if (_versionTag == bytes4(0)) revert VersionTagCannotBeZero();
i_versionTag = _versionTag;

i_rmn = IRMNRemote(rmnAddress);
i_rmn = IRMN(rmnAddress);
}

/// @notice Updates the storage locations.
Expand Down
19 changes: 7 additions & 12 deletions chains/evm/contracts/interfaces/IRMN.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,17 @@ pragma solidity ^0.8.0;

/// @notice This interface contains the only RMN-related functions that might be used on-chain by other CCIP contracts.
interface IRMN {
/// @notice A Merkle root tagged with the address of the commit store contract it is destined for.
struct TaggedRoot {
address commitStore;
bytes32 root;
}
/// @notice gets the current set of cursed subjects.
/// @return subjects the list of cursed subjects.
function getCursedSubjects() external view returns (bytes16[] memory subjects);

/// @notice Callers MUST NOT cache the return value as a blessed tagged root could become unblessed.
function isBlessed(
TaggedRoot calldata taggedRoot
) external view returns (bool);

/// @notice Iff there is an active global or legacy curse, this function returns true.
/// @notice If there is an active global or legacy curse, this function returns true.
/// @return bool true if there is an active global curse.
function isCursed() external view returns (bool);

/// @notice Iff there is an active global curse, or an active curse for `subject`, this function returns true.
/// @notice If there is an active global curse, or an active curse for `subject`, this function returns true.
/// @param subject To check whether a particular chain is cursed, set to bytes16(uint128(chainSelector)).
/// @return bool true if the provided subject is cursed *or* if there is an active global curse.
function isCursed(
bytes16 subject
) external view returns (bool);
Expand Down
39 changes: 0 additions & 39 deletions chains/evm/contracts/interfaces/IRMNRemote.sol

This file was deleted.

6 changes: 3 additions & 3 deletions chains/evm/contracts/offRamp/OffRamp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {ICrossChainVerifierResolver} from "../interfaces/ICrossChainVerifierReso
import {ICrossChainVerifierV1} from "../interfaces/ICrossChainVerifierV1.sol";
import {IPoolV1} from "../interfaces/IPool.sol";
import {IPoolV2} from "../interfaces/IPoolV2.sol";
import {IRMNRemote} from "../interfaces/IRMNRemote.sol";
import {IRMN} from "../interfaces/IRMN.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol";
import {ITypeAndVersion} from "@chainlink/contracts/src/v0.8/shared/interfaces/ITypeAndVersion.sol";
Expand Down Expand Up @@ -69,7 +69,7 @@ contract OffRamp is ITypeAndVersion, Ownable2StepMsgSender {
struct StaticConfig {
uint64 localChainSelector; // ──╮ Local chainSelector
uint16 gasForCallExactCheck; // │ Gas for call exact check
IRMNRemote rmnRemote; // ───────╯ RMN Verification Contract
IRMN rmnRemote; // ───────╯ RMN Verification Contract
address tokenAdminRegistry; // ───────╮ Token admin registry address
uint32 maxGasBufferToUpdateState; // ─╯ Max Gas Buffer to Update State
}
Expand Down Expand Up @@ -102,7 +102,7 @@ contract OffRamp is ITypeAndVersion, Ownable2StepMsgSender {
/// @dev ChainSelector of this chain.
uint64 internal immutable i_chainSelector;
/// @dev The RMN verification contract.
IRMNRemote internal immutable i_rmnRemote;
IRMN internal immutable i_rmnRemote;
/// @dev The address of the token admin registry.
address internal immutable i_tokenAdminRegistry;
/// @dev The minimum amount of gas to perform the call with exact gas.
Expand Down
6 changes: 3 additions & 3 deletions chains/evm/contracts/onRamp/OnRamp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {IExecutor} from "../interfaces/IExecutor.sol";
import {IFeeQuoter} from "../interfaces/IFeeQuoter.sol";
import {IPoolV1} from "../interfaces/IPool.sol";
import {IPoolV2} from "../interfaces/IPoolV2.sol";
import {IRMNRemote} from "../interfaces/IRMNRemote.sol";
import {IRMN} from "../interfaces/IRMN.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {ITokenAdminRegistry} from "../interfaces/ITokenAdminRegistry.sol";
import {ITypeAndVersion} from "@chainlink/contracts/src/v0.8/shared/interfaces/ITypeAndVersion.sol";
Expand Down Expand Up @@ -78,7 +78,7 @@ contract OnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, Ownable2StepMsgSender
// solhint-disable-next-line gas-struct-packing
struct StaticConfig {
uint64 chainSelector; // ─────────╮ Local chain selector.
IRMNRemote rmnRemote; // │ RMN remote address.
IRMN rmnRemote; // │ RMN remote address.
uint32 maxUSDCentsPerMessage; // ─╯ Maximum USD cent value per message.
address tokenAdminRegistry; // Token admin registry address.
}
Expand Down Expand Up @@ -150,7 +150,7 @@ contract OnRamp is IEVM2AnyOnRampClient, ITypeAndVersion, Ownable2StepMsgSender
/// @dev The chain ID of the source chain that this contract is deployed to.
uint64 private immutable i_localChainSelector;
/// @dev The rmn contract.
IRMNRemote private immutable i_rmnRemote;
IRMN private immutable i_rmnRemote;
/// @dev The address of the token admin registry.
address private immutable i_tokenAdminRegistry;
/// @dev The maximum USD cent value per message. Used to reduce impact of potential misconfigurations.
Expand Down
113 changes: 113 additions & 0 deletions chains/evm/contracts/rmn/RMN.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;

import {IRMN} from "../interfaces/IRMN.sol";
import {ITypeAndVersion} from "@chainlink/contracts/src/v0.8/shared/interfaces/ITypeAndVersion.sol";

import {AuthorizedCallers} from "@chainlink/contracts/src/v0.8/shared/access/AuthorizedCallers.sol";
import {EnumerableSet} from "@chainlink/contracts/src/v0.8/shared/enumerable/EnumerableSetWithBytes16.sol";

/// @dev An active curse on this subject will cause isCursed() and isCursed(bytes16) to return true. Use this subject
/// for issues affecting all of CCIP chains, or pertaining to the chain that this contract is deployed on, instead of
/// using the local chain selector as a subject.
bytes16 constant GLOBAL_CURSE_SUBJECT = 0x01000000000000000000000000000001;

/// @notice This contract supports cursing and uncursing of chains.
contract RMN is AuthorizedCallers, ITypeAndVersion, IRMN {
using EnumerableSet for EnumerableSet.Bytes16Set;

error AlreadyCursed(bytes16 subject);
error NotCursed(bytes16 subject);

event Cursed(bytes16[] subjects);
event Uncursed(bytes16[] subjects);

string public constant override typeAndVersion = "RMN 2.1.0";

EnumerableSet.Bytes16Set private s_cursedSubjects;

/// @param curseAdmins initial set of addresses authorized to call curse.
constructor(
address[] memory curseAdmins
) AuthorizedCallers(curseAdmins) {}

// ================================================================
// │ Cursing │
// ================================================================

/// @notice Curse a single subject.
/// @param subject the subject to curse.
function curse(
bytes16 subject
) external {
bytes16[] memory subjects = new bytes16[](1);
subjects[0] = subject;
curse(subjects);
}

/// @notice Curse an array of subjects.
/// @param subjects the subjects to curse.
/// @dev reverts if any of the subjects are already cursed or if there is a duplicate.
function curse(
bytes16[] memory subjects
) public {
if (msg.sender != owner()) _validateCaller();
for (uint256 i = 0; i < subjects.length; ++i) {
if (!s_cursedSubjects.add(subjects[i])) {
revert AlreadyCursed(subjects[i]);
}
}
emit Cursed(subjects);
}

/// @notice Uncurse a single subject.
/// @param subject the subject to uncurse.
function uncurse(
bytes16 subject
) external {
bytes16[] memory subjects = new bytes16[](1);
subjects[0] = subject;
uncurse(subjects);
}

/// @notice Uncurse an array of subjects.
/// @param subjects the subjects to uncurse.
/// @dev reverts if any of the subjects are not cursed or if there is a duplicate.
function uncurse(
bytes16[] memory subjects
) public onlyOwner {
for (uint256 i = 0; i < subjects.length; ++i) {
if (!s_cursedSubjects.remove(subjects[i])) {
revert NotCursed(subjects[i]);
}
}
emit Uncursed(subjects);
}

/// @inheritdoc IRMN
function getCursedSubjects() external view returns (bytes16[] memory subjects) {
return s_cursedSubjects.values();
}

/// @inheritdoc IRMN
function isCursed() external view override returns (bool) {
// There are zero curses under normal circumstances, which means it's cheaper to check for the absence of curses.
// than to check the subject list for the global curse subject.
if (s_cursedSubjects.length() == 0) {
return false;
}
return s_cursedSubjects.contains(GLOBAL_CURSE_SUBJECT);
}

/// @inheritdoc IRMN
function isCursed(
bytes16 subject
) external view override returns (bool) {
// There are zero curses under normal circumstances, which means it's cheaper to check for the absence of curses.
// than to check the subject list twice, as we have to check for both the given and global curse subjects.
if (s_cursedSubjects.length() == 0) {
return false;
}
return s_cursedSubjects.contains(subject) || s_cursedSubjects.contains(GLOBAL_CURSE_SUBJECT);
}
}
Loading
Loading