Skip to content

Commit

Permalink
add legacy fallback to RMNRemote (#15422)
Browse files Browse the repository at this point in the history
* add legacy fallback to RMNRemote

* changeset

* [Bot] Update changeset file with jira issues

* allow address 0

* fix solhint

---------

Co-authored-by: app-token-issuer-infra-releng[bot] <120227048+app-token-issuer-infra-releng[bot]@users.noreply.github.com>
  • Loading branch information
1 parent cb194d7 commit 3cecd5f
Show file tree
Hide file tree
Showing 9 changed files with 132 additions and 36 deletions.
10 changes: 10 additions & 0 deletions contracts/.changeset/bright-jokes-kiss.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@chainlink/contracts': patch
---

add legacy fallback to RMN


PR issue: CCIP-4261

Solidity Review issue: CCIP-3966
32 changes: 16 additions & 16 deletions contracts/gas-snapshots/ccip.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -573,27 +573,27 @@ RMNHome_validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_Dupli
RMNHome_validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_NotEnoughObservers_reverts() (gas: 21405)
RMNHome_validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_OutOfBoundsNodesLength_reverts() (gas: 137318)
RMNHome_validateStaticAndDynamicConfig:test_validateStaticAndDynamicConfig_OutOfBoundsObserverNodeIndex_reverts() (gas: 20522)
RMNRemote_constructor:test_constructor_success() (gas: 8334)
RMNRemote_constructor:test_constructor_zeroChainSelector_reverts() (gas: 59184)
RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154479)
RMNRemote_curse:test_curse_calledByNonOwner_reverts() (gas: 18712)
RMNRemote_curse:test_curse_success() (gas: 149431)
RMNRemote_global_and_legacy_curses:test_global_and_legacy_curses_success() (gas: 133512)
RMNRemote_constructor:test_constructor() (gas: 8398)
RMNRemote_curse:test_curse_AlreadyCursed_duplicateSubject_reverts() (gas: 154501)
RMNRemote_curse:test_curse_calledByNonOwner_reverts() (gas: 18734)
RMNRemote_curse:test_curse_success() (gas: 149475)
RMNRemote_global_and_legacy_curses:test_global_and_legacy_curses_success() (gas: 133441)
RMNRemote_isBlessed:test_isBlessed() (gas: 17588)
RMNRemote_setConfig:test_setConfig_ZeroValueNotAllowed_revert() (gas: 37971)
RMNRemote_setConfig:test_setConfig_addSigner_removeSigner_success() (gas: 993448)
RMNRemote_setConfig:test_setConfig_duplicateOnChainPublicKey_reverts() (gas: 323540)
RMNRemote_setConfig:test_setConfig_invalidSignerOrder_reverts() (gas: 80201)
RMNRemote_setConfig:test_setConfig_notEnoughSigners_reverts() (gas: 54232)
RMNRemote_uncurse:test_uncurse_NotCursed_duplicatedUncurseSubject_reverts() (gas: 51993)
RMNRemote_uncurse:test_uncurse_calledByNonOwner_reverts() (gas: 18682)
RMNRemote_uncurse:test_uncurse_success() (gas: 40171)
RMNRemote_verify_withConfigNotSet:test_verify_reverts() (gas: 13578)
RMNRemote_verify_withConfigSet:test_verify_InvalidSignature_reverts() (gas: 96449)
RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_duplicateSignature_reverts() (gas: 94267)
RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_not_sorted_reverts() (gas: 101330)
RMNRemote_verify_withConfigSet:test_verify_ThresholdNotMet_reverts() (gas: 304634)
RMNRemote_verify_withConfigSet:test_verify_UnexpectedSigner_reverts() (gas: 428126)
RMNRemote_verify_withConfigSet:test_verify_success() (gas: 86159)
RMNRemote_uncurse:test_uncurse_NotCursed_duplicatedUncurseSubject_reverts() (gas: 51940)
RMNRemote_uncurse:test_uncurse_calledByNonOwner_reverts() (gas: 18615)
RMNRemote_uncurse:test_uncurse_success() (gas: 40135)
RMNRemote_verify_withConfigNotSet:test_verify_reverts() (gas: 13600)
RMNRemote_verify_withConfigSet:test_verify_InvalidSignature_reverts() (gas: 96471)
RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_duplicateSignature_reverts() (gas: 94289)
RMNRemote_verify_withConfigSet:test_verify_OutOfOrderSignatures_not_sorted_reverts() (gas: 101352)
RMNRemote_verify_withConfigSet:test_verify_ThresholdNotMet_reverts() (gas: 304744)
RMNRemote_verify_withConfigSet:test_verify_UnexpectedSigner_reverts() (gas: 428284)
RMNRemote_verify_withConfigSet:test_verify_success() (gas: 86181)
RateLimiter_constructor:test_Constructor_Success() (gas: 19806)
RateLimiter_consume:test_AggregateValueMaxCapacityExceeded_Revert() (gas: 16042)
RateLimiter_consume:test_AggregateValueRateLimitReached_Revert() (gas: 22435)
Expand Down
34 changes: 28 additions & 6 deletions contracts/src/v0.8/ccip/rmn/RMNRemote.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.24;

import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {IRMN} from "../interfaces/IRMN.sol";
import {IRMNRemote} from "../interfaces/IRMNRemote.sol";

import {Ownable2StepMsgSender} from "../../shared/access/Ownable2StepMsgSender.sol";
Expand All @@ -19,7 +20,10 @@ bytes16 constant LEGACY_CURSE_SUBJECT = 0x01000000000000000000000000000000;
bytes16 constant GLOBAL_CURSE_SUBJECT = 0x01000000000000000000000000000001;

/// @notice This contract supports verification of RMN reports for any Any2EVM OffRamp.
contract RMNRemote is Ownable2StepMsgSender, ITypeAndVersion, IRMNRemote {
/// @dev This contract implements both the new IRMNRemote interface and the legacy IRMN interface. This is to allow for
/// a seamless migration from the legacy RMN contract to this one. The only function that has been dropped in the newer
/// interface is `isBlessed`. For the `isBlessed` function, this contract relays the call to the legacy RMN contract.
contract RMNRemote is Ownable2StepMsgSender, ITypeAndVersion, IRMNRemote, IRMN {
using EnumerableSet for EnumerableSet.Bytes16Set;

error AlreadyCursed(bytes16 subject);
Expand All @@ -33,6 +37,7 @@ contract RMNRemote is Ownable2StepMsgSender, ITypeAndVersion, IRMNRemote {
error ThresholdNotMet();
error UnexpectedSigner();
error ZeroValueNotAllowed();
error IsBlessedNotAvailable();

event ConfigSet(uint32 indexed version, Config config);
event Cursed(bytes16[] subjects);
Expand Down Expand Up @@ -67,6 +72,7 @@ contract RMNRemote is Ownable2StepMsgSender, ITypeAndVersion, IRMNRemote {

string public constant override typeAndVersion = "RMNRemote 1.6.0-dev";
uint64 internal immutable i_localChainSelector;
IRMN internal immutable i_legacyRMN;

Config private s_config;
uint32 private s_configCount;
Expand All @@ -80,11 +86,11 @@ contract RMNRemote is Ownable2StepMsgSender, ITypeAndVersion, IRMNRemote {
mapping(address signer => bool exists) private s_signers; // for more gas efficient verify.

/// @param localChainSelector the chain selector of the chain this contract is deployed to.
constructor(
uint64 localChainSelector
) {
constructor(uint64 localChainSelector, IRMN legacyRMN) {
if (localChainSelector == 0) revert ZeroValueNotAllowed();
i_localChainSelector = localChainSelector;

i_legacyRMN = legacyRMN;
}

// ================================================================
Expand Down Expand Up @@ -248,7 +254,7 @@ contract RMNRemote is Ownable2StepMsgSender, ITypeAndVersion, IRMNRemote {
}

/// @inheritdoc IRMNRemote
function isCursed() external view returns (bool) {
function isCursed() external view override(IRMN, IRMNRemote) 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 legacy and global curse subjects.
if (s_cursedSubjects.length() == 0) {
Expand All @@ -260,12 +266,28 @@ contract RMNRemote is Ownable2StepMsgSender, ITypeAndVersion, IRMNRemote {
/// @inheritdoc IRMNRemote
function isCursed(
bytes16 subject
) external view returns (bool) {
) external view override(IRMN, IRMNRemote) 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);
}

// ================================================================
// │ Legacy pass through │
// ================================================================

/// @inheritdoc IRMN
/// @dev This function is only expected to be used for messages from CCIP versions below 1.6.
function isBlessed(
TaggedRoot calldata taggedRoot
) external view returns (bool) {
if (i_legacyRMN == IRMN(address(0))) {
revert IsBlessedNotAvailable();
}

return i_legacyRMN.isBlessed(taggedRoot);
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {RMNRemote} from "../../../rmn/RMNRemote.sol";
import {RMNRemoteSetup} from "./RMNRemoteSetup.t.sol";

contract RMNRemote_constructor is RMNRemoteSetup {
function test_constructor_success() public view {
function test_constructor() public view {
assertEq(s_rmnRemote.getLocalChainSelector(), 1);
}

function test_constructor_zeroChainSelector_reverts() public {
vm.expectRevert(RMNRemote.ZeroValueNotAllowed.selector);
new RMNRemote(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {IRMN} from "../../../interfaces/IRMN.sol";

import {RMNRemote} from "../../../rmn/RMNRemote.sol";
import {RMNRemoteSetup} from "./RMNRemoteSetup.t.sol";

contract RMNRemote_isBlessed is RMNRemoteSetup {
function test_isBlessed() public {
IRMN.TaggedRoot memory taggedRoot = IRMN.TaggedRoot({root: keccak256("root"), commitStore: makeAddr("commitStore")});

vm.mockCall(
address(s_legacyRMN), abi.encodeWithSelector(s_legacyRMN.isBlessed.selector, taggedRoot), abi.encode(true)
);

assertTrue(s_rmnRemote.isBlessed(taggedRoot));

vm.mockCall(
address(s_legacyRMN), abi.encodeWithSelector(s_legacyRMN.isBlessed.selector, taggedRoot), abi.encode(false)
);

assertFalse(s_rmnRemote.isBlessed(taggedRoot));
}

function test_isBlessed_RevertWhen_IsBlessedNotAvailable() public {
IRMN.TaggedRoot memory taggedRoot = IRMN.TaggedRoot({root: keccak256("root"), commitStore: makeAddr("commitStore")});

s_rmnRemote = new RMNRemote(100, IRMN(address(0)));

vm.expectRevert(RMNRemote.IsBlessedNotAvailable.selector);
s_rmnRemote.isBlessed(taggedRoot);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;

import {IRMN} from "../../../interfaces/IRMN.sol";
import {IRMNRemote} from "../../../interfaces/IRMNRemote.sol";

import {Internal} from "../../../libraries/Internal.sol";
import {RMNRemote} from "../../../rmn/RMNRemote.sol";
import {BaseTest} from "../../BaseTest.t.sol";
Expand All @@ -21,9 +23,11 @@ contract RMNRemoteSetup is BaseTest {
bytes16 internal constant CURSE_SUBJ_2 = bytes16(keccak256("subject 2"));
bytes16[] internal s_curseSubjects;

IRMN internal s_legacyRMN = IRMN(makeAddr("legacyRMN"));

function setUp() public virtual override {
super.setUp();
s_rmnRemote = new RMNRemote(1);
s_rmnRemote = new RMNRemote(1, s_legacyRMN);
OFF_RAMP_ADDRESS = makeAddr("OFF RAMP");
s_curseSubjects = [CURSE_SUBJ_1, CURSE_SUBJ_2];

Expand Down
37 changes: 33 additions & 4 deletions core/gethwrappers/ccip/generated/rmn_remote/rmn_remote.go

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ registry_module_owner_custom: ../../../contracts/solc/v0.8.24/RegistryModuleOwne
report_codec: ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.abi ../../../contracts/solc/v0.8.24/ReportCodec/ReportCodec.bin 6c943b39f003aa67c3cefa19a8ff99e846236a058e1ceae77569c3a065ffd5c7
rmn_home: ../../../contracts/solc/v0.8.24/RMNHome/RMNHome.abi ../../../contracts/solc/v0.8.24/RMNHome/RMNHome.bin 84ca84b3d0c00949905a3d10a91255f877cf32b2a0d7f7f7ce3121ced34a8cb7
rmn_proxy_contract: ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.abi ../../../contracts/solc/v0.8.24/ARMProxy/ARMProxy.bin b048d8e752e3c41113ebb305c1efa06737ad36b4907b93e627fb0a3113023454
rmn_remote: ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.abi ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.bin faee0b0cdbe67f2e28deccf12acd4df13dd90992f6cbc0ba17bab845b8f4eb1c
rmn_remote: ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.abi ../../../contracts/solc/v0.8.24/RMNRemote/RMNRemote.bin 941118dfdc6bb042c339cfe8d8e0c7a0b486afb731a785d58a64994e7a13c459
router: ../../../contracts/solc/v0.8.24/Router/Router.abi ../../../contracts/solc/v0.8.24/Router/Router.bin 2e4f0a7826c8abb49d882bb49fc5ff20a186dbd3137624b9097ffed903ae4888
token_admin_registry: ../../../contracts/solc/v0.8.24/TokenAdminRegistry/TokenAdminRegistry.abi ../../../contracts/solc/v0.8.24/TokenAdminRegistry/TokenAdminRegistry.bin 397bc7be08c2848c0f4715f90b16206d6367f78ffb7cd48e2b1dfc0ccc5aea26
token_pool: ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.abi ../../../contracts/solc/v0.8.24/TokenPool/TokenPool.bin da86a1407f31134e7246bde63c80ce8c78ce7d7b44e267f3c1f6030441ff4252
Expand Down
5 changes: 4 additions & 1 deletion deployment/ccip/changeset/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"
"golang.org/x/sync/errgroup"

cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3"

"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/internal"

"github.com/smartcontractkit/chainlink/deployment"
Expand Down Expand Up @@ -599,6 +600,8 @@ func deployChainContracts(
chain.DeployerKey,
chain.Client,
chain.Selector,
// Indicates no legacy RMN contract
common.HexToAddress("0x0"),
)
return deployment.ContractDeploy[*rmn_remote.RMNRemote]{
rmnRemoteAddr, rmnRemote, tx, deployment.NewTypeAndVersion(RMNRemote, deployment.Version1_6_0_dev), err2,
Expand Down

0 comments on commit 3cecd5f

Please sign in to comment.